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

Side by Side Diff: tools/dom/src/NodeValidatorBuilder.dart

Issue 16374007: First rev of Safe DOM (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fixing up recent test additions. Created 7 years, 4 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of dart.dom.html;
6
7
8 /**
9 * Class which helps construct standard node validation policies.
10 *
11 * By default this will not accept anything, but the 'allow*' functions can be
12 * used to expand what types of elements or attributes are allowed.
13 *
14 * All allow functions are additive- elements will be accepted if they are
15 * accepted by any specific rule.
16 *
17 * It is important to remember that sanitization is not just intended to prevent
18 * cross-site scripting attacks, but also to prevent information from being
19 * displayed in unexpected ways. For example something displaying basic
20 * formatted text may not expect `<video>` tags to appear. In this case an
21 * empty NodeValidatorBuilder with just [allowTextElements] might be
22 * appropriate.
23 */
24 class NodeValidatorBuilder implements NodeValidator {
25
26 final List<NodeValidator> _validators = <NodeValidator>[];
27
28 NodeValidatorBuilder() {
29 }
30
31 /**
32 * Creates a new NodeValidatorBuilder which accepts common constructs.
33 *
34 * By default this will accept HTML5 elements and attributes with the default
35 * [UriPolicy] and templating elements.
36 *
37 * Notable syntax which is filtered:
38 *
39 * * Only known-good HTML5 elements and attributes are allowed.
40 * * All URLs must be same-origin, use [allowNavigation] and [allowImages] to
41 * specify additional URI policies.
42 * * Inline-styles are not allowed.
43 * * Custom element tags are disallowed, use [allowCustomElement].
44 * * Custom tags extensions are disallowed, use [allowTagExtension].
45 * * SVG Elements are not allowed, use [allowSvg].
46 *
47 * For scenarios where the HTML should only contain formatted text
48 * [allowTextElements] is more appropriate.
49 *
50 * Use [allowSvg] to allow SVG elements.
51 */
52 factory NodeValidatorBuilder.common() {
Jennifer Messerly 2013/08/17 06:07:07 as a factory constructor, this is hard to use in a
blois 2013/08/19 22:02:09 Done. Though most extension should be via composit
53 return new NodeValidatorBuilder()
54 ..allowHtml5()
55 ..allowTemplating();
56 }
57
58 /**
59 * Allows navigation elements- Form and Anchor tags, along with common
60 * attributes.
61 *
62 * The UriPolicy can be used to restrict the locations the navigation elements
63 * are allowed to direct to. By default this will use the default [UriPolicy].
64 */
65 void allowNavigation([UriPolicy uriPolicy]) {
66 if (uriPolicy == null) {
67 uriPolicy = new UriPolicy();
68 }
69 add(new _SimpleNodeValidator.allowNavigation(uriPolicy));
70 }
71
72 /**
73 * Allows image elements.
74 *
75 * The UriPolicy can be used to restrict the locations the images may be
76 * loaded from. By default this will use the default [UriPolicy].
77 */
78 void allowImages([UriPolicy uriPolicy]) {
79 if (uriPolicy == null) {
80 uriPolicy = new UriPolicy();
81 }
82 add(new _SimpleNodeValidator.allowImages(uriPolicy));
83 }
84
85 /**
86 * Allow basic text elements.
87 *
88 * This allows a subset of HTML5 elements, specifically just these tags and
89 * no attributes.
90 *
91 * * B
92 * * BLOCKQUOTE
93 * * BR
94 * * EM
95 * * H1
96 * * H2
97 * * H3
98 * * H4
99 * * H5
100 * * H6
101 * * HR
102 * * I
103 * * LI
104 * * OL
105 * * P
106 * * SPAN
107 * * UL
108 */
109 void allowTextElements() {
110 add(new _SimpleNodeValidator.allowTextElements());
111 }
112
113 /**
114 * Allow common safe HTML5 elements and attributes.
115 *
116 * This list is based off of the Caja whitelists at:
117 * https://code.google.com/p/google-caja/wiki/CajaWhitelists.
118 *
119 * Common things which are not allowed are script elements, style attributes
120 * and any script handlers.
121 */
122 void allowHtml5({UriPolicy uriPolicy}) {
123 add(new _Html5NodeValidator(uriPolicy: uriPolicy));
124 }
125
126 /**
127 * Allow SVG elements and attributes except for known bad ones.
128 */
129 void allowSvg() {
130 add(new _SvgNodeValidator());
131 }
132
133 /**
134 * Allow custom elements with the specified tag name and specified attributes.
135 *
136 * This will allow the elements as custom tags (such as <x-foo></x-foo>),
137 * but will not allow tag extensions. Use [allowTagExtension] to allow
138 * tag extensions.
139 */
140 void allowCustomElement(String tagName,
141 {UriPolicy uriPolicy,
142 Iterable<String> attributes,
143 Iterable<String> uriAttributes}) {
144
145 var tagNameUpper = tagName.toUpperCase();
146 var attrs;
147 if (attributes != null) {
148 attrs =
149 attributes.map((name) => '$tagNameUpper::${name.toLowerCase()}');
150 }
151 var uriAttrs;
152 if (uriAttributes != null) {
153 uriAttrs =
154 uriAttributes.map((name) => '$tagNameUpper::${name.toLowerCase()}');
155 }
156 if (uriPolicy == null) {
157 uriPolicy = new UriPolicy();
158 }
159
160 add(new _CustomElementNodeValidator(
161 uriPolicy,
162 [tagNameUpper],
163 attrs,
164 uriAttrs,
165 false,
166 true));
167 }
168
169 /**
170 * Allow custom tag extensions with the specified type name and specified
171 * attributes.
172 *
173 * This will allow tag extensions (such as <div is="x-foo"></div>),
174 * but will not allow custom tags. Use [allowCustomElement] to allow
175 * custom tags.
176 */
177 void allowTagExtension(String tagName, String baseName,
178 {UriPolicy uriPolicy,
179 Iterable<String> attributes,
180 Iterable<String> uriAttributes}) {
181
182 var baseNameUpper = baseName.toUpperCase();
183 var tagNameUpper = tagName.toUpperCase();
184 var attrs;
185 if (attributes != null) {
186 attrs =
187 attributes.map((name) => '$baseNameUpper::${name.toLowerCase()}');
188 }
189 var uriAttrs;
190 if (uriAttributes != null) {
191 uriAttrs =
192 uriAttributes.map((name) => '$baseNameUpper::${name.toLowerCase()}');
193 }
194 if (uriPolicy == null) {
195 uriPolicy = new UriPolicy();
196 }
197
198 add(new _CustomElementNodeValidator(
199 uriPolicy,
200 [tagNameUpper, baseNameUpper],
201 attrs,
202 uriAttrs,
203 true,
204 false));
205 }
206
207 void allowElement(String tagName, {UriPolicy uriPolicy,
208 Iterable<String> attributes,
209 Iterable<String> uriAttributes}) {
210
211 allowCustomElement(tagName, uriPolicy: uriPolicy,
212 attributes: attributes,
213 uriAttributes: uriAttributes);
214 }
215
216 /**
217 * Allow templating elements (such as <template> and template-related
218 * attributes.
219 *
220 * This still requires other validators to allow regular attributes to be
221 * bound (such as [allowHtml5]).
222 */
223 void allowTemplating() {
224 add(new _TemplatingNodeValidator());
225 }
226
227 /**
228 * Add an additional validator to the current list of validators.
229 *
230 * Elements and attributes will be accepted if they are accepted by any
231 * validators.
232 */
233 void add(NodeValidator validator) {
234 _validators.add(validator);
235 }
236
237 bool allowsElement(Element element) {
238 return _validators.any((v) => v.allowsElement(element));
239 }
240
241 bool allowsAttribute(Element element, String attributeName, String value) {
242 return _validators.any(
243 (v) => v.allowsAttribute(element, attributeName, value));
244 }
245 }
246
247 class _SimpleNodeValidator implements NodeValidator {
248 final Set<String> allowedElements;
249 final Set<String> allowedAttributes;
250 final Set<String> allowedUriAttributes;
251 final UriPolicy uriPolicy;
252
253 factory _SimpleNodeValidator.allowNavigation(UriPolicy uriPolicy) {
254 return new _SimpleNodeValidator(uriPolicy,
255 allowedElements: [
256 'A',
257 'FORM'],
258 allowedAttributes: [
259 'A::accesskey',
260 'A::coords',
261 'A::hreflang',
262 'A::name',
263 'A::shape',
264 'A::tabindex',
265 'A::target',
266 'A::type',
267 'FORM::accept',
268 'FORM::autocomplete',
269 'FORM::enctype',
270 'FORM::method',
271 'FORM::name',
272 'FORM::novalidate',
273 'FORM::target',
274 ],
275 allowedUriAttributes: [
276 'A::href',
277 'FORM::action',
278 ]);
279 }
280
281 factory _SimpleNodeValidator.allowImages(UriPolicy uriPolicy) {
282 return new _SimpleNodeValidator(uriPolicy,
283 allowedElements: [
284 'IMG'
285 ],
286 allowedAttributes: [
287 'IMG::align',
288 'IMG::alt',
289 'IMG::border',
290 'IMG::height',
291 'IMG::hspace',
292 'IMG::ismap',
293 'IMG::name',
294 'IMG::usemap',
295 'IMG::vspace',
296 'IMG::width',
297 ],
298 allowedUriAttributes: [
299 'IMG::src',
300 ]);
301 }
302
303 factory _SimpleNodeValidator.allowTextElements() {
304 return new _SimpleNodeValidator(null,
305 allowedElements: [
306 'B',
307 'BLOCKQUOTE',
308 'BR',
309 'EM',
310 'H1',
311 'H2',
312 'H3',
313 'H4',
314 'H5',
315 'H6',
316 'HR',
317 'I',
318 'LI',
319 'OL',
320 'P',
321 'SPAN',
322 'UL',
323 ]);
324 }
325
326 /**
327 * Elements must be uppercased tag names. For example `'IMG'`.
328 * Attributes must be uppercased tag name followed by :: followed by
329 * lowercase attribute name. For example `'IMG:src'`.
330 */
331 _SimpleNodeValidator(this.uriPolicy,
332 {Iterable<String> allowedElements, Iterable<String> allowedAttributes,
333 Iterable<String> allowedUriAttributes}):
334 this.allowedElements = allowedElements != null ?
335 new Set.from(allowedElements) : new Set(),
336 this.allowedAttributes = allowedAttributes != null ?
337 new Set.from(allowedAttributes) : new Set(),
338 this.allowedUriAttributes = allowedUriAttributes != null ?
339 new Set.from(allowedUriAttributes) : new Set();
340
341 bool allowsElement(Element element) {
342 return allowedElements.contains(element.tagName);
343 }
344
345 bool allowsAttribute(Element element, String attributeName, String value) {
346 var tagName = element.tagName;
347 if (allowedUriAttributes.contains('$tagName::$attributeName')) {
348 return uriPolicy.allowsUri(value);
349 } else if (allowedUriAttributes.contains('*::$attributeName')) {
350 return uriPolicy.allowsUri(value);
351 } else if (allowedAttributes.contains('$tagName::$attributeName')) {
352 return true;
353 } else if (allowedAttributes.contains('*::$attributeName')) {
354 return true;
355 } else if (allowedAttributes.contains('$tagName::*')) {
356 return true;
357 } else if (allowedAttributes.contains('*::*')) {
358 return true;
359 }
360 return false;
361 }
362 }
363
364 class _CustomElementNodeValidator extends _SimpleNodeValidator {
365 final bool allowTypeExtension;
366 final bool allowCustomTag;
367
368 _CustomElementNodeValidator(UriPolicy uriPolicy,
369 Iterable<String> allowedElements,
370 Iterable<String> allowedAttributes,
371 Iterable<String> allowedUriAttributes,
372 bool allowTypeExtension,
373 bool allowCustomTag):
374
375 super(uriPolicy,
376 allowedElements: allowedElements,
377 allowedAttributes: allowedAttributes,
378 allowedUriAttributes: allowedUriAttributes),
379 this.allowTypeExtension = allowTypeExtension == true,
380 this.allowCustomTag = allowCustomTag == true;
381
382 bool allowsElement(Element element) {
383 if (allowTypeExtension) {
384 var isAttr = element.attributes['is'];
385 if (isAttr != null) {
386 return allowedElements.contains(isAttr.toUpperCase()) &&
387 allowedElements.contains(element.tagName);
388 }
389 }
390 return allowCustomTag && allowedElements.contains(element.tagName);
391 }
392
393 bool allowsAttribute(Element element, String attributeName, String value) {
394 if (allowsElement(element)) {
395 if (allowTypeExtension && attributeName == 'is' &&
396 allowedElements.contains(value.toUpperCase())) {
397 return true;
398 }
399 return super.allowsAttribute(element, attributeName, value);
400 }
401 return false;
402 }
403 }
404
405 class _TemplatingNodeValidator extends _SimpleNodeValidator {
406 static const _TEMPLATE_ATTRS =
407 const <String>['bind', 'if', 'ref', 'repeat', 'syntax'];
408
409 final Set<String> _templateAttrs;
410
411 _TemplatingNodeValidator():
412 super(null,
413 allowedElements: [
414 'TEMPLATE'
415 ],
416 allowedAttributes: _TEMPLATE_ATTRS.map((attr) => 'TEMPLATE::$attr')),
417 _templateAttrs = new Set<String>.from(_TEMPLATE_ATTRS) {
418 }
419
420 bool allowsAttribute(Element element, String attributeName, String value) {
421 if (super.allowsAttribute(element, attributeName, value)) {
422 return true;
423 }
424
425 if (attributeName == 'template' && value == "") {
426 return true;
427 }
428
429 if (element.attributes['template'] == "" ) {
430 return _templateAttrs.contains(attributeName);
431 }
432 return false;
433 }
434 }
435
436
437 class _SvgNodeValidator implements NodeValidator {
438 bool allowsElement(Element element) {
439 if (element is svg.ScriptElement) {
440 return false;
441 }
442 if (element is svg.SvgElement) {
443 return true;
444 }
445 return false;
446 }
447
448 bool allowsAttribute(Element element, String attributeName, String value) {
449 if (attributeName == 'is' || attributeName.startsWith('on')) {
450 return false;
451 }
452 return allowsElement(element);
453 }
454 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698