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

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: Created 7 years, 3 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 | « tools/dom/src/Html5NodeValidator.dart ('k') | tools/dom/src/Validators.dart » ('j') | 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 (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 NodeValidatorBuilder.common() {
53 allowHtml5();
54 allowTemplating();
55 }
56
57 /**
58 * Allows navigation elements- Form and Anchor tags, along with common
59 * attributes.
60 *
61 * The UriPolicy can be used to restrict the locations the navigation elements
62 * are allowed to direct to. By default this will use the default [UriPolicy].
63 */
64 void allowNavigation([UriPolicy uriPolicy]) {
65 if (uriPolicy == null) {
66 uriPolicy = new UriPolicy();
67 }
68 add(new _SimpleNodeValidator.allowNavigation(uriPolicy));
69 }
70
71 /**
72 * Allows image elements.
73 *
74 * The UriPolicy can be used to restrict the locations the images may be
75 * loaded from. By default this will use the default [UriPolicy].
76 */
77 void allowImages([UriPolicy uriPolicy]) {
78 if (uriPolicy == null) {
79 uriPolicy = new UriPolicy();
80 }
81 add(new _SimpleNodeValidator.allowImages(uriPolicy));
82 }
83
84 /**
85 * Allow basic text elements.
86 *
87 * This allows a subset of HTML5 elements, specifically just these tags and
88 * no attributes.
89 *
90 * * B
91 * * BLOCKQUOTE
92 * * BR
93 * * EM
94 * * H1
95 * * H2
96 * * H3
97 * * H4
98 * * H5
99 * * H6
100 * * HR
101 * * I
102 * * LI
103 * * OL
104 * * P
105 * * SPAN
106 * * UL
107 */
108 void allowTextElements() {
109 add(new _SimpleNodeValidator.allowTextElements());
110 }
111
112 /**
113 * Allow common safe HTML5 elements and attributes.
114 *
115 * This list is based off of the Caja whitelists at:
116 * https://code.google.com/p/google-caja/wiki/CajaWhitelists.
117 *
118 * Common things which are not allowed are script elements, style attributes
119 * and any script handlers.
120 */
121 void allowHtml5({UriPolicy uriPolicy}) {
122 add(new _Html5NodeValidator(uriPolicy: uriPolicy));
123 }
124
125 /**
126 * Allow SVG elements and attributes except for known bad ones.
127 */
128 void allowSvg() {
129 add(new _SvgNodeValidator());
130 }
131
132 /**
133 * Allow custom elements with the specified tag name and specified attributes.
134 *
135 * This will allow the elements as custom tags (such as <x-foo></x-foo>),
136 * but will not allow tag extensions. Use [allowTagExtension] to allow
137 * tag extensions.
138 */
139 void allowCustomElement(String tagName,
140 {UriPolicy uriPolicy,
141 Iterable<String> attributes,
142 Iterable<String> uriAttributes}) {
143
144 var tagNameUpper = tagName.toUpperCase();
145 var attrs;
146 if (attributes != null) {
147 attrs =
148 attributes.map((name) => '$tagNameUpper::${name.toLowerCase()}');
149 }
150 var uriAttrs;
151 if (uriAttributes != null) {
152 uriAttrs =
153 uriAttributes.map((name) => '$tagNameUpper::${name.toLowerCase()}');
154 }
155 if (uriPolicy == null) {
156 uriPolicy = new UriPolicy();
157 }
158
159 add(new _CustomElementNodeValidator(
160 uriPolicy,
161 [tagNameUpper],
162 attrs,
163 uriAttrs,
164 false,
165 true));
166 }
167
168 /**
169 * Allow custom tag extensions with the specified type name and specified
170 * attributes.
171 *
172 * This will allow tag extensions (such as <div is="x-foo"></div>),
173 * but will not allow custom tags. Use [allowCustomElement] to allow
174 * custom tags.
175 */
176 void allowTagExtension(String tagName, String baseName,
177 {UriPolicy uriPolicy,
178 Iterable<String> attributes,
179 Iterable<String> uriAttributes}) {
180
181 var baseNameUpper = baseName.toUpperCase();
182 var tagNameUpper = tagName.toUpperCase();
183 var attrs;
184 if (attributes != null) {
185 attrs =
186 attributes.map((name) => '$baseNameUpper::${name.toLowerCase()}');
187 }
188 var uriAttrs;
189 if (uriAttributes != null) {
190 uriAttrs =
191 uriAttributes.map((name) => '$baseNameUpper::${name.toLowerCase()}');
192 }
193 if (uriPolicy == null) {
194 uriPolicy = new UriPolicy();
195 }
196
197 add(new _CustomElementNodeValidator(
198 uriPolicy,
199 [tagNameUpper, baseNameUpper],
200 attrs,
201 uriAttrs,
202 true,
203 false));
204 }
205
206 void allowElement(String tagName, {UriPolicy uriPolicy,
207 Iterable<String> attributes,
208 Iterable<String> uriAttributes}) {
209
210 allowCustomElement(tagName, uriPolicy: uriPolicy,
211 attributes: attributes,
212 uriAttributes: uriAttributes);
213 }
214
215 /**
216 * Allow templating elements (such as <template> and template-related
217 * attributes.
218 *
219 * This still requires other validators to allow regular attributes to be
220 * bound (such as [allowHtml5]).
221 */
222 void allowTemplating() {
223 add(new _TemplatingNodeValidator());
224 }
225
226 /**
227 * Add an additional validator to the current list of validators.
228 *
229 * Elements and attributes will be accepted if they are accepted by any
230 * validators.
231 */
232 void add(NodeValidator validator) {
233 _validators.add(validator);
234 }
235
236 bool allowsElement(Element element) {
237 return _validators.any((v) => v.allowsElement(element));
238 }
239
240 bool allowsAttribute(Element element, String attributeName, String value) {
241 return _validators.any(
242 (v) => v.allowsAttribute(element, attributeName, value));
243 }
244 }
245
246 class _SimpleNodeValidator implements NodeValidator {
247 final Set<String> allowedElements;
248 final Set<String> allowedAttributes;
249 final Set<String> allowedUriAttributes;
250 final UriPolicy uriPolicy;
251
252 factory _SimpleNodeValidator.allowNavigation(UriPolicy uriPolicy) {
253 return new _SimpleNodeValidator(uriPolicy,
254 allowedElements: [
255 'A',
256 'FORM'],
257 allowedAttributes: [
258 'A::accesskey',
259 'A::coords',
260 'A::hreflang',
261 'A::name',
262 'A::shape',
263 'A::tabindex',
264 'A::target',
265 'A::type',
266 'FORM::accept',
267 'FORM::autocomplete',
268 'FORM::enctype',
269 'FORM::method',
270 'FORM::name',
271 'FORM::novalidate',
272 'FORM::target',
273 ],
274 allowedUriAttributes: [
275 'A::href',
276 'FORM::action',
277 ]);
278 }
279
280 factory _SimpleNodeValidator.allowImages(UriPolicy uriPolicy) {
281 return new _SimpleNodeValidator(uriPolicy,
282 allowedElements: [
283 'IMG'
284 ],
285 allowedAttributes: [
286 'IMG::align',
287 'IMG::alt',
288 'IMG::border',
289 'IMG::height',
290 'IMG::hspace',
291 'IMG::ismap',
292 'IMG::name',
293 'IMG::usemap',
294 'IMG::vspace',
295 'IMG::width',
296 ],
297 allowedUriAttributes: [
298 'IMG::src',
299 ]);
300 }
301
302 factory _SimpleNodeValidator.allowTextElements() {
303 return new _SimpleNodeValidator(null,
304 allowedElements: [
305 'B',
306 'BLOCKQUOTE',
307 'BR',
308 'EM',
309 'H1',
310 'H2',
311 'H3',
312 'H4',
313 'H5',
314 'H6',
315 'HR',
316 'I',
317 'LI',
318 'OL',
319 'P',
320 'SPAN',
321 'UL',
322 ]);
323 }
324
325 /**
326 * Elements must be uppercased tag names. For example `'IMG'`.
327 * Attributes must be uppercased tag name followed by :: followed by
328 * lowercase attribute name. For example `'IMG:src'`.
329 */
330 _SimpleNodeValidator(this.uriPolicy,
331 {Iterable<String> allowedElements, Iterable<String> allowedAttributes,
332 Iterable<String> allowedUriAttributes}):
333 this.allowedElements = allowedElements != null ?
334 new Set.from(allowedElements) : new Set(),
335 this.allowedAttributes = allowedAttributes != null ?
336 new Set.from(allowedAttributes) : new Set(),
337 this.allowedUriAttributes = allowedUriAttributes != null ?
338 new Set.from(allowedUriAttributes) : new Set();
339
340 bool allowsElement(Element element) {
341 return allowedElements.contains(element.tagName);
342 }
343
344 bool allowsAttribute(Element element, String attributeName, String value) {
345 var tagName = element.tagName;
346 if (allowedUriAttributes.contains('$tagName::$attributeName')) {
347 return uriPolicy.allowsUri(value);
348 } else if (allowedUriAttributes.contains('*::$attributeName')) {
349 return uriPolicy.allowsUri(value);
350 } else if (allowedAttributes.contains('$tagName::$attributeName')) {
351 return true;
352 } else if (allowedAttributes.contains('*::$attributeName')) {
353 return true;
354 } else if (allowedAttributes.contains('$tagName::*')) {
355 return true;
356 } else if (allowedAttributes.contains('*::*')) {
357 return true;
358 }
359 return false;
360 }
361 }
362
363 class _CustomElementNodeValidator extends _SimpleNodeValidator {
364 final bool allowTypeExtension;
365 final bool allowCustomTag;
366
367 _CustomElementNodeValidator(UriPolicy uriPolicy,
368 Iterable<String> allowedElements,
369 Iterable<String> allowedAttributes,
370 Iterable<String> allowedUriAttributes,
371 bool allowTypeExtension,
372 bool allowCustomTag):
373
374 super(uriPolicy,
375 allowedElements: allowedElements,
376 allowedAttributes: allowedAttributes,
377 allowedUriAttributes: allowedUriAttributes),
378 this.allowTypeExtension = allowTypeExtension == true,
379 this.allowCustomTag = allowCustomTag == true;
380
381 bool allowsElement(Element element) {
382 if (allowTypeExtension) {
383 var isAttr = element.attributes['is'];
384 if (isAttr != null) {
385 return allowedElements.contains(isAttr.toUpperCase()) &&
386 allowedElements.contains(element.tagName);
387 }
388 }
389 return allowCustomTag && allowedElements.contains(element.tagName);
390 }
391
392 bool allowsAttribute(Element element, String attributeName, String value) {
393 if (allowsElement(element)) {
394 if (allowTypeExtension && attributeName == 'is' &&
395 allowedElements.contains(value.toUpperCase())) {
396 return true;
397 }
398 return super.allowsAttribute(element, attributeName, value);
399 }
400 return false;
401 }
402 }
403
404 class _TemplatingNodeValidator extends _SimpleNodeValidator {
405 static const _TEMPLATE_ATTRS =
406 const <String>['bind', 'if', 'ref', 'repeat', 'syntax'];
407
408 final Set<String> _templateAttrs;
409
410 _TemplatingNodeValidator():
411 super(null,
412 allowedElements: [
413 'TEMPLATE'
414 ],
415 allowedAttributes: _TEMPLATE_ATTRS.map((attr) => 'TEMPLATE::$attr')),
416 _templateAttrs = new Set<String>.from(_TEMPLATE_ATTRS) {
417 }
418
419 bool allowsAttribute(Element element, String attributeName, String value) {
420 if (super.allowsAttribute(element, attributeName, value)) {
421 return true;
422 }
423
424 if (attributeName == 'template' && value == "") {
425 return true;
426 }
427
428 if (element.attributes['template'] == "" ) {
429 return _templateAttrs.contains(attributeName);
430 }
431 return false;
432 }
433 }
434
435
436 class _SvgNodeValidator implements NodeValidator {
437 bool allowsElement(Element element) {
438 if (element is svg.ScriptElement) {
439 return false;
440 }
441 if (element is svg.SvgElement) {
442 return true;
443 }
444 return false;
445 }
446
447 bool allowsAttribute(Element element, String attributeName, String value) {
448 if (attributeName == 'is' || attributeName.startsWith('on')) {
449 return false;
450 }
451 return allowsElement(element);
452 }
453 }
OLDNEW
« no previous file with comments | « tools/dom/src/Html5NodeValidator.dart ('k') | tools/dom/src/Validators.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698