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