OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// This tests HTML validation and sanitization, which is very important | 5 /// This tests HTML validation and sanitization, which is very important |
6 /// for prevent XSS or other attacks. If you suppress this, or parts of it | 6 /// for prevent XSS or other attacks. If you suppress this, or parts of it |
7 /// please make it a critical bug and bring it to the attention of the | 7 /// please make it a critical bug and bring it to the attention of the |
8 /// dart:html maintainers. | 8 /// dart:html maintainers. |
9 library node_validator_test; | 9 library node_validator_test; |
10 | 10 |
11 import 'dart:html'; | 11 import 'dart:html'; |
12 import 'dart:svg' as svg; | 12 import 'dart:svg' as svg; |
13 import 'package:unittest/unittest.dart'; | 13 |
14 import 'package:unittest/html_individual_config.dart'; | 14 import 'package:expect/minitest.dart'; |
| 15 |
15 import 'utils.dart'; | 16 import 'utils.dart'; |
16 | 17 |
17 void validateHtml(String html, String reference, NodeValidator validator) { | 18 void validateHtml(String html, String reference, NodeValidator validator) { |
18 var a = document.body.createFragment(html, validator: validator); | 19 var a = document.body.createFragment(html, validator: validator); |
19 var b = document.body.createFragment(reference, | 20 var b = document.body.createFragment(reference, |
20 treeSanitizer: NodeTreeSanitizer.trusted); | 21 treeSanitizer: NodeTreeSanitizer.trusted); |
21 | 22 |
22 // Prevent a false pass when both the html and the reference both get entirely | 23 // Prevent a false pass when both the html and the reference both get entirely |
23 // deleted, which is technically a match, but unlikely to be what we meant. | 24 // deleted, which is technically a match, but unlikely to be what we meant. |
24 if (reference != '') { | 25 if (reference != '') { |
(...skipping 20 matching lines...) Expand all Loading... |
45 test(name, () { | 46 test(name, () { |
46 if (reference == null) { | 47 if (reference == null) { |
47 reference = html; | 48 reference = html; |
48 } | 49 } |
49 | 50 |
50 validateHtml(html, reference, validator); | 51 validateHtml(html, reference, validator); |
51 }); | 52 }); |
52 } | 53 } |
53 | 54 |
54 main() { | 55 main() { |
55 useHtmlIndividualConfiguration(); | |
56 | |
57 group('DOM_sanitization', () { | 56 group('DOM_sanitization', () { |
58 var validator = new NodeValidatorBuilder.common(); | 57 var validator = new NodeValidatorBuilder.common(); |
59 | 58 |
60 testHtml('allows simple constructs', | 59 testHtml('allows simple constructs', |
61 validator, | 60 validator, |
62 '<div class="baz">something</div>'); | 61 '<div class="baz">something</div>'); |
63 | 62 |
64 testHtml('blocks unknown attributes', | 63 testHtml('blocks unknown attributes', |
65 validator, | 64 validator, |
66 '<div foo="baz">something</div>', | 65 '<div foo="baz">something</div>', |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 test('sanitizes template contents', () { | 118 test('sanitizes template contents', () { |
120 if (!TemplateElement.supported) return; | 119 if (!TemplateElement.supported) return; |
121 | 120 |
122 var html = '<template>' | 121 var html = '<template>' |
123 '<div></div>' | 122 '<div></div>' |
124 '<script></script>' | 123 '<script></script>' |
125 '<img src="http://example.com/foo"/>' | 124 '<img src="http://example.com/foo"/>' |
126 '</template>'; | 125 '</template>'; |
127 | 126 |
128 var fragment = document.body.createFragment(html, validator: validator); | 127 var fragment = document.body.createFragment(html, validator: validator); |
129 var template = fragment.nodes.single; | 128 var template = fragment.nodes.single as TemplateElement; |
130 | 129 |
131 var expectedContent = document.body.createFragment( | 130 var expectedContent = document.body.createFragment( |
132 '<div></div>' | 131 '<div></div>' |
133 '<img/>'); | 132 '<img/>'); |
134 | 133 |
135 validateNodeTree(template.content, expectedContent); | 134 validateNodeTree(template.content, expectedContent); |
136 }); | 135 }); |
137 | 136 |
138 test("appendHtml is sanitized", () { | 137 test("appendHtml is sanitized", () { |
139 var html = '<body background="s"></body><div></div>'; | 138 var html = '<body background="s"></body><div></div>'; |
140 document.body.appendHtml('<div id="stuff"></div>'); | 139 document.body.appendHtml('<div id="stuff"></div>'); |
141 var stuff = document.querySelector("#stuff"); | 140 var stuff = document.querySelector("#stuff"); |
142 stuff.appendHtml(html); | 141 stuff.appendHtml(html); |
143 expect(stuff.childNodes.length, 1); | 142 expect(stuff.childNodes.length, 1); |
144 stuff.remove(); | 143 stuff.remove(); |
145 }); | 144 }); |
146 | 145 |
147 test("documentFragment.appendHtml is sanitized", () { | 146 test("documentFragment.appendHtml is sanitized", () { |
148 var html = '<div id="things></div>'; | 147 var html = '<div id="things></div>'; |
149 var fragment = new DocumentFragment.html(html); | 148 var fragment = new DocumentFragment.html(html); |
150 fragment.appendHtml('<div id="bad"><script></script></div>'); | 149 fragment.appendHtml('<div id="bad"><script></script></div>'); |
151 expect(fragment.childNodes.length, 1); | 150 expect(fragment.childNodes.length, 1); |
152 expect(fragment.childNodes[0].id, "bad"); | 151 var child = fragment.childNodes[0] as Element; |
153 expect(fragment.childNodes[0].childNodes.length, 0); | 152 expect(child.id, "bad"); |
| 153 expect(child.childNodes.length, 0); |
154 }); | 154 }); |
155 | 155 |
156 testHtml("sanitizes embed", | 156 testHtml("sanitizes embed", |
157 validator, | 157 validator, |
158 "<div><embed src='' type='application/x-shockwave-flash'></embed></div>", | 158 "<div><embed src='' type='application/x-shockwave-flash'></embed></div>", |
159 "<div></div>"); | 159 "<div></div>"); |
160 }); | 160 }); |
161 | 161 |
162 group('URI_sanitization', () { | 162 group('URI_sanitization', () { |
163 var recorder = new RecordingUriValidator(); | 163 var recorder = new RecordingUriValidator(); |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
493 | 493 |
494 group('svg', () { | 494 group('svg', () { |
495 test('parsing', () { | 495 test('parsing', () { |
496 var svgText = | 496 var svgText = |
497 '<svg xmlns="http://www.w3.org/2000/svg' | 497 '<svg xmlns="http://www.w3.org/2000/svg' |
498 'xmlns:xlink="http://www.w3.org/1999/xlink">' | 498 'xmlns:xlink="http://www.w3.org/1999/xlink">' |
499 '<image xlink:href="foo" data-foo="bar"/>' | 499 '<image xlink:href="foo" data-foo="bar"/>' |
500 '</svg>'; | 500 '</svg>'; |
501 | 501 |
502 var fragment = new DocumentFragment.svg(svgText); | 502 var fragment = new DocumentFragment.svg(svgText); |
503 var element = fragment.nodes.first; | 503 var element = fragment.nodes.first as Element; |
504 expect(element is svg.SvgSvgElement, isTrue); | 504 expect(element is svg.SvgSvgElement, isTrue); |
505 expect(element.children[0] is svg.ImageElement, isTrue); | 505 expect(element.children[0] is svg.ImageElement, isTrue); |
506 }); | 506 }); |
507 }); | 507 }); |
508 | 508 |
509 group('dom_clobbering', () { | 509 group('dom_clobbering', () { |
510 var validator = new NodeValidatorBuilder.common(); | 510 var validator = new NodeValidatorBuilder.common(); |
511 | 511 |
512 testHtml('DOM clobbering of attributes with single node', | 512 testHtml('DOM clobbering of attributes with single node', |
513 validator, | 513 validator, |
(...skipping 21 matching lines...) Expand all Loading... |
535 validator, | 535 validator, |
536 "<form><input name='children'><input name='children'>" | 536 "<form><input name='children'><input name='children'>" |
537 "<input id='children' name='lastChild'>" | 537 "<input id='children' name='lastChild'>" |
538 "<input id='bad' onmouseover='alert(1)'>", | 538 "<input id='bad' onmouseover='alert(1)'>", |
539 ""); | 539 ""); |
540 | 540 |
541 test('tagName makes containing form invalid', () { | 541 test('tagName makes containing form invalid', () { |
542 var fragment = document.body.createFragment( | 542 var fragment = document.body.createFragment( |
543 "<form onmouseover='alert(2)'><input name='tagName'>", | 543 "<form onmouseover='alert(2)'><input name='tagName'>", |
544 validator: validator); | 544 validator: validator); |
545 var form = fragment.lastChild; | 545 var form = fragment.lastChild as FormElement; |
546 // If the tagName was clobbered, the sanitizer should have removed | 546 // If the tagName was clobbered, the sanitizer should have removed |
547 // the whole thing and form is null. | 547 // the whole thing and form is null. |
548 // If the tagName was not clobbered, then there will be content, | 548 // If the tagName was not clobbered, then there will be content, |
549 // but the tagName should be the normal value. IE11 has started | 549 // but the tagName should be the normal value. IE11 has started |
550 // doing this. | 550 // doing this. |
551 if (form != null) { | 551 if (form != null) { |
552 expect(form.tagName, 'FORM'); | 552 expect(form.tagName, 'FORM'); |
553 } | 553 } |
554 }); | 554 }); |
555 | 555 |
556 test('tagName without mouseover', () { | 556 test('tagName without mouseover', () { |
557 var fragment = document.body.createFragment( | 557 var fragment = document.body.createFragment( |
558 "<form><input name='tagName'>", | 558 "<form><input name='tagName'>", |
559 validator: validator); | 559 validator: validator); |
560 var form = fragment.lastChild; | 560 var form = fragment.lastChild as FormElement; |
561 // If the tagName was clobbered, the sanitizer should have removed | 561 // If the tagName was clobbered, the sanitizer should have removed |
562 // the whole thing and form is null. | 562 // the whole thing and form is null. |
563 // If the tagName was not clobbered, then there will be content, | 563 // If the tagName was not clobbered, then there will be content, |
564 // but the tagName should be the normal value. | 564 // but the tagName should be the normal value. |
565 if (form != null) { | 565 if (form != null) { |
566 expect(form.tagName, 'FORM'); | 566 expect(form.tagName, 'FORM'); |
567 } | 567 } |
568 }); | 568 }); |
569 }); | 569 }); |
570 } | 570 } |
OLD | NEW |