[critical] fix(pdf): stop hex-obfuscated names erasing pdfid's indicators - #105
Open
elhoim wants to merge 1 commit into
Open
[critical] fix(pdf): stop hex-obfuscated names erasing pdfid's indicators#105elhoim wants to merge 1 commit into
elhoim wants to merge 1 commit into
Conversation
pdfid prints a keyword row as ' %-16s %7d' and appends the hex-encoded tally
as '(%d)', with no separating space, whenever any occurrence of that name was
written with an escaped character. A PDF whose action is spelled
/J#61vaScript therefore arrives as:
/JavaScript 1(1)
_extract_pdfid_count did int(line.rsplit(None, 1)[1]), which raises
ValueError on "1(1)" and returned 0. _run_pdfid keeps only indicators whose
count is > 0, so the indicator was discarded: the report came back with
has_javascript False and no "Contains JavaScript" reason, and analyze_pdf
skipped JavaScript extraction entirely because it gates that on the same
indicator. Escaping a name is the textbook way to hide it from pdfid, and
this parser turned that evasion into silence.
Parse the count with a regex that accepts the optional hexcode suffix and
keeps the total, which pdfid increments for every occurrence. Rows without
the suffix parse exactly as before, and unparseable rows still yield 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
elhoim
marked this pull request as ready for review
September 9, 2026 08:35
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.
BLUF
/J#61vaScriptinstead of/JavaScript— is reported byanalyze_pdfwithhas_javascript: Falseand no "Contains JavaScript" reason. Name escaping is the textbook way to hide from pdfid, and this parser turned it into silence._extract_pdfid_countdoesint(line.rsplit(None, 1)[1]). pdfid appends the hex-encoded tally to the count with no separating space, so the field is"1(1)",int()raisesValueError, and the function returns0._run_pdfidkeeps only indicators withcount > 0, so the indicator is discarded outright — andanalyze_pdfgates JavaScript extraction on that same indicator, so the JS is never even extracted. One badint()cascades into a clean bill of health.(N)suffix and keeps the total.src/mulder/server/tools/documents.py, one function plus one module constant.The bug
pdfid 0.2.10 formats each keyword row (
pdfid.py,PDFiD2String):and mulder parsed it:
Verified against the real tool. A minimal PDF whose OpenAction is
/S /J#61vaScript /J#53 (...), run through pdfid 0.2.10:and through today's
documents.py:A PDF that auto-executes a script is reported as having no JavaScript.
/EmbeddedFilevanishes the same way.The fix
The total is the right number to keep: in pdfid's
UpdateWords,words[name][0] += 1runs for every occurrence and the hexcode tally[1] += 1only additionally — so2(1)means two occurrences, one of which was escaped. Two tests pin that reading and the unchanged behaviour of ordinary rows.Deliberately out of scope
/JavaScripthas no legitimate purpose and is arguably an indicator in its own right; the regex captures it ashexcodebut nothing consumes it yet. Feeding it into_compute_pdf_riskchanges risk scoring and belongs in its own PR.extract_urls/extract_embeddedparameters (summary["urls"] = [],summary["embedded_files"] = []) — a separate defect, submitted separately.git merge-tree --write-tree).Verification
uvx pre-commit runover the changed files → ruff, ruff-format, mypy all pass.uv run --locked --extra dev pytest tests/ -q→ 860 passed, nothing deselected.documents.pyrestored toorigin/mainand the new tests kept, 3 of 5 fail and the 2 narrowness tests pass:Context
Recovered from closed PR #79, which bundled several unrelated PDF detection gaps into a 284-line diff. This is one of them, resubmitted on its own. No outcome framework, no shared taxonomy, no new abstraction — one regex and one function, per the review on #32:
Branched fresh from current
main(2e5432c); the closed branch was not revised in place.🤖 Generated with Claude Code