prqlc Debug Report HTML Does Not Escape Source File Name, Enabling Stored XSS via PRQLC_DEBUG_LOG
Maintainers usually reply within 3 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 75/100
Research direction
Start with prqlc/prqlc/src/debug/render_html.rs, especially write_repr_prql() and write_key_values(), where the issue identifies the path rendering and escaping order. Check existing HTML escaping tests and add coverage for a file name containing a backslash and double quote. Done when the debug report renders that path without allowing injected HTML to execute.
Written by the indexing model from the issue text.
Description
What happened?
prqlc's debug-report renderer write_repr_prql() in prqlc/prqlc/src/debug/render_html.rs correctly HTML-escapes the source contents it embeds in <code id="source-{id}">, but the file path rendered one line earlier by write_key_values(w, &[("path", &path), ...]) is passed through escape_html(&format!("{v:?}")), i.e. escape_html is applied to the Debug representation of the PathBuf rather than to the raw path string. For a file name containing both a backslash and a double quote, the Debug formatter first turns the quote into \", after which escape_html emits the backslash literally. The HTML parser then treats that backslash as a literal \ followed by a real attribute-closing quote, breaking out of the attribute context and allowing an attacker-controlled <svg/onload=...> (or any tag) to execute in the context of the debug report. Because the report is generated by design and is commonly treated as a trusted, shareable artifact (support troubleshooting, issue attachments, docs sites), the injection lands in a high-trust product.
PRQL input
prqlc's debug-report renderer `write_repr_prql()` in `prqlc/prqlc/src/debug/render_html.rs` correctly HTML-escapes the source *contents* it embeds in `<code id="source-{id}">`, but the *file path* rendered one line earlier by `write_key_values(w, &[("path", &path), ...])` is passed through `escape_html(&format!("{v:?}"))`, i.e. `escape_html` is applied to the `Debug` representation of the `PathBuf` rather than to the raw path string. For a file name containing both a backslash and a double quote, the `Debug` formatter first turns the quote into `\"`, after which `escape_html` emits the backslash literally. The HTML parser then treats that backslash as a literal `\` followed by a real attribute-closing quote, breaking out of the attribute context and allowing an attacker-controlled `<svg/onload=...>` (or any tag) to execute in the context of the debug report. Because the report is generated by design and is commonly treated as a trusted, shareable artifact (support troubleshooting, issue attachments, docs sites), the injection lands in a high-trust product.
SQL output
prqlc's debug-report renderer `write_repr_prql()` in `prqlc/prqlc/src/debug/render_html.rs` correctly HTML-escapes the source *contents* it embeds in `<code id="source-{id}">`, but the *file path* rendered one line earlier by `write_key_values(w, &[("path", &path), ...])` is passed through `escape_html(&format!("{v:?}"))`, i.e. `escape_html` is applied to the `Debug` representation of the `PathBuf` rather than to the raw path string. For a file name containing both a backslash and a double quote, the `Debug` formatter first turns the quote into `\"`, after which `escape_html` emits the backslash literally. The HTML parser then treats that backslash as a literal `\` followed by a real attribute-closing quote, breaking out of the attribute context and allowing an attacker-controlled `<svg/onload=...>` (or any tag) to execute in the context of the debug report. Because the report is generated by design and is commonly treated as a trusted, shareable artifact (support troubleshooting, issue attachments, docs sites), the injection lands in a high-trust product.
Expected SQL output
MVCE confirmation
- Minimal example
- New issue
Anything else?
No response
- Dominant language
- Rust
- Stars
- 10.9k
- Forks
- 261
- Avg merge
- 3d 21h
- Merged PRs (30d)
- 84
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from PRQL/prql
-
Underscore does not work as expectedPossibly taken @prql-bot claimed this 2 days ago. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 3 days
-
friendliness integrations
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
PRQL/prql#1636 · 2 comments · 2 reactions ·
Maintainers usually reply within 3 days
-
Difficulty 4/5 1-2 days Newbie friendliness 12/100
Maintainers usually reply within 3 days
-
review-runs-tracking
Difficulty 3/5 Half a day Newbie friendliness 5/100
Maintainers usually reply within 3 days
-
Formatted SQL adds spaces inside the next string literal after a string ending in a backslashMay be free again A pull request for this issue was closed without being merged. Openbug
Difficulty 4/5 1-2 days Newbie friendliness 38/100
Maintainers usually reply within 3 days
Similar issues
-
github_actions security
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
dekobon/big-code-analysis#1815 ·
Maintainers usually reply within 1 day
-
area: png-glitch bug P0 security
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
chikoski/png-glitch#45 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 85/100
nekko-labs/nekko-agent#1756 ·
Maintainers usually reply within 1 day
-
upstream
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
yologdev/yoyo-evolve#1138 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
gitbutlerapp/gitbutler#16982 ·
Maintainers usually reply within 1 day