agent-mcp-guard 0.4.8 → 0.4.9
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.
- package/README.md +30 -2
- package/docs/baseline.md +2 -2
- package/docs/github-action.md +5 -5
- package/docs/launch-checklist.md +1 -1
- package/docs/marketplace-action-readme.md +4 -4
- package/docs/marketplace.md +7 -7
- package/docs/rules.md +29 -21
- package/docs/trusted-publishing.md +2 -2
- package/package.json +1 -1
- package/site/e2e/audit/mcp-guard-audit-manifest.json +9 -9
- package/site/e2e/audit/mcp-guard-executive-summary.md +1 -1
- package/site/e2e/audit/mcp-guard-remediation-checklist.md +1 -1
- package/site/e2e/audit/mcp-guard-remediation.md +1 -1
- package/site/e2e/audit/mcp-guard-report.html +1 -1
- package/site/e2e/audit/mcp-guard-report.json +2 -2
- package/site/e2e/audit/mcp-guard-report.md +1 -1
- package/site/e2e/audit/mcp-guard.sarif +1 -1
- package/site/e2e/report.html +1 -1
- package/site/e2e/report.json +2 -2
- package/site/e2e/report.md +1 -1
- package/site/e2e/report.sarif +1 -1
- package/src/cli.js +49 -1
- package/src/rule-catalog.js +169 -0
package/README.md
CHANGED
|
@@ -19,9 +19,30 @@ Live demo PR: [mcp-guard-demo#1](https://github.com/ChaoYue0307/mcp-guard-demo/p
|
|
|
19
19
|
<a href="https://github.com/marketplace/actions/mcp-guard-mcp-security-scanner"><img alt="GitHub Marketplace" src="https://img.shields.io/badge/Marketplace-mcp--guard-0f766e?logo=github"></a>
|
|
20
20
|
<a href="https://github.com/ChaoYue0307/mcp-guard/actions"><img alt="CI" src="https://github.com/ChaoYue0307/mcp-guard/actions/workflows/ci.yml/badge.svg"></a>
|
|
21
21
|
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-111827"></a>
|
|
22
|
-
<a href="https://github.com/ChaoYue0307/mcp-guard/releases/tag/v0.4.
|
|
22
|
+
<a href="https://github.com/ChaoYue0307/mcp-guard/releases/tag/v0.4.9"><img alt="Release" src="https://img.shields.io/github/v/release/ChaoYue0307/mcp-guard?color=7c2d12"></a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
|
+
## MCP Basics
|
|
26
|
+
|
|
27
|
+
MCP, the Model Context Protocol, is a common way for AI apps and agents to connect to external tools, files, and services.
|
|
28
|
+
|
|
29
|
+
In practice, most MCP risk lives in the config file that tells an AI client what servers to start or call.
|
|
30
|
+
|
|
31
|
+
| Concept | Plain-English meaning | Why security teams should care |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| MCP client | The AI app or agent runtime, such as Claude Desktop, Cursor, Codex, or an internal agent. | It decides which MCP servers are available to the model. |
|
|
34
|
+
| MCP server | A local process or remote endpoint that exposes tools, resources, or prompts. | It may read files, run commands, call SaaS APIs, or send data outside the machine. |
|
|
35
|
+
| MCP config | JSON or project config that lists server commands, URLs, args, env vars, working directories, and headers. | A small config change can introduce shell execution, broad filesystem access, or long-lived credentials. |
|
|
36
|
+
| Review point | The moment before a server is added locally or merged into a repo. | This is where `mcp-guard` scans for risky commands, unpinned packages, leaked secrets, broad paths, and remote endpoints. |
|
|
37
|
+
|
|
38
|
+
`mcp-guard` does not try to replace MCP. It gives teams a quick way to review MCP tool access before agents can use it.
|
|
39
|
+
|
|
40
|
+
To see the exact risk checks in the current release:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
mcp-guard rules --format markdown
|
|
44
|
+
```
|
|
45
|
+
|
|
25
46
|
## Install
|
|
26
47
|
|
|
27
48
|
```bash
|
|
@@ -65,6 +86,13 @@ Generate SARIF for GitHub code scanning:
|
|
|
65
86
|
mcp-guard scan --format sarif --output mcp-guard.sarif
|
|
66
87
|
```
|
|
67
88
|
|
|
89
|
+
Inspect the rule catalog:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
mcp-guard rules
|
|
93
|
+
mcp-guard rules --format json
|
|
94
|
+
```
|
|
95
|
+
|
|
68
96
|
Generate a review-ready audit pack:
|
|
69
97
|
|
|
70
98
|
```bash
|
|
@@ -94,7 +122,7 @@ mcp-guard scan --config .mcp.json --baseline .mcp-guard-baseline.json --fail-on
|
|
|
94
122
|
Use the GitHub Action:
|
|
95
123
|
|
|
96
124
|
```yaml
|
|
97
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
125
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
98
126
|
with:
|
|
99
127
|
config: .mcp.json
|
|
100
128
|
# policy: .mcp-guard-policy.json
|
package/docs/baseline.md
CHANGED
|
@@ -30,7 +30,7 @@ If the scan finds only baseline-accepted findings, the exit code is `0`. If a ne
|
|
|
30
30
|
## GitHub Action
|
|
31
31
|
|
|
32
32
|
```yaml
|
|
33
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
33
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
34
34
|
with:
|
|
35
35
|
config: .mcp.json
|
|
36
36
|
baseline: .mcp-guard-baseline.json
|
|
@@ -45,7 +45,7 @@ The generated Markdown, HTML, JSON, and PR comment separate active findings from
|
|
|
45
45
|
{
|
|
46
46
|
"version": 1,
|
|
47
47
|
"generatedAt": "2026-05-10T00:00:00.000Z",
|
|
48
|
-
"toolVersion": "0.4.
|
|
48
|
+
"toolVersion": "0.4.9",
|
|
49
49
|
"findings": [
|
|
50
50
|
{
|
|
51
51
|
"fingerprint": "mcpg_a009b2c2",
|
package/docs/github-action.md
CHANGED
|
@@ -39,7 +39,7 @@ jobs:
|
|
|
39
39
|
runs-on: ubuntu-latest
|
|
40
40
|
steps:
|
|
41
41
|
- uses: actions/checkout@v6
|
|
42
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
42
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
43
43
|
with:
|
|
44
44
|
config: .mcp.json
|
|
45
45
|
fail-on: high
|
|
@@ -67,7 +67,7 @@ jobs:
|
|
|
67
67
|
runs-on: ubuntu-latest
|
|
68
68
|
steps:
|
|
69
69
|
- uses: actions/checkout@v6
|
|
70
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
70
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
71
71
|
with:
|
|
72
72
|
config: .mcp.json
|
|
73
73
|
fail-on: high
|
|
@@ -79,7 +79,7 @@ jobs:
|
|
|
79
79
|
Use `fail-on: none` when you want artifacts and summaries without blocking a pull request.
|
|
80
80
|
|
|
81
81
|
```yaml
|
|
82
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
82
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
83
83
|
with:
|
|
84
84
|
fail-on: none
|
|
85
85
|
```
|
|
@@ -95,7 +95,7 @@ mcp-guard scan --config .mcp.json --write-baseline .mcp-guard-baseline.json
|
|
|
95
95
|
Commit `.mcp-guard-baseline.json`, then reference it from the action:
|
|
96
96
|
|
|
97
97
|
```yaml
|
|
98
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
98
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
99
99
|
with:
|
|
100
100
|
config: .mcp.json
|
|
101
101
|
baseline: .mcp-guard-baseline.json
|
|
@@ -109,7 +109,7 @@ Reports will show active findings separately from findings accepted by the basel
|
|
|
109
109
|
Use a policy when you want CI to enforce approved commands, packages, directories, and remote URLs.
|
|
110
110
|
|
|
111
111
|
```yaml
|
|
112
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
112
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
113
113
|
with:
|
|
114
114
|
config: .mcp.json
|
|
115
115
|
policy: .mcp-guard-policy.json
|
package/docs/launch-checklist.md
CHANGED
|
@@ -38,7 +38,7 @@ mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
|
|
|
38
38
|
## GitHub Action Setup
|
|
39
39
|
|
|
40
40
|
```yaml
|
|
41
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
41
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
42
42
|
with:
|
|
43
43
|
config: .mcp.json
|
|
44
44
|
baseline: .mcp-guard-baseline.json
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
runs-on: ubuntu-latest
|
|
24
24
|
steps:
|
|
25
25
|
- uses: actions/checkout@v6
|
|
26
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
26
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
27
27
|
with:
|
|
28
28
|
config: .mcp.json
|
|
29
29
|
fail-on: high
|
|
@@ -42,7 +42,7 @@ jobs:
|
|
|
42
42
|
runs-on: ubuntu-latest
|
|
43
43
|
steps:
|
|
44
44
|
- uses: actions/checkout@v6
|
|
45
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
45
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
46
46
|
with:
|
|
47
47
|
config: .mcp.json
|
|
48
48
|
fail-on: high
|
|
@@ -103,7 +103,7 @@ mcp-guard scan --config .mcp.json --write-baseline .mcp-guard-baseline.json
|
|
|
103
103
|
Then enforce only new findings:
|
|
104
104
|
|
|
105
105
|
```yaml
|
|
106
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
106
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
107
107
|
with:
|
|
108
108
|
config: .mcp.json
|
|
109
109
|
baseline: .mcp-guard-baseline.json
|
|
@@ -115,7 +115,7 @@ Then enforce only new findings:
|
|
|
115
115
|
Commit `.mcp-guard-policy.json` or pass `policy` to enforce approved commands, remote packages, directories, and remote MCP URLs.
|
|
116
116
|
|
|
117
117
|
```yaml
|
|
118
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
118
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
119
119
|
with:
|
|
120
120
|
config: .mcp.json
|
|
121
121
|
policy: .mcp-guard-policy.json
|
package/docs/marketplace.md
CHANGED
|
@@ -82,18 +82,18 @@ Code quality
|
|
|
82
82
|
Current release title:
|
|
83
83
|
|
|
84
84
|
```text
|
|
85
|
-
v0.4.
|
|
85
|
+
v0.4.9
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
Release notes:
|
|
89
89
|
|
|
90
90
|
```text
|
|
91
|
-
|
|
91
|
+
Rule catalog and MCP education release.
|
|
92
92
|
|
|
93
|
-
- Adds `mcp-guard
|
|
94
|
-
-
|
|
95
|
-
- Keeps remediation checklist, first remediation steps in PR comments, executive summary, remediation plan, Markdown, HTML, JSON, SARIF, policy, baseline, artifacts, and SARIF upload support.
|
|
96
|
-
- Improves
|
|
93
|
+
- Adds `mcp-guard rules` so users can inspect every scanner rule from the CLI in text, Markdown, or JSON.
|
|
94
|
+
- Adds MCP basics to the README and website so new readers can understand clients, servers, configs, and review points.
|
|
95
|
+
- Keeps audit pack verification, remediation checklist, first remediation steps in PR comments, executive summary, remediation plan, Markdown, HTML, JSON, SARIF, policy, baseline, artifacts, and SARIF upload support.
|
|
96
|
+
- Improves product transparency by making both the documented E2E example and the scanner rule catalog visible before adoption.
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
## Manual Publishing Steps
|
|
@@ -106,7 +106,7 @@ Completed:
|
|
|
106
106
|
- README, docs, and website examples now use:
|
|
107
107
|
|
|
108
108
|
```yaml
|
|
109
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
109
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.9
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
Remaining Marketplace web step:
|
package/docs/rules.md
CHANGED
|
@@ -2,27 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
`mcp-guard` uses practical heuristics for the first public version. It is designed to surface risky MCP configuration quickly, not to prove a system is fully secure.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
5
|
+
The same catalog is available from the CLI:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
mcp-guard rules
|
|
9
|
+
mcp-guard rules --format markdown
|
|
10
|
+
mcp-guard rules --format json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Rule | Severity | Title | What it detects | Recommended response |
|
|
14
|
+
| --- | --- | --- | --- | --- |
|
|
15
|
+
| MCP000 | Low | No MCP config files found | No MCP config files were found in common project or user locations. | Pass `--config` when the MCP config lives outside default discovery paths. |
|
|
16
|
+
| MCP001 | High | Server has no command or URL | An MCP server entry has neither `command` nor `url`. | Remove the server or define an explicit command or URL with reviewed settings. |
|
|
17
|
+
| MCP002 | Medium | Config has no MCP servers object | A config file does not contain an `mcpServers` or `servers` object. | Check that the file is the intended MCP config before relying on the scan. |
|
|
18
|
+
| MCP003 | High | Config cannot be parsed as JSON | A config file could not be parsed as JSON. | Fix the JSON syntax so the scanner and MCP client can read the same config. |
|
|
19
|
+
| MCP010 | High/Critical | MCP server runs through a shell | Shell wrappers such as `sh`, `bash`, `zsh`, `fish`, PowerShell, or `cmd`, especially inline scripts. | Use a direct, pinned executable instead of a shell wrapper. |
|
|
20
|
+
| MCP011 | High | Interpreter eval mode is enabled | Interpreter eval flags such as `node -e`, `python -c`, `ruby -e`, or similar inline code execution. | Replace inline code with a reviewed package or checked-in script. |
|
|
21
|
+
| MCP020 | Medium | Remote package runner is used | Remote package runners such as `npx`, `uvx`, `bunx`, `pipx`, or package-manager `dlx` commands. | Pin the package version and prefer a reviewed lockfile or vendored executable for sensitive tools. |
|
|
22
|
+
| MCP021 | High | Remote MCP package is not version pinned | Remote package execution without an exact package version. | Pin the package to an exact version such as `package@1.2.3` and review updates before changing it. |
|
|
23
|
+
| MCP030 | High | Secret-like environment variable is exposed | Secret-like environment variable names or values passed into an MCP server. | Use least-privilege, short-lived credentials and dedicated service accounts. |
|
|
24
|
+
| MCP040 | Medium/High | MCP server has a broad working directory | Broad working directories such as home, root, Desktop, Documents, or Downloads. | Run the server in a narrow project directory or sandbox with only the files it needs. |
|
|
25
|
+
| MCP041 | Medium/High | MCP server argument grants broad filesystem access | Filesystem arguments that grant broad access to home, root, or sensitive user folders. | Replace broad filesystem paths with a dedicated project folder or read-only sandbox path. |
|
|
26
|
+
| MCP050 | Critical | Command includes a dangerous operation | Dangerous command patterns such as `rm -rf`, `sudo`, `chmod 777`, force push, or curl-pipe-shell. | Remove dangerous startup operations and run setup steps manually after review. |
|
|
27
|
+
| MCP060 | Medium | Remote MCP server URL is configured | Remote HTTP or HTTPS MCP server URLs. | Verify the provider, document data sent to the server, and keep an allowlist of approved endpoints. |
|
|
28
|
+
| MCP061 | High | Secret-like header is configured | Secret-like remote MCP headers such as authorization or API key headers. | Use scoped, short-lived credentials and avoid long-lived secrets in MCP config files. |
|
|
29
|
+
| MCP070 | High | Command is outside policy | An MCP server command that is not listed in `allowedCommands`. | Use an approved command or update policy only after review. |
|
|
30
|
+
| MCP071 | High | Remote package is outside policy | A remote MCP package that is not listed in `allowedPackages`. | Use an approved package or update policy only after package review. |
|
|
31
|
+
| MCP072 | High | Working directory is outside policy | A server working directory outside `allowedDirectories`. | Move the server into an approved workspace or update policy only after review. |
|
|
32
|
+
| MCP073 | High | Filesystem argument is outside policy | A filesystem argument outside `allowedDirectories`. | Limit filesystem arguments to approved directories or update policy only after review. |
|
|
33
|
+
| MCP074 | High | Remote MCP URL is outside policy | A remote MCP endpoint outside `allowedRemoteUrls`. | Use an approved endpoint or update policy only after remote provider review. |
|
|
26
34
|
|
|
27
35
|
## Severity Model
|
|
28
36
|
|
|
@@ -36,7 +36,7 @@ Configure this once on npmjs.com:
|
|
|
36
36
|
After this is saved, run the workflow from GitHub Actions with the release tag, for example:
|
|
37
37
|
|
|
38
38
|
```text
|
|
39
|
-
v0.4.
|
|
39
|
+
v0.4.9
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
## Release Flow After Setup
|
|
@@ -44,7 +44,7 @@ v0.4.8
|
|
|
44
44
|
1. Update `package.json` and `src/cli.js`.
|
|
45
45
|
2. Run `npm test` and `npm run release:check`.
|
|
46
46
|
3. Commit and push to `main`.
|
|
47
|
-
4. Create a GitHub release tag such as `v0.4.
|
|
47
|
+
4. Create a GitHub release tag such as `v0.4.9`.
|
|
48
48
|
5. Run the `Publish npm` workflow with the same tag.
|
|
49
49
|
6. Verify npm:
|
|
50
50
|
|
package/package.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"tool": {
|
|
4
4
|
"name": "mcp-guard",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.9"
|
|
6
6
|
},
|
|
7
|
-
"generatedAt": "2026-05-10T20:
|
|
7
|
+
"generatedAt": "2026-05-10T20:51:28.472Z",
|
|
8
8
|
"status": "needs_review",
|
|
9
9
|
"failOn": "none",
|
|
10
10
|
"outputDir": "site/e2e/audit",
|
|
@@ -44,43 +44,43 @@
|
|
|
44
44
|
"key": "executiveSummary",
|
|
45
45
|
"path": "site/e2e/audit/mcp-guard-executive-summary.md",
|
|
46
46
|
"bytes": 1764,
|
|
47
|
-
"sha256": "
|
|
47
|
+
"sha256": "6418987f5621711309d1a0af3edf4b1c86f1b069f90e88e7bf0b62748d11bdbe"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"key": "remediation",
|
|
51
51
|
"path": "site/e2e/audit/mcp-guard-remediation.md",
|
|
52
52
|
"bytes": 2752,
|
|
53
|
-
"sha256": "
|
|
53
|
+
"sha256": "208468feb8bc3f7071654ad0e3901cd48312c97dd94f48389aac59ea10012103"
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"key": "remediationChecklist",
|
|
57
57
|
"path": "site/e2e/audit/mcp-guard-remediation-checklist.md",
|
|
58
58
|
"bytes": 2056,
|
|
59
|
-
"sha256": "
|
|
59
|
+
"sha256": "e51752f15bd31384b5a170504f38861db503ac95850d720031b8ab858c9d7205"
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"key": "markdownReport",
|
|
63
63
|
"path": "site/e2e/audit/mcp-guard-report.md",
|
|
64
64
|
"bytes": 3297,
|
|
65
|
-
"sha256": "
|
|
65
|
+
"sha256": "c493c4af9ddbcafd8f0538647662068a55206a0c670ccff2ff8ce038b0a6baa3"
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
"key": "htmlReport",
|
|
69
69
|
"path": "site/e2e/audit/mcp-guard-report.html",
|
|
70
70
|
"bytes": 12988,
|
|
71
|
-
"sha256": "
|
|
71
|
+
"sha256": "0c0908853c1f375ce425f5996d2239feb3cae9910139b6c594fe888f9ba22612"
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
"key": "jsonReport",
|
|
75
75
|
"path": "site/e2e/audit/mcp-guard-report.json",
|
|
76
76
|
"bytes": 5938,
|
|
77
|
-
"sha256": "
|
|
77
|
+
"sha256": "16feaffcd491dd09864dcd548570c4a1adaea7f3ef401fce4f7fa4151af75796"
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"key": "sarifReport",
|
|
81
81
|
"path": "site/e2e/audit/mcp-guard.sarif",
|
|
82
82
|
"bytes": 20856,
|
|
83
|
-
"sha256": "
|
|
83
|
+
"sha256": "5a00e73219117d9f8e193519a9ddb174b2640c30ade4e963d0829ac94608fa62"
|
|
84
84
|
}
|
|
85
85
|
]
|
|
86
86
|
}
|
|
@@ -297,7 +297,7 @@
|
|
|
297
297
|
<div class="metric"><strong>1</strong><span>Scanned files</span></div>
|
|
298
298
|
<div class="metric"><strong>3</strong><span>MCP servers</span></div>
|
|
299
299
|
<div class="metric"><strong>9</strong><span>Active findings</span></div>
|
|
300
|
-
<div class="metric"><strong>2026-05-10 20:
|
|
300
|
+
<div class="metric"><strong>2026-05-10 20:51 UTC</strong><span>Generated</span></div>
|
|
301
301
|
</div>
|
|
302
302
|
</div>
|
|
303
303
|
<aside class="scorecard" aria-label="Risk score">
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"metadata": {
|
|
3
|
-
"generatedAt": "2026-05-10T20:
|
|
3
|
+
"generatedAt": "2026-05-10T20:51:28.472Z",
|
|
4
4
|
"cwd": ".",
|
|
5
5
|
"home": "~",
|
|
6
6
|
"policyPath": "",
|
|
7
7
|
"policyEnabled": false,
|
|
8
|
-
"toolVersion": "0.4.
|
|
8
|
+
"toolVersion": "0.4.9"
|
|
9
9
|
},
|
|
10
10
|
"policy": null,
|
|
11
11
|
"scannedFiles": [
|
package/site/e2e/report.html
CHANGED
|
@@ -297,7 +297,7 @@
|
|
|
297
297
|
<div class="metric"><strong>1</strong><span>Scanned files</span></div>
|
|
298
298
|
<div class="metric"><strong>3</strong><span>MCP servers</span></div>
|
|
299
299
|
<div class="metric"><strong>9</strong><span>Active findings</span></div>
|
|
300
|
-
<div class="metric"><strong>2026-05-10 20:
|
|
300
|
+
<div class="metric"><strong>2026-05-10 20:51 UTC</strong><span>Generated</span></div>
|
|
301
301
|
</div>
|
|
302
302
|
</div>
|
|
303
303
|
<aside class="scorecard" aria-label="Risk score">
|
package/site/e2e/report.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"metadata": {
|
|
3
|
-
"generatedAt": "2026-05-10T20:
|
|
3
|
+
"generatedAt": "2026-05-10T20:51:23.431Z",
|
|
4
4
|
"cwd": ".",
|
|
5
5
|
"home": "~",
|
|
6
6
|
"policyPath": "",
|
|
7
7
|
"policyEnabled": false,
|
|
8
|
-
"toolVersion": "0.4.
|
|
8
|
+
"toolVersion": "0.4.9"
|
|
9
9
|
},
|
|
10
10
|
"policy": null,
|
|
11
11
|
"scannedFiles": [
|
package/site/e2e/report.md
CHANGED
package/site/e2e/report.sarif
CHANGED
package/src/cli.js
CHANGED
|
@@ -5,9 +5,10 @@ import { applyBaseline, loadBaselineFile, writeBaselineFile } from "./baseline.j
|
|
|
5
5
|
import { initProject, renderInitSummary } from "./init.js";
|
|
6
6
|
import { scan } from "./scan.js";
|
|
7
7
|
import { generateHtmlReport, generateJsonReport, generateMarkdownReport, generateSarifReport, generateTextReport } from "./report.js";
|
|
8
|
+
import { generateRulesJson, generateRulesMarkdown, generateRulesText } from "./rule-catalog.js";
|
|
8
9
|
import { compareSeverity, severityRank } from "./severity.js";
|
|
9
10
|
|
|
10
|
-
const VERSION = "0.4.
|
|
11
|
+
const VERSION = "0.4.9";
|
|
11
12
|
|
|
12
13
|
export async function runCli(argv, io) {
|
|
13
14
|
const args = argv.slice(2);
|
|
@@ -23,6 +24,17 @@ export async function runCli(argv, io) {
|
|
|
23
24
|
return 0;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
if (command === "rules") {
|
|
28
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
29
|
+
io.stdout.write(helpText());
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const options = parseRulesArgs(args.slice(1));
|
|
34
|
+
io.stdout.write(renderRules(options.format));
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
if (command === "init") {
|
|
27
39
|
if (args.includes("--help") || args.includes("-h")) {
|
|
28
40
|
io.stdout.write(helpText());
|
|
@@ -372,6 +384,27 @@ function parseVerifyAuditArgs(args, defaultCwd) {
|
|
|
372
384
|
return options;
|
|
373
385
|
}
|
|
374
386
|
|
|
387
|
+
function parseRulesArgs(args) {
|
|
388
|
+
const options = {
|
|
389
|
+
format: "text"
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
393
|
+
const arg = args[index];
|
|
394
|
+
if (arg === "--format" || arg === "-f") {
|
|
395
|
+
options.format = readValue(args, index, arg);
|
|
396
|
+
index += 1;
|
|
397
|
+
if (!["text", "markdown", "json"].includes(options.format)) {
|
|
398
|
+
throw new Error("--format must be one of: text, markdown, json");
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
throw new Error(`Unknown rules option: ${arg}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return options;
|
|
406
|
+
}
|
|
407
|
+
|
|
375
408
|
function readValue(args, index, optionName) {
|
|
376
409
|
const value = args[index + 1];
|
|
377
410
|
if (!value || value.startsWith("--")) {
|
|
@@ -400,6 +433,16 @@ function renderReport(result, format) {
|
|
|
400
433
|
return generateTextReport(result);
|
|
401
434
|
}
|
|
402
435
|
|
|
436
|
+
function renderRules(format) {
|
|
437
|
+
if (format === "json") {
|
|
438
|
+
return generateRulesJson();
|
|
439
|
+
}
|
|
440
|
+
if (format === "markdown") {
|
|
441
|
+
return generateRulesMarkdown();
|
|
442
|
+
}
|
|
443
|
+
return generateRulesText();
|
|
444
|
+
}
|
|
445
|
+
|
|
403
446
|
function shouldFail(result, failOn) {
|
|
404
447
|
const threshold = severityRank(failOn);
|
|
405
448
|
return result.findings.some((finding) => compareSeverity(finding.severity, threshold) >= 0);
|
|
@@ -414,6 +457,7 @@ Usage:
|
|
|
414
457
|
mcp-guard scan [options]
|
|
415
458
|
mcp-guard audit [options]
|
|
416
459
|
mcp-guard verify-audit [options]
|
|
460
|
+
mcp-guard rules [options]
|
|
417
461
|
mcp-guard init [options]
|
|
418
462
|
mcp-guard version
|
|
419
463
|
mcp-guard help
|
|
@@ -469,10 +513,14 @@ Verify audit options:
|
|
|
469
513
|
Default: mcp-guard-audit/mcp-guard-audit-manifest.json.
|
|
470
514
|
--cwd <path> Working directory for resolving relative artifact paths.
|
|
471
515
|
|
|
516
|
+
Rules options:
|
|
517
|
+
-f, --format <format> text, markdown, or json. Default: text.
|
|
518
|
+
|
|
472
519
|
Examples:
|
|
473
520
|
mcp-guard init
|
|
474
521
|
mcp-guard init --write-baseline --upload-sarif
|
|
475
522
|
mcp-guard scan
|
|
523
|
+
mcp-guard rules --format markdown
|
|
476
524
|
mcp-guard audit --config .mcp.json --output-dir mcp-guard-audit
|
|
477
525
|
mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
|
|
478
526
|
mcp-guard audit --config .mcp.json --policy .mcp-guard-policy.json --fail-on high
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
export const RULE_CATALOG = [
|
|
2
|
+
{
|
|
3
|
+
id: "MCP000",
|
|
4
|
+
severity: "low",
|
|
5
|
+
title: "No MCP config files found",
|
|
6
|
+
detects: "No MCP config files were found in common project or user locations.",
|
|
7
|
+
recommendation: "Pass --config when the MCP config lives outside default discovery paths."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: "MCP001",
|
|
11
|
+
severity: "high",
|
|
12
|
+
title: "Server has no command or URL",
|
|
13
|
+
detects: "An MCP server entry has neither command nor url.",
|
|
14
|
+
recommendation: "Remove the server or define an explicit command or URL with reviewed settings."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "MCP002",
|
|
18
|
+
severity: "medium",
|
|
19
|
+
title: "Config has no MCP servers object",
|
|
20
|
+
detects: "A config file does not contain an mcpServers or servers object.",
|
|
21
|
+
recommendation: "Check that the file is the intended MCP config before relying on the scan."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "MCP003",
|
|
25
|
+
severity: "high",
|
|
26
|
+
title: "Config cannot be parsed as JSON",
|
|
27
|
+
detects: "A config file could not be parsed as JSON.",
|
|
28
|
+
recommendation: "Fix the JSON syntax so the scanner and MCP client can read the same config."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "MCP010",
|
|
32
|
+
severity: "high/critical",
|
|
33
|
+
title: "MCP server runs through a shell",
|
|
34
|
+
detects: "Shell wrappers such as sh, bash, zsh, fish, PowerShell, or cmd, especially inline scripts.",
|
|
35
|
+
recommendation: "Use a direct, pinned executable instead of a shell wrapper."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "MCP011",
|
|
39
|
+
severity: "high",
|
|
40
|
+
title: "Interpreter eval mode is enabled",
|
|
41
|
+
detects: "Interpreter eval flags such as node -e, python -c, ruby -e, or similar inline code execution.",
|
|
42
|
+
recommendation: "Replace inline code with a reviewed package or checked-in script."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "MCP020",
|
|
46
|
+
severity: "medium",
|
|
47
|
+
title: "Remote package runner is used",
|
|
48
|
+
detects: "Remote package runners such as npx, uvx, bunx, pipx, or package-manager dlx commands.",
|
|
49
|
+
recommendation: "Pin the package version and prefer a reviewed lockfile or vendored executable for sensitive tools."
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "MCP021",
|
|
53
|
+
severity: "high",
|
|
54
|
+
title: "Remote MCP package is not version pinned",
|
|
55
|
+
detects: "Remote package execution without an exact package version.",
|
|
56
|
+
recommendation: "Pin the package to an exact version such as package@1.2.3 and review updates before changing it."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "MCP030",
|
|
60
|
+
severity: "high",
|
|
61
|
+
title: "Secret-like environment variable is exposed",
|
|
62
|
+
detects: "Secret-like environment variable names or values passed into an MCP server.",
|
|
63
|
+
recommendation: "Use least-privilege, short-lived credentials and dedicated service accounts."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "MCP040",
|
|
67
|
+
severity: "medium/high",
|
|
68
|
+
title: "MCP server has a broad working directory",
|
|
69
|
+
detects: "Broad working directories such as home, root, Desktop, Documents, or Downloads.",
|
|
70
|
+
recommendation: "Run the server in a narrow project directory or sandbox with only the files it needs."
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "MCP041",
|
|
74
|
+
severity: "medium/high",
|
|
75
|
+
title: "MCP server argument grants broad filesystem access",
|
|
76
|
+
detects: "Filesystem arguments that grant broad access to home, root, or sensitive user folders.",
|
|
77
|
+
recommendation: "Replace broad filesystem paths with a dedicated project folder or read-only sandbox path."
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "MCP050",
|
|
81
|
+
severity: "critical",
|
|
82
|
+
title: "Command includes a dangerous operation",
|
|
83
|
+
detects: "Dangerous command patterns such as rm -rf, sudo, chmod 777, force push, or curl-pipe-shell.",
|
|
84
|
+
recommendation: "Remove dangerous startup operations and run setup steps manually after review."
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "MCP060",
|
|
88
|
+
severity: "medium",
|
|
89
|
+
title: "Remote MCP server URL is configured",
|
|
90
|
+
detects: "Remote HTTP or HTTPS MCP server URLs.",
|
|
91
|
+
recommendation: "Verify the provider, document data sent to the server, and keep an allowlist of approved endpoints."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "MCP061",
|
|
95
|
+
severity: "high",
|
|
96
|
+
title: "Secret-like header is configured",
|
|
97
|
+
detects: "Secret-like remote MCP headers such as authorization or API key headers.",
|
|
98
|
+
recommendation: "Use scoped, short-lived credentials and avoid long-lived secrets in MCP config files."
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "MCP070",
|
|
102
|
+
severity: "high",
|
|
103
|
+
title: "Command is outside policy",
|
|
104
|
+
detects: "An MCP server command that is not listed in allowedCommands.",
|
|
105
|
+
recommendation: "Use an approved command or update policy only after review."
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: "MCP071",
|
|
109
|
+
severity: "high",
|
|
110
|
+
title: "Remote package is outside policy",
|
|
111
|
+
detects: "A remote MCP package that is not listed in allowedPackages.",
|
|
112
|
+
recommendation: "Use an approved package or update policy only after package review."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "MCP072",
|
|
116
|
+
severity: "high",
|
|
117
|
+
title: "Working directory is outside policy",
|
|
118
|
+
detects: "A server working directory outside allowedDirectories.",
|
|
119
|
+
recommendation: "Move the server into an approved workspace or update policy only after review."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "MCP073",
|
|
123
|
+
severity: "high",
|
|
124
|
+
title: "Filesystem argument is outside policy",
|
|
125
|
+
detects: "A filesystem argument outside allowedDirectories.",
|
|
126
|
+
recommendation: "Limit filesystem arguments to approved directories or update policy only after review."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "MCP074",
|
|
130
|
+
severity: "high",
|
|
131
|
+
title: "Remote MCP URL is outside policy",
|
|
132
|
+
detects: "A remote MCP endpoint outside allowedRemoteUrls.",
|
|
133
|
+
recommendation: "Use an approved endpoint or update policy only after remote provider review."
|
|
134
|
+
}
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
export function generateRulesText(rules = RULE_CATALOG) {
|
|
138
|
+
const lines = [];
|
|
139
|
+
lines.push("mcp-guard rule reference");
|
|
140
|
+
lines.push(`Rules: ${rules.length}`);
|
|
141
|
+
lines.push("");
|
|
142
|
+
for (const rule of rules) {
|
|
143
|
+
lines.push(`- [${rule.severity.toUpperCase()}] ${rule.id} ${rule.title}`);
|
|
144
|
+
lines.push(` Detects: ${rule.detects}`);
|
|
145
|
+
lines.push(` Fix: ${rule.recommendation}`);
|
|
146
|
+
}
|
|
147
|
+
return `${lines.join("\n")}\n`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function generateRulesMarkdown(rules = RULE_CATALOG) {
|
|
151
|
+
const lines = [];
|
|
152
|
+
lines.push("# mcp-guard Rule Reference");
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push("| Rule | Severity | Title | What it detects | Recommended response |");
|
|
155
|
+
lines.push("| --- | --- | --- | --- | --- |");
|
|
156
|
+
for (const rule of rules) {
|
|
157
|
+
lines.push(`| ${cell(rule.id)} | ${cell(rule.severity)} | ${cell(rule.title)} | ${cell(rule.detects)} | ${cell(rule.recommendation)} |`);
|
|
158
|
+
}
|
|
159
|
+
lines.push("");
|
|
160
|
+
return `${lines.join("\n")}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function generateRulesJson(rules = RULE_CATALOG) {
|
|
164
|
+
return `${JSON.stringify({ rules }, null, 2)}\n`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function cell(value) {
|
|
168
|
+
return String(value).replace(/\|/g, "\\|");
|
|
169
|
+
}
|