Skip to content

Render: sanitize lets Mermaid directives and angle brackets through from role text #29

Description

@jbeda

Found by the adversarial fixture run during review of #28. Pre-existing on main, not
a regression from that PR
— filing separately so it doesn't creep into its scope.

sanitize in internal/render/mermaid/mermaid.go neutralizes backticks, quotes, square
brackets and newlines, but passes <, > and %% through untouched.

1. A role can restyle the entire diagram

Mermaid directives use %%{...}%%. A role containing one is emitted verbatim into the
generated diagram source and interpreted as a directive rather than as label text:

relationships:
  - entity: Policy
    cardinality: "1:n"
    role: "%%{init:{'theme':'dark'}}%%"

The rendered diagram comes out restyled. A directive is not limited to theming, so the
blast radius is whatever the consuming Mermaid build honours in a directive block.

This matters because *.modelith.yaml files are checked into repositories and rendered
into Markdown that gets committed and published — the input is not always written by the
person reading the output.

2. Angle brackets silently vanish

role: "<angle>"

renders as an empty-looking label — Mermaid's label handling treats it as markup. A model
author using <...> as a placeholder convention loses the text with no diagnostic.

Repro

Fixture and full evidence matrix from the #28 review round:

  • .scratch/reviews/pr28-fixtures/f16-html-inject.modelith.yaml
  • .scratch/reviews/pr28-adversarial.md
modelith render f16-html-inject.modelith.yaml
npx -y @mermaid-js/mermaid-cli@11 -i out.mmd -o out.png -b white

Confirmed identical on main and on the #28 branch.

Suggested fix

Extend sanitize to neutralize %% and to escape or replace < / >. Worth auditing at
the same time whether any other Mermaid metacharacter reaches a label unescaped, and
whether the Markdown renderer has the same class of gap — sanitize only guards the
Mermaid path.

Add fixtures for these to the renderer tests so the guarantee is pinned rather than
assumed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions