CodeVetter · Repo Unpack · Risk map
Graphify-Labs/graphify
The main risks are (a) SSRF/SSRF-adjacent fetching — mitigated by a purpose-built guarded-connection stack; (b) prompt injection from attacker-controlled corpus files into the semantic LLM pass — mitigated but explicitly 'table stakes'; (c) the 8,300-line extract.py monolith mid-migration where the shared _extract_generic core and cross-file resolvers concentrate most defect density; (d) shell-out paths (git hooks, detached rebuilds, gh/git subprocesses, claude CLI) with heavy escaping/Windows edge-case handling; and (e) graph.json integrity guards (size cap, shrink guard, path validation) that protect but can also mask silent data loss if misread.
- All outbound fetching goes through security.py: validate_url allows only http/https, blocks private/reserved/loopback/link-local/CGN ranges and cloud metadata hosts, and _SSRFGuarded*Connection subclasses resolve+validate DNS once then connect to that exact IP to defeat DNS-rebind TOCTOU; redirects are re-validated by _NoFileRedirectHandler.
- Fetch size caps: safe_fetch aborts streaming reads at 50 MB (safe_fetch_text at 10 MB) and raises HTTPError on non-2xx; graph-file loads are capped at 512 MiB (GRAPHIFY_MAX_GRAPH_BYTES) to prevent memory-bomb json.loads.
- Prompt-injection surface: corpus files are wrapped in <untrusted_source sha256=...> blocks, _neutralise_injection_sentinels defangs chat-template tokens ([INST], <<SYS>>, forged closing tags), and sanitize_label/sanitize_metadata strip control chars + HTML-escape before export/MCP output — but SECURITY.md itself calls this 'table-stakes' and admits evasion is possible.
- detect.py silently skips credential-shaped files (.env, .pem/.key/.p12, id_rsa/id_ed25519, .npmrc/.pypirc/.git-credentials/.boto, secring) and refuses to descend into .ssh/.gnupg/.aws/.gcloud dirs — an attacker naming real source `id_rsa.py`-style or a user pointing graphify at a secrets dir still loses coverage silently.
- The HTTP MCP transport is opt-in and unauthenticated by default: serve_http warns loudly when binding 0.0.0.0 without --api-key/GRAPHIFY_API_KEY, and _ApiKeyMiddleware does a constant-time compare of X-API-Key/Bearer tokens — but a user can still expose the graph read-only to the network.
- extract.py is ~8,300 lines holding the shared _extract_generic core plus ~20 in-place cross-file resolution passes (member calls for swift/python/typescript/cpp/csharp/java/objc/kotlin/rust, qualified calls, interface dispatch); extractors/MIGRATION.md documents the ongoing one-language-per-PR extraction — this is the blast-radius hotspot where most issue-numbered regressions live.
- hooks.py embeds multi-stage shell+Python payloads in Python string constants with strict character constraints (no `"`, `$`, backtick, backslash inside the -c payload); a careless edit to _REBUILD_BODY_* or _LAUNCHER_TEMPLATE can silently break every installed hook — and the failure mode is a stale graph, not an error.
- Shell-outs: prs.py shells to `gh`/`git` (subprocess.run, no shell=True), cli._clone_repo runs git clone, llm._call_claude_cli shells to the claude binary, transcribe.py invokes yt-dlp — each is a supply-chain/PATH dependency that fails closed only if the binary is absent.
- install.py writes into user config dirs (~/.claude, ~/.codex, ~/.config/..., ~/.agents) and mutates assistant config files (settings.json hooks, AGENTS.md/CLAUDE.md sections) — a mis-install or stale uninstaller can corrupt the user's assistant setup; the conftest HOME sandbox exists precisely because this is dangerous in tests.
- scip_ingest.py is a self-declared skeleton ('NOT a full SCIP protobuf implementation ... Not wired to the CLI in this phase') — dead/legacy code carrying an ingestion surface with no caller or test coverage beyond import checks.
- Graph-integrity guards can mask failure modes: the to_json shrink guard (#479) refuses to overwrite a larger graph (good) but requires users to discover --force/GRAPHIFY_FORCE; the semantic 'unverified shrink' guard similarly stalls writes until --allow-partial; corrupt graph.json exits the process on CLI paths while MCP converts it to tool errors.
- Incremental correctness depends on manifest stamping discipline — a long trail of fixed bugs (#933, #1666, #1897, #1909, #1941, #2015, #2543) shows files could be silently marked done with no extraction output; _stamped_manifest_files is the single chokepoint an agent must not weaken.
- Windows portability is a recurring defect class addressed throughout (os_replace_with_fallback for WinError 5/32/17, MAX_PATH filename budgets, 61-worker process cap, PowerShell-safe hook payloads) — code touching filesystem identity or subprocess spawning must handle both platforms explicitly.