I'm playing with bypassSecurityTrust* functions of Angular. Goal is to get a script tag to execute on the page. But it either keeps sanitizing with the message
WARNING: sanitizing HTML stripped some content
or I see in the console a
SafeHtmlImpl {changingThisBreaksApplicationSecurity: "<script>alert(1)</script>.
Goal is to get this working.
What I currently use and tried:
#Pipe({ name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value: string): string {
console.log(this.sanitized.sanitize(SecurityContext.NONE, value))
return this.sanitized.sanitize(SecurityContext.NONE, value);
}
}
#Component({
selector: 'app-demo',
templateUrl: './demo.component.html',
styleUrls: ['./demo.component.css']
})
export class DemoComponent implements OnInit {
name: string;
html: string;
constructor(private sanitizer: DomSanitizer) {
this.name = 'Angular2';
this.html = "<script> alert(8) </script>";
}
ngOnInit() {
}
}
and the template html:
<div [innerHtml]="html | safeHtml"></div>
I tried both sanitize with SecurityContext.NONE which should work looking at the code and bypassSecurityTrustHtml(value). The above code was inspired by this answer.
Any ideas on how to execute that JavaScript?
So yes, innerHtml can't insert script tags, but it doesn't stop it from one of the many other ways to inject JavaScript.
Working example:
import { Component, Pipe, PipeTransform, SecurityContext} from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser'
#Pipe({ name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
transform(value: string) {
console.log(this.sanitized.bypassSecurityTrustHtml(value));
return this.sanitized.bypassSecurityTrustHtml(value);
}
}
#Component({
selector: 'app-demo',
template: `
<div [innerHtml]="html | safeHtml">
</div>
`
})
export class DemoComponent {
html: string;
h_html: string;
constructor(private sanitizer: DomSanitizer) {
this.html = "<svg onload=\"alert(1)\"> blah </svg>"
this.h_html = sanitizer.sanitize(SecurityContext.HTML, "<svg onload=\"alert(2)\"> blah </svg>');
}
}
What doesn't work is
return this.sanitized.sanitize(SecurityContext.HTML, value);
or using
<div [innerHtml]="h_tmpl"></div>
Not sure why. Should behave the same afaiu.
Related
Whatever i do angular does not detect change on talks array. I have a handleSubmit function to send the toolbar. Toolbar use it to send the changes to parent from input field.
My app.component.ts file
import { Component, Type, OnChanges, SimpleChanges } from '#angular/core';
import { getResponse } from '../api/API';
declare module '../api/API' {
export interface NlpAPI {
getResponse(data: any): Promise<any>;
}
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnChanges {
talks: string[];
title: string;
ngOnChanges(changes: SimpleChanges): void {
console.log(changes);
}
constructor() {
this.talks = [];
this.title = 'Talks';
}
ngOnInit() {
this.talks.push('Welcome to ProjectX! How can I help you?');
this.talks.push('I am a chatbot. I can help you with your queries.');
}
handleSubmit(data: any): void {
this.talks.push(data.talk);
}
messageResponse() {
// #ts-ignore: Object is possibly 'null'.
const x = document.getElementById('txt').value;
// #ts-ignore: Object is possibly 'null'.
document.getElementById('output').innerHTML =
'Your message is ' + '"' + x + '"';
}
}
My app.component.html
<!-- Toolbar -->
<app-custom-toolbar [handleSubmit]="handleSubmit"></app-custom-toolbar>
<!-- Highlight Card -->
<app-text-area [talksFromUser]="talks" [title]="title"></app-text-area>
<!-- Bottombar -->
<router-outlet></router-outlet>
My text-area.component.ts file
import { Component, Input, OnChanges, SimpleChanges } from '#angular/core';
#Component({
selector: 'app-text-area',
templateUrl: './text-area.component.html',
styleUrls: ['./text-area.component.css'],
})
export class TextAreaComponent implements OnChanges {
#Input() talksFromUser: string[] = [];
#Input() title: string = '';
constructor() {}
ngOnChanges(changes: SimpleChanges): void {
console.log(changes);
}
}
My text-area.component.html
<div class="main-text-area">
<div *ngFor="let item of talksFromUser">{{ item }}</div>
</div>
custom-toolbar.component.ts file
import { Component, Input, OnInit } from '#angular/core';
import { NgForm } from '#angular/forms';
#Component({
selector: 'app-custom-toolbar',
templateUrl: './custom-toolbar.component.html',
styleUrls: ['./custom-toolbar.component.css'],
})
export class CustomToolbarComponent implements OnInit {
talks: string[] = [];
#Input() handleSubmit!: (args: any) => void;
constructor() {}
ngOnInit(): void {}
onSubmit(f: NgForm) {
this.handleSubmit(f.value);
f.resetForm();
}
}
I tried also
this.talks = [...this.talks, data.talk]
Thank you all.
There are two issues in your code:
First one, you are calling handleSubmit("string") (so data is a string), but you are pushing data.talk, which is undefined (so talks will be [undefined, undefined, ...]). To fix it, use data:
handleSubmit(data: any): void {
this.talks.push(data); // use "data" instead of "data.talk"
}
Second one, you are using a AppComponent method into CustomToolbarComponent class. You need to keep the this scope of AppComponent. Also, you should use arrow functions:
handleSubmit = (data: any): void => {
this.talks.push(data);
}
I am using angular 5 and java 8 for my web application. I am having a java service which return html content with angular click event. The same i am binding to angular component. The html content is working but click event is not working.
Below is the sample code from Java
#RequestMapping(value="/hitSample",method = RequestMethod.GET)
public String hitSample() {
StringBuilder sb = new StringBuilder();
sb.append("<a (click)=\"callSampleFunction()\"><p>This is a paragraph.</p> A Tag end
</a>");
sb.append("<p>This is a paragraph. 2 </p>");
return sb.toString();
}
SampleComponent.ts
import { Component, OnInit } from '#angular/core';
import {ViewReviewService} from '../../services/view-review-service';
import {Response} from '#angular/http';
#Component({
selector: 'app-sample-hit',
templateUrl: './sample-hit.component.html',
styleUrls: ['./sample-hit.component.css']
})
export class SampleHitComponent implements OnInit {
sampleData: any;
constructor(private viewReviewService: ViewReviewService) { }
ngOnInit() {
this.hitSample();
}
hitSample() {
this.viewReviewService.hitSample().subscribe((res: Response) => {
console.log(res['_body']);
this.sampleData = res['_body'];
});
}
callSampleFunction() {
alert('got call');
}
}
sampleComponent.html
<div [innerHTML]="sampleData" > </div>
I want callSampleFunction() to be triggered on click.
You can use the same way you've used <div [innerHTML]="sampleData" > </div> but you need to sanitise the string template for security reasons.
<div [innerHtml]="sampleData | safeHtml">
SafeHtmlPipe.ts
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
import DOMPurify from 'dompurify';
#Pipe({
name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
constructor(protected sanitizer: DomSanitizer) {}
public transform(value: any, type: string): any {
const sanitizedContent = DOMPurify.sanitize(value);
return this.sanitizer.bypassSecurityTrustHtml(sanitizedContent);
}
}
Ionic tags not working in ionic 3. for example (click) method is not working with [innerHTML]. Below is my code.
#IonicPage()
#Component({
selector: 'page-html-test',
templateUrl: 'html-test.html',
})
export class detailNewsPage {
html: any;
constructor(public navCtrl: NavController,
public navParams: NavParams,
) {}
ionViewDidLoad() {
this.detailNews = `<button ion-button (click)="detailNews1()">Test</button>`;
}
detailNews1(){
console.log('test-detailNews1 button clicked');
}
}
**htmlTest.html:**
<ion-content padding>
<div [innerHTML]="detailNews | safeHtml"></div>
</ion-content>
**safeHtml.ts**
#Pipe({
name: 'safeHtml',
})
export class SafeHtmlPipe implements PipeTransform {
/**
* Takes a value and makes it lowercase.
*/
constructor(private sanitizer:DomSanitizer){}
transform(html) {
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}
Any help, much appreciate
in my component I open a MatDialog and pass data to it. In my object data.obj is under this.data.obj.html html-code stored.
In electron I would use a webview to display the html-site.
How do I display the html-code in proper way in my MatDialog in angular 5? Its possible to create the template dynamically or is there any smoother way?
#Component({
selector: 'dialog-popup',
template:`
<h1 mat-dialog-title>Content-HTML</h1>
<mat-dialog-content>
{{this.data.obj.html}}
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button>Complain</button>
<button mat-button (click)=onNoClick()>Cancel</button>
</mat-dialog-actions>
`
})
export class DialogOverview {
constructor(
public dialogRef: MatDialogRef<DialogOverview>,
#Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit() {
console.log(this.data.obj.html);
}
onNoClick(): void {
this.dialogRef.close();
}
}
You can bind it to the [innerHtml] property of a html element
<mat-dialog-content>
<div [innerHtml]="data.obj.html | keepHtml"></div>
</mat-dialog-content>
You can use dom sanitizer and write a html pipe like below to escape html sanitizing
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({ name: 'keepHtml', pure: false })
export class KeepHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(content) {
return this.sanitizer.bypassSecurityTrustHtml(content);
}
}
I have a simple pipe that returns some html
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'rating',
pure: false
})
export class RatingPipe implements PipeTransform {
transform(value: any): string {
let stars = "<ion-icon name='star'>" + value + "</ion-icon>";
return stars;
}
}
the problem is when i use it, i get nothing
// this works fine
<p><span innerHtml="{{'<h1>some text</h1>'}}"></span></p>
// if I add a pipe, it doesn't work
<p><span innerHtml="{{variableFromControler | rating}}"></span></p>
// if I add a pipe, it doesn't work
<p><span [innerHtml]="variableFromControler | rating"></span></p>
any ideas?
One solution
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizationService } from '#angular/platform-browser'; // to become DomSanitizer
#Pipe({
name: 'rating',
pure: false
})
export class RatingPipe implements PipeTransform {
sanitizer: any;
constructor(private domSanitizationService: DomSanitizationService) {
this.sanitizer = domSanitizationService;
}
transform(value: any): string {
value = parseInt(value);
let stars = '';
for(let i = 1; i <= 5; i++) {
stars += i <= value ? "<ion-icon class='ion-ios-star'></ion-icon>" : "<ion-icon class='ion-ios-star-outline'></ion-icon>";
}
return this.sanitizer.bypassSecurityTrustHtml(stars);
}
}
documentation on DomSanitizationService
It won't work with this html
"<ion-icon name='star'>" + value + "</ion-icon>"
because ion-icon is a ionic-angular component and it should load via angular2 instead of just using innerHTML.
Anyway you should use DomSanitanizeService for your html pipe like this:
#Pipe({
name: 'rating',
pure: false
})
export class RatingPipe implements PipeTransform {
constructor(private domSanitizer: DomSanitizationService){}
transform(value: any): string {
let stars = "<div>" + value + "</div>";
return this.domSanitizer.bypassSecurityTrustHtml(stars);
}
}
And in your html you have to use property binding:
<span [innerHtml]="text | rating"></span>
I would leverage a custom icon wrapper for your case:
#Component({
selector: 'ion-rating-icon',
template: '<ion-icon name="star">{{rating}}</ion-icon>'
})
class RatingIconComponent {
#Input() rating;
}
Then use it like:
<ion-rating-icon [rating]="text"></ion-rating-icon>
See all examples in Plunker