Repository navigation
fix(security): sanitize color params and escape title to stop SVG/HTML injection - #4
Merged
Merged
Conversation
…L injection The /graph endpoint interpolated custom_title and all *_color query params directly into the generated SVG (a <style> block, fill/stroke attributes, and a <foreignObject><h1> title) with no validation or escaping. A crafted custom_title or color value could break out and inject a <script> tag, which executes since the response is served as image/svg+xml with no CSP when the URL is navigated to directly or embedded via <object>/<iframe>. Verified with a PoC (payloads like `color=fff} </style><script>...` and `custom_title=</h1><script>...`) that the raw <script> tag survived unmodified before this change. - Whitelist color query params to hex-digit-only before use, falling back to the theme default otherwise. - HTML-escape the resolved title before it reaches the SVG template. - Add a Content-Security-Policy: script-src 'none'; sandbox header on SVG responses as defense in depth. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR hardens the /graph SVG output against injection by sanitizing user-controlled color query parameters, escaping the rendered title, and adding a restrictive CSP header as defense in depth.
Changes:
- Add
sanitizeColor(hex-only) for all color query params and apply theme-default fallbacks. - HTML-escape the resolved graph title before embedding it into the SVG
<foreignObject>. - Add
Content-Security-Policy: script-src 'none'; sandboxfor SVG responses and add regression tests for sanitization/escaping.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/utils.ts |
Adds color sanitization, title escaping, and sets CSP header on SVG responses. |
src/handlers.ts |
Adds CSP header on the error SVG response path. |
__test__/utils.test.ts |
Adds regression tests for malicious color params and custom_title escaping. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Restrict HEX_COLOR to the lengths CSS/SVG actually accept (3, 4, 6, 8) instead of any 1-8 digit run, so an accepted-but-invalid length (e.g. 1, 2, 5, 7 digits) can no longer render as a broken color. - Assert the Content-Security-Policy header is present on both the success and error /graph route tests, not just Content-Type and Cache-Control. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/graphinterpolatedcustom_titleand every*_colorquery param directly into the generated SVG — a<style>block,fill/strokeattributes, and a<foreignObject><h1>title — with no validation or escaping.A crafted
color/bg_color/border_color/title_color/line/point/area_colororcustom_titlevalue can break out of its context and inject a<script>tag. Since the response is served asimage/svg+xmlwith no CSP, that script executes when the URL is navigated to directly or embedded via<object>/<iframe>.Verified with a PoC before this fix — e.g.
color=fff} </style><script>alert(document.domain)</script><style>{orcustom_title=</h1><script>alert(1)</script>— the raw<script>tag survived unmodified in the response body.Not previously reported anywhere I could find (no advisory on this repo, the upstream repo, or the fork). The sibling project this one is modeled after,
github-readme-stats, had a similar bug class (CVE-2020-23986, fixed in PR #255).Changes
src/utils.ts: whitelist all color query params to hex-digit-only (sanitizeColor), falling back to the theme default on anything else. HTML-escape the resolved title (escapeHtml) before it reaches the SVG template.src/utils.ts/src/handlers.ts: addContent-Security-Policy: script-src 'none'; sandboxon SVG responses as defense in depth, so a future injection bug can't execute script even if it slips past validation.__test__/utils.test.ts: regression tests covering malicious color params (fall back to theme default) and a maliciouscustom_title(HTML-escaped, no raw<script>in output).Test plan
npx tsc --noEmit— cleannpx jest— all tests pass except one pre-existing network-dependent test that needs a real GitHubTOKEN(fails identically on the base branch, unrelated to this change)<script>survives and malicious colors fall back to theme defaults🤖 Generated with Claude Code