mcp-capdiff 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. mcp_capdiff-0.1.1/CHANGELOG.md +40 -0
  2. mcp_capdiff-0.1.1/LICENSE +21 -0
  3. mcp_capdiff-0.1.1/MANIFEST.in +8 -0
  4. mcp_capdiff-0.1.1/PKG-INFO +173 -0
  5. mcp_capdiff-0.1.1/README.md +150 -0
  6. mcp_capdiff-0.1.1/SECURITY.md +11 -0
  7. mcp_capdiff-0.1.1/action.yml +58 -0
  8. mcp_capdiff-0.1.1/docs/policy.md +20 -0
  9. mcp_capdiff-0.1.1/docs/precision-matrix.md +18 -0
  10. mcp_capdiff-0.1.1/docs/releasing.md +39 -0
  11. mcp_capdiff-0.1.1/docs/report-schema.md +32 -0
  12. mcp_capdiff-0.1.1/docs/rules/MCP001.md +37 -0
  13. mcp_capdiff-0.1.1/docs/rules/MCP002.md +36 -0
  14. mcp_capdiff-0.1.1/docs/rules/MCP003.md +36 -0
  15. mcp_capdiff-0.1.1/docs/rules/MCP004.md +35 -0
  16. mcp_capdiff-0.1.1/docs/rules/MCP005.md +30 -0
  17. mcp_capdiff-0.1.1/docs/rules/MCP007.md +29 -0
  18. mcp_capdiff-0.1.1/docs/rules/MCP010.md +33 -0
  19. mcp_capdiff-0.1.1/docs/rules/MCP016.md +29 -0
  20. mcp_capdiff-0.1.1/docs/rules/MCP017.md +29 -0
  21. mcp_capdiff-0.1.1/docs/rules/README.md +17 -0
  22. mcp_capdiff-0.1.1/docs/v0.1-scope.md +40 -0
  23. mcp_capdiff-0.1.1/docs/versioning.md +14 -0
  24. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/.github/workflows/mcp-audit.yml +25 -0
  25. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/README.md +18 -0
  26. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/mcp-audit.yaml +6 -0
  27. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/pyproject.toml +5 -0
  28. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/approval_removed/after.py +8 -0
  29. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/approval_removed/before.py +8 -0
  30. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/filesystem_expansion/after.py +9 -0
  31. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/filesystem_expansion/before.py +13 -0
  32. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/network_allowlist_added/after.py +14 -0
  33. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/network_allowlist_added/before.py +9 -0
  34. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/unrestricted_fetch/after.py +9 -0
  35. mcp_capdiff-0.1.1/examples/vulnerable-fastmcp/scenarios/unrestricted_fetch/before.py +9 -0
  36. mcp_capdiff-0.1.1/fixtures/destructive_with_approval/server.py +8 -0
  37. mcp_capdiff-0.1.1/fixtures/destructive_without_approval/server.py +8 -0
  38. mcp_capdiff-0.1.1/fixtures/fastmcp_async_annotations/server.py +16 -0
  39. mcp_capdiff-0.1.1/fixtures/filesystem_guard_after_read/server.py +15 -0
  40. mcp_capdiff-0.1.1/fixtures/filesystem_restricted/server.py +14 -0
  41. mcp_capdiff-0.1.1/fixtures/filesystem_unrestricted/server.py +10 -0
  42. mcp_capdiff-0.1.1/fixtures/postgresql_read_only/server.py +17 -0
  43. mcp_capdiff-0.1.1/fixtures/safe_server/server.py +16 -0
  44. mcp_capdiff-0.1.1/fixtures/sensitive_read_only/server.py +14 -0
  45. mcp_capdiff-0.1.1/fixtures/sensitive_to_network/server.py +20 -0
  46. mcp_capdiff-0.1.1/fixtures/shell_direct/server.py +10 -0
  47. mcp_capdiff-0.1.1/fixtures/shell_dynamic_no_shell/server.py +11 -0
  48. mcp_capdiff-0.1.1/fixtures/shell_safe_allowlist/server.py +12 -0
  49. mcp_capdiff-0.1.1/fixtures/sql_destructive/server.py +10 -0
  50. mcp_capdiff-0.1.1/fixtures/url_allowlisted/server.py +14 -0
  51. mcp_capdiff-0.1.1/fixtures/url_arbitrary/server.py +9 -0
  52. mcp_capdiff-0.1.1/fixtures/url_guard_after_request/server.py +15 -0
  53. mcp_capdiff-0.1.1/fixtures/url_hostname_unguarded/server.py +13 -0
  54. mcp_capdiff-0.1.1/fixtures/vulnerable_server/server.py +32 -0
  55. mcp_capdiff-0.1.1/mcp-audit.yaml +17 -0
  56. mcp_capdiff-0.1.1/pyproject.toml +48 -0
  57. mcp_capdiff-0.1.1/setup.cfg +4 -0
  58. mcp_capdiff-0.1.1/src/mcp_audit/__init__.py +6 -0
  59. mcp_capdiff-0.1.1/src/mcp_audit/__main__.py +6 -0
  60. mcp_capdiff-0.1.1/src/mcp_audit/analysis/__init__.py +1 -0
  61. mcp_capdiff-0.1.1/src/mcp_audit/analysis/diff.py +235 -0
  62. mcp_capdiff-0.1.1/src/mcp_audit/analysis/scan.py +29 -0
  63. mcp_capdiff-0.1.1/src/mcp_audit/cli/__init__.py +1 -0
  64. mcp_capdiff-0.1.1/src/mcp_audit/cli/main.py +112 -0
  65. mcp_capdiff-0.1.1/src/mcp_audit/models/__init__.py +12 -0
  66. mcp_capdiff-0.1.1/src/mcp_audit/models/capability.py +73 -0
  67. mcp_capdiff-0.1.1/src/mcp_audit/models/finding.py +87 -0
  68. mcp_capdiff-0.1.1/src/mcp_audit/models/report.py +120 -0
  69. mcp_capdiff-0.1.1/src/mcp_audit/policy/__init__.py +1 -0
  70. mcp_capdiff-0.1.1/src/mcp_audit/policy/evaluator.py +29 -0
  71. mcp_capdiff-0.1.1/src/mcp_audit/policy/loader.py +138 -0
  72. mcp_capdiff-0.1.1/src/mcp_audit/reporters/__init__.py +1 -0
  73. mcp_capdiff-0.1.1/src/mcp_audit/reporters/json.py +9 -0
  74. mcp_capdiff-0.1.1/src/mcp_audit/reporters/manifest.py +30 -0
  75. mcp_capdiff-0.1.1/src/mcp_audit/reporters/sarif.py +94 -0
  76. mcp_capdiff-0.1.1/src/mcp_audit/reporters/terminal.py +97 -0
  77. mcp_capdiff-0.1.1/src/mcp_audit/rules/__init__.py +1 -0
  78. mcp_capdiff-0.1.1/src/mcp_audit/rules/engine.py +177 -0
  79. mcp_capdiff-0.1.1/src/mcp_audit/scanners/__init__.py +1 -0
  80. mcp_capdiff-0.1.1/src/mcp_audit/scanners/source/__init__.py +1 -0
  81. mcp_capdiff-0.1.1/src/mcp_audit/scanners/source/python.py +441 -0
  82. mcp_capdiff-0.1.1/src/mcp_capdiff.egg-info/PKG-INFO +173 -0
  83. mcp_capdiff-0.1.1/src/mcp_capdiff.egg-info/SOURCES.txt +98 -0
  84. mcp_capdiff-0.1.1/src/mcp_capdiff.egg-info/dependency_links.txt +1 -0
  85. mcp_capdiff-0.1.1/src/mcp_capdiff.egg-info/entry_points.txt +2 -0
  86. mcp_capdiff-0.1.1/src/mcp_capdiff.egg-info/requires.txt +1 -0
  87. mcp_capdiff-0.1.1/src/mcp_capdiff.egg-info/top_level.txt +1 -0
  88. mcp_capdiff-0.1.1/tests/golden/diff.txt +42 -0
  89. mcp_capdiff-0.1.1/tests/golden/manifest.yaml +14 -0
  90. mcp_capdiff-0.1.1/tests/golden/scan.json +80 -0
  91. mcp_capdiff-0.1.1/tests/golden/scan.sarif.json +90 -0
  92. mcp_capdiff-0.1.1/tests/golden/terminal.txt +41 -0
  93. mcp_capdiff-0.1.1/tests/test_demo_scenarios.py +46 -0
  94. mcp_capdiff-0.1.1/tests/test_diff.py +176 -0
  95. mcp_capdiff-0.1.1/tests/test_discovery.py +49 -0
  96. mcp_capdiff-0.1.1/tests/test_golden.py +68 -0
  97. mcp_capdiff-0.1.1/tests/test_policy.py +92 -0
  98. mcp_capdiff-0.1.1/tests/test_reporters.py +44 -0
  99. mcp_capdiff-0.1.1/tests/test_rule_accuracy.py +111 -0
  100. mcp_capdiff-0.1.1/tests/test_scan.py +32 -0
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes follow Keep a Changelog. Releases use semantic versioning for the CLI package; rules and output schemas are versioned independently.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.1] - 2026-10-01
8
+
9
+ ### Added
10
+
11
+ - Dedicated PyPI Trusted Publishing workflow with tag-pinned builds and OIDC authentication.
12
+
13
+ ### Changed
14
+
15
+ - Rename the PyPI distribution to `mcp-capdiff` while preserving the MCP Audit product name, `mcp_audit` import package, and `mcp-audit` command.
16
+ - Prepare version `0.1.1` so the public `v0.1.0` tag remains immutable.
17
+
18
+ ## [0.1.0] - 2026-10-01
19
+
20
+ ### Fixed
21
+
22
+ - Prevent `PostgreSQL` from being misclassified as an HTTP `POST` side effect.
23
+ - Attach destructive semantic evidence to MCP004 and MCP010 instead of unrelated database-read evidence.
24
+ - Fail closed on inaccessible source trees and zero-tool scans instead of reporting a false PASS.
25
+
26
+ ### Added
27
+
28
+ - Discovery benchmark for async `@mcp.tool()` functions with FastMCP `ToolAnnotations`.
29
+ - Explicit `--allow-empty` escape hatch for intentional zero-tool scans.
30
+ - Python/FastMCP source discovery and capability extraction.
31
+ - Nine security and regression rules in ruleset `0.1`.
32
+ - Cross-tool sensitive-data path detection with MCP-context isolation.
33
+ - Terminal, JSON, SARIF, manifest, and Git security-diff output.
34
+ - Versioned report and manifest schema `1.0`.
35
+ - Policy thresholds and reasoned, expiring suppressions.
36
+ - Composite GitHub Action with Code Scanning upload.
37
+
38
+ ### Verified
39
+
40
+ - External PostgreSQL MCP benchmark: 20 tools discovered, 6 legitimate findings, no known false positives from `PostgreSQL` token matching, and no false PASS.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MCP Audit contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ include action.yml
2
+ include CHANGELOG.md
3
+ include SECURITY.md
4
+ include mcp-audit.yaml
5
+ recursive-include docs *.md
6
+ recursive-include examples *
7
+ recursive-include fixtures *.py
8
+ recursive-include tests/golden *
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-capdiff
3
+ Version: 0.1.1
4
+ Summary: Security regression scanner for Model Context Protocol servers.
5
+ Author: MCP Audit contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/robbyfa/MCP-Audit
8
+ Project-URL: Repository, https://github.com/robbyfa/MCP-Audit
9
+ Project-URL: Issues, https://github.com/robbyfa/MCP-Audit/issues
10
+ Keywords: mcp,security,static-analysis,sarif,ci
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Security
15
+ Classifier: Topic :: Software Development :: Quality Assurance
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: PyYAML<7,>=6.0
22
+ Dynamic: license-file
23
+
24
+ # MCP Audit
25
+
26
+ MCP Audit is an open-source security regression scanner for Model Context Protocol servers. It extracts tool capabilities and permissions, detects risky cross-tool data flows, compares security posture across Git revisions, and emits CI-friendly evidence.
27
+
28
+ The first version focuses on Python/FastMCP projects and the core product thesis from the spec: show what an MCP server can do, what changed, and whether the change creates a dangerous capability path.
29
+
30
+ MCP005 is the capability-graph rule: it identifies when one tool returns classified data and another tool in the same MCP registration context can send it to an external destination. Findings include the source, data classification, sink, destination, path, source evidence, impact, and remediation.
31
+
32
+ ## Install locally
33
+
34
+ ```bash
35
+ uv sync
36
+ ```
37
+
38
+ Run project commands through `uv run`, or activate the virtual environment with
39
+ `source .venv/bin/activate` before using bare commands.
40
+
41
+ The package also supports isolated CLI installation directly from a checkout:
42
+
43
+ ```bash
44
+ pipx install .
45
+ mcp-audit --version
46
+ mcp-audit scan .
47
+ ```
48
+
49
+ The product and CLI are named MCP Audit, while the PyPI distribution is named `mcp-capdiff`:
50
+
51
+ ```bash
52
+ pipx install mcp-capdiff
53
+ mcp-audit --version
54
+ ```
55
+
56
+ ## Scan a server
57
+
58
+ ```bash
59
+ uv run mcp-audit scan .
60
+ ```
61
+
62
+ Scans fail closed when source cannot be read or when no MCP tools are discovered. Use `--allow-empty` only when an empty result is intentional.
63
+
64
+ Useful output formats:
65
+
66
+ ```bash
67
+ uv run mcp-audit scan . --format json
68
+ uv run mcp-audit scan . --format sarif --output mcp-audit.sarif
69
+ ```
70
+
71
+ ## Generate a capability manifest
72
+
73
+ ```bash
74
+ uv run mcp-audit manifest .
75
+ ```
76
+
77
+ ## Compare against a Git baseline
78
+
79
+ ```bash
80
+ uv run mcp-audit diff --base origin/main
81
+ ```
82
+
83
+ The diff report focuses on security changes: before/after risk, added and changed tools, expanded capabilities, new findings, resolved findings, and approval removal.
84
+
85
+ ## Run tests
86
+
87
+ ```bash
88
+ uv run pytest
89
+ ```
90
+
91
+ The corpus contains positive, negative, and edge cases under `fixtures/`, including host allowlisting, path-root validation, approval gating, and cross-tool sensitive-data paths.
92
+
93
+ ## GitHub Action
94
+
95
+ The repository includes a composite action that runs the security diff, uploads SARIF to GitHub Code Scanning, and fails the check when policy thresholds are crossed:
96
+
97
+ ```yaml
98
+ permissions:
99
+ actions: read
100
+ contents: read
101
+ security-events: write
102
+
103
+ steps:
104
+ - uses: actions/checkout@v4
105
+ with:
106
+ fetch-depth: 0
107
+ - uses: actions/setup-python@v5
108
+ with:
109
+ python-version: "3.12"
110
+ - uses: your-org/mcp-audit@v0.1
111
+ with:
112
+ target: .
113
+ base: origin/main
114
+ policy: mcp-audit.yaml
115
+ ```
116
+
117
+ JSON reports and manifests use the versioned schema documented in `docs/report-schema.md`.
118
+
119
+ The copyable [vulnerable FastMCP demo](examples/vulnerable-fastmcp/) includes four before/after pull-request scenarios and its own Action workflow.
120
+
121
+ ## V0.1 rules
122
+
123
+ - `MCP001` arbitrary shell execution.
124
+ - `MCP002` unrestricted filesystem access.
125
+ - `MCP003` arbitrary URL or SSRF surface.
126
+ - `MCP004` high-impact side effect without approval.
127
+ - `MCP005` sensitive read to external write path.
128
+ - `MCP007` unbounded security-sensitive input.
129
+ - `MCP010` destructive tool exposed.
130
+ - `MCP016` capability escalation in a diff.
131
+ - `MCP017` approval removed in a diff.
132
+
133
+ Each rule's detection behavior, examples, remediation, and limitations are documented in [docs/rules](docs/rules/README.md).
134
+
135
+ ## Demo fixtures
136
+
137
+ ```bash
138
+ uv run mcp-audit scan fixtures/safe_server
139
+ uv run mcp-audit scan fixtures/vulnerable_server
140
+ ```
141
+
142
+ The vulnerable fixture includes a sensitive file read tool, an unrestricted URL fetcher, shell execution, and a destructive operation. MCP Audit should flag the individual findings and the cross-tool path:
143
+
144
+ ```text
145
+ read_customer_file -> agent_context -> fetch_url
146
+ ```
147
+
148
+ ## Policy
149
+
150
+ The default policy fails on high and critical findings. A minimal policy file can set a risk threshold and suppress reviewed findings:
151
+
152
+ ```yaml
153
+ policy:
154
+ ci:
155
+ fail_on:
156
+ - critical
157
+ - high
158
+ max_risk_score: 60
159
+
160
+ suppress:
161
+ - rule: MCP003
162
+ tool: internal_fetch
163
+ reason: "Network egress is enforced by service mesh"
164
+ expires: 2027-01-31
165
+ ```
166
+
167
+ Suppression reasons are required. Expired suppressions no longer hide findings, and `mcp-audit policy check` reports them. See [policy documentation](docs/policy.md).
168
+
169
+ ## Version contracts
170
+
171
+ CLI, ruleset, report schema, and manifest schema versions evolve independently. See [versioning](docs/versioning.md) and [report schema](docs/report-schema.md).
172
+
173
+ Passing MCP Audit is technical security evidence, not a legal compliance determination.
@@ -0,0 +1,150 @@
1
+ # MCP Audit
2
+
3
+ MCP Audit is an open-source security regression scanner for Model Context Protocol servers. It extracts tool capabilities and permissions, detects risky cross-tool data flows, compares security posture across Git revisions, and emits CI-friendly evidence.
4
+
5
+ The first version focuses on Python/FastMCP projects and the core product thesis from the spec: show what an MCP server can do, what changed, and whether the change creates a dangerous capability path.
6
+
7
+ MCP005 is the capability-graph rule: it identifies when one tool returns classified data and another tool in the same MCP registration context can send it to an external destination. Findings include the source, data classification, sink, destination, path, source evidence, impact, and remediation.
8
+
9
+ ## Install locally
10
+
11
+ ```bash
12
+ uv sync
13
+ ```
14
+
15
+ Run project commands through `uv run`, or activate the virtual environment with
16
+ `source .venv/bin/activate` before using bare commands.
17
+
18
+ The package also supports isolated CLI installation directly from a checkout:
19
+
20
+ ```bash
21
+ pipx install .
22
+ mcp-audit --version
23
+ mcp-audit scan .
24
+ ```
25
+
26
+ The product and CLI are named MCP Audit, while the PyPI distribution is named `mcp-capdiff`:
27
+
28
+ ```bash
29
+ pipx install mcp-capdiff
30
+ mcp-audit --version
31
+ ```
32
+
33
+ ## Scan a server
34
+
35
+ ```bash
36
+ uv run mcp-audit scan .
37
+ ```
38
+
39
+ Scans fail closed when source cannot be read or when no MCP tools are discovered. Use `--allow-empty` only when an empty result is intentional.
40
+
41
+ Useful output formats:
42
+
43
+ ```bash
44
+ uv run mcp-audit scan . --format json
45
+ uv run mcp-audit scan . --format sarif --output mcp-audit.sarif
46
+ ```
47
+
48
+ ## Generate a capability manifest
49
+
50
+ ```bash
51
+ uv run mcp-audit manifest .
52
+ ```
53
+
54
+ ## Compare against a Git baseline
55
+
56
+ ```bash
57
+ uv run mcp-audit diff --base origin/main
58
+ ```
59
+
60
+ The diff report focuses on security changes: before/after risk, added and changed tools, expanded capabilities, new findings, resolved findings, and approval removal.
61
+
62
+ ## Run tests
63
+
64
+ ```bash
65
+ uv run pytest
66
+ ```
67
+
68
+ The corpus contains positive, negative, and edge cases under `fixtures/`, including host allowlisting, path-root validation, approval gating, and cross-tool sensitive-data paths.
69
+
70
+ ## GitHub Action
71
+
72
+ The repository includes a composite action that runs the security diff, uploads SARIF to GitHub Code Scanning, and fails the check when policy thresholds are crossed:
73
+
74
+ ```yaml
75
+ permissions:
76
+ actions: read
77
+ contents: read
78
+ security-events: write
79
+
80
+ steps:
81
+ - uses: actions/checkout@v4
82
+ with:
83
+ fetch-depth: 0
84
+ - uses: actions/setup-python@v5
85
+ with:
86
+ python-version: "3.12"
87
+ - uses: your-org/mcp-audit@v0.1
88
+ with:
89
+ target: .
90
+ base: origin/main
91
+ policy: mcp-audit.yaml
92
+ ```
93
+
94
+ JSON reports and manifests use the versioned schema documented in `docs/report-schema.md`.
95
+
96
+ The copyable [vulnerable FastMCP demo](examples/vulnerable-fastmcp/) includes four before/after pull-request scenarios and its own Action workflow.
97
+
98
+ ## V0.1 rules
99
+
100
+ - `MCP001` arbitrary shell execution.
101
+ - `MCP002` unrestricted filesystem access.
102
+ - `MCP003` arbitrary URL or SSRF surface.
103
+ - `MCP004` high-impact side effect without approval.
104
+ - `MCP005` sensitive read to external write path.
105
+ - `MCP007` unbounded security-sensitive input.
106
+ - `MCP010` destructive tool exposed.
107
+ - `MCP016` capability escalation in a diff.
108
+ - `MCP017` approval removed in a diff.
109
+
110
+ Each rule's detection behavior, examples, remediation, and limitations are documented in [docs/rules](docs/rules/README.md).
111
+
112
+ ## Demo fixtures
113
+
114
+ ```bash
115
+ uv run mcp-audit scan fixtures/safe_server
116
+ uv run mcp-audit scan fixtures/vulnerable_server
117
+ ```
118
+
119
+ The vulnerable fixture includes a sensitive file read tool, an unrestricted URL fetcher, shell execution, and a destructive operation. MCP Audit should flag the individual findings and the cross-tool path:
120
+
121
+ ```text
122
+ read_customer_file -> agent_context -> fetch_url
123
+ ```
124
+
125
+ ## Policy
126
+
127
+ The default policy fails on high and critical findings. A minimal policy file can set a risk threshold and suppress reviewed findings:
128
+
129
+ ```yaml
130
+ policy:
131
+ ci:
132
+ fail_on:
133
+ - critical
134
+ - high
135
+ max_risk_score: 60
136
+
137
+ suppress:
138
+ - rule: MCP003
139
+ tool: internal_fetch
140
+ reason: "Network egress is enforced by service mesh"
141
+ expires: 2027-01-31
142
+ ```
143
+
144
+ Suppression reasons are required. Expired suppressions no longer hide findings, and `mcp-audit policy check` reports them. See [policy documentation](docs/policy.md).
145
+
146
+ ## Version contracts
147
+
148
+ CLI, ruleset, report schema, and manifest schema versions evolve independently. See [versioning](docs/versioning.md) and [report schema](docs/report-schema.md).
149
+
150
+ Passing MCP Audit is technical security evidence, not a legal compliance determination.
@@ -0,0 +1,11 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ MCP Audit is pre-1.0. Security fixes are applied to the latest released minor version.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Please use GitHub private vulnerability reporting for the repository. Do not include secrets, production source code, or private scan output in a public issue.
10
+
11
+ Include the affected MCP Audit version, a minimal reproducer, impact, and any known workaround. Maintainers should acknowledge a report within seven days and coordinate disclosure after a fix is available.
@@ -0,0 +1,58 @@
1
+ name: MCP Audit
2
+ description: Scan a Python/FastMCP server for security capability regressions and upload SARIF.
3
+ inputs:
4
+ target:
5
+ description: Path to the MCP server source.
6
+ required: false
7
+ default: "."
8
+ base:
9
+ description: Git revision used as the approved security baseline.
10
+ required: false
11
+ default: "origin/main"
12
+ policy:
13
+ description: Optional MCP Audit policy file.
14
+ required: false
15
+ default: ""
16
+ sarif:
17
+ description: SARIF output path.
18
+ required: false
19
+ default: "mcp-audit.sarif"
20
+ outputs:
21
+ exit-code:
22
+ description: MCP Audit exit code (0 pass, 1 policy failure).
23
+ value: ${{ steps.audit.outputs.exit-code }}
24
+ runs:
25
+ using: composite
26
+ steps:
27
+ - name: Install MCP Audit
28
+ shell: bash
29
+ run: python -m pip install "${{ github.action_path }}"
30
+ - name: Generate security diff
31
+ id: audit
32
+ shell: bash
33
+ env:
34
+ MCP_AUDIT_TARGET: ${{ inputs.target }}
35
+ MCP_AUDIT_BASE: ${{ inputs.base }}
36
+ MCP_AUDIT_POLICY: ${{ inputs.policy }}
37
+ MCP_AUDIT_SARIF: ${{ inputs.sarif }}
38
+ run: |
39
+ set +e
40
+ args=(diff "$MCP_AUDIT_TARGET" --base "$MCP_AUDIT_BASE" --format sarif --output "$MCP_AUDIT_SARIF")
41
+ if [[ -n "$MCP_AUDIT_POLICY" ]]; then
42
+ args+=(--policy "$MCP_AUDIT_POLICY")
43
+ fi
44
+ mcp-audit "${args[@]}"
45
+ code=$?
46
+ echo "exit-code=$code" >> "$GITHUB_OUTPUT"
47
+ if [[ $code -eq 2 ]]; then
48
+ exit 2
49
+ fi
50
+ exit 0
51
+ - name: Upload SARIF
52
+ uses: github/codeql-action/upload-sarif@v4
53
+ with:
54
+ sarif_file: ${{ inputs.sarif }}
55
+ - name: Enforce policy result
56
+ if: steps.audit.outputs.exit-code == '1'
57
+ shell: bash
58
+ run: exit 1
@@ -0,0 +1,20 @@
1
+ # Policy and suppressions
2
+
3
+ Policies define CI thresholds and reviewed exceptions:
4
+
5
+ ```yaml
6
+ policy:
7
+ ci:
8
+ fail_on: [critical, high]
9
+ max_risk_score: 60
10
+
11
+ suppress:
12
+ - rule: MCP003
13
+ tool: internal_fetch
14
+ reason: "Egress is restricted to api.example.com by the service mesh"
15
+ expires: 2027-01-31
16
+ ```
17
+
18
+ `reason` is required. `tool` is optional and omission suppresses the rule for all tools. `expires` is optional and must use `YYYY-MM-DD`; after that date the finding is no longer suppressed. `mcp-audit policy check` exits with status `1` when a suppression is expired and status `2` for invalid policy syntax.
19
+
20
+ Suppressions are policy decisions, not proof that a finding is safe. Prefer a narrow tool-specific suppression with a short expiry.
@@ -0,0 +1,18 @@
1
+ # Rule precision matrix
2
+
3
+ The release gate requires positive, negative, guarded, and edge coverage for the differentiating rules.
4
+
5
+ | Rule | True positive | True negative | Guarded safe | Tricky edge |
6
+ | --- | --- | --- | --- | --- |
7
+ | MCP001 | `shell_direct` | `safe_server` | `shell_safe_allowlist` | `shell_dynamic_no_shell` detects a dynamic executable without `shell=True` |
8
+ | MCP002 | `filesystem_unrestricted` | `sensitive_read_only` | `filesystem_restricted` | `filesystem_guard_after_read` rejects a late guard |
9
+ | MCP003 | `url_arbitrary` | fixed URL in demo baseline | `url_allowlisted` | hostname mention and guard-after-request remain findings |
10
+ | MCP005 | `sensitive_to_network` | `sensitive_read_only` | constrained source-only server | root corpus proves independent MCP contexts are isolated |
11
+ | MCP016 | network expansion diff | unchanged diff | allowlist-added demo resolves risk | a new high-impact tool is an escalation |
12
+ | MCP017 | approval-removed diff | preserved approval | approval-added demo resolves risk | capability field change is retained in machine output |
13
+
14
+ MCP004, MCP007, and MCP010 also have paired approved/bounded and unapproved/unbounded fixtures. The matrix is intentionally tied to fixture and test names so reviewers can audit the claim.
15
+
16
+ `postgresql_read_only` protects the semantic classifier from matching `post` inside `PostgreSQL`; `sql_destructive` proves that actual `DELETE`/`DROP` semantics still produce MCP004 and MCP010 with relevant evidence.
17
+
18
+ Discovery is separately benchmarked by `fastmcp_async_annotations`, which must find both plain async `@mcp.tool()` and `@mcp.tool(annotations=ToolAnnotations(...))` decorators. Missing targets, unreadable trees, parse failures, and unexpected zero-tool scans fail closed.
@@ -0,0 +1,39 @@
1
+ # Release checklist
2
+
3
+ ## Local preflight
4
+
5
+ ```bash
6
+ uv sync --locked
7
+ uv run pytest
8
+ uv build
9
+ ```
10
+
11
+ Install the wheel into a clean environment and verify `mcp-audit --version`, a passing scan, a failing scan, and `policy check`.
12
+
13
+ ## Contract review
14
+
15
+ - Confirm CLI/package, ruleset, report schema, and manifest schema versions.
16
+ - Review golden-output changes explicitly.
17
+ - Update `CHANGELOG.md` and every affected rule page.
18
+ - Confirm the composite Action references supported major action versions.
19
+
20
+ ## Publish
21
+
22
+ 1. Create and push the matching Git tag, such as `v0.1.1`.
23
+ 2. Publish the matching GitHub Release. This automatically runs `release.yml`.
24
+ 3. Verify `pipx install mcp-capdiff` in a clean environment.
25
+ 4. Run the tagged Action from the demo repository and confirm SARIF appears in Code Scanning.
26
+
27
+ For the first release, configure a GitHub Actions Pending Trusted Publisher on PyPI before running the workflow:
28
+
29
+ ```text
30
+ PyPI project name: mcp-capdiff
31
+ Owner: robbyfa
32
+ Repository: MCP-Audit
33
+ Workflow filename: release.yml
34
+ Environment: pypi
35
+ ```
36
+
37
+ The GitHub environment must be named `pypi`. Publishing uses OIDC and does not require a `PYPI_TOKEN` secret. Manual workflow dispatches require a release tag; the workflow checks out that immutable tag and verifies that the distribution version matches it.
38
+
39
+ The PyPI distribution is `mcp-capdiff`; the product remains MCP Audit and the installed command remains `mcp-audit`. Name availability is only guaranteed when the first release is registered.
@@ -0,0 +1,32 @@
1
+ # Report schema 1.0
2
+
3
+ MCP Audit uses the same dataclass-backed objects for terminal, JSON, SARIF, manifest, and diff output. JSON reports identify the contract with:
4
+
5
+ ```json
6
+ {
7
+ "schema_version": "1.0",
8
+ "report_type": "scan"
9
+ }
10
+ ```
11
+
12
+ Reports also include the CLI and ruleset versions. Manifests carry `schema_version` and `rules_version`; SARIF uses `semanticVersion` and `properties.rulesVersion` on the tool driver.
13
+
14
+ `report_type` is either `scan` or `diff`.
15
+
16
+ ## Finding contract
17
+
18
+ Every finding includes:
19
+
20
+ - `rule_id`, `title`, and `severity` for identity and prioritization.
21
+ - `tool` and `location` for the affected MCP surface.
22
+ - `message` describing what was detected.
23
+ - `impact` describing the security consequence.
24
+ - `recommendation` describing the expected remediation.
25
+ - `evidence`, a list of typed source locations and snippets.
26
+ - `path`, `data_classification`, and `destination` for capability-graph findings.
27
+
28
+ ## Diff contract
29
+
30
+ Diff reports contain before/after risk, new/removed/changed tools, field-level capability changes, new findings, resolved findings, and synthetic regression findings (`MCP016` and `MCP017`). Policies are evaluated against new and regression findings, not the entire current scan.
31
+
32
+ Additive fields may be introduced in schema `1.x`. Removing or changing the meaning of an existing field requires a new major schema version.
@@ -0,0 +1,37 @@
1
+ # MCP001: Arbitrary shell execution
2
+
3
+ **Severity:** Critical
4
+
5
+ ## What it detects
6
+
7
+ FastMCP tools that invoke `os.system`, dynamic `eval`/`exec`/`compile`, subprocess APIs with `shell=True`, or a model-controlled executable/interpreter payload.
8
+
9
+ ## Why it matters
10
+
11
+ Model-controlled input may execute commands with the MCP server process's privileges.
12
+
13
+ ## Vulnerable example
14
+
15
+ ```python
16
+ @mcp.tool()
17
+ def run(command: str):
18
+ return subprocess.run(command, shell=True)
19
+ ```
20
+
21
+ ## Safe example
22
+
23
+ ```python
24
+ @mcp.tool()
25
+ def status(service: str):
26
+ if service not in {"api", "worker"}:
27
+ raise ValueError("unsupported")
28
+ return subprocess.run(["systemctl", "status", service])
29
+ ```
30
+
31
+ ## Remediation
32
+
33
+ Expose explicit operations, allowlist values, pass argument arrays, and avoid a shell interpreter.
34
+
35
+ ## Limitations
36
+
37
+ Ruleset 0.1 recognizes common standard-library execution APIs. Wrapper functions and indirect aliases may be missed.
@@ -0,0 +1,36 @@
1
+ # MCP002: Unrestricted filesystem access
2
+
3
+ **Severity:** High
4
+
5
+ ## What it detects
6
+
7
+ Filesystem reads or writes reached by an MCP tool without a detected approved-root check before the operation.
8
+
9
+ ## Why it matters
10
+
11
+ An attacker may read secrets or modify files available to the server process.
12
+
13
+ ## Vulnerable example
14
+
15
+ ```python
16
+ @mcp.tool()
17
+ def read_file(path: str):
18
+ return Path(path).read_text()
19
+ ```
20
+
21
+ ## Safe example
22
+
23
+ ```python
24
+ resolved = (REPORT_ROOT / path).resolve()
25
+ if REPORT_ROOT not in resolved.parents:
26
+ raise ValueError("invalid path")
27
+ return resolved.read_text()
28
+ ```
29
+
30
+ ## Remediation
31
+
32
+ Resolve the requested path, reject traversal outside an explicit root, and perform the check before access.
33
+
34
+ ## Limitations
35
+
36
+ Ruleset 0.1 recognizes direct `Path` and `open` patterns. Framework-specific storage wrappers may require future adapters.