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

Unified 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, 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/dom/src/Html5NodeValidator.dart ('k') | tools/dom/src/Validators.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/dom/src/NodeValidatorBuilder.dart
diff --git a/tools/dom/src/NodeValidatorBuilder.dart b/tools/dom/src/NodeValidatorBuilder.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c5f0c80542a90835393044215c1d36965ea7bdbf
--- /dev/null
+++ b/tools/dom/src/NodeValidatorBuilder.dart
@@ -0,0 +1,453 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.dom.html;
+
+
+/**
+ * Class which helps construct standard node validation policies.
+ *
+ * By default this will not accept anything, but the 'allow*' functions can be
+ * used to expand what types of elements or attributes are allowed.
+ *
+ * All allow functions are additive- elements will be accepted if they are
+ * accepted by any specific rule.
+ *
+ * It is important to remember that sanitization is not just intended to prevent
+ * cross-site scripting attacks, but also to prevent information from being
+ * displayed in unexpected ways. For example something displaying basic
+ * formatted text may not expect `<video>` tags to appear. In this case an
+ * empty NodeValidatorBuilder with just [allowTextElements] might be
+ * appropriate.
+ */
+class NodeValidatorBuilder implements NodeValidator {
+
+ final List<NodeValidator> _validators = <NodeValidator>[];
+
+ NodeValidatorBuilder() {
+ }
+
+ /**
+ * Creates a new NodeValidatorBuilder which accepts common constructs.
+ *
+ * By default this will accept HTML5 elements and attributes with the default
+ * [UriPolicy] and templating elements.
+ *
+ * Notable syntax which is filtered:
+ *
+ * * Only known-good HTML5 elements and attributes are allowed.
+ * * All URLs must be same-origin, use [allowNavigation] and [allowImages] to
+ * specify additional URI policies.
+ * * Inline-styles are not allowed.
+ * * Custom element tags are disallowed, use [allowCustomElement].
+ * * Custom tags extensions are disallowed, use [allowTagExtension].
+ * * SVG Elements are not allowed, use [allowSvg].
+ *
+ * For scenarios where the HTML should only contain formatted text
+ * [allowTextElements] is more appropriate.
+ *
+ * Use [allowSvg] to allow SVG elements.
+ */
+ NodeValidatorBuilder.common() {
+ allowHtml5();
+ allowTemplating();
+ }
+
+ /**
+ * Allows navigation elements- Form and Anchor tags, along with common
+ * attributes.
+ *
+ * The UriPolicy can be used to restrict the locations the navigation elements
+ * are allowed to direct to. By default this will use the default [UriPolicy].
+ */
+ void allowNavigation([UriPolicy uriPolicy]) {
+ if (uriPolicy == null) {
+ uriPolicy = new UriPolicy();
+ }
+ add(new _SimpleNodeValidator.allowNavigation(uriPolicy));
+ }
+
+ /**
+ * Allows image elements.
+ *
+ * The UriPolicy can be used to restrict the locations the images may be
+ * loaded from. By default this will use the default [UriPolicy].
+ */
+ void allowImages([UriPolicy uriPolicy]) {
+ if (uriPolicy == null) {
+ uriPolicy = new UriPolicy();
+ }
+ add(new _SimpleNodeValidator.allowImages(uriPolicy));
+ }
+
+ /**
+ * Allow basic text elements.
+ *
+ * This allows a subset of HTML5 elements, specifically just these tags and
+ * no attributes.
+ *
+ * * B
+ * * BLOCKQUOTE
+ * * BR
+ * * EM
+ * * H1
+ * * H2
+ * * H3
+ * * H4
+ * * H5
+ * * H6
+ * * HR
+ * * I
+ * * LI
+ * * OL
+ * * P
+ * * SPAN
+ * * UL
+ */
+ void allowTextElements() {
+ add(new _SimpleNodeValidator.allowTextElements());
+ }
+
+ /**
+ * Allow common safe HTML5 elements and attributes.
+ *
+ * This list is based off of the Caja whitelists at:
+ * https://code.google.com/p/google-caja/wiki/CajaWhitelists.
+ *
+ * Common things which are not allowed are script elements, style attributes
+ * and any script handlers.
+ */
+ void allowHtml5({UriPolicy uriPolicy}) {
+ add(new _Html5NodeValidator(uriPolicy: uriPolicy));
+ }
+
+ /**
+ * Allow SVG elements and attributes except for known bad ones.
+ */
+ void allowSvg() {
+ add(new _SvgNodeValidator());
+ }
+
+ /**
+ * Allow custom elements with the specified tag name and specified attributes.
+ *
+ * This will allow the elements as custom tags (such as <x-foo></x-foo>),
+ * but will not allow tag extensions. Use [allowTagExtension] to allow
+ * tag extensions.
+ */
+ void allowCustomElement(String tagName,
+ {UriPolicy uriPolicy,
+ Iterable<String> attributes,
+ Iterable<String> uriAttributes}) {
+
+ var tagNameUpper = tagName.toUpperCase();
+ var attrs;
+ if (attributes != null) {
+ attrs =
+ attributes.map((name) => '$tagNameUpper::${name.toLowerCase()}');
+ }
+ var uriAttrs;
+ if (uriAttributes != null) {
+ uriAttrs =
+ uriAttributes.map((name) => '$tagNameUpper::${name.toLowerCase()}');
+ }
+ if (uriPolicy == null) {
+ uriPolicy = new UriPolicy();
+ }
+
+ add(new _CustomElementNodeValidator(
+ uriPolicy,
+ [tagNameUpper],
+ attrs,
+ uriAttrs,
+ false,
+ true));
+ }
+
+ /**
+ * Allow custom tag extensions with the specified type name and specified
+ * attributes.
+ *
+ * This will allow tag extensions (such as <div is="x-foo"></div>),
+ * but will not allow custom tags. Use [allowCustomElement] to allow
+ * custom tags.
+ */
+ void allowTagExtension(String tagName, String baseName,
+ {UriPolicy uriPolicy,
+ Iterable<String> attributes,
+ Iterable<String> uriAttributes}) {
+
+ var baseNameUpper = baseName.toUpperCase();
+ var tagNameUpper = tagName.toUpperCase();
+ var attrs;
+ if (attributes != null) {
+ attrs =
+ attributes.map((name) => '$baseNameUpper::${name.toLowerCase()}');
+ }
+ var uriAttrs;
+ if (uriAttributes != null) {
+ uriAttrs =
+ uriAttributes.map((name) => '$baseNameUpper::${name.toLowerCase()}');
+ }
+ if (uriPolicy == null) {
+ uriPolicy = new UriPolicy();
+ }
+
+ add(new _CustomElementNodeValidator(
+ uriPolicy,
+ [tagNameUpper, baseNameUpper],
+ attrs,
+ uriAttrs,
+ true,
+ false));
+ }
+
+ void allowElement(String tagName, {UriPolicy uriPolicy,
+ Iterable<String> attributes,
+ Iterable<String> uriAttributes}) {
+
+ allowCustomElement(tagName, uriPolicy: uriPolicy,
+ attributes: attributes,
+ uriAttributes: uriAttributes);
+ }
+
+ /**
+ * Allow templating elements (such as <template> and template-related
+ * attributes.
+ *
+ * This still requires other validators to allow regular attributes to be
+ * bound (such as [allowHtml5]).
+ */
+ void allowTemplating() {
+ add(new _TemplatingNodeValidator());
+ }
+
+ /**
+ * Add an additional validator to the current list of validators.
+ *
+ * Elements and attributes will be accepted if they are accepted by any
+ * validators.
+ */
+ void add(NodeValidator validator) {
+ _validators.add(validator);
+ }
+
+ bool allowsElement(Element element) {
+ return _validators.any((v) => v.allowsElement(element));
+ }
+
+ bool allowsAttribute(Element element, String attributeName, String value) {
+ return _validators.any(
+ (v) => v.allowsAttribute(element, attributeName, value));
+ }
+}
+
+class _SimpleNodeValidator implements NodeValidator {
+ final Set<String> allowedElements;
+ final Set<String> allowedAttributes;
+ final Set<String> allowedUriAttributes;
+ final UriPolicy uriPolicy;
+
+ factory _SimpleNodeValidator.allowNavigation(UriPolicy uriPolicy) {
+ return new _SimpleNodeValidator(uriPolicy,
+ allowedElements: [
+ 'A',
+ 'FORM'],
+ allowedAttributes: [
+ 'A::accesskey',
+ 'A::coords',
+ 'A::hreflang',
+ 'A::name',
+ 'A::shape',
+ 'A::tabindex',
+ 'A::target',
+ 'A::type',
+ 'FORM::accept',
+ 'FORM::autocomplete',
+ 'FORM::enctype',
+ 'FORM::method',
+ 'FORM::name',
+ 'FORM::novalidate',
+ 'FORM::target',
+ ],
+ allowedUriAttributes: [
+ 'A::href',
+ 'FORM::action',
+ ]);
+ }
+
+ factory _SimpleNodeValidator.allowImages(UriPolicy uriPolicy) {
+ return new _SimpleNodeValidator(uriPolicy,
+ allowedElements: [
+ 'IMG'
+ ],
+ allowedAttributes: [
+ 'IMG::align',
+ 'IMG::alt',
+ 'IMG::border',
+ 'IMG::height',
+ 'IMG::hspace',
+ 'IMG::ismap',
+ 'IMG::name',
+ 'IMG::usemap',
+ 'IMG::vspace',
+ 'IMG::width',
+ ],
+ allowedUriAttributes: [
+ 'IMG::src',
+ ]);
+ }
+
+ factory _SimpleNodeValidator.allowTextElements() {
+ return new _SimpleNodeValidator(null,
+ allowedElements: [
+ 'B',
+ 'BLOCKQUOTE',
+ 'BR',
+ 'EM',
+ 'H1',
+ 'H2',
+ 'H3',
+ 'H4',
+ 'H5',
+ 'H6',
+ 'HR',
+ 'I',
+ 'LI',
+ 'OL',
+ 'P',
+ 'SPAN',
+ 'UL',
+ ]);
+ }
+
+ /**
+ * Elements must be uppercased tag names. For example `'IMG'`.
+ * Attributes must be uppercased tag name followed by :: followed by
+ * lowercase attribute name. For example `'IMG:src'`.
+ */
+ _SimpleNodeValidator(this.uriPolicy,
+ {Iterable<String> allowedElements, Iterable<String> allowedAttributes,
+ Iterable<String> allowedUriAttributes}):
+ this.allowedElements = allowedElements != null ?
+ new Set.from(allowedElements) : new Set(),
+ this.allowedAttributes = allowedAttributes != null ?
+ new Set.from(allowedAttributes) : new Set(),
+ this.allowedUriAttributes = allowedUriAttributes != null ?
+ new Set.from(allowedUriAttributes) : new Set();
+
+ bool allowsElement(Element element) {
+ return allowedElements.contains(element.tagName);
+ }
+
+ bool allowsAttribute(Element element, String attributeName, String value) {
+ var tagName = element.tagName;
+ if (allowedUriAttributes.contains('$tagName::$attributeName')) {
+ return uriPolicy.allowsUri(value);
+ } else if (allowedUriAttributes.contains('*::$attributeName')) {
+ return uriPolicy.allowsUri(value);
+ } else if (allowedAttributes.contains('$tagName::$attributeName')) {
+ return true;
+ } else if (allowedAttributes.contains('*::$attributeName')) {
+ return true;
+ } else if (allowedAttributes.contains('$tagName::*')) {
+ return true;
+ } else if (allowedAttributes.contains('*::*')) {
+ return true;
+ }
+ return false;
+ }
+}
+
+class _CustomElementNodeValidator extends _SimpleNodeValidator {
+ final bool allowTypeExtension;
+ final bool allowCustomTag;
+
+ _CustomElementNodeValidator(UriPolicy uriPolicy,
+ Iterable<String> allowedElements,
+ Iterable<String> allowedAttributes,
+ Iterable<String> allowedUriAttributes,
+ bool allowTypeExtension,
+ bool allowCustomTag):
+
+ super(uriPolicy,
+ allowedElements: allowedElements,
+ allowedAttributes: allowedAttributes,
+ allowedUriAttributes: allowedUriAttributes),
+ this.allowTypeExtension = allowTypeExtension == true,
+ this.allowCustomTag = allowCustomTag == true;
+
+ bool allowsElement(Element element) {
+ if (allowTypeExtension) {
+ var isAttr = element.attributes['is'];
+ if (isAttr != null) {
+ return allowedElements.contains(isAttr.toUpperCase()) &&
+ allowedElements.contains(element.tagName);
+ }
+ }
+ return allowCustomTag && allowedElements.contains(element.tagName);
+ }
+
+ bool allowsAttribute(Element element, String attributeName, String value) {
+ if (allowsElement(element)) {
+ if (allowTypeExtension && attributeName == 'is' &&
+ allowedElements.contains(value.toUpperCase())) {
+ return true;
+ }
+ return super.allowsAttribute(element, attributeName, value);
+ }
+ return false;
+ }
+}
+
+class _TemplatingNodeValidator extends _SimpleNodeValidator {
+ static const _TEMPLATE_ATTRS =
+ const <String>['bind', 'if', 'ref', 'repeat', 'syntax'];
+
+ final Set<String> _templateAttrs;
+
+ _TemplatingNodeValidator():
+ super(null,
+ allowedElements: [
+ 'TEMPLATE'
+ ],
+ allowedAttributes: _TEMPLATE_ATTRS.map((attr) => 'TEMPLATE::$attr')),
+ _templateAttrs = new Set<String>.from(_TEMPLATE_ATTRS) {
+ }
+
+ bool allowsAttribute(Element element, String attributeName, String value) {
+ if (super.allowsAttribute(element, attributeName, value)) {
+ return true;
+ }
+
+ if (attributeName == 'template' && value == "") {
+ return true;
+ }
+
+ if (element.attributes['template'] == "" ) {
+ return _templateAttrs.contains(attributeName);
+ }
+ return false;
+ }
+}
+
+
+class _SvgNodeValidator implements NodeValidator {
+ bool allowsElement(Element element) {
+ if (element is svg.ScriptElement) {
+ return false;
+ }
+ if (element is svg.SvgElement) {
+ return true;
+ }
+ return false;
+ }
+
+ bool allowsAttribute(Element element, String attributeName, String value) {
+ if (attributeName == 'is' || attributeName.startsWith('on')) {
+ return false;
+ }
+ return allowsElement(element);
+ }
+}
« 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