| 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 |