8

I have encounter error "Removing disallowed attribute" after I upgraded my dart editor in SDK 0.7.3.1_r27487.

I have a custom tag which template contains boostarp attributes "data-target" and "data-toggle". It work under previous version but encounter error after upgraded.

Console

Removing disallowed attribute <A data-toggle="dropdown">
Removing disallowed attribute <BUTTON data-target=".navbar-collapse">
Removing disallowed attribute <BUTTON data-toggle="collapse">

.html Code

<element extends="div" name="x-navbar" constructor="Navbar">
<template>    
  ..
  <a name="top" href="#" class="dropdown-toggle" data-toggle="dropdown">Shop <b class="caret"></b></a>
  ..
  <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"></button>
</template>
<script type="application/dart" src="custom_ui.dart"></script> 
</element>
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Roger Chan
  • 1,293
  • 2
  • 11
  • 24
  • Roger, does the code still execute as intended? I occasionally also see such messages (and have even before the latest Editor release), but my Polymer app continues to work fine. – Shailen Tuli Sep 18 '13 at 15:06

6 Answers6

10

You're probably using Element.innerHtml. You should use Element.setInnerHtml instead.

As you can see with the parameters of this new method, the HTML code is now validated and filtered. To restore the old behavior, you must provide a validator or a tree sanitizer allowing all attributes.

To explicitly allow "data" on anchors and buttons:

// Call NodeValidatorBuilder.allowX() methods to customize the validator.

final NodeValidatorBuilder _htmlValidator=new NodeValidatorBuilder.common()
  ..allowElement('a', attributes: ['data-target', 'data-toggle'])
  ..allowElement('button', attributes: ['data-target', 'data-toggle']);

query('#anElement').setInnerHtml('a content', validator: _htmlValidator);

Element and CustomElement classes use HTML sanitization in several places (Element.html factory, innerHtml property, createFragment method...).

Even if you don't use these methods directly in your own code, they're called by the underlying Dart libraries (CustomElement class was created for Polymer library but is also used by latest releases of Web UI library).

For the moment, there is NO way to globally disable or customize the default sanitization rules. So I guess you'll have to deal with setInnerHtml calls... or wait for another SDK release to fix the issue ("data-" attributes are valid HTML5 code, but the default sanitization filter doesn't allow them as well as inline styles: why these attributes are considered insecure?).

Note: you should consider switching from Web UI to Polymer, as Web UI is now deprecated.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
CedX
  • 3,854
  • 2
  • 36
  • 45
4

For those who are working with angular dart, you will have to reimplement the NgDirective with custom validations like so:

library ng_bind_html_unsafe;

import 'dart:html' as dom;
import 'package:angular/angular.dart';

@NgDirective(
    selector: '[ng-bind-html-unsafe]',
    map: const {'ng-bind-html-unsafe': '=>value'}    )
class NgBindHtmlUnsafeDirective{
  final dom.Element element;

  NgBindHtmlUnsafeDirective(this.element);

  set value(value) => element.setInnerHtml(value == null ? '' : value.toString(),
                                             validator: new dom.NodeValidatorBuilder()
                                             ..allowHtml5()
                                             ..allowElement('a', attributes: ['href'])
                                             ..allowElement('img', attributes: ['src']));
}

In that specific example, I am allowing links and pictures without any sanitization you can extend this however you'd like.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
mabounassif
  • 2,311
  • 6
  • 29
  • 46
2

In Angular 0.9.9 ng-bind-html supports custom NodeValidators.

Just register a factory that returns a customized NodeValidator like shown in the other answers

factory(NodeValidator, (Injector inj) => getNodeValidator());

and ng-bind-html will use this NodeValidator.

I found that the HTML included this way isn't processed by Angular (any directives/components/expressions are ignored).

When I need this I use a custom bind-html directive like shown by @mabounassif with this additional code in the value setter after element.setInnerHtml

if(value != null) {
  _compiler(_element.childNodes, _directiveMap)(_injector, _element.childNodes);
}

see also https://github.com/angular/angular.dart/issues/742

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
2

I wanted to use img element with innerHtml and found that the above answers were incomplete. It was missing the uriPolicy that defaults to the same origin. Defining a custom UriPolicy fixed the issue for me.

class DefaultUriPolicy implements UriPolicy {
  DefaultUriPolicy();

  bool allowsUri(String uri) {
    // Return true/false based on uri comparison
    return true;
  }
}

Define validator like below:

allowElement('img',
              attributes: ['src'], uriPolicy: new DefaultUriPolicy()))
Kuldeep Saxena
  • 1,803
  • 1
  • 14
  • 17
2

For anyone who wants to use setInnerHtml and wants to allow all tags and attributes, you can use this validator:

class AllowAll implements NodeValidator {
    @override
    bool allowsAttribute(Element element, String attributeName, String value) {
       return true;
    }

    @override
    bool allowsElement(Element element) {
      return true;
    }
}

usage:

element.setInnerHtml(
    value,
    validator: AllowAll()
);
markt
  • 884
  • 1
  • 6
  • 11
0

Taking @mabounassif's answer I did the following to also allow links to another sites:

import 'dart:html' as dom;
import 'package:angular/angular.dart';

@Directive(selector: '[html-inseguro]')
class HtmlInseguro {
  final dom.Element element;

  final dom.NodeValidatorBuilder _validador;

  HtmlInseguro(this.element)
      : _validador = dom.NodeValidatorBuilder()
          ..allowHtml5()
          ..allowElement('a', attributes: ['href'], uriPolicy: _TodoUriPolicy())
          ..allowElement('img',
              attributes: ['src'], uriPolicy: _TodoUriPolicy());

  @Input('html-inseguro')
  set htmlInseguro(String value) {
    element.setInnerHtml(value?.toString() ?? '', validator: _validador);
  }
}

class _TodoUriPolicy implements dom.UriPolicy {
  @override
  bool allowsUri(String uri) {
    return true;
  }
}

Then in the component you should have sth like this:

@Component(
  template: '''
<div class="vista-previa-mensaje" [html-inseguro]="mensajeAEnviar | dataReplacer: reemplazos | md2html"></div>
''',
  directives: [
    HtmlInseguro,
  ],
  pipes: [Md2Html, DataReplacer])
class YourComponent{
  //...
Nico Rodsevich
  • 2,393
  • 2
  • 22
  • 32