Reputation: 330
I need to make a private service like codepen.io. User must have the possibility of creating mixed content with included scripts, styles, inline-scripts, and inline-styles (yes, XSS-unsafe content with a lot of vulnerabilities).
My service has an iframe and Monaco code editor. How should I insert user-generated HTML, styles, and scripts to the iframe without Angular XSS-defence and sanitizing?
How can I do it with Angular 7?
Maybe exists a method of manually marking user-generated content as sanitized?
Important pieces of code.
view.component.html:
<iframe id="view" [srcdoc]="resultPage | safe: 'html'"></iframe>
view.component.ts:
@Component({
selector: 'app-view',
templateUrl: './view.component.html',
styleUrls: ['./view.component.scss']
})
export class ViewComponent implements OnInit {
resultPage = '';
_task: Task;
set task(value: Task) {
this._task = value;
if (value) {
this.refreshPage();
}
}
get task() {
return this._task;
}
constructor(private store: Store<RootStoreState.State>) { }
ngOnInit() {
this.store.pipe(select(selectSelectedTask))
.subscribe((task) => {
this.task = task;
});
}
refreshPage() {
if (!this.task) {return; }
const htmlFile = this.task.files.find((item) => item.language === 'html')
const cssFile = this.task.files.find((item) => item.language === 'css')
const jsFile = this.task.files.find((item) => item.language === 'js')
let html = htmlFile ? htmlFile.content : null;
if (!html) {
return;
}
let css = cssFile ? cssFile.content : null;
let js = jsFile ? jsFile.content : null;
html = this.insertCss(html, css);
this.resultPage = html;
}
insertCss(html: string, css: string): string {
const cssPlaceholder = `<style>${css}</style>`;
const closedHead = html.indexOf('</head>');
if (closedHead > -1) {
html.replace('</head>', cssPlaceholder + '</head>');
console.log(1, html);
return html;
}
const openedBody = html.indexOf('<body>');
if (openedBody > -1) {
html.replace('<body>', '<body>' + cssPlaceholder);
console.log(2, html);
return html;
}
html += css;
console.log(3, html);
return html;
}
}
Upvotes: 1
Views: 1767
Reputation: 388
I have encountered a similiar issue. I found a work around as follows.
Instead of using srcdoc directive
provided by angular, I created a custom directive to set srcdoc attribute
of iframe element
Here is the code
srcdoc.directive.ts
import { Directive, Input, ElementRef, Renderer2, OnChanges, SimpleChanges } from "@angular/core";
@Directive({
selector : '[app-srcdoc]'
})
export class SrcdocDirective implements OnChanges{
// add data binding to directive itself
@Input("app-srcdoc") source:string;
constructor(private elementRef:ElementRef,private renderer:Renderer2) {}
// update the srcdoc attribute whenever the binding changes
ngOnChanges(changes: SimpleChanges): void {
this.renderer.setAttribute(this.elementRef.nativeElement,"srcdoc",changes.source.currentValue);
}
}
use it your component html as follows
<iframe id="output" [app-srcdoc]="iframeContent" ></iframe>
Upvotes: 1
Reputation: 347
Try using DomSanitizer
Calling any of the bypassSecurityTrust... APIs disables Angular's built-in sanitization for the value passed in
Upvotes: 0