Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running scripts in trusted HTML with Angular 2

What I've got

I'm building a personal blog app with Angular 2. I have blog posts in a JSON file which is served from my server.

// Single post route
apiRouter.route("/post/:id")
    // Get a specific post
    .get((req: express.Request, res: express.Response) => {
        let post = blogData.posts.find(post => post.date === Number(req.params.id));
        res.json(post);
    })
);

Individual entries in the JSON blog data file look like this:

"posts": [
  {
    "title": "Some Post's Title",
    "body": "Some post's body.",
    "date": 1481582451092,
    "headerImageName": ""
  },
  { ... }, ...
]

In my webapp I want to have a single "Blog Post" typescript component that displays an individual post when a visitor is on a route which maps to a single post.

I've defined a simple post data class like this:

export class PostData {
    body: string;
    date: number;
    imageFileName: string;
    title: string;
}

When displaying the post body I pass it through this pipe:

@Pipe({
    name: "trustPipe"
})
export class TrustPipe implements PipeTransform {

    constructor(@Inject(DomSanitizer) private sanitizer: DomSanitizer) { }

    transform(html: string): SafeHtml {
        return this.sanitizer.bypassSecurityTrustHtml(html);
    }
} 

In order to display it, I've written the following component:

import { TrustPipe } from "./trust.pipe";

@Component({
    selector: "active-component",
    template: `
    <div class="card">
        <div *ngIf="post">
            <div class="card-title">
                <span>{{ post.title }}</span>
            </div>
            <div [innerHTML]="post.body | trustPipe"></div>
        </div>
    </div>`,
    styleUrls: ["styles/post.component.css", "styles/card.css"]
})
export class PostComponent implements OnInit {
  
    // Post is a superclass to PostData, it just provides
    // some helper functions on the base class, such as truncation
    // and a means of formatting dates
    post: Post;

    constructor(
        @Inject(BlogService) private blogService: BlogService,
        @Inject(ActivatedRoute) private activatedRoute: ActivatedRoute
    ) { }

    ngOnInit(): void {
        this.activatedRoute.params.forEach((params: Params) => {
            const id: number = +params["id"];
            this.blogService
                .getPost(id)
                .then(data => this.post = new Post(data));
        });
    }
}

What Does this all do?

With that out of the way, the important takeaway is that I have some variant string (JSON for a post's body) which represents a section of HTML I'd like to insert as a child to some DOM element. To do so I used the [innerHTML] attribute and wrote an Angular Pipe that uses bypassSecurityTrustHtml for trusting the string as safe HTML.

What happens?

Script tags don't execute. Say I've written a blog post which has something embedded in it - in my case it's likely a Github Gist, Instagram photo, or Google photos album. I found that this is true - script tags don't execute - and a means of using Javascript to circumvent it, but I want to know if angular has a means of inserting HTML like I'm trying. I have seen usage of ComponentFactory but I don't know if this is what I want. I'm guessing this will also stop me from using routerLinks in my post's bodies. I could write a child component for the post body, but then how do I dynamically swap out templates?

Questions

a) Why does Angular stop scripts from running?

b) Any other means to get scripts to run?

c) Should I use a different design pattern for this - serving posts as HTML for example?

like image 421
Zev Isert Avatar asked Oct 29 '22 15:10

Zev Isert


1 Answers

a) Security and in particular protection against XSS - https://angular.io/docs/ts/latest/guide/security.html

b) Yes but but be very careful - https://angular.io/docs/ts/latest/guide/security.html#!#bypass-security-apis

Have you tried bypassing the security apis in your component instead of a pipe? Also what security context are you using?

c) Can you parse the blog post and save it in a safe format or do you really want to allow any js to run on your website?

like image 74
shusson Avatar answered Nov 15 '22 07:11

shusson