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?
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With