graphify: host-file read via Fortran cpp includes
Fortran extractor: incomplete cpp #include hardening allows arbitrary host-file read when indexing an untrusted repo (GHSA-pcc4-rvhr-2pr8)
Fixed in graphifyy 0.9.70 (2026-09-27) by commit e1d9dee, which strips every #include directive before preprocessing and feeds the source to cpp on stdin. Track the fix at Graphify-Labs/graphify@e1d9dee.
- Product
- graphify (PyPI graphifyy) in Graphify-Labs/graphify
- Affected versions
- graphifyy before 0.9.70. Fixed in 0.9.70 (2026-09-27).
- Severity
- HIGH
- Status
- Patched
- Weaknesses
- CWE-22Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
- CWE-73External Control of File Name or Path
- GitHub advisory
- GHSA-pcc4-rvhr-2pr8
Published by the maintainer on 2026-09-27 - CVE
- Pending (requested from MITRE)
- Published
- Credit
- Finder: Aaron Elijah Mars of Aeon. Tool: Aeon (https://www.aeon.fun).
What users should do now
- Upgrade graphify to 0.9.70 or later (
pip install -U graphifyy). - If you indexed an untrusted repository with an older version, check its
graph.json,GRAPH_REPORT.md, wiki and HTML/SVG exports for content from outside the repo before you share them, and rotate any secret that may have been read. - Until you upgrade, do not index untrusted repositories that contain
.F,.F90,.F95,.F03or.F08files on a machine that holds secrets, or run graphify wherecppis not onPATH.
Summary
graphify turns a folder of code into a knowledge graph. Fortran files with a capital-F extension go through the C preprocessor (cpp) first. A security comment in the code ("F-007") said the flags -nostdinc -I /dev/null stop a malicious source file from pulling host files in with #include. They do not. cpp always opens absolute include paths, and always looks in the source file's own directory for quoted includes, so a relative path can walk out of the repo. Indexing an untrusted repo could therefore copy any file the user can read into the graph, the LLM context and the files graphify writes to disk.
Affected versions
graphifyy (PyPI) before 0.9.70, when it indexes a folder that contains a .F, .F90, .F95, .F03 or .F08 file and cpp is on PATH. These extensions are picked up by default, and the step runs on the default offline path: no API key, network access or LLM call is needed. Lower-case .f/.f90 files do not go through cpp and are not affected.
Impact
- Arbitrary local file read. A hostile Fortran file can include an absolute path (for example a key or token file in the user's home directory) or a relative path that climbs out of the repo.
cppinlines that file into the preprocessed source. - Exfiltration into shared outputs. The preprocessed text is parsed into graph nodes and edges, so file contents can end up in
graph.json,GRAPH_REPORT.md, the Obsidian wiki and the HTML/SVG exports, and in what the MCP tools return to an agent or LLM. - Fidelity. Fortran is case-insensitive, so captured identifiers are lower-cased. Single-line secrets such as API key or token files, paths, hostnames and hex tokens come through in full (lower-cased); mixed-case blobs are partly altered but still show that the file exists and is readable.
- The attacker only needs the user to index a repo they control. The user does not need to run any code from it.
Affected code
Permalinks at c66eebe, the commit just before the fix.
- The F-007 comment that claims the flags block include-based reads: graphify/extractors/fortran.py#L18-L23
cpp -w -P -nostdinc -I /dev/null <file>run on the source file path: graphify/extractors/fortran.py#L35-L39- Capital-F extensions routed to
_cpp_preprocess(): graphify/extractors/fortran.py#L9 and #L61 - These extensions are collected by default: graphify/detect.py#L44
- And dispatched to the Fortran extractor: graphify/extract.py#L6656-L6665
Fix
- e1d9dee blanks every
#includeline before preprocessing (line numbers are kept) and passes the cleaned source tocppon stdin instead of a file path, socppnever opens the file's directory or any include path. Macro expansion (#define,#ifdef) still works; includes across files are no longer followed. The no-cppfallback returns the same cleaned bytes. Released in 0.9.70. - Feeding stdin also removes the older edge case where a corpus file named like a
cppoption could be read as one.
Detection (for defenders)
Search repos you index for #include lines in .F, .F90, .F95, .F03 or .F08 files that use an absolute path or climb out of the repo with ../. In existing graphify output, look for node labels or imports edges that look like secrets, paths or hostnames from your machine rather than Fortran names. A working exploit is withheld.
Timeline
Credit
Finder: Aaron Elijah Mars of Aeon. Tool: Aeon.
References
- Fix commit: Graphify-Labs/graphify@e1d9dee
- Fixed release: graphify 0.9.70
- GitHub advisory: GHSA-pcc4-rvhr-2pr8
- CVE: pending (requested from MITRE; maintainer asked to request one in #4259)
- Affected repository: Graphify-Labs/graphify