I found this issue while reviewing Memray, Bloomberg’s Python memory profiler, with a simple question in mind:
Can attacker-controlled runtime metadata cross into browser-rendered report output unsafely?
In this case, the answer was yes.
The bug was in Memray’s HTML report generation path, where command-line metadata was rendered into a browser-opened report without escaping. That turned an operational field into an executable HTML sink and ultimately became CVE-2026-32722.
Project: Memray on GitHub
Advisory: GHSA-r5pr-887v-m2w9 /CVE-2026-32722
attacker-controlled argv → metadata.command_line → HTML template sink without escaping → raw markup in generated report → browser-side JavaScript execution
Memray is a Python memory profiler.
It instruments a Python process, records allocation behavior, and produces reports that help developers understand:
Some of those reports are emitted as HTML and opened in a browser.
That makes report generation a real security boundary.
The relevant question is not whether Memray is a “local tool.”
The relevant question is whether attacker-controlled data can cross into browser-rendered output unsafely.
In this case, it could.
A lot of people underestimate developer tooling.
That is a mistake.
Once a tool:
it inherits the same output-encoding risks as a web application.
That was the core issue here.
This bug was not in profiling logic. It was not in allocation tracking. It was not in native memory handling.
It was a classic trust-boundary failure:
That is enough to create a real vulnerability.
I did not approach Memray by fuzzing random CLI options or chasing crashes.
The stronger approach was to identify the highest-probability security surface first.
For Memray, that was HTML report generation.
Why?
Because HTML output introduces a browser sink, and browser sinks turn ordinary metadata bugs into security issues if:
That is exactly what happened here.
The bug reduces to two lines.
In:
the Jinja environment is created without autoescape.
Then in:
metadata.command_line is rendered directly into HTML.
That is the whole vulnerability.
Because metadata.command_line is attacker-influenced.
Memray records the command line used to run the profiled program.
That means user-controlled values from argv are preserved as metadata and later inserted into the report.
So the exploit chain is straightforward:
metadata.command_lineThat turns metadata into executable browser content.
The important distinction is execution.
Plenty of bugs produce malformed HTML. That alone is not enough.
Here, attacker-controlled content was not merely visible in the page source. It was interpreted by the browser as active HTML and executed as JavaScript.
That is the difference between:
So the question was not:
“Can HTML appear in the report?”
The real question was:
“Can attacker-controlled HTML become executable when the report is opened?”
The answer was yes.
My initial reproducer used an explicit script plus an attacker-controlled argument:
The generated HTML contained raw attacker-controlled markup:
Opening or refreshing the generated report triggered JavaScript execution.
That established the core claim:
Later, during coordinated disclosure, the maintainer simplified the reproducer further:
That version is better because it isolates the vulnerable boundary more directly:
The payload was intentionally simple:
This is not about flashy payloads.
It is a clean execution probe because:
For this class of bug, that is enough.
One report type would already have been enough to justify the issue.
But I wanted to know whether this was isolated or structural.
I confirmed the same behavior in:
--no-webThat mattered for two reasons.
It showed the vulnerable sink was reused across multiple HTML outputs.
It proved the bug was not dependent on external CDN-hosted assets.
--no-web still reproduced the issue, which means the problem was in Memray’s own generated HTML and template handling, not in remote JS behavior.
That made the case much stronger.
The maintainer’s main concern was practical attacker control.
That is fair.
This is not the kind of issue where an unauthenticated remote attacker hits an exposed HTTP endpoint and gets instant impact.
The exploit condition is narrower:
So the realistic classification was low severity.
That does not make it a weak bug.
Severity is about exploit conditions and likely impact. Validity is about whether the issue is real.
This issue was clearly real:
That is why it still became a CVE.
The fix was minimal and correct.
The maintainer changed:
to:
That is the right fix because it addresses the vulnerable sink directly.
Instead of emitting raw markup like:
the template now emits escaped text:
That preserves the informational value of the command-line field while removing the browser execution path.
The maintainer also reviewed the rest of the template context and concluded that:
|tojson
So the issue was correctly narrowed to metadata.command_line.That is exactly the kind of fix review you want in a real disclosure.
This was reported privately through GitHub Security Advisories.
The maintainers:
The issue was assigned: CVE-2026-32722
The key lesson here is simple:
metadata is not automatically trusted just because it looks operational.
None of that matters once attacker-controlled content crosses into browser-rendered output without escaping.
The moment a tool emits HTML, it needs to be treated like an HTML-producing application.
That is the real takeaway.
This vulnerability was not about a clever payload.
It was about identifying the right boundary.
Memray took attacker-influenced command-line metadata and rendered it into generated HTML without escaping it. The browser did the rest.
That is why this became CVE-2026-32722.
Fixed in Memray 1.19.2.