Skip to content

Safety

Why untrusted Markdown cannot execute anything.

Blasdoc treats content as potentially untrusted, because it usually is — a CMS payload, a pull request, a comment box.

Expressions are interpreted

An expression is parsed into an AST and walked by an evaluator that can reach exactly two things: the context object you passed, and the locals of the current call. The prototype chain is blocked — constructor, prototype and __proto__ are refused — so the classic escape of walking from any value to Function and compiling a string has no path to take.

ts
// Reported as BLASDOC_EXPRESSION_FAILED, never executed:
// <app-button (clicked)="x.constructor.constructor('…')()">

There is no new, no arrow function, no await, and no way to name a global.

Raw HTML is filtered

HTML in the content becomes structured IR nodes, filtered through an allow-list in the core — so every consumer of the IR gets the same guarantee, not just the Angular renderer. Dangerous tags are dropped with their subtree and reported; event-handler attributes and javascript: URLs are removed the same way.

A script tag written here never becomes a script.

You can tighten or relax the policy:

ts
provideBlasdoc({
  parseOptions: {
    htmlPolicy: {
      tags: ['div', 'span', 'kbd'],
      attributes: { '*': ['class', 'title'] },
    },
  },
});

Components come only from the registry

A tag naming a component you did not register produces a diagnostic and a comment node. There is no path from a string in the content to a class that was not explicitly provided.

The renderer never injects markup

Nodes are created through Angular's Renderer2 — createElement, setAttribute, appendChild. There is no innerHTML anywhere in the project, including in the syntax highlighting, which is why highlighting had to produce IR nodes rather than an HTML string.

What is still yours

Blasdoc protects the rendering path. It cannot protect a context you fill with dangerous functions: anything you put in the context is, by design, callable from the content.

ts
// Don't:
provideBlasdoc({ context: { fetch, localStorage } });