Files
Open_Ultra_SAST/benchmarks/measurements/2026-10-12-hunter-fair-targets/dcmcp-findings.json
T
Marius CiepluchandClaude Opus 4.8 04667c815e measure(g0): fair-target deep scan -- detection 3/5 on app-shaped+sound vs ~0 library-heavy
Ran ousast scan --mode deep on the 5 app-shaped + sound pool targets (the fair change-scoped
eval set from the shape classification). Detection 3/5: graphiti (hunter, Cypher injection),
mcp-remote (hunter, url-scheme injection), dcmcp (ENGINE overlay -- 'outbound.request reached
from parameter' on readFileFromUrl; parameter-as-source fired because an entry point is present).
2/5 missed: malla (DOM/template XSS) and dms (Jinja SSTI), both in families families.toml
documents as limited.

This validates the strategic pivot: the near-zero catch was substantially a TARGET MISMATCH --
on app-shaped repos the engine itself detects (0 -> 3/5). But all hits stop at suspicion/
static_corroboration; release-grade BLOCK still needs proof-arm elevation + family coverage +
the DSPy hint loop. Cost ~$0.007/repo. So bet #1 (fix eval target) is necessary and material
but not sufficient.

Next: recompute a floor on an app-shaped sound subset / real app corpus (VAmPI, NodeGoat,
DjanGoat, vibe-py); measure proof-arm suspicion->proven lift.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-12 11:16:35 +02:00

491 lines
21 KiB
JSON

{
"findings": [
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "js-command-exec:setup-claude-server.js:36",
"function_name": null,
"line": 36,
"path": "setup-claude-server.js",
"proposed_snippet": null,
"ranking_priority": 1.5,
"rationale": "Static pattern js-command-exec (CWE-78) matched exec on line 36; manual verification still required.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "critical",
"status": "enabled",
"tags": [
"auth_boundary",
"deserialization",
"filesystem_entry",
"network_entry",
"parser",
"syscall_entry"
],
"title": "Node child_process command execution needs review"
},
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "js-command-exec:setup-claude-server.js:216",
"function_name": null,
"line": 216,
"path": "setup-claude-server.js",
"proposed_snippet": null,
"ranking_priority": 1.5,
"rationale": "Static pattern js-command-exec (CWE-78) matched exec on line 216; manual verification still required.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "critical",
"status": "enabled",
"tags": [
"auth_boundary",
"deserialization",
"filesystem_entry",
"network_entry",
"parser",
"syscall_entry"
],
"title": "Node child_process command execution needs review"
},
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "overlay-coverage:src/terminal-manager.ts:57:prototype.merge",
"function_name": null,
"line": 57,
"path": "src/terminal-manager.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "uninventoried sink prototype.merge reached from parameter",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "info",
"status": "enabled",
"tags": [
"overlay-originated"
],
"title": "Overlay coverage: prototype.merge"
},
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "overlay-coverage:src/terminal-manager.ts:84:prototype.merge",
"function_name": null,
"line": 84,
"path": "src/terminal-manager.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "uninventoried sink prototype.merge reached from parameter",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "info",
"status": "enabled",
"tags": [
"overlay-originated"
],
"title": "Overlay coverage: prototype.merge"
},
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "overlay-coverage:src/tools/edit.ts:11:path.open",
"function_name": null,
"line": 11,
"path": "src/tools/edit.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "uninventoried sink path.open reached from parameter",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"overlay-originated"
],
"title": "Overlay coverage: path.open"
},
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "overlay-coverage:src/tools/filesystem.ts:174:outbound.request",
"function_name": null,
"line": 174,
"path": "src/tools/filesystem.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "uninventoried sink outbound.request reached from parameter",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"overlay-originated"
],
"title": "Overlay coverage: outbound.request"
},
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "overlay-coverage:src/tools/filesystem.ts:321:path.open",
"function_name": null,
"line": 321,
"path": "src/tools/filesystem.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "uninventoried sink path.open reached from parameter",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"overlay-originated"
],
"title": "Overlay coverage: path.open"
},
{
"confidence": "medium",
"evidence_level": "static_corroboration",
"finding_id": "overlay-coverage:src/tools/search.ts:176:path.open",
"function_name": null,
"line": 176,
"path": "src/tools/search.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "uninventoried sink path.open reached from parameter",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"overlay-originated"
],
"title": "Overlay coverage: path.open"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:scripts/sync-version.js:24",
"function_name": null,
"line": 24,
"path": "scripts/sync-version.js",
"proposed_snippet": null,
"ranking_priority": 15.1974,
"rationale": "sync-version.js reads package.json, optionally bumps the version, and writes src/version.ts via template literal `export const VERSION = '${version}';`. The version value is taken from package.json without validation; a crafted version string containing a quote could break out of the string literal and inject code into the generated TypeScript source. This is the deserialization/parser/filesystem_entry hotspot.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:code-injection"
],
"title": "Version string interpolated into generated source file without escaping"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:setup-claude-server.js:14",
"function_name": null,
"line": 14,
"path": "setup-claude-server.js",
"proposed_snippet": null,
"ranking_priority": 22.5805,
"rationale": "The setup script embeds a literal PostHog project API key and imports node-machine-id to derive a stable uniqueUserId, then sends analytics with flushAt:1. This is a hardcoded credential plus device fingerprinting in an install/setup entrypoint (network_entry, syscall_entry tags). The key is committed to the repository and cannot be rotated without a code change.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:hardcoded-secret"
],
"title": "Hardcoded PostHog API key and machine-id telemetry on startup"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:setup-claude-server.js:38",
"function_name": null,
"line": 38,
"path": "setup-claude-server.js",
"proposed_snippet": null,
"ranking_priority": 22.5805,
"rationale": "getNpmVersion invokes exec('npm --version'), which runs through a shell. The command is static so not directly injectable, but it establishes a shell-execution syscall entry in the setup script that also handles filesystem writes and network telemetry, matching the syscall_entry/network_entry tags on this top-ranked hotspot.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:command-execution"
],
"title": "exec of npm --version in setup entrypoint"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/command-manager.ts:152",
"function_name": null,
"line": 152,
"path": "src/command-manager.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "validateCommand relies on extractCommands/getBaseCommand, which split on separators and take the first whitespace-delimited token lowercased. Blocklist matching is exact-string against blockedCommands. A blocked command invoked as '/bin/rm', './rm', 'rm.exe', or via env/quoting (e.g. 'r''m', '\\rm', 'command rm') yields a base command that does not equal the blocked entry, so the blocklist is trivially evaded. This is the auth_boundary/parser hotspot flagged in src/tools/execute.ts and src/config-manager.ts.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:command-injection"
],
"title": "Command blocklist validation is bypassable via shell metacharacters and path-qualified binaries"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/config-manager.ts:44",
"function_name": null,
"line": 44,
"path": "src/config-manager.ts",
"proposed_snippet": null,
"ranking_priority": 19.6849,
"rationale": "init() reads ~/.claude-server-commander/config.json and assigns JSON.parse(configData) directly to this.config with no schema or type validation. A malformed or attacker-controlled config file can set allowedDirectories to '/' or blockedCommands to [], or inject unexpected keys (the interface has an index signature [key: string]: any). This is the deserialization/filesystem_entry/auth_boundary hotspot.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:unsafe-deserialization"
],
"title": "Config JSON parsed from disk without schema validation"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/config-manager.ts:76",
"function_name": null,
"line": 76,
"path": "src/config-manager.ts",
"proposed_snippet": null,
"ranking_priority": 19.6849,
"rationale": "getDefaultConfig ships a fixed denylist of dangerous commands (mkfs, dd, sudo, etc.). Denylists are inherently incomplete: commands not enumerated (e.g. 'sh', 'bash', 'python', 'curl', 'nc', 'chmod', 'kill') are permitted by default. Combined with the base-command matching weakness in command-manager.ts, the default posture is permissive. This is the auth_boundary hotspot in config-manager.ts.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:insecure-default"
],
"title": "Default blockedCommands is a denylist, not an allowlist"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/handlers/filesystem-handlers.ts:51",
"function_name": null,
"line": 51,
"path": "src/handlers/filesystem-handlers.ts",
"proposed_snippet": null,
"ranking_priority": 14.1621,
"rationale": "handleReadFile calls readFile(parsed.path, parsed.isUrl). When isUrl is true, filesystem.ts routes to readFileFromUrl which performs fetch(url). The URL is not validated against any allowlist or private-IP blocklist, so the handler can be used to fetch internal/loopback/metadata endpoints (SSRF). This is the filesystem_entry/parser hotspot in filesystem-handlers.ts.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:ssrf"
],
"title": "read_file handler forwards isUrl flag to readFile, enabling server-side request forgery"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/tools/config.ts:65",
"function_name": null,
"line": 65,
"path": "src/tools/config.ts",
"proposed_snippet": null,
"ranking_priority": 16.2332,
"rationale": "setConfigValue accepts arbitrary keys including allowedDirectories and blockedCommands and writes them via configManager.setValue. A caller can widen allowedDirectories to '/' or empty the blockedCommands array, disabling both the filesystem and command boundaries at runtime. The server.ts description itself warns this must be used in a separate chat, confirming it is a security-sensitive control surface reachable through the same tool interface.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:privilege-escalation"
],
"title": "set_config_value allows runtime mutation of allowedDirectories and blockedCommands"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/tools/execute.ts:32",
"function_name": null,
"line": 32,
"path": "src/tools/execute.ts",
"proposed_snippet": null,
"ranking_priority": 13.5639,
"rationale": "executeCommand extracts commands for analytics, then calls commandManager.validateCommand, then terminalManager.executeCommand. The only authorization check is the blocklist-based validateCommand, which is bypassable (see command-manager finding). There is no allowlist, no shell-escape sanitization, and the raw parsed.data.command string is passed to the terminal manager. This is the auth_boundary hotspot in execute.ts.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:command-injection"
],
"title": "Command validation occurs after analytics extraction and is the sole gate before execution"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/tools/filesystem.ts:101",
"function_name": null,
"line": 101,
"path": "src/tools/filesystem.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "When the requested path does not exist and validateParentDirectories returns false, validatePath still returns the absolute path rather than throwing. Callers (readFile, writeFile, search, move) treat the return as validated, so a non-existent path outside allowed directories can be passed through to subsequent operations. The allowlist check happens before realpath resolution, so symlink-based escapes are also possible for existing paths.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:path-traversal"
],
"title": "validatePath returns unvalidated absolute path when no valid parent exists"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/tools/filesystem.ts:78",
"function_name": null,
"line": 78,
"path": "src/tools/filesystem.ts",
"proposed_snippet": null,
"ranking_priority": 0.0,
"rationale": "isPathAllowed returns true if allowedDirectories includes '/' OR allowedDirectories.length === 0. getAllowedDirs returns [] on any error (catch block), and validatePath's error path also returns the absolute path anyway. Combined with config-manager defaulting allowedDirectories to homedir, an empty/failed config silently grants access to all paths, defeating the filesystem boundary tagged on src/tools/search.ts, src/handlers/filesystem-handlers.ts, and src/config-manager.ts.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:path-traversal"
],
"title": "Path allowlist fails open when allowedDirectories is empty or '/'"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/tools/search.ts:36",
"function_name": null,
"line": 36,
"path": "src/tools/search.ts",
"proposed_snippet": null,
"ranking_priority": 19.995,
"rationale": "The pattern and filePattern are pushed into the ripgrep argv array. While spawn avoids shell interpretation, a pattern beginning with '-' can be interpreted as a ripgrep flag (argument injection), and filePattern via -g can broaden or redirect the search scope. validatePath is applied to rootPath but not to the pattern/filePattern, so the deserialization/parser hotspot can be steered outside intended semantics.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:argument-injection"
],
"title": "searchCode passes user pattern directly to ripgrep spawn without argument hardening"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:src/tools/search.ts:88",
"function_name": null,
"line": 88,
"path": "src/tools/search.ts",
"proposed_snippet": null,
"ranking_priority": 19.995,
"rationale": "stdoutBuffer accumulates all ripgrep output in memory before splitting and JSON.parse-ing each line. A large or adversarial search result set can exhaust memory, and the per-line JSON.parse is wrapped in a catch that only logs, so malformed output is silently dropped. This is the deserialization/parser tag on this high-band hotspot.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:resource-exhaustion"
],
"title": "Unbounded stdout buffering and JSON.parse of ripgrep output"
},
{
"confidence": "low",
"evidence_level": "suspicion",
"finding_id": "tool-hunter:test/test-blocked-commands.js:147",
"function_name": null,
"line": 147,
"path": "test/test-blocked-commands.js",
"proposed_snippet": null,
"ranking_priority": 13.5864,
"rationale": "The test sets blockedCommands to a custom list, asserts behavior, then teardown calls configManager.updateConfig(originalConfig). Because the test operates on the real config file (~/.claude-server-commander/config.json) rather than an isolated temp config, a crash between setValue and teardown leaves the persisted blocklist modified, weakening the command boundary. This is the filesystem_entry/parser hotspot in the test file.",
"reachability_conditions": [],
"reachability_evidence": [],
"reachability_status": "unknown",
"rung": "suspicion",
"severity": "medium",
"status": "enabled",
"tags": [
"tool-hunter",
"family:test-isolation"
],
"title": "Test mutates live blockedCommands config and restores via updateConfig"
}
]
}