Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(568)

Side by Side Diff: Source/core/svg/UnsafeSVGAttributeSanitizationTest.cpp

Issue 1171223004: Sanitize SVG animation attributes which could set JavaScript URL values. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Thanks for feedback. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/svg/SVGAnimationElement.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "config.h"
6 // FIXME(dominicc): Poor confused check-webkit-style demands Attribute.h here.
7 #include "core/dom/Attribute.h"
8
9 #include "core/HTMLNames.h"
10 #include "core/SVGNames.h"
11 #include "core/XLinkNames.h"
12 #include "core/clipboard/Pasteboard.h"
13 #include "core/dom/QualifiedName.h"
14 #include "core/editing/Editor.h"
15 #include "core/editing/SelectionType.h"
16 #include "core/editing/VisibleSelection.h"
17 #include "core/html/HTMLElement.h"
18 #include "core/svg/SVGAElement.h"
19 #include "core/svg/SVGAnimateElement.h"
20 #include "core/svg/SVGDiscardElement.h"
21 #include "core/svg/SVGSetElement.h"
22 #include "core/svg/animation/SVGSMILElement.h"
23 #include "core/svg/properties/SVGPropertyInfo.h"
24 #include "core/testing/DummyPageHolder.h"
25 #include "platform/geometry/IntSize.h"
26 #include "platform/weborigin/KURL.h"
27 #include "wtf/Vector.h"
28 #include "wtf/text/AtomicString.h"
29 #include "wtf/text/WTFString.h"
30 #include <gtest/gtest.h>
31
32 // Test that SVG content with JavaScript URLs is sanitized by removing
33 // the URLs. This sanitization happens when the content is pasted or
34 // drag-dropped into an editable element.
35 //
36 // There are two vectors for JavaScript URLs in SVG content:
37 //
38 // 1. Attributes, for example xlink:href in an <svg:a> element.
39 // 2. Animations which set those attributes, for example
40 // <animate attributeName="xlink:href" values="javascript:...
41 //
42 // The following SVG elements, although related to animation, cannot
43 // set JavaScript URLs:
44 //
45 // - 'discard' can only remove elements, not set their attributes
46 // - 'animateMotion' does not use attribute name and produces floats
47 // - 'animateTransform' can only animate transform lists
48
49 namespace blink {
50
51 // Pastes htmlToPaste into the body of pageHolder's document, and
52 // returns the new content of the body.
53 String contentAfterPastingHTML(
54 DummyPageHolder* pageHolder,
55 const char* htmlToPaste)
56 {
57 LocalFrame& frame = pageHolder->frame();
58 HTMLElement* body = pageHolder->document().body();
59
60 // Make the body editable, and put the caret in it.
61 body->setAttribute(HTMLNames::contenteditableAttr, "true");
62 frame.selection().setSelection(VisibleSelection::selectionFromContentsOfNode (body));
63 EXPECT_EQ(CaretSelection, frame.selection().selectionType());
64 EXPECT_TRUE(frame.selection().isContentEditable()) <<
65 "We should be pasting into something editable.";
66
67 Pasteboard* pasteboard = Pasteboard::generalPasteboard();
68 pasteboard->writeHTML(htmlToPaste, blankURL(), "", false);
69 EXPECT_TRUE(frame.editor().executeCommand("Paste"));
70
71 return body->innerHTML();
72 }
73
74 // Integration tests.
75
76 TEST(
77 UnsafeSVGAttributeSanitizationTest,
78 pasteAnchor_javaScriptHrefIsStripped)
79 {
80 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1));
81 static const char unsafeContent[] =
82 "<svg xmlns='http://www.w3.org/2000/svg' "
83 " xmlns:xlink='http://www.w3.org/1999/xlink'"
84 " width='1cm' height='1cm'>"
85 " <a xlink:href='javascript:alert()'></a>"
86 "</svg>";
87 String sanitizedContent =
88 contentAfterPastingHTML(pageHolder.get(), unsafeContent);
89
90 EXPECT_TRUE(sanitizedContent.contains("</a>")) <<
91 "We should have pasted *something*; the document is: " <<
92 sanitizedContent.utf8().data();
93 EXPECT_FALSE(sanitizedContent.contains(":alert()")) <<
94 "The JavaScript URL is unsafe and should have been stripped; "
95 "instead: " <<
96 sanitizedContent.utf8().data();
97 }
98
99 TEST(
100 UnsafeSVGAttributeSanitizationTest,
101 pasteAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol)
102 {
103 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1));
104 static const char unsafeContent[] =
105 "<svg xmlns='http://www.w3.org/2000/svg' "
106 " xmlns:xlink='http://www.w3.org/1999/xlink'"
107 " width='1cm' height='1cm'>"
108 " <a xlink:href='j&#x41;vascriPT:alert()'></a>"
109 "</svg>";
110 String sanitizedContent =
111 contentAfterPastingHTML(pageHolder.get(), unsafeContent);
112
113 EXPECT_TRUE(sanitizedContent.contains("</a>")) <<
114 "We should have pasted *something*; the document is: " <<
115 sanitizedContent.utf8().data();
116 EXPECT_FALSE(sanitizedContent.contains(":alert()")) <<
117 "The JavaScript URL is unsafe and should have been stripped; "
118 "instead: " <<
119 sanitizedContent.utf8().data();
120 }
121
122 TEST(
123 UnsafeSVGAttributeSanitizationTest,
124 pasteAnchor_javaScriptHrefIsStripped_entityWithoutSemicolonInProtocol)
125 {
126 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1));
127 static const char unsafeContent[] =
128 "<svg xmlns='http://www.w3.org/2000/svg' "
129 " xmlns:xlink='http://www.w3.org/1999/xlink'"
130 " width='1cm' height='1cm'>"
131 " <a xlink:href='jav&#x61script:alert()'></a>"
132 "</svg>";
133 String sanitizedContent =
134 contentAfterPastingHTML(pageHolder.get(), unsafeContent);
135
136 EXPECT_TRUE(sanitizedContent.contains("</a>")) <<
137 "We should have pasted *something*; the document is: " <<
138 sanitizedContent.utf8().data();
139 EXPECT_FALSE(sanitizedContent.contains(":alert()")) <<
140 "The JavaScript URL is unsafe and should have been stripped; "
141 "instead: " <<
142 sanitizedContent.utf8().data();
143 }
144
145 // Other sanitization integration tests are layout tests that use
146 // document.execCommand('Copy') to source content that they later
147 // paste. However SVG animation elements are not serialized when
148 // copying, which means we can't test sanitizing these attributes in
149 // layout tests: there is nowhere to source the unsafe content from.
150 TEST(
151 UnsafeSVGAttributeSanitizationTest,
152 pasteAnimatedAnchor_javaScriptHrefIsStripped_caseAndEntityInProtocol)
153 {
154 OwnPtr<DummyPageHolder> pageHolder = DummyPageHolder::create(IntSize(1, 1));
155 static const char unsafeContent[] =
156 "<svg xmlns='http://www.w3.org/2000/svg' "
157 " xmlns:xlink='http://www.w3.org/1999/xlink'"
158 " width='1cm' height='1cm'>"
159 " <a xlink:href='https://www.google.com/'>"
160 " <animate xmlns:ng='http://www.w3.org/1999/xlink' "
161 " attributeName='ng:href' values='evil;J&#x61VaSCRIpT:alert( )'>"
162 " </a>"
163 "</svg>";
164 String sanitizedContent =
165 contentAfterPastingHTML(pageHolder.get(), unsafeContent);
166
167 EXPECT_TRUE(sanitizedContent.contains("<a xlink:href=\"https://www.goo")) <<
168 "We should have pasted *something*; the document is: " <<
169 sanitizedContent.utf8().data();
170 EXPECT_FALSE(sanitizedContent.contains(":alert()")) <<
171 "The JavaScript URL is unsafe and should have been stripped; "
172 "instead: " <<
173 sanitizedContent.utf8().data();
174 }
175
176 // Unit tests
177
178 // stripScriptingAttributes inspects animation attributes for
179 // javascript: URLs. This check could be defeated if strings supported
180 // addition. If this test starts failing you must strengthen
181 // Element::stripScriptingAttributes, perhaps to strip all
182 // SVG animation attributes.
183 TEST(UnsafeSVGAttributeSanitizationTest, stringsShouldNotSupportAddition)
184 {
185 RefPtrWillBeRawPtr<Document> document = Document::create();
186 RefPtrWillBeRawPtr<SVGElement> target = SVGAElement::create(*document);
187 RefPtrWillBeRawPtr<SVGAnimateElement> element = SVGAnimateElement::create(*d ocument);
188 element->setTargetElement(target.get());
189 element->setAttributeName(XLinkNames::hrefAttr);
190
191 // Sanity check that xlink:href was identified as a "string" attribute
192 EXPECT_EQ(AnimatedString, element->animatedPropertyType());
193
194 EXPECT_FALSE(element->animatedPropertyTypeSupportsAddition());
195 }
196
197 TEST(
198 UnsafeSVGAttributeSanitizationTest,
199 stripScriptingAttributes_animateElement)
200 {
201 Vector<Attribute> attributes;
202 attributes.append(Attribute(XLinkNames::hrefAttr, "javascript:alert()"));
203 attributes.append(Attribute(SVGNames::fromAttr, "/home"));
204 attributes.append(Attribute(SVGNames::toAttr, "javascript:own3d()"));
205
206 RefPtrWillBeRawPtr<Document> document = Document::create();
207 RefPtrWillBeRawPtr<Element> element = SVGAnimateElement::create(*document);
208 element->stripScriptingAttributes(attributes);
209
210 EXPECT_EQ(2ul, attributes.size()) <<
211 "One of the attributes should have been stripped.";
212 EXPECT_EQ(XLinkNames::hrefAttr, attributes[0].name()) <<
213 "The 'xlink:href' attribute should not have been stripped from "
214 "<animate> because it is not a URL attribute of <animate>.";
215 EXPECT_EQ(SVGNames::fromAttr, attributes[1].name()) <<
216 "The 'from' attribute should not have been strippef from <animate> "
217 "because its value is innocuous.";
218 }
219
220 TEST(
221 UnsafeSVGAttributeSanitizationTest,
222 isJavaScriptURLAttribute_xlinkHrefContainingJavascriptURL)
223 {
224 Attribute attribute(XLinkNames::hrefAttr, "javascript:alert()");
225 RefPtrWillBeRawPtr<Document> document = Document::create();
226 RefPtrWillBeRawPtr<Element> element = SVGAElement::create(*document);
227 EXPECT_TRUE(
228 element->isJavaScriptURLAttribute(attribute)) <<
229 "The 'a' element should identify an 'xlink:href' attribute with a "
230 "JavaScript URL value as a JavaScript URL attribute";
231 }
232
233 TEST(
234 UnsafeSVGAttributeSanitizationTest,
235 isJavaScriptURLAttribute_xlinkHrefContainingJavascriptURL_alternatePrefix)
236 {
237 QualifiedName hrefAlternatePrefix(
238 "foo", "href", XLinkNames::xlinkNamespaceURI);
239 Attribute evilAttribute(hrefAlternatePrefix, "javascript:alert()");
240 RefPtrWillBeRawPtr<Document> document = Document::create();
241 RefPtrWillBeRawPtr<Element> element = SVGAElement::create(*document);
242 EXPECT_TRUE(element->isJavaScriptURLAttribute(evilAttribute)) <<
243 "The XLink 'href' attribute with a JavaScript URL value should be "
244 "identified as a JavaScript URL attribute, even if the attribute "
245 "doesn't use the typical 'xlink' prefix.";
246 }
247
248 TEST(
249 UnsafeSVGAttributeSanitizationTest,
250 isSVGAnimationAttributeSettingJavaScriptURL_fromContainingJavaScriptURL)
251 {
252 Attribute evilAttribute(SVGNames::fromAttr, "javascript:alert()");
253 RefPtrWillBeRawPtr<Document> document = Document::create();
254 RefPtrWillBeRawPtr<Element> element = SVGAnimateElement::create(*document);
255 EXPECT_TRUE(
256 element->isSVGAnimationAttributeSettingJavaScriptURL(evilAttribute)) <<
257 "The animate element should identify a 'from' attribute with a "
258 "JavaScript URL value as setting a JavaScript URL.";
259 }
260
261 TEST(
262 UnsafeSVGAttributeSanitizationTest,
263 isSVGAnimationAttributeSettingJavaScriptURL_toContainingJavaScripURL)
264 {
265 Attribute evilAttribute(SVGNames::toAttr, "javascript:window.close()");
266 RefPtrWillBeRawPtr<Document> document = Document::create();
267 RefPtrWillBeRawPtr<Element> element = SVGSetElement::create(*document);
268 EXPECT_TRUE(
269 element->isSVGAnimationAttributeSettingJavaScriptURL(evilAttribute)) <<
270 "The set element should identify a 'to' attribute with a JavaScript "
271 "URL value as setting a JavaScript URL.";
272 }
273
274 TEST(
275 UnsafeSVGAttributeSanitizationTest,
276 isSVGAnimationAttributeSettingJavaScriptURL_valuesContainingJavaScriptURL)
277 {
278 Attribute evilAttribute(SVGNames::valuesAttr, "hi!; javascript:confirm()");
279 RefPtrWillBeRawPtr<Document> document = Document::create();
280 RefPtrWillBeRawPtr<Element> element = SVGAnimateElement::create(*document);
281 element = SVGAnimateElement::create(*document);
282 EXPECT_TRUE(
283 element->isSVGAnimationAttributeSettingJavaScriptURL(evilAttribute)) <<
284 "The animate element should identify a 'values' attribute with a "
285 "JavaScript URL value as setting a JavaScript URL.";
286 }
287
288 TEST(
289 UnsafeSVGAttributeSanitizationTest,
290 isSVGAnimationAttributeSettingJavaScriptURL_innocuousAnimationAttribute)
291 {
292 Attribute fineAttribute(SVGNames::fromAttr, "hello, world!");
293 RefPtrWillBeRawPtr<Document> document = Document::create();
294 RefPtrWillBeRawPtr<Element> element = SVGSetElement::create(*document);
295 EXPECT_FALSE(
296 element->isSVGAnimationAttributeSettingJavaScriptURL(fineAttribute)) <<
297 "The animate element should not identify a 'from' attribute with an "
298 "innocuous value as setting a JavaScript URL.";
299 }
300
301 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/svg/SVGAnimationElement.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698