mcp-riskmap 0.1.2__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 (31) hide show
  1. mcp_riskmap-0.1.2/LICENSE +21 -0
  2. mcp_riskmap-0.1.2/PKG-INFO +221 -0
  3. mcp_riskmap-0.1.2/README.md +174 -0
  4. mcp_riskmap-0.1.2/pyproject.toml +39 -0
  5. mcp_riskmap-0.1.2/setup.cfg +4 -0
  6. mcp_riskmap-0.1.2/src/mcp_riskmap/__init__.py +5 -0
  7. mcp_riskmap-0.1.2/src/mcp_riskmap/analyzers/__init__.py +1 -0
  8. mcp_riskmap-0.1.2/src/mcp_riskmap/analyzers/common.py +35 -0
  9. mcp_riskmap-0.1.2/src/mcp_riskmap/analyzers/config.py +124 -0
  10. mcp_riskmap-0.1.2/src/mcp_riskmap/analyzers/js_source.py +66 -0
  11. mcp_riskmap-0.1.2/src/mcp_riskmap/analyzers/python_source.py +81 -0
  12. mcp_riskmap-0.1.2/src/mcp_riskmap/analyzers/repo_hygiene.py +29 -0
  13. mcp_riskmap-0.1.2/src/mcp_riskmap/cli.py +87 -0
  14. mcp_riskmap-0.1.2/src/mcp_riskmap/models.py +62 -0
  15. mcp_riskmap-0.1.2/src/mcp_riskmap/redaction.py +18 -0
  16. mcp_riskmap-0.1.2/src/mcp_riskmap/reporters/__init__.py +1 -0
  17. mcp_riskmap-0.1.2/src/mcp_riskmap/reporters/json_reporter.py +9 -0
  18. mcp_riskmap-0.1.2/src/mcp_riskmap/reporters/markdown.py +28 -0
  19. mcp_riskmap-0.1.2/src/mcp_riskmap/reporters/sarif.py +88 -0
  20. mcp_riskmap-0.1.2/src/mcp_riskmap/reporters/table.py +33 -0
  21. mcp_riskmap-0.1.2/src/mcp_riskmap/rules/__init__.py +1 -0
  22. mcp_riskmap-0.1.2/src/mcp_riskmap/rules/registry.py +54 -0
  23. mcp_riskmap-0.1.2/src/mcp_riskmap/scanner.py +73 -0
  24. mcp_riskmap-0.1.2/src/mcp_riskmap.egg-info/PKG-INFO +221 -0
  25. mcp_riskmap-0.1.2/src/mcp_riskmap.egg-info/SOURCES.txt +29 -0
  26. mcp_riskmap-0.1.2/src/mcp_riskmap.egg-info/dependency_links.txt +1 -0
  27. mcp_riskmap-0.1.2/src/mcp_riskmap.egg-info/entry_points.txt +2 -0
  28. mcp_riskmap-0.1.2/src/mcp_riskmap.egg-info/top_level.txt +1 -0
  29. mcp_riskmap-0.1.2/tests/test_cli.py +73 -0
  30. mcp_riskmap-0.1.2/tests/test_reporters.py +76 -0
  31. mcp_riskmap-0.1.2/tests/test_scanner.py +127 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mcp-riskmap 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,221 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-riskmap
3
+ Version: 0.1.2
4
+ Summary: Local-first static auditor for risky MCP and agent-tool repository patterns.
5
+ Author: mcp-riskmap contributors
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 mcp-riskmap contributors
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/vawkdh-job/mcp-riskmap
29
+ Project-URL: Repository, https://github.com/vawkdh-job/mcp-riskmap
30
+ Project-URL: Issues, https://github.com/vawkdh-job/mcp-riskmap/issues
31
+ Project-URL: Changelog, https://github.com/vawkdh-job/mcp-riskmap/blob/main/CHANGELOG.md
32
+ Keywords: mcp,security,agent,static-analysis,sarif,github-actions,code-scanning
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Environment :: Console
35
+ Classifier: Intended Audience :: Developers
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.10
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Topic :: Security
42
+ Classifier: Topic :: Software Development :: Quality Assurance
43
+ Requires-Python: >=3.10
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE
46
+ Dynamic: license-file
47
+
48
+ # mcp-riskmap
49
+
50
+ [![CI](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/ci.yml)
51
+ [![mcp-riskmap](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/mcp-riskmap.yml/badge.svg?branch=main)](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/mcp-riskmap.yml)
52
+ [![Release](https://img.shields.io/github/v/release/vawkdh-job/mcp-riskmap)](https://github.com/vawkdh-job/mcp-riskmap/releases)
53
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
54
+
55
+ `mcp-riskmap` is a local-first static auditor for MCP and agent-tool repositories. It looks for risky MCP configuration, shell-enabled tool handlers, prompt-injection-like tool descriptions, and missing maintainer guidance without starting untrusted MCP servers.
56
+
57
+ This project is intentionally small and conservative. It is designed for maintainers who want a quick review signal in local development, pull requests, and GitHub Code Scanning.
58
+
59
+ ## Why this exists
60
+
61
+ MCP servers often expose tools that can touch files, shells, networks, credentials, or local developer state. Reference servers and community examples are useful, but each maintainer still needs a threat model and basic safeguards before sharing configs or accepting tool changes.
62
+
63
+ `mcp-riskmap` focuses on static signals that are cheap to review:
64
+
65
+ - MCP config that starts through `cmd`, `powershell`, `bash`, or `sh`
66
+ - Remote install pipelines such as `curl ... | sh` or `curl ... | iex`
67
+ - Secret-like environment variables passed into MCP servers
68
+ - Python `subprocess(..., shell=True)`, `os.system`, `eval`, and `exec`
69
+ - JavaScript `child_process.exec` and `spawn(..., { shell: true })`
70
+ - Tool text that looks like model-control prompt injection
71
+ - Missing `AGENTS.md`, `SECURITY.md`, or `LICENSE`
72
+
73
+ ## Install
74
+
75
+ From a checkout:
76
+
77
+ ```bash
78
+ python -m pip install -e .
79
+ ```
80
+
81
+ From GitHub:
82
+
83
+ ```bash
84
+ python -m pip install "git+https://github.com/vawkdh-job/mcp-riskmap.git@v0.1.2"
85
+ ```
86
+
87
+ For development without installing:
88
+
89
+ ```bash
90
+ $env:PYTHONPATH = "src"
91
+ python -m mcp_riskmap.cli scan examples/unsafe-mcp-server
92
+ python -m mcp_riskmap.cli --version
93
+ ```
94
+
95
+ ## Usage
96
+
97
+ ```bash
98
+ mcp-riskmap scan .
99
+ mcp-riskmap scan . --format json
100
+ mcp-riskmap scan . --format markdown --output report.md
101
+ mcp-riskmap scan . --format sarif --output results.sarif --fail-on high
102
+ mcp-riskmap scan . --exclude "examples/**" --exclude "tests/**"
103
+ ```
104
+
105
+ `--fail-on high` returns exit code `1` when at least one finding is high or critical.
106
+
107
+ Use `--exclude` for reviewed fixture directories, generated output, or intentionally unsafe examples that should not block CI.
108
+
109
+ ## Examples
110
+
111
+ - `examples/unsafe-mcp-server/` contains intentionally risky MCP config and tool-handler patterns for scanner demonstrations.
112
+ - `examples/safe-mcp-server/` contains a safer file-read pattern using a resolved base directory boundary check.
113
+
114
+ ## Example output
115
+
116
+ ```text
117
+ SEVERITY RULE LOCATION MESSAGE
118
+ -------- ------------------------- ------------ ------------------------------------------------
119
+ CRITICAL MCP-CONFIG-REMOTE-INSTALL mcp.json:5 MCP server 'unsafe-demo' downloads and executes...
120
+ HIGH PY-SHELL-TRUE server.py:6 A Python tool handler can pass input through a shell.
121
+ HIGH JS-CHILD-PROCESS-EXEC server.js:4 A JavaScript tool handler can pass input through a shell.
122
+ ```
123
+
124
+ ## Output formats
125
+
126
+ - `table`: compact terminal output
127
+ - `json`: automation-friendly structured output
128
+ - `markdown`: issue and release-note friendly report
129
+ - `sarif`: GitHub Code Scanning compatible output
130
+
131
+ Structured outputs redact secret-like evidence values before writing JSON or SARIF.
132
+
133
+ ## Reviewed suppressions
134
+
135
+ If a maintainer reviews a finding and accepts the risk, add a narrow suppression on the same line or the previous line:
136
+
137
+ ```python
138
+ # mcp-riskmap: ignore PY-SHELL-TRUE
139
+ subprocess.run(command, shell=True)
140
+ ```
141
+
142
+ Use rule-specific suppressions where possible. `mcp-riskmap: ignore` suppresses all rules on the next line and should be reserved for generated or documented fixture code.
143
+
144
+ ## GitHub Action
145
+
146
+ This repository includes a composite GitHub Action:
147
+
148
+ ```yaml
149
+ name: mcp-riskmap
150
+
151
+ on:
152
+ pull_request:
153
+ push:
154
+ branches: [main]
155
+
156
+ jobs:
157
+ scan:
158
+ runs-on: ubuntu-latest
159
+ permissions:
160
+ contents: read
161
+ security-events: write
162
+ steps:
163
+ - uses: actions/checkout@v4
164
+ - uses: vawkdh-job/mcp-riskmap@v0.1.2
165
+ with:
166
+ path: .
167
+ format: sarif
168
+ output: mcp-riskmap.sarif
169
+ fail-on: high
170
+ exclude: |
171
+ examples/**
172
+ tests/**
173
+ - uses: github/codeql-action/upload-sarif@v3
174
+ if: always()
175
+ with:
176
+ sarif_file: mcp-riskmap.sarif
177
+ ```
178
+
179
+ ## Safety stance
180
+
181
+ `mcp-riskmap` does not execute MCP servers. It reads files and reports static findings. That means it will miss runtime-only behavior, but it is safer for quick review of unknown configs and pull requests.
182
+
183
+ ## Why static only?
184
+
185
+ Some MCP scanners inspect live tool descriptions by starting configured servers. That can be useful, but it is risky when a reviewer is looking at an unknown repository or pull request. `mcp-riskmap` is meant to run earlier in the review flow: it reads files, flags obvious risk, and produces review artifacts without executing commands from the target project.
186
+
187
+ ## Compared with dynamic scanners
188
+
189
+ `mcp-riskmap` is not a replacement for dynamic MCP inspection. It is a first-pass guardrail for maintainers who need quick review signals in CI and code review.
190
+
191
+ | Area | mcp-riskmap | Dynamic scanners |
192
+ | --- | --- | --- |
193
+ | Starts scanned MCP servers | No | Often yes |
194
+ | Safe for unknown PRs | Designed for this | Depends on sandboxing |
195
+ | CI/SARIF friendly | Yes | Depends on tool |
196
+ | Runtime behavior coverage | Limited | Better |
197
+ | Static source/config review | Primary focus | Varies |
198
+
199
+ ## Current limitations
200
+
201
+ - Rules are conservative regex/static checks, not full taint analysis.
202
+ - The scanner does not inspect live MCP tool responses.
203
+ - Secret detection is key-name based and does not do high-entropy scanning.
204
+ - JavaScript and Python analyzers focus on common high-risk patterns first.
205
+
206
+ ## Roadmap
207
+
208
+ - Add more MCP client config locations.
209
+ - Detect unsafe filesystem writes and path traversal candidates.
210
+ - Add rule severity profiles.
211
+ - Add Semgrep-compatible pattern export.
212
+
213
+ See [ROADMAP.md](ROADMAP.md) for issue-sized milestones.
214
+
215
+ ## OpenAI Codex for OSS fit
216
+
217
+ This project is intended to be maintained as an open-source security and maintainer automation tool. It includes tests, CI, SARIF output, examples, security docs, contribution guidance, AGENTS.md, and tagged releases.
218
+
219
+ Codex/API credits would be useful for reviewing rule changes, generating regression tests, triaging issues, improving documentation, and producing release notes. AI output should be reviewed by maintainers before merge.
220
+
221
+ See [docs/codex-for-oss.md](docs/codex-for-oss.md) for application-specific maintainer workflow notes.
@@ -0,0 +1,174 @@
1
+ # mcp-riskmap
2
+
3
+ [![CI](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/ci.yml)
4
+ [![mcp-riskmap](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/mcp-riskmap.yml/badge.svg?branch=main)](https://github.com/vawkdh-job/mcp-riskmap/actions/workflows/mcp-riskmap.yml)
5
+ [![Release](https://img.shields.io/github/v/release/vawkdh-job/mcp-riskmap)](https://github.com/vawkdh-job/mcp-riskmap/releases)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+
8
+ `mcp-riskmap` is a local-first static auditor for MCP and agent-tool repositories. It looks for risky MCP configuration, shell-enabled tool handlers, prompt-injection-like tool descriptions, and missing maintainer guidance without starting untrusted MCP servers.
9
+
10
+ This project is intentionally small and conservative. It is designed for maintainers who want a quick review signal in local development, pull requests, and GitHub Code Scanning.
11
+
12
+ ## Why this exists
13
+
14
+ MCP servers often expose tools that can touch files, shells, networks, credentials, or local developer state. Reference servers and community examples are useful, but each maintainer still needs a threat model and basic safeguards before sharing configs or accepting tool changes.
15
+
16
+ `mcp-riskmap` focuses on static signals that are cheap to review:
17
+
18
+ - MCP config that starts through `cmd`, `powershell`, `bash`, or `sh`
19
+ - Remote install pipelines such as `curl ... | sh` or `curl ... | iex`
20
+ - Secret-like environment variables passed into MCP servers
21
+ - Python `subprocess(..., shell=True)`, `os.system`, `eval`, and `exec`
22
+ - JavaScript `child_process.exec` and `spawn(..., { shell: true })`
23
+ - Tool text that looks like model-control prompt injection
24
+ - Missing `AGENTS.md`, `SECURITY.md`, or `LICENSE`
25
+
26
+ ## Install
27
+
28
+ From a checkout:
29
+
30
+ ```bash
31
+ python -m pip install -e .
32
+ ```
33
+
34
+ From GitHub:
35
+
36
+ ```bash
37
+ python -m pip install "git+https://github.com/vawkdh-job/mcp-riskmap.git@v0.1.2"
38
+ ```
39
+
40
+ For development without installing:
41
+
42
+ ```bash
43
+ $env:PYTHONPATH = "src"
44
+ python -m mcp_riskmap.cli scan examples/unsafe-mcp-server
45
+ python -m mcp_riskmap.cli --version
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ```bash
51
+ mcp-riskmap scan .
52
+ mcp-riskmap scan . --format json
53
+ mcp-riskmap scan . --format markdown --output report.md
54
+ mcp-riskmap scan . --format sarif --output results.sarif --fail-on high
55
+ mcp-riskmap scan . --exclude "examples/**" --exclude "tests/**"
56
+ ```
57
+
58
+ `--fail-on high` returns exit code `1` when at least one finding is high or critical.
59
+
60
+ Use `--exclude` for reviewed fixture directories, generated output, or intentionally unsafe examples that should not block CI.
61
+
62
+ ## Examples
63
+
64
+ - `examples/unsafe-mcp-server/` contains intentionally risky MCP config and tool-handler patterns for scanner demonstrations.
65
+ - `examples/safe-mcp-server/` contains a safer file-read pattern using a resolved base directory boundary check.
66
+
67
+ ## Example output
68
+
69
+ ```text
70
+ SEVERITY RULE LOCATION MESSAGE
71
+ -------- ------------------------- ------------ ------------------------------------------------
72
+ CRITICAL MCP-CONFIG-REMOTE-INSTALL mcp.json:5 MCP server 'unsafe-demo' downloads and executes...
73
+ HIGH PY-SHELL-TRUE server.py:6 A Python tool handler can pass input through a shell.
74
+ HIGH JS-CHILD-PROCESS-EXEC server.js:4 A JavaScript tool handler can pass input through a shell.
75
+ ```
76
+
77
+ ## Output formats
78
+
79
+ - `table`: compact terminal output
80
+ - `json`: automation-friendly structured output
81
+ - `markdown`: issue and release-note friendly report
82
+ - `sarif`: GitHub Code Scanning compatible output
83
+
84
+ Structured outputs redact secret-like evidence values before writing JSON or SARIF.
85
+
86
+ ## Reviewed suppressions
87
+
88
+ If a maintainer reviews a finding and accepts the risk, add a narrow suppression on the same line or the previous line:
89
+
90
+ ```python
91
+ # mcp-riskmap: ignore PY-SHELL-TRUE
92
+ subprocess.run(command, shell=True)
93
+ ```
94
+
95
+ Use rule-specific suppressions where possible. `mcp-riskmap: ignore` suppresses all rules on the next line and should be reserved for generated or documented fixture code.
96
+
97
+ ## GitHub Action
98
+
99
+ This repository includes a composite GitHub Action:
100
+
101
+ ```yaml
102
+ name: mcp-riskmap
103
+
104
+ on:
105
+ pull_request:
106
+ push:
107
+ branches: [main]
108
+
109
+ jobs:
110
+ scan:
111
+ runs-on: ubuntu-latest
112
+ permissions:
113
+ contents: read
114
+ security-events: write
115
+ steps:
116
+ - uses: actions/checkout@v4
117
+ - uses: vawkdh-job/mcp-riskmap@v0.1.2
118
+ with:
119
+ path: .
120
+ format: sarif
121
+ output: mcp-riskmap.sarif
122
+ fail-on: high
123
+ exclude: |
124
+ examples/**
125
+ tests/**
126
+ - uses: github/codeql-action/upload-sarif@v3
127
+ if: always()
128
+ with:
129
+ sarif_file: mcp-riskmap.sarif
130
+ ```
131
+
132
+ ## Safety stance
133
+
134
+ `mcp-riskmap` does not execute MCP servers. It reads files and reports static findings. That means it will miss runtime-only behavior, but it is safer for quick review of unknown configs and pull requests.
135
+
136
+ ## Why static only?
137
+
138
+ Some MCP scanners inspect live tool descriptions by starting configured servers. That can be useful, but it is risky when a reviewer is looking at an unknown repository or pull request. `mcp-riskmap` is meant to run earlier in the review flow: it reads files, flags obvious risk, and produces review artifacts without executing commands from the target project.
139
+
140
+ ## Compared with dynamic scanners
141
+
142
+ `mcp-riskmap` is not a replacement for dynamic MCP inspection. It is a first-pass guardrail for maintainers who need quick review signals in CI and code review.
143
+
144
+ | Area | mcp-riskmap | Dynamic scanners |
145
+ | --- | --- | --- |
146
+ | Starts scanned MCP servers | No | Often yes |
147
+ | Safe for unknown PRs | Designed for this | Depends on sandboxing |
148
+ | CI/SARIF friendly | Yes | Depends on tool |
149
+ | Runtime behavior coverage | Limited | Better |
150
+ | Static source/config review | Primary focus | Varies |
151
+
152
+ ## Current limitations
153
+
154
+ - Rules are conservative regex/static checks, not full taint analysis.
155
+ - The scanner does not inspect live MCP tool responses.
156
+ - Secret detection is key-name based and does not do high-entropy scanning.
157
+ - JavaScript and Python analyzers focus on common high-risk patterns first.
158
+
159
+ ## Roadmap
160
+
161
+ - Add more MCP client config locations.
162
+ - Detect unsafe filesystem writes and path traversal candidates.
163
+ - Add rule severity profiles.
164
+ - Add Semgrep-compatible pattern export.
165
+
166
+ See [ROADMAP.md](ROADMAP.md) for issue-sized milestones.
167
+
168
+ ## OpenAI Codex for OSS fit
169
+
170
+ This project is intended to be maintained as an open-source security and maintainer automation tool. It includes tests, CI, SARIF output, examples, security docs, contribution guidance, AGENTS.md, and tagged releases.
171
+
172
+ Codex/API credits would be useful for reviewing rule changes, generating regression tests, triaging issues, improving documentation, and producing release notes. AI output should be reviewed by maintainers before merge.
173
+
174
+ See [docs/codex-for-oss.md](docs/codex-for-oss.md) for application-specific maintainer workflow notes.
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mcp-riskmap"
7
+ version = "0.1.2"
8
+ description = "Local-first static auditor for risky MCP and agent-tool repository patterns."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { file = "LICENSE" }
12
+ authors = [
13
+ { name = "mcp-riskmap contributors" }
14
+ ]
15
+ keywords = ["mcp", "security", "agent", "static-analysis", "sarif", "github-actions", "code-scanning"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Security",
26
+ "Topic :: Software Development :: Quality Assurance",
27
+ ]
28
+
29
+ [project.scripts]
30
+ mcp-riskmap = "mcp_riskmap.cli:main"
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/vawkdh-job/mcp-riskmap"
34
+ Repository = "https://github.com/vawkdh-job/mcp-riskmap"
35
+ Issues = "https://github.com/vawkdh-job/mcp-riskmap/issues"
36
+ Changelog = "https://github.com/vawkdh-job/mcp-riskmap/blob/main/CHANGELOG.md"
37
+
38
+ [tool.setuptools.packages.find]
39
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """Static MCP and agent-tool repository risk scanner."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.2"
@@ -0,0 +1 @@
1
+ """File analyzers used by the scanner."""
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+ SUPPRESSION_RE = re.compile(r"mcp-riskmap:\s*ignore(?:\s+([A-Z0-9_, -]+))?", re.IGNORECASE)
7
+
8
+
9
+ def relative_path(root: Path, path: Path) -> str:
10
+ try:
11
+ return path.relative_to(root).as_posix()
12
+ except ValueError:
13
+ return path.as_posix()
14
+
15
+
16
+ def read_text(path: Path) -> str:
17
+ return path.read_text(encoding="utf-8", errors="ignore")
18
+
19
+
20
+ def is_suppressed(lines: list[str], line_index: int, rule_id: str) -> bool:
21
+ comment_lines = [lines[line_index]]
22
+ if line_index > 0:
23
+ comment_lines.append(lines[line_index - 1])
24
+ return any(_suppresses_rule(line, rule_id) for line in comment_lines)
25
+
26
+
27
+ def _suppresses_rule(line: str, rule_id: str) -> bool:
28
+ match = SUPPRESSION_RE.search(line)
29
+ if not match:
30
+ return False
31
+ raw_rules = match.group(1)
32
+ if not raw_rules:
33
+ return True
34
+ rules = {rule.strip().upper() for rule in re.split(r"[,\s]+", raw_rules) if rule.strip()}
35
+ return "ALL" in rules or rule_id.upper() in rules
@@ -0,0 +1,124 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from mcp_riskmap.analyzers.common import relative_path, read_text
9
+ from mcp_riskmap.models import Finding
10
+
11
+ CONFIG_NAMES = {
12
+ "mcp.json",
13
+ "mcp.config.json",
14
+ "claude_desktop_config.json",
15
+ "settings.json",
16
+ }
17
+
18
+ SECRET_KEY_RE = re.compile(r"(api[_-]?key|token|secret|password|credential)", re.IGNORECASE)
19
+
20
+
21
+ def is_candidate(path: Path) -> bool:
22
+ return path.name.lower() in CONFIG_NAMES or ".mcp" in path.name.lower()
23
+
24
+
25
+ def analyze_config(root: Path, path: Path) -> list[Finding]:
26
+ text = read_text(path)
27
+ if "mcpServers" not in text and "mcp_servers" not in text:
28
+ return []
29
+
30
+ findings: list[Finding] = []
31
+ try:
32
+ data = json.loads(text)
33
+ except json.JSONDecodeError:
34
+ return findings
35
+
36
+ servers = _server_entries(data)
37
+ for server_name, server in servers:
38
+ command = str(server.get("command", ""))
39
+ args = [str(arg) for arg in server.get("args", [])]
40
+ command_line = " ".join([command, *args]).lower()
41
+ path_text = relative_path(root, path)
42
+
43
+ if _looks_like_shell_wrapper(command, args):
44
+ findings.append(
45
+ Finding(
46
+ rule_id="MCP-CONFIG-SHELL",
47
+ title="MCP server starts through a shell wrapper",
48
+ severity="high",
49
+ message=f"MCP server '{server_name}' starts through a shell-capable command.",
50
+ path=path_text,
51
+ line=_line_for(text, command),
52
+ remediation="Pin the server executable directly and avoid cmd, powershell, bash, or sh wrappers for untrusted configs.",
53
+ evidence=" ".join([command, *args])[:240],
54
+ )
55
+ )
56
+
57
+ if "curl " in command_line and ("| sh" in command_line or "| iex" in command_line):
58
+ findings.append(
59
+ Finding(
60
+ rule_id="MCP-CONFIG-REMOTE-INSTALL",
61
+ title="MCP config pipes remote content into an interpreter",
62
+ severity="critical",
63
+ message=f"MCP server '{server_name}' downloads and executes remote content.",
64
+ path=path_text,
65
+ line=_line_for(text, "curl"),
66
+ remediation="Replace remote install pipelines with pinned packages, checksums, or reviewed local scripts.",
67
+ evidence=" ".join([command, *args])[:240],
68
+ )
69
+ )
70
+
71
+ env = server.get("env")
72
+ if isinstance(env, dict):
73
+ secret_keys = sorted(key for key in env if SECRET_KEY_RE.search(str(key)))
74
+ if secret_keys:
75
+ findings.append(
76
+ Finding(
77
+ rule_id="MCP-CONFIG-SECRET-ENV",
78
+ title="MCP config passes secret-like environment variables",
79
+ severity="medium",
80
+ message=f"MCP server '{server_name}' passes secret-like environment keys: {', '.join(secret_keys)}.",
81
+ path=path_text,
82
+ line=_line_for(text, secret_keys[0]),
83
+ remediation="Pass only the minimum required environment variables and document why each secret is needed.",
84
+ evidence=", ".join(secret_keys),
85
+ )
86
+ )
87
+
88
+ if command.lower() == "npx" and any(arg == "-y" for arg in args):
89
+ findings.append(
90
+ Finding(
91
+ rule_id="MCP-CONFIG-NPX-LATEST",
92
+ title="MCP config allows non-interactive npx package execution",
93
+ severity="medium",
94
+ message=f"MCP server '{server_name}' runs npx with automatic yes.",
95
+ path=path_text,
96
+ line=_line_for(text, "npx"),
97
+ remediation="Pin package versions and review the resolved package before using npx -y in shared configs.",
98
+ evidence=" ".join([command, *args])[:240],
99
+ )
100
+ )
101
+
102
+ return findings
103
+
104
+
105
+ def _server_entries(data: dict[str, Any]) -> list[tuple[str, dict[str, Any]]]:
106
+ servers = data.get("mcpServers") or data.get("mcp_servers") or {}
107
+ if not isinstance(servers, dict):
108
+ return []
109
+ return [(str(name), server) for name, server in servers.items() if isinstance(server, dict)]
110
+
111
+
112
+ def _looks_like_shell_wrapper(command: str, args: list[str]) -> bool:
113
+ shell_commands = {"cmd", "cmd.exe", "powershell", "powershell.exe", "pwsh", "bash", "sh"}
114
+ if Path(command).name.lower() in shell_commands:
115
+ return True
116
+ shell_flags = {"/c", "-c", "-command", "/command"}
117
+ return any(arg.lower() in shell_flags for arg in args)
118
+
119
+
120
+ def _line_for(text: str, needle: str) -> int:
121
+ for index, line in enumerate(text.splitlines(), start=1):
122
+ if needle and needle in line:
123
+ return index
124
+ return 1