BUG-HUNT: [security] HTML injection vulnerability in ADR metadata rendering allows XSS attacks #7288

Open
opened 2026-04-10 15:10:08 +00:00 by HAL9000 · 4 comments
Owner

Background

Multiple functions in hooks/adr_hooks.py directly embed user-controlled content from YAML front-matter into HTML without proper sanitization. This creates Cross-Site Scripting (XSS) vulnerabilities that allow malicious content in ADR metadata to execute arbitrary JavaScript when the generated documentation is viewed in a browser.

The affected functions are _build_header, _build_timeline, and _build_related_adrs_section, which use Python f-strings to construct raw HTML by interpolating untrusted YAML values without escaping.

Current Behavior

Raw user-controlled content from ADR YAML front-matter is directly embedded into generated HTML output. An attacker who can author or modify an ADR file can inject arbitrary HTML/JavaScript that executes in the browser of any team member viewing the documentation.

Affected locations:

  • hooks/adr_hooks.py_build_header (lines 245–250): tier_title embedded unescaped
  • hooks/adr_hooks.py_build_timeline (lines 335–340): entry_people embedded unescaped
  • hooks/adr_hooks.py_build_related_adrs_section (lines 530–540): title and relationship embedded unescaped in Markdown table that renders to HTML

Evidence:

# _build_header — direct embedding of tier_title without escaping
tier_part = (
    f'<span class="adr-tier-badge">Tier {tier}</span>'
    f'<span class="adr-tier-title">{tier_title}</span>'  # UNESCAPED
)

# _build_timeline — direct embedding of entry_people without escaping
people_html = (
    f'<div class="adr-timeline-person">{ICON_PERSON}{entry_people}</div>'  # UNESCAPED
)

# _build_related_adrs_section — direct embedding of title/relationship
lines.append(f"| {adr_col} | {title} | {relationship} |")  # UNESCAPED in markdown→HTML

Attack vector — malicious ADR front-matter:

---
title: "Innocent Title<script>alert('XSS')</script>"
authors: ['<img src=x onerror=alert("XSS")>']
related_adrs:
  - number: 1
    title: "</td><script>document.cookie</script>"
    relationship: "<svg/onload=alert('XSS')>"
---

Expected Behavior

All user-controlled content sourced from YAML front-matter must be HTML-escaped before being embedded in generated HTML output. No raw user input should ever appear unescaped inside an HTML context.

Acceptance Criteria

  • html.escape() (or equivalent) is applied to every user-controlled variable before interpolation into HTML f-strings in _build_header, _build_timeline, and _build_related_adrs_section
  • A full audit of all f-string HTML generation in hooks/adr_hooks.py is performed and any additional unescaped interpolations are fixed
  • BDD scenarios cover injection of <script>, onerror, and onload payloads in all affected metadata fields, asserting the output is escaped
  • All nox stages pass with coverage ≥ 97%

Impact

  • XSS attacks against documentation viewers (MkDocs-generated site)
  • Potential credential theft or session hijacking for team members viewing documentation
  • Compromise of development team members' browsers when reviewing ADRs
  • CRITICAL security vulnerability requiring immediate attention

Supporting Information

  • File: hooks/adr_hooks.py
  • Functions: _build_header, _build_timeline, _build_related_adrs_section
  • Affected lines: 245–250, 335–340, 530–540
  • Category: security / XSS
  • Severity: Critical
  • Suggested fix: Import html.escape() and wrap all user-controlled variables before HTML embedding; consider a template engine with auto-escaping (e.g., Jinja2 with autoescape=True) for long-term hardening; add security regression tests for malicious metadata

Metadata

  • Branch: bugfix/mN-adr-hooks-xss-html-injection
  • Commit Message: fix(docs): escape HTML in ADR metadata rendering to prevent XSS attacks
  • Milestone: v3.7.0
  • Parent Epic: (orphan — see comment below)

Subtasks

  • Audit all f-string HTML generation in hooks/adr_hooks.py for unescaped user-controlled interpolations
  • Apply html.escape() to tier_title in _build_header
  • Apply html.escape() to entry_people in _build_timeline
  • Apply html.escape() to title and relationship in _build_related_adrs_section
  • Verify no other functions in the module embed unescaped user content in HTML
  • Write BDD scenarios covering XSS payloads in all affected metadata fields
  • Confirm all nox stages pass (lint, typecheck, security, unit_tests, coverage ≥ 97%)

Definition of Done

  • All unescaped user-controlled HTML interpolations in hooks/adr_hooks.py are fixed
  • BDD scenarios assert that <script>, onerror, and onload payloads in ADR front-matter are escaped in rendered output
  • No regressions in existing ADR rendering tests
  • nox -s security_scan passes with no new findings
  • All nox stages pass
  • Coverage >= 97%

Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: new-issue-creator

## Background Multiple functions in `hooks/adr_hooks.py` directly embed user-controlled content from YAML front-matter into HTML without proper sanitization. This creates Cross-Site Scripting (XSS) vulnerabilities that allow malicious content in ADR metadata to execute arbitrary JavaScript when the generated documentation is viewed in a browser. The affected functions are `_build_header`, `_build_timeline`, and `_build_related_adrs_section`, which use Python f-strings to construct raw HTML by interpolating untrusted YAML values without escaping. ## Current Behavior Raw user-controlled content from ADR YAML front-matter is directly embedded into generated HTML output. An attacker who can author or modify an ADR file can inject arbitrary HTML/JavaScript that executes in the browser of any team member viewing the documentation. **Affected locations:** - `hooks/adr_hooks.py` — `_build_header` (lines 245–250): `tier_title` embedded unescaped - `hooks/adr_hooks.py` — `_build_timeline` (lines 335–340): `entry_people` embedded unescaped - `hooks/adr_hooks.py` — `_build_related_adrs_section` (lines 530–540): `title` and `relationship` embedded unescaped in Markdown table that renders to HTML **Evidence:** ```python # _build_header — direct embedding of tier_title without escaping tier_part = ( f'<span class="adr-tier-badge">Tier {tier}</span>' f'<span class="adr-tier-title">{tier_title}</span>' # UNESCAPED ) # _build_timeline — direct embedding of entry_people without escaping people_html = ( f'<div class="adr-timeline-person">{ICON_PERSON}{entry_people}</div>' # UNESCAPED ) # _build_related_adrs_section — direct embedding of title/relationship lines.append(f"| {adr_col} | {title} | {relationship} |") # UNESCAPED in markdown→HTML ``` **Attack vector — malicious ADR front-matter:** ```yaml --- title: "Innocent Title<script>alert('XSS')</script>" authors: ['<img src=x onerror=alert("XSS")>'] related_adrs: - number: 1 title: "</td><script>document.cookie</script>" relationship: "<svg/onload=alert('XSS')>" --- ``` ## Expected Behavior All user-controlled content sourced from YAML front-matter must be HTML-escaped before being embedded in generated HTML output. No raw user input should ever appear unescaped inside an HTML context. ## Acceptance Criteria - `html.escape()` (or equivalent) is applied to every user-controlled variable before interpolation into HTML f-strings in `_build_header`, `_build_timeline`, and `_build_related_adrs_section` - A full audit of all f-string HTML generation in `hooks/adr_hooks.py` is performed and any additional unescaped interpolations are fixed - BDD scenarios cover injection of `<script>`, `onerror`, and `onload` payloads in all affected metadata fields, asserting the output is escaped - All nox stages pass with coverage ≥ 97% ## Impact - XSS attacks against documentation viewers (MkDocs-generated site) - Potential credential theft or session hijacking for team members viewing documentation - Compromise of development team members' browsers when reviewing ADRs - CRITICAL security vulnerability requiring immediate attention ## Supporting Information - **File**: `hooks/adr_hooks.py` - **Functions**: `_build_header`, `_build_timeline`, `_build_related_adrs_section` - **Affected lines**: 245–250, 335–340, 530–540 - **Category**: security / XSS - **Severity**: Critical - **Suggested fix**: Import `html.escape()` and wrap all user-controlled variables before HTML embedding; consider a template engine with auto-escaping (e.g., Jinja2 with `autoescape=True`) for long-term hardening; add security regression tests for malicious metadata --- ## Metadata - **Branch**: `bugfix/mN-adr-hooks-xss-html-injection` - **Commit Message**: `fix(docs): escape HTML in ADR metadata rendering to prevent XSS attacks` - **Milestone**: v3.7.0 - **Parent Epic**: *(orphan — see comment below)* ## Subtasks - [ ] Audit all f-string HTML generation in `hooks/adr_hooks.py` for unescaped user-controlled interpolations - [ ] Apply `html.escape()` to `tier_title` in `_build_header` - [ ] Apply `html.escape()` to `entry_people` in `_build_timeline` - [ ] Apply `html.escape()` to `title` and `relationship` in `_build_related_adrs_section` - [ ] Verify no other functions in the module embed unescaped user content in HTML - [ ] Write BDD scenarios covering XSS payloads in all affected metadata fields - [ ] Confirm all nox stages pass (lint, typecheck, security, unit_tests, coverage ≥ 97%) ## Definition of Done - [ ] All unescaped user-controlled HTML interpolations in `hooks/adr_hooks.py` are fixed - [ ] BDD scenarios assert that `<script>`, `onerror`, and `onload` payloads in ADR front-matter are escaped in rendered output - [ ] No regressions in existing ADR rendering tests - [ ] `nox -s security_scan` passes with no new findings - [ ] All nox stages pass - [ ] Coverage >= 97% --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: new-issue-creator
HAL9000 added this to the v3.7.0 milestone 2026-04-10 15:10:16 +00:00
Author
Owner

⚠️ Action Required: Labels and Parent Epic

Labels to Apply

This issue requires the following labels (automated label application is currently unavailable):

  • State/Unverified
  • Type/Bug
  • Priority/Critical

Parent Epic — Orphan Issue

No parent Epic for the ADR hooks / documentation rendering subsystem was found in the open issue tracker. This issue is currently an orphan and must be linked to a parent Epic before work begins.

Required action: A maintainer must:

  1. Identify or create the appropriate parent Epic for ADR documentation rendering / MkDocs hooks
  2. Link this issue as a child (this issue blocks the parent Epic) using Forgejo's dependency system

TDD Companion Issue Required

Per CONTRIBUTING.md § Bug Fix Workflow, a paired Type/Testing issue with title prefix TDD: must be created before this bug can be fixed. The bug fix issue must depend on the TDD issue.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: new-issue-creator

## ⚠️ Action Required: Labels and Parent Epic ### Labels to Apply This issue requires the following labels (automated label application is currently unavailable): - `State/Unverified` - `Type/Bug` - `Priority/Critical` ### Parent Epic — Orphan Issue No parent Epic for the ADR hooks / documentation rendering subsystem was found in the open issue tracker. This issue is currently an **orphan** and must be linked to a parent Epic before work begins. **Required action:** A maintainer must: 1. Identify or create the appropriate parent Epic for ADR documentation rendering / MkDocs hooks 2. Link this issue as a child (this issue **blocks** the parent Epic) using Forgejo's dependency system ### TDD Companion Issue Required Per CONTRIBUTING.md § Bug Fix Workflow, a paired `Type/Testing` issue with title prefix `TDD:` must be created before this bug can be fixed. The bug fix issue must **depend on** the TDD issue. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: new-issue-creator
Author
Owner

Verified — Security bug: HTML injection in ADR metadata rendering allows XSS. MoSCoW: Must-have. Priority: High.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Security bug: HTML injection in ADR metadata rendering allows XSS. MoSCoW: Must-have. Priority: High. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Security bug: HTML injection in ADR metadata rendering allows XSS. MoSCoW: Must-have. Priority: High.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Security bug: HTML injection in ADR metadata rendering allows XSS. MoSCoW: Must-have. Priority: High. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Security bug: HTML injection in ADR metadata rendering allows XSS. MoSCoW: Must-have. Priority: High.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Security bug: HTML injection in ADR metadata rendering allows XSS. MoSCoW: Must-have. Priority: High. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#7288
No description provided.