| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // FIXME(dominicc): Poor confused check-webkit-style demands Attribute.h here. | 5 // FIXME(dominicc): Poor confused check-webkit-style demands Attribute.h here. |
| 6 #include "core/dom/Attribute.h" | 6 #include "core/dom/Attribute.h" |
| 7 | 7 |
| 8 #include "core/HTMLNames.h" | 8 #include "core/HTMLNames.h" |
| 9 #include "core/SVGNames.h" | 9 #include "core/SVGNames.h" |
| 10 #include "core/XLinkNames.h" | 10 #include "core/XLinkNames.h" |
| 11 #include "core/clipboard/Pasteboard.h" | 11 #include "core/clipboard/Pasteboard.h" |
| 12 #include "core/dom/QualifiedName.h" | 12 #include "core/dom/QualifiedName.h" |
| 13 #include "core/editing/Editor.h" | 13 #include "core/editing/Editor.h" |
| 14 #include "core/editing/SelectionType.h" | 14 #include "core/editing/SelectionType.h" |
| 15 #include "core/editing/VisibleSelection.h" | 15 #include "core/editing/VisibleSelection.h" |
| 16 #include "core/html/HTMLElement.h" | 16 #include "core/html/HTMLElement.h" |
| 17 #include "core/svg/SVGAElement.h" | 17 #include "core/svg/SVGAElement.h" |
| 18 #include "core/svg/SVGAnimateElement.h" | 18 #include "core/svg/SVGAnimateElement.h" |
| 19 #include "core/svg/SVGDiscardElement.h" | 19 #include "core/svg/SVGDiscardElement.h" |
| 20 #include "core/svg/SVGSetElement.h" | 20 #include "core/svg/SVGSetElement.h" |
| 21 #include "core/svg/animation/SVGSMILElement.h" | 21 #include "core/svg/animation/SVGSMILElement.h" |
| 22 #include "core/svg/properties/SVGPropertyInfo.h" | 22 #include "core/svg/properties/SVGPropertyInfo.h" |
| 23 #include "core/testing/DummyPageHolder.h" | 23 #include "core/testing/DummyPageHolder.h" |
| 24 #include "platform/geometry/IntSize.h" | 24 #include "platform/geometry/IntSize.h" |
| 25 #include "platform/weborigin/KURL.h" | 25 #include "platform/weborigin/KURL.h" |
| 26 #include "testing/gtest/include/gtest/gtest.h" | 26 #include "testing/gtest/include/gtest/gtest.h" |
| 27 #include "wtf/Vector.h" | 27 #include "wtf/Vector.h" |
| 28 #include "wtf/text/AtomicString.h" | 28 #include "wtf/text/AtomicString.h" |
| 29 #include "wtf/text/WTFString.h" | 29 #include "wtf/text/WTFString.h" |
| 30 #include <memory> | |
| 31 | 30 |
| 32 // Test that SVG content with JavaScript URLs is sanitized by removing | 31 // Test that SVG content with JavaScript URLs is sanitized by removing |
| 33 // the URLs. This sanitization happens when the content is pasted or | 32 // the URLs. This sanitization happens when the content is pasted or |
| 34 // drag-dropped into an editable element. | 33 // drag-dropped into an editable element. |
| 35 // | 34 // |
| 36 // There are two vectors for JavaScript URLs in SVG content: | 35 // There are two vectors for JavaScript URLs in SVG content: |
| 37 // | 36 // |
| 38 // 1. Attributes, for example xlink:href/href in an <svg:a> element. | 37 // 1. Attributes, for example xlink:href/href in an <svg:a> element. |
| 39 // 2. Animations which set those attributes, for example | 38 // 2. Animations which set those attributes, for example |
| 40 // <animate attributeName="xlink:href" values="javascript:... | 39 // <animate attributeName="xlink:href" values="javascript:... |
| (...skipping 29 matching lines...) Expand all Loading... |
| 70 | 69 |
| 71 return body->innerHTML(); | 70 return body->innerHTML(); |
| 72 } | 71 } |
| 73 | 72 |
| 74 // Integration tests. | 73 // Integration tests. |
| 75 | 74 |
| 76 TEST( | 75 TEST( |
| 77 UnsafeSVGAttributeSanitizationTest, | 76 UnsafeSVGAttributeSanitizationTest, |
| 78 pasteAnchor_javaScriptHrefIsStripped) | 77 pasteAnchor_javaScriptHrefIsStripped) |
| 79 { | 78 { |
| 80 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 79 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 81 static const char unsafeContent[] = | 80 static const char unsafeContent[] = |
| 82 "<svg xmlns='http://www.w3.org/2000/svg' " | 81 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 83 " width='1cm' height='1cm'>" | 82 " width='1cm' height='1cm'>" |
| 84 " <a href='javascript:alert()'></a>" | 83 " <a href='javascript:alert()'></a>" |
| 85 "</svg>"; | 84 "</svg>"; |
| 86 String sanitizedContent = | 85 String sanitizedContent = |
| 87 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 86 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 88 | 87 |
| 89 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 88 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 90 "We should have pasted *something*; the document is: " << | 89 "We should have pasted *something*; the document is: " << |
| 91 sanitizedContent.utf8().data(); | 90 sanitizedContent.utf8().data(); |
| 92 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 91 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 93 "The JavaScript URL is unsafe and should have been stripped; " | 92 "The JavaScript URL is unsafe and should have been stripped; " |
| 94 "instead: " << | 93 "instead: " << |
| 95 sanitizedContent.utf8().data(); | 94 sanitizedContent.utf8().data(); |
| 96 } | 95 } |
| 97 | 96 |
| 98 TEST( | 97 TEST( |
| 99 UnsafeSVGAttributeSanitizationTest, | 98 UnsafeSVGAttributeSanitizationTest, |
| 100 pasteAnchor_javaScriptXlinkHrefIsStripped) | 99 pasteAnchor_javaScriptXlinkHrefIsStripped) |
| 101 { | 100 { |
| 102 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 101 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 103 static const char unsafeContent[] = | 102 static const char unsafeContent[] = |
| 104 "<svg xmlns='http://www.w3.org/2000/svg' " | 103 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 105 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 104 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
| 106 " width='1cm' height='1cm'>" | 105 " width='1cm' height='1cm'>" |
| 107 " <a xlink:href='javascript:alert()'></a>" | 106 " <a xlink:href='javascript:alert()'></a>" |
| 108 "</svg>"; | 107 "</svg>"; |
| 109 String sanitizedContent = | 108 String sanitizedContent = |
| 110 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 109 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 111 | 110 |
| 112 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 111 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 113 "We should have pasted *something*; the document is: " << | 112 "We should have pasted *something*; the document is: " << |
| 114 sanitizedContent.utf8().data(); | 113 sanitizedContent.utf8().data(); |
| 115 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 114 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 116 "The JavaScript URL is unsafe and should have been stripped; " | 115 "The JavaScript URL is unsafe and should have been stripped; " |
| 117 "instead: " << | 116 "instead: " << |
| 118 sanitizedContent.utf8().data(); | 117 sanitizedContent.utf8().data(); |
| 119 } | 118 } |
| 120 | 119 |
| 121 TEST( | 120 TEST( |
| 122 UnsafeSVGAttributeSanitizationTest, | 121 UnsafeSVGAttributeSanitizationTest, |
| 123 pasteAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) | 122 pasteAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) |
| 124 { | 123 { |
| 125 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 124 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 126 static const char unsafeContent[] = | 125 static const char unsafeContent[] = |
| 127 "<svg xmlns='http://www.w3.org/2000/svg' " | 126 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 128 " width='1cm' height='1cm'>" | 127 " width='1cm' height='1cm'>" |
| 129 " <a href='jAvascriPT:alert()'></a>" | 128 " <a href='jAvascriPT:alert()'></a>" |
| 130 "</svg>"; | 129 "</svg>"; |
| 131 String sanitizedContent = | 130 String sanitizedContent = |
| 132 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 131 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 133 | 132 |
| 134 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 133 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 135 "We should have pasted *something*; the document is: " << | 134 "We should have pasted *something*; the document is: " << |
| 136 sanitizedContent.utf8().data(); | 135 sanitizedContent.utf8().data(); |
| 137 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 136 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 138 "The JavaScript URL is unsafe and should have been stripped; " | 137 "The JavaScript URL is unsafe and should have been stripped; " |
| 139 "instead: " << | 138 "instead: " << |
| 140 sanitizedContent.utf8().data(); | 139 sanitizedContent.utf8().data(); |
| 141 } | 140 } |
| 142 | 141 |
| 143 TEST( | 142 TEST( |
| 144 UnsafeSVGAttributeSanitizationTest, | 143 UnsafeSVGAttributeSanitizationTest, |
| 145 pasteAnchor_javaScriptXlinkHrefIsStripped_caseAndEntityInProtocol) | 144 pasteAnchor_javaScriptXlinkHrefIsStripped_caseAndEntityInProtocol) |
| 146 { | 145 { |
| 147 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 146 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 148 static const char unsafeContent[] = | 147 static const char unsafeContent[] = |
| 149 "<svg xmlns='http://www.w3.org/2000/svg' " | 148 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 150 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 149 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
| 151 " width='1cm' height='1cm'>" | 150 " width='1cm' height='1cm'>" |
| 152 " <a xlink:href='jAvascriPT:alert()'></a>" | 151 " <a xlink:href='jAvascriPT:alert()'></a>" |
| 153 "</svg>"; | 152 "</svg>"; |
| 154 String sanitizedContent = | 153 String sanitizedContent = |
| 155 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 154 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 156 | 155 |
| 157 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 156 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 158 "We should have pasted *something*; the document is: " << | 157 "We should have pasted *something*; the document is: " << |
| 159 sanitizedContent.utf8().data(); | 158 sanitizedContent.utf8().data(); |
| 160 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 159 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 161 "The JavaScript URL is unsafe and should have been stripped; " | 160 "The JavaScript URL is unsafe and should have been stripped; " |
| 162 "instead: " << | 161 "instead: " << |
| 163 sanitizedContent.utf8().data(); | 162 sanitizedContent.utf8().data(); |
| 164 } | 163 } |
| 165 | 164 |
| 166 TEST( | 165 TEST( |
| 167 UnsafeSVGAttributeSanitizationTest, | 166 UnsafeSVGAttributeSanitizationTest, |
| 168 pasteAnchor_javaScriptHrefIsStripped_entityWithoutSemicolonInProtocol) | 167 pasteAnchor_javaScriptHrefIsStripped_entityWithoutSemicolonInProtocol) |
| 169 { | 168 { |
| 170 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 169 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 171 static const char unsafeContent[] = | 170 static const char unsafeContent[] = |
| 172 "<svg xmlns='http://www.w3.org/2000/svg' " | 171 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 173 " width='1cm' height='1cm'>" | 172 " width='1cm' height='1cm'>" |
| 174 " <a href='javascript:alert()'></a>" | 173 " <a href='javascript:alert()'></a>" |
| 175 "</svg>"; | 174 "</svg>"; |
| 176 String sanitizedContent = | 175 String sanitizedContent = |
| 177 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 176 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 178 | 177 |
| 179 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 178 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 180 "We should have pasted *something*; the document is: " << | 179 "We should have pasted *something*; the document is: " << |
| 181 sanitizedContent.utf8().data(); | 180 sanitizedContent.utf8().data(); |
| 182 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 181 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 183 "The JavaScript URL is unsafe and should have been stripped; " | 182 "The JavaScript URL is unsafe and should have been stripped; " |
| 184 "instead: " << | 183 "instead: " << |
| 185 sanitizedContent.utf8().data(); | 184 sanitizedContent.utf8().data(); |
| 186 } | 185 } |
| 187 | 186 |
| 188 TEST( | 187 TEST( |
| 189 UnsafeSVGAttributeSanitizationTest, | 188 UnsafeSVGAttributeSanitizationTest, |
| 190 pasteAnchor_javaScriptXlinkHrefIsStripped_entityWithoutSemicolonInProtocol) | 189 pasteAnchor_javaScriptXlinkHrefIsStripped_entityWithoutSemicolonInProtocol) |
| 191 { | 190 { |
| 192 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 191 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 193 static const char unsafeContent[] = | 192 static const char unsafeContent[] = |
| 194 "<svg xmlns='http://www.w3.org/2000/svg' " | 193 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 195 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 194 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
| 196 " width='1cm' height='1cm'>" | 195 " width='1cm' height='1cm'>" |
| 197 " <a xlink:href='javascript:alert()'></a>" | 196 " <a xlink:href='javascript:alert()'></a>" |
| 198 "</svg>"; | 197 "</svg>"; |
| 199 String sanitizedContent = | 198 String sanitizedContent = |
| 200 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 199 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 201 | 200 |
| 202 EXPECT_TRUE(sanitizedContent.contains("</a>")) << | 201 EXPECT_TRUE(sanitizedContent.contains("</a>")) << |
| 203 "We should have pasted *something*; the document is: " << | 202 "We should have pasted *something*; the document is: " << |
| 204 sanitizedContent.utf8().data(); | 203 sanitizedContent.utf8().data(); |
| 205 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 204 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 206 "The JavaScript URL is unsafe and should have been stripped; " | 205 "The JavaScript URL is unsafe and should have been stripped; " |
| 207 "instead: " << | 206 "instead: " << |
| 208 sanitizedContent.utf8().data(); | 207 sanitizedContent.utf8().data(); |
| 209 } | 208 } |
| 210 | 209 |
| 211 // Other sanitization integration tests are layout tests that use | 210 // Other sanitization integration tests are layout tests that use |
| 212 // document.execCommand('Copy') to source content that they later | 211 // document.execCommand('Copy') to source content that they later |
| 213 // paste. However SVG animation elements are not serialized when | 212 // paste. However SVG animation elements are not serialized when |
| 214 // copying, which means we can't test sanitizing these attributes in | 213 // copying, which means we can't test sanitizing these attributes in |
| 215 // layout tests: there is nowhere to source the unsafe content from. | 214 // layout tests: there is nowhere to source the unsafe content from. |
| 216 TEST( | 215 TEST( |
| 217 UnsafeSVGAttributeSanitizationTest, | 216 UnsafeSVGAttributeSanitizationTest, |
| 218 pasteAnimatedAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) | 217 pasteAnimatedAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol) |
| 219 { | 218 { |
| 220 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 219 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 221 static const char unsafeContent[] = | 220 static const char unsafeContent[] = |
| 222 "<svg xmlns='http://www.w3.org/2000/svg' " | 221 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 223 " width='1cm' height='1cm'>" | 222 " width='1cm' height='1cm'>" |
| 224 " <a href='https://www.google.com/'>" | 223 " <a href='https://www.google.com/'>" |
| 225 " <animate attributeName='href' values='evil;JaVaSCRIpT:alert()'>
" | 224 " <animate attributeName='href' values='evil;JaVaSCRIpT:alert()'>
" |
| 226 " </a>" | 225 " </a>" |
| 227 "</svg>"; | 226 "</svg>"; |
| 228 String sanitizedContent = | 227 String sanitizedContent = |
| 229 contentAfterPastingHTML(pageHolder.get(), unsafeContent); | 228 contentAfterPastingHTML(pageHolder.get(), unsafeContent); |
| 230 | 229 |
| 231 EXPECT_TRUE(sanitizedContent.contains("<a href=\"https://www.goo")) << | 230 EXPECT_TRUE(sanitizedContent.contains("<a href=\"https://www.goo")) << |
| 232 "We should have pasted *something*; the document is: " << | 231 "We should have pasted *something*; the document is: " << |
| 233 sanitizedContent.utf8().data(); | 232 sanitizedContent.utf8().data(); |
| 234 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << | 233 EXPECT_FALSE(sanitizedContent.contains(":alert()")) << |
| 235 "The JavaScript URL is unsafe and should have been stripped; " | 234 "The JavaScript URL is unsafe and should have been stripped; " |
| 236 "instead: " << | 235 "instead: " << |
| 237 sanitizedContent.utf8().data(); | 236 sanitizedContent.utf8().data(); |
| 238 } | 237 } |
| 239 | 238 |
| 240 TEST( | 239 TEST( |
| 241 UnsafeSVGAttributeSanitizationTest, | 240 UnsafeSVGAttributeSanitizationTest, |
| 242 pasteAnimatedAnchor_javaScriptXlinkHrefIsStripped_caseAndEntityInProtocol) | 241 pasteAnimatedAnchor_javaScriptXlinkHrefIsStripped_caseAndEntityInProtocol) |
| 243 { | 242 { |
| 244 std::unique_ptr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSiz
e(1, 1)); | 243 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1)); |
| 245 static const char unsafeContent[] = | 244 static const char unsafeContent[] = |
| 246 "<svg xmlns='http://www.w3.org/2000/svg' " | 245 "<svg xmlns='http://www.w3.org/2000/svg' " |
| 247 " xmlns:xlink='http://www.w3.org/1999/xlink'" | 246 " xmlns:xlink='http://www.w3.org/1999/xlink'" |
| 248 " width='1cm' height='1cm'>" | 247 " width='1cm' height='1cm'>" |
| 249 " <a xlink:href='https://www.google.com/'>" | 248 " <a xlink:href='https://www.google.com/'>" |
| 250 " <animate xmlns:ng='http://www.w3.org/1999/xlink' " | 249 " <animate xmlns:ng='http://www.w3.org/1999/xlink' " |
| 251 " attributeName='ng:href' values='evil;JaVaSCRIpT:alert(
)'>" | 250 " attributeName='ng:href' values='evil;JaVaSCRIpT:alert(
)'>" |
| 252 " </a>" | 251 " </a>" |
| 253 "</svg>"; | 252 "</svg>"; |
| 254 String sanitizedContent = | 253 String sanitizedContent = |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 406 Attribute fineAttribute(SVGNames::fromAttr, "hello, world!"); | 405 Attribute fineAttribute(SVGNames::fromAttr, "hello, world!"); |
| 407 Document* document = Document::create(); | 406 Document* document = Document::create(); |
| 408 Element* element = SVGSetElement::create(*document); | 407 Element* element = SVGSetElement::create(*document); |
| 409 EXPECT_FALSE( | 408 EXPECT_FALSE( |
| 410 element->isSVGAnimationAttributeSettingJavaScriptURL(fineAttribute)) << | 409 element->isSVGAnimationAttributeSettingJavaScriptURL(fineAttribute)) << |
| 411 "The animate element should not identify a 'from' attribute with an " | 410 "The animate element should not identify a 'from' attribute with an " |
| 412 "innocuous value as setting a JavaScript URL."; | 411 "innocuous value as setting a JavaScript URL."; |
| 413 } | 412 } |
| 414 | 413 |
| 415 } // namespace blink | 414 } // namespace blink |
| OLD | NEW |