Skip to content

Cover HTML renderer inserts raw document metadata as markup #416

Description

@MicroMilo

Summary

skills/minimax-pdf/scripts/cover.py interpolates user-controlled document tokens directly into generated cover HTML. Values such as title, subtitle, and similar metadata are inserted without HTML escaping, so plain text containing markup is rendered as real HTML instead of text. This can break generated covers and can execute active markup if the HTML is opened in a browser or converted by an HTML renderer that honors scripts/events.

Code path

  • skills/minimax-pdf/scripts/cover.py:83-89 builds the optional subtitle block with raw t['subtitle'].
  • skills/minimax-pdf/scripts/cover.py:142, 219, 353, 459, 567, 669, 822, 961, 1063, 1165, 1332, and 1494 insert raw t['title'] across cover patterns.
  • skills/minimax-pdf/scripts/cover.py:1532-1536 dispatches the selected pattern from render(tokens).
  • skills/minimax-pdf/scripts/cover.py:1548-1562 reads tokens.json and optional --subtitle before rendering the HTML file.

Steps to reproduce

Validation level: dynamic reproduction plus source-control-flow inspection.

import importlib.util
from pathlib import Path

def load_module(rel_path):
    path = Path(rel_path)
    spec = importlib.util.spec_from_file_location(path.stem, path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

palette = load_module("skills/minimax-pdf/scripts/palette.py")
cover = load_module("skills/minimax-pdf/scripts/cover.py")

payload = "<script>alert(1)</script>"
tokens = palette.build_tokens(title=payload, doc_type="academic", author=payload)
tokens["subtitle"] = payload
html = cover.render(tokens)

print(payload in html)
print(html[html.find(payload) - 60 : html.find(payload) + len(payload) + 60])

Observed output:

True
<div class="first-word"><script>alert(1)</script></div>

Expected behavior

Document metadata should be treated as text by default. If a title, subtitle, or author contains characters such as <, >, or &, the generated cover should contain escaped text such as &lt;script&gt;...&lt;/script&gt;, not executable HTML.

Actual behavior

The generated cover HTML contains the raw payload. This turns text metadata into markup and may execute active content when the cover HTML is viewed or processed by an HTML renderer.

Existing coverage

I searched current issues and PRs for cover.py, cover.render, _pattern_*, title, subtitle, script, html escape, XSS, and minimax-pdf. I did not find an existing issue or PR that covers this root cause.

Suggested fix

Escape text-valued tokens before interpolation into HTML, for example with html.escape(..., quote=True). Keep CSS/color/font tokens validated separately instead of blindly escaping every token, because those values are used inside CSS declarations.

A small helper such as text_token(t, key) could make the pattern templates consistent and avoid missing one cover pattern.

Suggested tests

  • Add a regression test that renders every cover pattern with a title like <script>alert(1)</script> and asserts the raw payload is absent while the escaped text is present.
  • Add a subtitle-specific test, because --subtitle can override the token value at the CLI layer.
  • Add a test for ordinary text containing & and <2026> to ensure non-malicious markup-like titles are displayed literally.

Submitted with Codex.

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