[high] fix(pdf): implement the advertised extract_urls and extract_embedded - #118
Draft
elhoim wants to merge 1 commit into
Draft
[high] fix(pdf): implement the advertised extract_urls and extract_embedded#118elhoim wants to merge 1 commit into
elhoim wants to merge 1 commit into
Conversation
analyze_pdf accepts extract_urls and extract_embedded, documents both in its
docstring, and echoes both into the audited params -- then built its result
as:
summary["urls"] = []
summary["embedded_files"] = []
with no code path anywhere that populated either. A PDF carrying a link to an
attacker-controlled host and an embedded evil.exe was reported as having no
URLs and no embedded files. Because nothing was produced, nothing was indexed
either, so search() over the case could never surface them.
Add _extract_pdf_urls and _extract_pdf_embedded_files over a shared
_run_pdf_parser helper, wire them to the parameters that already existed, and
append what they find to the indexed text -- analyze_pdf sets `source`, so
tool_response returns the compact preview envelope and the case DB is how an
analyst reaches this later. Embedded filenames carrying an executable suffix
are flagged suspicious.
Extraction is gated on the existing parameters, so a caller that passes
extract_urls=False or extract_embedded=False pays no pdf-parser run. A missing
or timing-out pdf-parser yields an empty list rather than raising, matching
the existing JavaScript extractor.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
elhoim
force-pushed
the
fix/pdf-urls-embedded
branch
from
September 8, 2026 06:34
d34ca8b to
8fd026b
Compare
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
analyze_pdfadvertises URL and embedded-file extraction that it never performs. The signature acceptsextract_urlsandextract_embedded, the docstring documents both, and both are echoed into the auditedparams— then the result is built withsummary["urls"] = []andsummary["embedded_files"] = [], and no code path anywhere populates either.evil.exeis reported as having no URLs and no embedded files.search()over the case can never surface them. The evidence is not merely missing from one response; it never enters the case at all._extract_pdf_urlsand_extract_pdf_embedded_filesover a shared_run_pdf_parserhelper, wire them to the parameters that already existed, and append their findings to the indexed text.src/mulder/server/tools/documents.py. No new dependency — it uses the same pdf-parser the JavaScript extractor already uses.The bug
grep -n 'urls\|embedded_files'over the module onmainreturns exactly these two assignments plus the parameter declarations and the docstring that promises them. There is no producer.The fix
A shared runner, so the new extractors resolve pdf-parser exactly the way
_extract_pdf_javascriptalready did (bundled Didier Stevens script first, PATH binary second):/URIaction values are reported asuri_action— the links a reader actually follows — and any otherhttp(s)URL in an object body asobject_body. Embedded files come from/Filespecentries, de-duplicated because/Fand/UFname the same attachment, and flaggedsuspiciouson an executable suffix.Both are gated on the parameters that already existed, so
extract_urls=Falsestill costs nothing. A missing or timing-out pdf-parser returns""and both extractors yield[]rather than raising — matching the existing JavaScript extractor's behaviour.The findings are also appended to
index_parts. That part is load-bearing rather than cosmetic:analyze_pdfpasses asource, sotool_responsereturns the compact preview envelope, and the case DB is how an analyst reaches this later.Verified against the real tool
A minimal PDF with one
/URIlink action and one/Filespecnamingevil.exe, through pdf-parser 0.7.11 and the new extractors:The test fixtures are that run's verbatim stdout, so the tests need no installed binary but are grounded in real output.
Deliberately out of scope
_compute_pdf_riskalready deriveshas_embedded_filesfrom pdfid's/EmbeddedFileindicator. Feeding URL reputation or attachment types into the risk level is a separate judgement call and would belong in its own PR._extract_pdf_javascriptuses--type /JS --filter, which does not reach JavaScript stored as a literal string in an action dictionary. I could not verify that end-to-end here, so I have deliberately not "fixed" it — flagging it instead.git merge-tree --write-tree, and against the pdfid-count fix submitted alongside this one.Verification
uvx pre-commit runover the changed files → ruff, ruff-format, mypy all pass.uv run --locked --extra dev pytest tests/ -q→ 862 passed, nothing deselected.analyze_pdfentry point and stub the wrapped tools atsubprocess.run, so they import nothing that this PR adds and fail behaviourally against unmodified code. Withdocuments.pyrestored toorigin/mainand the new tests kept, 5 of 7 fail:The two that pass on both are the narrowness guards — a benign PDF yields nothing, and
extract_urls=False/extract_embedded=Falsesuppress the work rather than merely blanking the result. A detector that flags ordinary documents is as useless as one that misses malicious ones.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; the pdfid hex-obfuscation gap is submitted separately, and both are branched independently off
mainwith no stacking. No outcome framework and no shared taxonomy are reintroduced, per the review on #32:Branched fresh from current
main(2e5432c); the closed branch was not revised in place.🤖 Generated with Claude Code