Skip to content

fix(rules): cover managed-mcp.json in tamper.agent_config_write - #25

Open
ousamabenyounes wants to merge 1 commit into
perplexityai:mainfrom
ousamabenyounes:fix/issue-24
Open

fix(rules): cover managed-mcp.json in tamper.agent_config_write#25
ousamabenyounes wants to merge 1 commit into
perplexityai:mainfrom
ousamabenyounes:fix/issue-24

Conversation

@ousamabenyounes

Copy link
Copy Markdown

Summary

tamper.agent_config_write matched Claude Code's managed-settings.json in the
three admin directories (/etc/claude-code/, /Library/Application Support/ClaudeCode/,
C:\Program Files\ClaudeCode\) but did not match managed-mcp.json, the
enterprise control that restricts which MCP servers may load. It ships to the
same three directories. Per Anthropic's docs, deploying a managed-mcp.json with
an empty server map blocks every MCP server fleet-wide — so deleting that one
file re-enables every MCP server on the endpoint. That mutation is squarely
defense impairment, which is the rule's existing attack.t1562.001 scope.

This extends the Claude managed-settings fragment to
managed-(settings(\.d/…)?|mcp)\.json in all three regex branches (file-path,
shell redirect, shell argv), scoped to those admin directories only. User/project
MCP stores (~/.claude.json, ~/.cursor/mcp.json, ~/.codex/config.toml) are
deliberately left out — they are capability acquisition, not defense impairment
(a documented false negative, consistent with the rule's precision policy). This
is the conservative line the issue proposed.

Fixes #24

Changes

  • rules/tamper/agent_config_write.yaml: add managed-mcp.json alongside
    managed-settings.json for the three ClaudeCode admin dirs, in the file-path,
    redirect, and argv match branches. Bump rule version 1.7 → 1.8.
  • rules/catalog_test.go: add TestTamperRules cases for managed-mcp.json
    covering delete/write (file-path branch, macOS/Linux/Windows dirs), echo >
    (redirect branch), and rm -f (argv branch).

Scope / precision

  • Existing managed-settings.json and managed-settings.d/<file>.json matches
    are unchanged (no regression). managed-mcp.json has no .d/ drop-in arm, so
    the addition is a single filename, not a directory tree.
  • The nested-repo exclusion still applies to managed-mcp.json, so a repository
    checkout containing …/Library/Application Support/ClaudeCode/managed-mcp.json
    stays quiet, exactly like the existing managed-settings.json negative.
  • No wire-contract/schema change; rule-version bump only.

Test verification (RED → GREEN)

RED — new tests on the unmodified rule (go test ./rules/ -run TestTamperRules):

--- FAIL: TestTamperRules (0.42s)
    --- FAIL: TestTamperRules/claude_managed-mcp_delete_macOS
        builtin_test.go:693: expected tamper.agent_config_write to fire, got []
    --- FAIL: TestTamperRules/claude_managed-mcp_write_Linux
        builtin_test.go:693: expected tamper.agent_config_write to fire, got []
    --- FAIL: TestTamperRules/claude_managed-mcp_write_Windows
        builtin_test.go:693: expected tamper.agent_config_write to fire, got []
    --- FAIL: TestTamperRules/claude_managed-mcp_redirect
        builtin_test.go:693: expected tamper.agent_config_write to fire, got []
    --- FAIL: TestTamperRules/claude_managed-mcp_remove
        builtin_test.go:693: expected tamper.agent_config_write to fire, got []
FAIL

GREEN — same tests after the rule change:

--- PASS: TestTamperRules/claude_managed-mcp_delete_macOS (0.00s)
--- PASS: TestTamperRules/claude_managed-mcp_write_Linux (0.00s)
--- PASS: TestTamperRules/claude_managed-mcp_write_Windows (0.00s)
--- PASS: TestTamperRules/claude_managed-mcp_redirect (0.00s)
--- PASS: TestTamperRules/claude_managed-mcp_remove (0.00s)
ok  	github.com/perplexityai/numbat/rules	0.511s

Verification commands run (Go 1.26.5)

go mod tidy && git diff --exit-code -- go.mod go.sum
gofmt -l .            # empty
go vet ./...
go test -race ./...   # all packages ok
go build -buildvcs=false ./cmd/numbat
golangci-lint run     # 0 issues
golangci-lint fmt --diff
govulncheck ./...      # No vulnerabilities found

tamper.agent_config_write matched Claude Code's managed-settings.json in the
three admin directories (/etc/claude-code, /Library/Application Support/
ClaudeCode, C:/Program Files/ClaudeCode) but not managed-mcp.json, the
enterprise control that restricts which MCP servers may load. It ships to the
same directories. Deleting a managed-mcp.json that carries an empty server map
re-enables every MCP server on the endpoint fleet-wide, so the file mutation is
squarely defense impairment (the rule's existing attack.t1562.001 scope).

Extend the Claude managed-settings fragment to managed-(settings(.d/..)?|mcp)
in all three regex branches (file-path, shell redirect, shell argv), scoped to
those admin directories only. User/project MCP stores stay out of scope.
Bump the rule version 1.7 -> 1.8 and add TestTamperRules cases covering the
delete, write, redirect, and argv paths across the three directories.

Fixes perplexityai#24

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

tamper.agent_config_write: managed-mcp.json not covered alongside managed-settings.json

1 participant