Skip to content

feat: config-file-first audit mode (--from-config) (#83) - #91

Merged
ksek87 merged 5 commits into
mainfrom
claude/config-audit-83
Jun 13, 2026
Merged

feat: config-file-first audit mode (--from-config) (#83)#91
ksek87 merged 5 commits into
mainfrom
claude/config-audit-83

Conversation

@ksek87

@ksek87 ksek87 commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • fuzzd audit --from-config <PATH> reads a Claude Desktop or Cline claude_desktop_config.json and audits every configured MCP server in one pass
  • --from-config auto searches standard platform paths (macOS ~/Library/Application Support/Claude/, Linux ~/.config/claude/, Cline ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/, Windows %APPDATA%/Claude/)
  • Findings tagged server-name/tool-name; per-server summary printed at the end of the run
  • All --attacks modules work as normal per server; gracefully skips servers that fail to start
  • Env var passthrough from config.envStdioTransport.spawn_with_args() accepts pre-split args and extra env; values never logged (may contain tokens like GITHUB_TOKEN)

Architecture

  • New src/config.rsMcpServerConfig, DesktopConfig, parse_config(), load_config(), auto_detect()
  • StdioTransport::spawn_with_args(program, args, env) — pre-split spawn for config-driven invocations; spawn() now delegates to shared spawn_inner()
  • run_audit refactored into collect_audit_findings<T> (returns (Vec<Finding>, usize)) + run_audit wrapper — config path calls collect_audit_findings per server and aggregates before a single report write
  • AuditArgs.from_config: Option<String> with conflicts_with_all = [cmd, url, transport]

Test plan

  • cargo test — 288 tests pass
  • cargo clippy -- -D warnings — clean
  • cargo fmt --check — clean
  • fuzzd audit --from-config path/to/claude_desktop_config.json --attacks tool_poisoning — audits all servers in config
  • fuzzd audit --from-config auto — finds config at platform path or prints helpful error
  • Server that fails to start → skipped with warning, remaining servers continue

Note: This branch is based on claude/tier2-detection (PR #90). The diff will show cleanly after #89 and #90 are merged.

https://claude.ai/code/session_014T1x8ZiDbJcVvkZBfP91nk


Generated by Claude Code

claude added 5 commits June 13, 2026 05:08
…ts/resources, SARIF tags, escape stub

#71 — Wire HTTP transport: replace the bail!() stub in main.rs with a real
HttpTransport::connect(url) call. The HTTP transport, CLI --url flag, and all
test infrastructure were already complete; this is the missing CLI hookup.
Also removes the file-level #![allow(dead_code)] from mcp.rs and session.rs
that were placeholders for this wire-up.

#75 — Annotation deception scanning (FUZZD-028): adds annotations: Option<Value>
to ToolDefinition per the MCP 2025-11-05 spec. New scan_annotations() pass fires
AnnotationDeception when readOnlyHint/destructiveHint/openWorldHint contradict
the tool's actual description — the exact attack documented in arXiv:2603.22489
where false hints suppress client confirmation dialogs for destructive operations.
7 new tests; 0 new false positives on clean_tools.json.

#76 — Prompts/resources scanning (full pipeline): adds PromptDefinition,
ResourceDefinition, ListPromptsResult, ListResourcesResult to mcp.rs; adds
list_prompts()/list_resources() to Session; adds enumerate_prompts/resources()
to Harness; adds PROMPTS_LIST/RESOURCES_LIST method constants. New scan_surface()
on DescriptionScanner runs the four injection-detection passes on (name,
description) pairs. Wired into the ToolPoisoning audit path — gracefully skips
if the server returns method-not-found for unsupported endpoints.

#77 — OWASP/NSA SARIF compliance tags: adds tags() method to Signal returning
OWASP MCP Top-10, OWASP Agentic Top-10 (ASI series), and CWE identifiers.
sarif_rules() now emits properties.tags for every rule, enabling SARIF consumers
(GitHub Code Scanning, SonarQube) to cross-reference fuzzd findings against
industry frameworks without a separate mapping step.

#78 — Escape module stub: implements the no-op fuzz_escape() function in a new
src/fuzzer/escape.rs and wires it into the dispatch so Escape no longer emits
"not yet implemented" warnings in default scans.

https://claude.ai/code/session_014T1x8ZiDbJcVvkZBfP91nk
…ick wins

- bench/README: 27→28 signals, add annotation_deception row, update pass count
  to 5, note prompts/resources and annotations as scanned surfaces
- README roadmap: add HTTP transport, annotation scanning, prompts/resources,
  SARIF compliance tags as shipped; update signal count 23→28
- CHANGELOG: [Unreleased] entry for #71/#75/#76/#77/#78 with full detail

https://claude.ai/code/session_014T1x8ZiDbJcVvkZBfP91nk
…RF/ReDoS payloads, tool pinning

#80 — API key and token patterns (CredentialReference): 13 new high-confidence
AC needles targeting cloud platform key prefixes and auth headers — AWS AKIA,
GitHub gh{p,u,s}_, Slack xox{b,p}-, Anthropic sk-ant-, Google AIzaSy,
Stripe sk_{live,test}_, Kubernetes ~/.kube/config, Docker config, Bearer token.
All needles lowercase to work with the ascii_case_insensitive automaton.

#82 — Multilingual injection patterns (14 new needles): Chinese (必须, 您必须,
立即执行, 不得告知, 系统规则), Japanese/Traditional Chinese (必須, 絶対に),
Korean (반드시, 알리지 마), Russian (должны, обязательно, не сообщайте),
Arabic (يجب, إلزامي). Covers the non-English imperative and stealth-language
patterns used to evade English-only scanners, documented in Perez & Ribeiro (2022)
and observed in Wang et al. (2025) MCPTox samples. 0 new false positives.

#81 — SSRF and ReDoS payloads in argument fuzzer: adds two new PayloadCategory
statics to payloads.rs included in ALL_CATEGORIES — SSRF (14 payloads: AWS/GCP/DO
metadata endpoints, localhost variants, file://, gopher://, dict://) and REDOS
(4 catastrophic-backtracking strings). Also removes stale #![allow(dead_code)]
from payloads.rs. Basis: PortSwigger SSRF cheatsheet; OWASP ReDoS; Davis et al.

#79 — Tool pinning and rug-pull detection: Harness now records a u64 content
hash (name + description + inputSchema + annotations via DefaultHasher) for each
tool on first enumerate_tools() call. New recheck_tool_integrity() method
re-fetches tools/list and returns the names of any tools whose definition changed.
Wired into the ToolPoisoning audit path in main.rs — changed tools emit
ConditionalActivation (FUZZD-011) findings at Critical severity. 3 new tests
cover the detect-change, no-change, and before-enumerate cases.

https://claude.ai/code/session_014T1x8ZiDbJcVvkZBfP91nk
fuzzd audit --from-config <PATH> reads a Claude Desktop or Cline
claude_desktop_config.json and audits every configured MCP server
in one pass; --from-config auto searches standard platform paths.

Findings are tagged server-name/tool-name and a per-server summary
is printed. StdioTransport gains spawn_with_args() for env passthrough
from config (values never logged). Gracefully skips servers that fail
to start. 9 new unit tests in src/config.rs; 2 new CLI parse tests.

run_audit refactored into collect_audit_findings + run_audit to share
single-server logic between the direct and config-driven paths.

https://claude.ai/code/session_014T1x8ZiDbJcVvkZBfP91nk
@ksek87
ksek87 merged commit 69f2897 into main Jun 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants