Escape the evidence media name in report media tags - #393
Merged
Conversation
Port of the iLEAPP change. html_media_tag() interpolated the media item's name straight into the title= attribute of the <img> it builds, and both it and media_to_html() dropped the raw evidence filename into the fallback anchor text. A crafted attachment filename closed the attribute and ran in the examiner's report (stored XSS, CWE-79). This reaches every media column tool-wide, because the framework adds media columns to the no-escape list itself. safe_local_path() is added to html_safe.py: it percent-encodes a report-relative path for an href/src attribute and returns '' for anything not report-relative, so a crafted media name can neither point the report at a remote host nor climb out of the report folder. _is_report_relative() is factored out of safe_local_link() so both helpers apply one rule. media_to_html() passes allow_parent=True, because its relative_paths() deliberately emits ../data/... to reach the extraction folder beside the report. That case is now permitted explicitly instead of silently. The media name is esc()'d in title= and in the fallback link text, the image style is esc()'d, and the malformed </> closing the fallback anchor is </a>. Validated: py_compile clean; unit tests OK; PluginLoader loads every plugin; a filename of x" onerror=alert(1) t=".jpg renders escaped with no attribute breakout, and a normal HEIC still renders its thumbnail unchanged. Co-Authored-By: Claude Opus 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.
Why
html_media_tag()interpolated the media item's name straight into thetitle=attribute of the<img>it builds, and both it andmedia_to_html()dropped the raw evidence filename into the fallback anchor text. A crafted attachment filename closed the attribute and ran in the examiner's report (stored XSS, CWE-79).GHSA-45q2-q93c-cfv2 fixed the artifact-side
html_columnsbuilders in July and explicitly deferred this one. It reaches every media column tool-wide — including in cores where no artifact declareshtml_columnsby hand, because the framework adds media columns to the no-escape list itself.What changed
safe_local_path()is added tohtml_safe.py. It percent-encodes a report-relative path for anhref/srcattribute and returns''for anything that is not report-relative, so a crafted media name can neither point the report at a remote host nor climb out of the report folder._is_report_relative()is factored out ofsafe_local_link()so both helpers apply one rule.media_to_html()passesallow_parent=True. Itsrelative_paths()deliberately emits../data/...to reach the extraction folder beside the report — part of the report layout, not an escape — so that case is permitted explicitly rather than silently.In both functions the media name is now
esc()'d intitle=and in the fallback link text, the image style isesc()'d, and the malformed</>that closed the fallback anchor is now</a>.Before / after
A media filename of
x" onerror=alert(1) t=".jpg:The evidence is fully preserved in both cases; only the breakout is gone.
Validation
py_compileclean;lint_changedreports no new warningsPluginLoaderloads every plugin<script>filename renders inert in the fallback link" onload="is escaped🤖 Generated with Claude Code