other
- Location
scripts/format_report.py:42- Finding
Untrusted GitHub Release Notes Are Emitted Verbatim into Agent-Facing Markdown
- Content
View full analysis
Vulnerability Details
File Location:
scripts/check_github.py:62-74,scripts/compare_versions.py:113-119, andscripts/format_report.py:42-48, 89-99
Vulnerability Type: Indirect prompt injection and unsafe Markdown rendering
Risk Level: MediumComplete Code Snippets
scripts/check_github.py:62-74retrieves the externally controlled release body:python releases = [] for r in data: releases.append({ "source": "github", "owner": owner, "repo": repo, "tag": r.get("tag_name", ""), "name": r.get("name", "") or r.get("tag_name", ""), "body": r.get("body", "") or "", "published_at": r.get("published_at", ""), "prerelease": r.get("prerelease", False), "draft": r.get("draft", False), "url": r.get("html_url", ""), })scripts/compare_versions.py:113-119propagates the data unchanged:python new_releases.append({ **r, "key": key, "display_name": entry.get("name", f"{owner}/{repo}"), "previous_version": last_seen, })scripts/format_report.py:42-48selects release-note lines without sanitization:python def summarize_body(body: str, max_lines: int = 8) -> str: """Return first meaningful lines of release body, trimmed.""" if not body or not body.strip(): return "" lines = [l.rstrip() for l in body.splitlines() if l.strip()] return "\n".join(lines[:max_lines])scripts/format_report.py:89-99inserts those lines directly into Markdown:python # Summary summary = summarize_body(body) if summary: lines.append("### What's New") lines.append(summary) lines.append("") else: lines.append("*No release notes provided.*") lines.append("")Technical Analysis
GitHub release bodies cross an external trust boundary. They may be authored by repository maintainers, an attacker controlling a watched repository, or an attacker who has compromised a legitimate repository account. ...[truncated 2430 chars]
- Remediation
View remediation
Remediation Suggestions
- Treat all release metadata as untrusted data. Clearly delimit externally sourced text and explicitly state in the agent workflow that release-note content must never be interpreted as instructions.
- Generate plain-text summaries instead of reproducing source Markdown. Strip Markdown and HTML before including release notes in reports.
- Escape active Markdown constructs. At minimum, neutralize raw HTML, images, embedded resources, and unsafe link schemes. Permit links only for an explicit scheme allowlist such as
https. - Render external text as quoted data. Prefix each release-note line with a Markdown blockquote marker after sanitization, or place it in another inert representation appropriate to the consumer.
- Apply strict output limits. Enforce maximum line lengths, total character limits, and maximum numbers of extracted items.
- Sanitize breaking-change extraction as well.
detect_breaking_changes()also returns attacker-controlled lines and must use the same sanitization procedure. - Keep trusted labels separate from remote content. Do not allow release bodies to affect report headings, delimiters, or other structural elements.
- Add adversarial tests. Cover prompt-like instructions, raw HTML, Markdown images, nested links, control characters, and oversized release bodies.
