165

Since upgrading to the latest Angular 2 release candidate, my img tags:

<img class='photo-img' [hidden]="!showPhoto1" src='{{theMediaItem.photoURL1}}'>

are throwing a browser error:

ORIGINAL EXCEPTION: Error: unsafe value used in a resource URL context

The value of the url is:

http://veeu-images.s3.amazonaws.com/media/userphotos/116_1464645173408_cdv_photo_007.jpg

EDIT:

I have tried the suggestion made in the other solution that this question is supposed to be a duplicate of but I am getting the same error.

I have added the following code to the controller:

import {DomSanitizationService} from '@angular/platform-browser';

@Component({
  templateUrl: 'build/pages/veeu/veeu.html'
})
export class VeeUPage {
  static get parameters() {
    return [[NavController], [App], [MenuController], [DomSanitizationService]];
  }

  constructor(nav, app, menu, sanitizer) {

    this.app = app;
    this.nav = nav;
    this.menu = menu;
    this.sanitizer = sanitizer;

    this.theMediaItem.photoURL1 = this.sanitizer.bypassSecurityTrustUrl(this.mediaItems[1].url);
  }

I am still getting the same error message.

EDIT2:

I have also changed the html to:

<img class='photo-img' [hidden]="!showPhoto1" [src]='theMediaItem.photoURL1'>

I still get the same error message

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Bill Noble
  • 6,466
  • 19
  • 74
  • 133

11 Answers11

212

Pipe

    // Angular
    import { Pipe, PipeTransform } from '@angular/core';
    import { DomSanitizer, SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl } from '@angular/platform-browser';
    
    /**
     * Sanitize HTML
     */
    @Pipe({
      name: 'safe'
    })
    export class SafePipe implements PipeTransform {
      /**
       * Pipe Constructor
       *
       * @param _sanitizer: DomSanitezer
       */
      // tslint:disable-next-line
      constructor(protected _sanitizer: DomSanitizer) {
      }
    
      /**
       * Transform
       *
       * @param value: string
       * @param type: string
       */
      transform(value: string, type: string): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl {
        switch (type) {
          case 'html':
            return this._sanitizer.bypassSecurityTrustHtml(value);
          case 'style':
            return this._sanitizer.bypassSecurityTrustStyle(value);
          case 'script':
            return this._sanitizer.bypassSecurityTrustScript(value);
          case 'url':
            return this._sanitizer.bypassSecurityTrustUrl(value);
          case 'resourceUrl':
            return this._sanitizer.bypassSecurityTrustResourceUrl(value);
          default:
            return this._sanitizer.bypassSecurityTrustHtml(value);
        }
      }
    }

Template

{{ data.url | safe:'url' }}

That's it!

Note: You shouldn't need it but here is the component use of the pipe

      // Public properties
      itsSafe: SafeHtml;

      // Private properties
      private safePipe: SafePipe = new SafePipe(this.domSanitizer);
    
      /**
       * Component constructor
       *
       * @param safePipe: SafeHtml
       * @param domSanitizer: DomSanitizer
       */
      constructor(private safePipe: SafePipe, private domSanitizer: DomSanitizer) {
      }
    
      /**
       * On init
       */
      ngOnInit(): void {
        this.itsSafe = this.safePipe.transform('<h1>Hi</h1>', 'html');
      }
RamblinRose
  • 4,883
  • 2
  • 21
  • 33
Post Impatica
  • 14,999
  • 9
  • 67
  • 78
121

I'm using rc.4 and this method works for ES2015(ES6):

import {DomSanitizationService} from '@angular/platform-browser';

@Component({
  templateUrl: 'build/pages/veeu/veeu.html'
})
export class VeeUPage {
  static get parameters() {
    return [NavController, App, MenuController, DomSanitizationService];
  }

  constructor(nav, app, menu, sanitizer) {

    this.app = app;
    this.nav = nav;
    this.menu = menu;
    this.sanitizer = sanitizer;    
  }

  photoURL() {
    return this.sanitizer.bypassSecurityTrustUrl(this.mediaItems[1].url);
  }
}

In the HTML:

<iframe [src]='photoURL()' width="640" height="360" frameborder="0"
    webkitallowfullscreen mozallowfullscreen allowfullscreen>
</iframe>

Using a function will ensure that the value doesn't change after you sanitize it. Also be aware that the sanitization function you use depends on the context.

For images, bypassSecurityTrustUrl will work but for other uses you need to refer to the documentation:

https://angular.io/docs/ts/latest/api/platform-browser/index/DomSanitizer-class.html

Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
elkelk
  • 1,692
  • 2
  • 13
  • 20
  • 3
    What is "rc4" (and later [Helzgate refers to RC3](http://stackoverflow.com/a/38079724/1175496) ) ? I mean, how do I map that to a github version? In both github and npm I only see versions like 2.4.4 or 2.4.5. I'm currently on 2.4.4 and it seems the DomSanitizer changed ; so this is the import you need: `import {DomSanitizer} from '@angular/platform-browser';` – Nate Anderson Jan 26 '17 at 04:20
  • Oh, I think [angular's github](https://github.com/angular/angular/tree/2.0.0-rc.3/modules) **branches** will refer to `2.4.x` for example, but github **tags** will refer to release candidates, like `2.0.0-rc3`. And I can see [in rc3](https://github.com/angular/angular/blob/2.0.0-rc.3/modules/%40angular/platform-browser/index.ts), for example the class was still named `DomSanitizationService`. – Nate Anderson Jan 26 '17 at 04:24
  • 1
    `this.sanitizer.bypassSecurityTrustResourceUrl(url)` for videos – prayagupa Jul 11 '17 at 07:16
  • 2
    Read the documentation carefully before using this: **bypassSecurityTrustUrl()** **WARNING:** *calling this method with untrusted user data exposes your application to XSS security risks!* Seems to me that it is not safe to do so, unless you are really sure about the image source being trusted. Even if it comes from a server, if it was uploaded by a user it would be possible to exploit such a solution. – Wilt May 16 '19 at 12:56
  • 1
    Don't use src="{{photoUrl()}}" as this still throws the exception so [src]="photoUrl()" does the trick. – Youp Bernoulli Feb 15 '21 at 13:21
60

The most elegant way to fix this: use pipe. Here is example (my blog). So you can then simply use url | safe pipe to bypass the security.

<iframe [src]="url | safe"></iframe>

Refer to the documentation on npm for details: https://www.npmjs.com/package/safe-pipe

mikegross
  • 1,674
  • 2
  • 12
  • 27
Filip Molcik
  • 1,011
  • 10
  • 12
47

Use Safe Pipe to fix it.

  • Create a safe pipe if u haven't any.

    ng g pipe safe

  • add Safe pipe in app.module.ts

    declarations: [SafePipe]

  • declare safe pipe in your ts

Import Dom Sanitizer and Safe Pipe to access url safely

import { Pipe, PipeTransform} from '@angular/core';
import { DomSanitizer } from "@angular/platform-browser";

@Pipe({ name: 'safe' })

export class SafePipe implements PipeTransform {

constructor(private sanitizer: DomSanitizer) { }
transform(url) {
 return this.sanitizer.bypassSecurityTrustResourceUrl(url);
  }
}
  • Add safe with src url

    <img width="900" height="500" [src]="link | safe"/>

Youp Bernoulli
  • 5,303
  • 5
  • 39
  • 59
sjsj15
  • 745
  • 6
  • 11
20

Either you can expose sanitizer to the view, or expose a method that forwards the call to bypassSecurityTrustUrl

<img class='photo-img' [hidden]="!showPhoto1" 
    [src]='sanitizer.bypassSecurityTrustUrl(theMediaItem.photoURL1)'>
  • Thanks for reminding me that we can make methods public in the constructor and consume them in them template. – Jakub Dec 30 '20 at 10:20
11

Angular treats all values as untrusted by default. When a value is inserted into the DOM from a template, via property, attribute, style, class binding, or interpolation, Angular sanitizes and escapes untrusted values.

So if you are manipulating DOM directly and inserting content in it, you need to sanitize it, otherwise Angular will throw errors.

I have created the pipe SanitizeUrlPipe for this

import { PipeTransform, Pipe } from "@angular/core";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";

@Pipe({
    name: "sanitizeUrl"
})
export class SanitizeUrlPipe implements PipeTransform {

    constructor(private _sanitizer: DomSanitizer) { }

    transform(v: string): SafeHtml {
        return this._sanitizer.bypassSecurityTrustResourceUrl(v);
    }
}

and this is how you can use

<iframe [src]="url | sanitizeUrl" width="100%" height="500px"></iframe>

If you want to add HTML, then SanitizeHtmlPipe can help

import { PipeTransform, Pipe } from "@angular/core";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";

@Pipe({
    name: "sanitizeHtml"
})
export class SanitizeHtmlPipe implements PipeTransform {

    constructor(private _sanitizer: DomSanitizer) { }

    transform(v: string): SafeHtml {
        return this._sanitizer.bypassSecurityTrustHtml(v);
    }
}

Read more about angular security here.

Sunil Garg
  • 14,608
  • 25
  • 132
  • 189
  • "So if you are manipulating DOM directly and inserting content in it, you need to sanitize it, otherwise Angular will throw errors." But you're not sanitising in your pipe, you're bypassing sanitisation. – Patrick Oct 07 '22 at 08:53
4

I usually add separate safe pipe reusable component as following

# Add Safe Pipe

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';

@Pipe({name: 'mySafe'})
export class SafePipe implements PipeTransform {
    constructor(private sanitizer: DomSanitizer) {
    }

    public transform(url) {
        return this.sanitizer.bypassSecurityTrustResourceUrl(url);
    }
}
# then create shared pipe module as following 

import { NgModule } from '@angular/core'; 
import { SafePipe } from './safe.pipe';
@NgModule({
    declarations: [
        SafePipe
    ],
    exports: [
        SafePipe
    ]
})
export class SharedPipesModule {
}
# import shared pipe module in your native module

@NgModule({
    declarations: [],
    imports: [
        SharedPipesModule,
    ],
})
export class SupportModule {
}
<!-------------------
call your url (`trustedUrl` for me) and add `mySafe` as defined in Safe Pipe
---------------->
<div class="container-fluid" *ngIf="trustedUrl">
    <iframe [src]="trustedUrl | mySafe" align="middle" width="100%" height="800" frameborder="0"></iframe>
</div>
Janki
  • 191
  • 1
  • 8
1

Here's a better and tested safe pipe:

import { Pipe, PipeTransform, SecurityContext } from '@angular/core';
import {
  DomSanitizer,
  SafeHtml,
  SafeResourceUrl,
  SafeScript,
  SafeStyle,
  SafeUrl,
  SafeValue,
} from '@angular/platform-browser';

@Pipe({
  name: 'safe',
})
export class SafePipe implements PipeTransform {
  constructor(private readonly domSanitizer: DomSanitizer) {}

  transform(
    value: string | undefined,
    type: string,
    bypass: boolean
  ):
    | SafeHtml
    | SafeStyle
    | SafeScript
    | SafeUrl
    | SafeResourceUrl
    | SafeValue
    | null {
    if (!value) {
      return null;
    }
    switch (type) {
      case 'style':
        return bypass
          ? this.domSanitizer.bypassSecurityTrustStyle(value)
          : this.domSanitizer.sanitize(SecurityContext.STYLE, value);
      case 'script':
        return bypass
          ? this.domSanitizer.bypassSecurityTrustScript(value)
          : this.domSanitizer.sanitize(SecurityContext.SCRIPT, value);
      case 'url':
        return bypass
          ? this.domSanitizer.bypassSecurityTrustUrl(value)
          : this.domSanitizer.sanitize(SecurityContext.URL, value);
      case 'resourceUrl':
        return bypass
          ? this.domSanitizer.bypassSecurityTrustResourceUrl(value)
          : this.domSanitizer.sanitize(SecurityContext.RESOURCE_URL, value);
      default:
        return bypass
          ? this.domSanitizer.bypassSecurityTrustHtml(value)
          : this.domSanitizer.sanitize(SecurityContext.HTML, value);
    }
  }
}



import { DomSanitizer } from '@angular/platform-browser';
import { SafePipe } from './safe.pipe';

const mockDomSanitizer = ({
  sanitize: jest.fn(),
  bypassSecurityTrustHtml: jest.fn(),
  bypassSecurityTrustStyle: jest.fn(),
  bypassSecurityTrustUrl: jest.fn(),
  bypassSecurityTrustResourceUrl: jest.fn(),
  bypassSecurityTrustScript: jest.fn(),
} as unknown) as DomSanitizer;

describe('SafePipe', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });
  it('should return null value when value received is undefined', () => {
    const pipe = new SafePipe(mockDomSanitizer);
    expect(pipe.transform(undefined, 'style', true)).toBeNull();
  });
  test.each([
    { a: 'style', b: true, expected: 'bypassSecurityTrustStyle' },
    { a: 'script', b: true, expected: 'bypassSecurityTrustScript' },
    { a: 'url', b: true, expected: 'bypassSecurityTrustUrl' },
    { a: 'resourceUrl', b: true, expected: 'bypassSecurityTrustResourceUrl' },
    { a: 'html', b: true, expected: 'bypassSecurityTrustHtml' },
  ])('should call the correspondent method', ({ a, b, expected }) => {
    const pipe = new SafePipe(mockDomSanitizer);
    pipe.transform('value', a, b);
    const domSanitizer = mockDomSanitizer as never;
    expect(domSanitizer[expected]).toHaveBeenCalled();
  });

  test.each([
    { a: 'style', b: false, expected: 'sanitize' },
    { a: 'script', b: false, expected: 'sanitize' },
    { a: 'url', b: false, expected: 'sanitize' },
    { a: 'resourceUrl', b: false, expected: 'sanitize' },
    { a: 'html', b: false, expected: 'sanitize' },
  ])('should call the correspondent method', ({ a, b, expected }) => {
    const pipe = new SafePipe(mockDomSanitizer);
    pipe.transform('value', a, b);
    const domSanitizer = mockDomSanitizer as never;
    expect(domSanitizer[expected]).toHaveBeenCalled();
  });
});
rmiranda
  • 11
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 13 '22 at 10:50
0
import {DomSanitizationService} from '@angular/platform-browser';
@Component({
 templateUrl: 'build/pages/veeu/veeu.html'
 })
  export class VeeUPage {
     trustedURL:any;
      static get parameters() {
               return [NavController, App, MenuController, 
              DomSanitizationService];
        }
      constructor(nav, app, menu, sanitizer) {
        this.app = app;
        this.nav = nav;
        this.menu = menu;
        this.sanitizer = sanitizer;  
        this.trustedURL  = sanitizer.bypassSecurityTrustUrl(this.mediaItems[1].url);
        } 
 }



 <iframe [src]='trustedURL' width="640" height="360" frameborder="0"
   webkitallowfullscreen mozallowfullscreen allowfullscreen>
</iframe>


User property binding instead of function.
ani
  • 111
  • 1
  • 5
  • 14
0

It is possible to set image as background image to avoid unsafe url error:

<div [style.backgroundImage]="'url(' + imageUrl + ')'" class="show-image"></div>

CSS:

.show-image {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-size: cover;        
}
StepUp
  • 36,391
  • 15
  • 88
  • 148
0
<img class='photo-img' [hidden]="!showPhoto1" src="data:image/jpeg;base64,{{theMediaItem.photoURL1}}">
Ryan M
  • 18,333
  • 31
  • 67
  • 74