abyrne85
abyrne85

Reputation: 1450

Angular sanitize html but leave inline style

Using the DomSanitizer service in Angular 2+ is it possible to sanitize the html but leave in the css.

For example this :

<p style="text-align: center;">
   <span style="color: #e03e2d;">Hello</span>
</p>

<script src="https://nefarious.com/script.js">

Would become this :

<p style="text-align: center;">
   <span style="color: #e03e2d;">Hello</span>
</p>

Rather than this :

<p>
   <span>Hello</span>
</p>

To give some context, I want to save the content of a TinyMCE editor.

Upvotes: 5

Views: 6270

Answers (2)

Simon_Weaver
Simon_Weaver

Reputation: 145950

I got super annoyed about this one and ended up... (judge me if you want) doing this...

First of all my needs:

  • I am using ngx-markdown but I needed some customization, which involved the need to add some simple style attributes. Since the markdown compiler just gives me back a single 'dumb' HTML string I can't use Angular's [style.color]="..." or even elementRef.style.color = ...

Angular's sanitizer basically has a white listed set of attributes that are allowed to sail through the sanitizer. style="..." is not one of them. Here are the bulk of them that are allowed (I didn't include ARIA_ATTRS as there are so many).

const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href');
const SRCSET_ATTRS = tagSet('srcset');
const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' +    'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' +    'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' +    'scope,scrolling,shape,size,sizes,span,srclang,start,summary,tabindex,target,title,translate,type,usemap,' +
    'valign,value,vspace,width');


const VALID_ATTRS = merge(URI_ATTRS, SRCSET_ATTRS, HTML_ATTRS, ARIA_ATTRS);

So anything here is fine and won't get stripped.

So what did I do? In my custom markdown functions I set itemprop instead of style and then after sanitizing (and it's very important to sanitize any user content to verify there are no 'script' tags etc.) I just replace itemprop= with style=. These styles are all 100% safe from XSS since I made them myself. The alternative of disabling sanitization for all markdown wasn't a good option (since it would allow script through).

// the ngx-markdown internally calls the Angular sanitizer
// this can only be disabled globally, which wasn't an option
let html = this.markdownService.compile(markdown, ....);

// (not shown) I have a custom markdown renderer which adds itemprop= attributes
// https://marked.js.org/using_pro#renderer

// replace itemprop= with style=
const markdown_with_style = encapsulated.replace(/ itemprop=\"/g, ' style="');

// mark the string as bypassed before applying it with innerHTML
return this.sanitizer.sanitize(SecurityContext.HTML, this.sanitizer.bypassSecurityTrustHtml(markdown_with_style));

I'm completely fine with this approach in this limited scope. The only better approach I can think of would be if the Angular sanitizer would let me mark style as safe when sanitizing an HTML string.

Final note: If you can abstract your styles into classes then the class attribute can safely be added - it's only style that is forbidden.

Upvotes: 3

Fy Z1K
Fy Z1K

Reputation: 657

Problem with your solution is if you want style attributes then you'd had to allow CSS in general which is not XSS proof and therefore DomSanitizer.sanitize(...) is cutting out everything that could lead to a XSS.

If you really need your HTML to show the style attributes then use bypassSecurityTrustHtml(value: string) instead! But be carefull this will also allow <script> Tags aswell.

It can be considered safe if TinyMCE is used by only you, trusted users, trusted stakeholders, trusted vip's or moderators that would provide the content with TinyMCE. But if your TinyMCE is thought for your userbase then I'd strongly recommend to NOT use TinyMCE for this approach and rather look out for another solution.

For example you could use something with bbcode functionality like the ckeditor for angular.

Happy coding :)

Upvotes: 4

Related Questions