agent-mcp-guard 0.4.6 → 0.4.8
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 +6 -4
- package/docs/audit.md +11 -2
- package/docs/baseline.md +2 -2
- package/docs/business-playbook.md +1 -1
- package/docs/examples/e2e-example.md +2 -0
- package/docs/github-action.md +8 -6
- package/docs/launch-checklist.md +2 -1
- package/docs/marketplace-action-readme.md +5 -5
- package/docs/marketplace.md +7 -6
- package/docs/paid-audit.md +1 -1
- package/docs/roadmap.md +1 -1
- package/docs/trusted-publishing.md +2 -2
- package/package.json +1 -1
- package/site/e2e/audit/mcp-guard-audit-manifest.json +49 -2
- 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/audit.js +163 -8
- package/src/cli.js +52 -2
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ 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.8"><img alt="Release" src="https://img.shields.io/github/v/release/ChaoYue0307/mcp-guard?color=7c2d12"></a>
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
## Install
|
|
@@ -69,6 +69,7 @@ Generate a review-ready audit pack:
|
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
71
|
mcp-guard audit --config .mcp.json --policy .mcp-guard-policy.json --output-dir mcp-guard-audit
|
|
72
|
+
mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
Use in CI:
|
|
@@ -93,7 +94,7 @@ mcp-guard scan --config .mcp.json --baseline .mcp-guard-baseline.json --fail-on
|
|
|
93
94
|
Use the GitHub Action:
|
|
94
95
|
|
|
95
96
|
```yaml
|
|
96
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
97
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
97
98
|
with:
|
|
98
99
|
config: .mcp.json
|
|
99
100
|
# policy: .mcp-guard-policy.json
|
|
@@ -117,8 +118,9 @@ Use the transparent example to evaluate what the scanner actually does:
|
|
|
117
118
|
- generated audit summary: [site/e2e/audit/mcp-guard-executive-summary.md](site/e2e/audit/mcp-guard-executive-summary.md)
|
|
118
119
|
- generated remediation plan: [site/e2e/audit/mcp-guard-remediation.md](site/e2e/audit/mcp-guard-remediation.md)
|
|
119
120
|
- generated remediation checklist: [site/e2e/audit/mcp-guard-remediation-checklist.md](site/e2e/audit/mcp-guard-remediation-checklist.md)
|
|
121
|
+
- generated audit manifest: [site/e2e/audit/mcp-guard-audit-manifest.json](site/e2e/audit/mcp-guard-audit-manifest.json)
|
|
120
122
|
|
|
121
|
-
The example scans 3 MCP servers and reports 9 active findings with a risk score of 98. It is synthetic, but fully reproducible from committed files.
|
|
123
|
+
The example scans 3 MCP servers and reports 9 active findings with a risk score of 98. It is synthetic, but fully reproducible from committed files. The audit manifest can be verified with `mcp-guard verify-audit` to confirm the generated reports still match their recorded SHA-256 hashes.
|
|
122
124
|
|
|
123
125
|
For the GitHub Action workflow, inspect the public demo repository: [ChaoYue0307/mcp-guard-demo](https://github.com/ChaoYue0307/mcp-guard-demo).
|
|
124
126
|
|
|
@@ -152,7 +154,7 @@ For paid setup or internal review handoff, `mcp-guard audit` writes a complete e
|
|
|
152
154
|
- remediation plan grouped by MCP server;
|
|
153
155
|
- remediation checklist for PR or handoff tracking;
|
|
154
156
|
- Markdown, HTML, JSON, and SARIF reports;
|
|
155
|
-
- machine-readable audit manifest.
|
|
157
|
+
- machine-readable audit manifest with artifact hashes and a CLI verifier.
|
|
156
158
|
|
|
157
159
|
For stricter governance, commit `.mcp-guard-policy.json` and define the commands, remote packages, filesystem roots, and remote MCP endpoints the team has approved. See [Policy files](docs/policy.md).
|
|
158
160
|
|
package/docs/audit.md
CHANGED
|
@@ -26,6 +26,14 @@ Use `--fail-on` in CI when the audit should write artifacts and then fail on act
|
|
|
26
26
|
mcp-guard audit --config .mcp.json --fail-on high
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
Verify the audit pack before handoff or after downloading a CI artifact:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`verify-audit` recalculates every recorded artifact size and SHA-256 hash. It exits `0` when the pack still matches the manifest and exits `2` when a report is missing or changed.
|
|
36
|
+
|
|
29
37
|
## Generated Files
|
|
30
38
|
|
|
31
39
|
| File | Purpose |
|
|
@@ -37,7 +45,7 @@ mcp-guard audit --config .mcp.json --fail-on high
|
|
|
37
45
|
| `mcp-guard-report.html` | Readable HTML report for review artifacts. |
|
|
38
46
|
| `mcp-guard-report.json` | Redacted machine-readable report for automation. |
|
|
39
47
|
| `mcp-guard.sarif` | SARIF 2.1.0 report for GitHub code scanning. |
|
|
40
|
-
| `mcp-guard-audit-manifest.json` | Manifest listing status, summary, policy/baseline context, and
|
|
48
|
+
| `mcp-guard-audit-manifest.json` | Manifest listing status, summary, policy/baseline context, file paths, SHA-256 hashes, and artifact sizes. |
|
|
41
49
|
|
|
42
50
|
## Review Flow
|
|
43
51
|
|
|
@@ -46,7 +54,8 @@ mcp-guard audit --config .mcp.json --fail-on high
|
|
|
46
54
|
3. Work through `mcp-guard-remediation.md` with the engineering team.
|
|
47
55
|
4. Track concrete work in `mcp-guard-remediation-checklist.md`.
|
|
48
56
|
5. Use `mcp-guard-report.html` for readable evidence and `mcp-guard-report.json` or `mcp-guard.sarif` for automation.
|
|
49
|
-
6.
|
|
57
|
+
6. Run `mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json` when you need to prove an audit artifact has not changed.
|
|
58
|
+
7. Commit a reviewed policy and baseline only after the team has decided what risk is intentionally accepted.
|
|
50
59
|
|
|
51
60
|
## Privacy
|
|
52
61
|
|
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.8
|
|
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.8",
|
|
49
49
|
"findings": [
|
|
50
50
|
{
|
|
51
51
|
"fingerprint": "mcpg_a009b2c2",
|
|
@@ -17,7 +17,7 @@ Deliverables:
|
|
|
17
17
|
- install the CLI and GitHub Action;
|
|
18
18
|
- run `mcp-guard init` or generate an equivalent workflow manually;
|
|
19
19
|
- generate Markdown, HTML, JSON, and SARIF reports;
|
|
20
|
-
- generate a customer handoff audit pack with executive summary, remediation plan, remediation checklist, reports, and
|
|
20
|
+
- generate a customer handoff audit pack with executive summary, remediation plan, remediation checklist, reports, hashed manifest, and verifier output;
|
|
21
21
|
- define an initial `.mcp-guard-policy.json` for approved commands, packages, directories, and remote URLs;
|
|
22
22
|
- create an initial baseline for accepted known findings;
|
|
23
23
|
- enable PR comments and optional SARIF upload;
|
|
@@ -24,6 +24,7 @@ node ./bin/mcp-guard.js scan --config site/e2e/claude_desktop_config.json --form
|
|
|
24
24
|
node ./bin/mcp-guard.js scan --config site/e2e/claude_desktop_config.json --format json --output site/e2e/report.json
|
|
25
25
|
node ./bin/mcp-guard.js scan --config site/e2e/claude_desktop_config.json --format sarif --output site/e2e/report.sarif
|
|
26
26
|
node ./bin/mcp-guard.js audit --config site/e2e/claude_desktop_config.json --output-dir site/e2e/audit
|
|
27
|
+
node ./bin/mcp-guard.js verify-audit --manifest site/e2e/audit/mcp-guard-audit-manifest.json
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
## Expected Result
|
|
@@ -63,3 +64,4 @@ Important findings include:
|
|
|
63
64
|
- Secret-like values are redacted in reports.
|
|
64
65
|
- Findings include rule IDs, severity, evidence, and remediation guidance.
|
|
65
66
|
- The same scan can feed a human-readable HTML report, automation JSON, GitHub code scanning SARIF, and a review-ready audit handoff package with a remediation checklist.
|
|
67
|
+
- The audit handoff can be verified later because the manifest records SHA-256 hashes and byte sizes for each generated report.
|
package/docs/github-action.md
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Use the `mcp-guard` action to scan MCP and AI agent tool configuration in pull requests and CI.
|
|
4
4
|
|
|
5
|
-
The action runs the CLI from the pinned GitHub Action tag, generates an audit pack with Markdown, HTML, JSON, SARIF, remediation, checklist, and manifest files, writes a job summary, uploads reports as an artifact, and fails the job when findings meet your selected severity threshold.
|
|
5
|
+
The action runs the CLI from the pinned GitHub Action tag, generates an audit pack with Markdown, HTML, JSON, SARIF, remediation, checklist, and hashed manifest files, writes a job summary, uploads reports as an artifact, and fails the job when findings meet your selected severity threshold.
|
|
6
6
|
|
|
7
7
|
It can also use a committed baseline to accept known findings, enforce a committed policy file, and optionally post a pull request comment with only the active findings.
|
|
8
8
|
|
|
9
|
+
After downloading the artifact, run `mcp-guard verify-audit --manifest mcp-guard-report/mcp-guard-audit-manifest.json` to confirm the generated reports still match the manifest hashes.
|
|
10
|
+
|
|
9
11
|
If `.mcp-guard-policy.json` is committed at the repository root, the CLI auto-loads it. Use the `policy` input when the policy file lives elsewhere.
|
|
10
12
|
|
|
11
13
|
Marketplace/action repository: <https://github.com/ChaoYue0307/mcp-guard-action>
|
|
@@ -37,7 +39,7 @@ jobs:
|
|
|
37
39
|
runs-on: ubuntu-latest
|
|
38
40
|
steps:
|
|
39
41
|
- uses: actions/checkout@v6
|
|
40
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
42
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
41
43
|
with:
|
|
42
44
|
config: .mcp.json
|
|
43
45
|
fail-on: high
|
|
@@ -65,7 +67,7 @@ jobs:
|
|
|
65
67
|
runs-on: ubuntu-latest
|
|
66
68
|
steps:
|
|
67
69
|
- uses: actions/checkout@v6
|
|
68
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
70
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
69
71
|
with:
|
|
70
72
|
config: .mcp.json
|
|
71
73
|
fail-on: high
|
|
@@ -77,7 +79,7 @@ jobs:
|
|
|
77
79
|
Use `fail-on: none` when you want artifacts and summaries without blocking a pull request.
|
|
78
80
|
|
|
79
81
|
```yaml
|
|
80
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
82
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
81
83
|
with:
|
|
82
84
|
fail-on: none
|
|
83
85
|
```
|
|
@@ -93,7 +95,7 @@ mcp-guard scan --config .mcp.json --write-baseline .mcp-guard-baseline.json
|
|
|
93
95
|
Commit `.mcp-guard-baseline.json`, then reference it from the action:
|
|
94
96
|
|
|
95
97
|
```yaml
|
|
96
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
98
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
97
99
|
with:
|
|
98
100
|
config: .mcp.json
|
|
99
101
|
baseline: .mcp-guard-baseline.json
|
|
@@ -107,7 +109,7 @@ Reports will show active findings separately from findings accepted by the basel
|
|
|
107
109
|
Use a policy when you want CI to enforce approved commands, packages, directories, and remote URLs.
|
|
108
110
|
|
|
109
111
|
```yaml
|
|
110
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
112
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
111
113
|
with:
|
|
112
114
|
config: .mcp.json
|
|
113
115
|
policy: .mcp-guard-policy.json
|
package/docs/launch-checklist.md
CHANGED
|
@@ -32,12 +32,13 @@ mcp-guard scan --config .mcp.json --fail-on high
|
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
mcp-guard audit --config .mcp.json --policy .mcp-guard-policy.json --output-dir mcp-guard-audit
|
|
35
|
+
mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
## GitHub Action Setup
|
|
38
39
|
|
|
39
40
|
```yaml
|
|
40
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
41
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
41
42
|
with:
|
|
42
43
|
config: .mcp.json
|
|
43
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.8
|
|
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.8
|
|
46
46
|
with:
|
|
47
47
|
config: .mcp.json
|
|
48
48
|
fail-on: high
|
|
@@ -88,7 +88,7 @@ The action generates an audit pack:
|
|
|
88
88
|
- SARIF 2.1.0 for GitHub code scanning.
|
|
89
89
|
- Remediation Markdown for server-by-server handoff.
|
|
90
90
|
- Remediation checklist for PR and setup tracking.
|
|
91
|
-
- Audit manifest JSON for
|
|
91
|
+
- Audit manifest JSON with SHA-256 hashes, byte sizes, and `mcp-guard verify-audit` support for downloaded artifacts.
|
|
92
92
|
|
|
93
93
|
Secret-like values are redacted before reports are written.
|
|
94
94
|
|
|
@@ -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.8
|
|
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.8
|
|
119
119
|
with:
|
|
120
120
|
config: .mcp.json
|
|
121
121
|
policy: .mcp-guard-policy.json
|
package/docs/marketplace.md
CHANGED
|
@@ -82,17 +82,18 @@ Code quality
|
|
|
82
82
|
Current release title:
|
|
83
83
|
|
|
84
84
|
```text
|
|
85
|
-
v0.4.
|
|
85
|
+
v0.4.8
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
Release notes:
|
|
89
89
|
|
|
90
90
|
```text
|
|
91
|
-
|
|
91
|
+
Audit verification release.
|
|
92
92
|
|
|
93
|
-
- Adds
|
|
94
|
-
-
|
|
95
|
-
- Keeps executive summary, remediation plan, Markdown, HTML, JSON, SARIF,
|
|
93
|
+
- Adds `mcp-guard verify-audit` so downloaded audit packs can be checked against the manifest without custom scripts.
|
|
94
|
+
- Verifies SHA-256 checksums and byte sizes for generated audit artifacts in `mcp-guard-audit-manifest.json`.
|
|
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 paid setup and internal review handoff by making generated evidence easier to verify after delivery or CI download.
|
|
96
97
|
```
|
|
97
98
|
|
|
98
99
|
## Manual Publishing Steps
|
|
@@ -105,7 +106,7 @@ Completed:
|
|
|
105
106
|
- README, docs, and website examples now use:
|
|
106
107
|
|
|
107
108
|
```yaml
|
|
108
|
-
- uses: ChaoYue0307/mcp-guard-action@v0.4.
|
|
109
|
+
- uses: ChaoYue0307/mcp-guard-action@v0.4.8
|
|
109
110
|
```
|
|
110
111
|
|
|
111
112
|
Remaining Marketplace web step:
|
package/docs/paid-audit.md
CHANGED
|
@@ -13,7 +13,7 @@ It is not currently advertised as an active consulting service. Keep public webs
|
|
|
13
13
|
## Deliverables
|
|
14
14
|
|
|
15
15
|
- MCP and agent tool inventory.
|
|
16
|
-
- `mcp-guard audit` evidence pack with executive summary, remediation plan, remediation checklist, reports, and
|
|
16
|
+
- `mcp-guard audit` evidence pack with executive summary, remediation plan, remediation checklist, reports, hashed manifest, and `verify-audit` output.
|
|
17
17
|
- Risk report covering shell access, package execution, filesystem scope, secrets, remote servers, and dangerous commands.
|
|
18
18
|
- Practical remediation checklist.
|
|
19
19
|
- Optional PR with safer config and policy changes.
|
package/docs/roadmap.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- Optional GitHub pull request comments from the Marketplace Action.
|
|
14
14
|
- `mcp-guard init` for bootstrapping a GitHub Action workflow and optional baseline.
|
|
15
15
|
- Policy file enforcement for approved commands, packages, directories, and remote URLs.
|
|
16
|
-
- `mcp-guard audit` for review-ready
|
|
16
|
+
- `mcp-guard audit` and `mcp-guard verify-audit` for review-ready summaries, remediation plans, checklists, reports, and verifiable manifests.
|
|
17
17
|
- npm Trusted Publishing workflow prepared for tokenless release publishing.
|
|
18
18
|
|
|
19
19
|
## Next
|
|
@@ -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.8
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
## Release Flow After Setup
|
|
@@ -44,7 +44,7 @@ v0.4.6
|
|
|
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.8`.
|
|
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.8"
|
|
6
6
|
},
|
|
7
|
-
"generatedAt": "2026-05-
|
|
7
|
+
"generatedAt": "2026-05-10T20:20:35.470Z",
|
|
8
8
|
"status": "needs_review",
|
|
9
9
|
"failOn": "none",
|
|
10
10
|
"outputDir": "site/e2e/audit",
|
|
@@ -36,5 +36,52 @@
|
|
|
36
36
|
"jsonReport": "site/e2e/audit/mcp-guard-report.json",
|
|
37
37
|
"sarifReport": "site/e2e/audit/mcp-guard.sarif",
|
|
38
38
|
"manifest": "site/e2e/audit/mcp-guard-audit-manifest.json"
|
|
39
|
+
},
|
|
40
|
+
"integrity": {
|
|
41
|
+
"algorithm": "sha256",
|
|
42
|
+
"artifacts": [
|
|
43
|
+
{
|
|
44
|
+
"key": "executiveSummary",
|
|
45
|
+
"path": "site/e2e/audit/mcp-guard-executive-summary.md",
|
|
46
|
+
"bytes": 1764,
|
|
47
|
+
"sha256": "63e42ed47cc429f8ccdfe1bb47c35c62d47f483e4ed6582c87d2542e7cbaf7e6"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"key": "remediation",
|
|
51
|
+
"path": "site/e2e/audit/mcp-guard-remediation.md",
|
|
52
|
+
"bytes": 2752,
|
|
53
|
+
"sha256": "2a71e9fb0f426ee99ee045112264bc24d220610098c1e991e0a3dc409310fed8"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"key": "remediationChecklist",
|
|
57
|
+
"path": "site/e2e/audit/mcp-guard-remediation-checklist.md",
|
|
58
|
+
"bytes": 2056,
|
|
59
|
+
"sha256": "26902af05eb8d4c880b32ec96130177c0ff00ade96b004e15564caa82cd01d48"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"key": "markdownReport",
|
|
63
|
+
"path": "site/e2e/audit/mcp-guard-report.md",
|
|
64
|
+
"bytes": 3297,
|
|
65
|
+
"sha256": "6882a79e02c821697e8e4a02168f9f87372822ac2b1e135ea399621c77f742c0"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"key": "htmlReport",
|
|
69
|
+
"path": "site/e2e/audit/mcp-guard-report.html",
|
|
70
|
+
"bytes": 12988,
|
|
71
|
+
"sha256": "eefc4b523c90dcb46c2ebad6dff6b7885be913d1a8bfb36ff930b35dd0da7ca9"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"key": "jsonReport",
|
|
75
|
+
"path": "site/e2e/audit/mcp-guard-report.json",
|
|
76
|
+
"bytes": 5938,
|
|
77
|
+
"sha256": "e7c65a367d5115e25042a71e9408427e25c357c762a00747d2046167980b54f5"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"key": "sarifReport",
|
|
81
|
+
"path": "site/e2e/audit/mcp-guard.sarif",
|
|
82
|
+
"bytes": 20856,
|
|
83
|
+
"sha256": "16bfca40d3158c8864bc2d0dd120ffb48e2277f60e576477c65b22fd3eb2bbd0"
|
|
84
|
+
}
|
|
85
|
+
]
|
|
39
86
|
}
|
|
40
87
|
}
|
|
@@ -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
|
|
300
|
+
<div class="metric"><strong>2026-05-10 20:20 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-
|
|
3
|
+
"generatedAt": "2026-05-10T20:20:35.470Z",
|
|
4
4
|
"cwd": ".",
|
|
5
5
|
"home": "~",
|
|
6
6
|
"policyPath": "",
|
|
7
7
|
"policyEnabled": false,
|
|
8
|
-
"toolVersion": "0.4.
|
|
8
|
+
"toolVersion": "0.4.8"
|
|
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
|
|
300
|
+
<div class="metric"><strong>2026-05-10 20:20 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-
|
|
3
|
+
"generatedAt": "2026-05-10T20:20:32.748Z",
|
|
4
4
|
"cwd": ".",
|
|
5
5
|
"home": "~",
|
|
6
6
|
"policyPath": "",
|
|
7
7
|
"policyEnabled": false,
|
|
8
|
-
"toolVersion": "0.4.
|
|
8
|
+
"toolVersion": "0.4.8"
|
|
9
9
|
},
|
|
10
10
|
"policy": null,
|
|
11
11
|
"scannedFiles": [
|
package/site/e2e/report.md
CHANGED
package/site/e2e/report.sarif
CHANGED
package/src/audit.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
+
import crypto from "node:crypto";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { applyBaseline, loadBaselineFile } from "./baseline.js";
|
|
4
5
|
import { displayPath } from "./fingerprint.js";
|
|
@@ -44,12 +45,6 @@ export async function writeAuditPack({
|
|
|
44
45
|
|
|
45
46
|
await fs.mkdir(resolvedOutputDir, { recursive: true });
|
|
46
47
|
|
|
47
|
-
const manifest = buildAuditManifest(result, files, {
|
|
48
|
-
cwd,
|
|
49
|
-
outputDir: resolvedOutputDir,
|
|
50
|
-
failOn
|
|
51
|
-
});
|
|
52
|
-
|
|
53
48
|
await Promise.all([
|
|
54
49
|
fs.writeFile(files.executiveSummary, generateExecutiveSummary(result, { failOn }), "utf8"),
|
|
55
50
|
fs.writeFile(files.remediation, generateRemediationPlan(result), "utf8"),
|
|
@@ -59,6 +54,14 @@ export async function writeAuditPack({
|
|
|
59
54
|
fs.writeFile(files.jsonReport, `${generateJsonReport(result)}\n`, "utf8"),
|
|
60
55
|
fs.writeFile(files.sarifReport, `${generateSarifReport(result)}\n`, "utf8")
|
|
61
56
|
]);
|
|
57
|
+
|
|
58
|
+
const artifacts = await auditArtifacts(files, cwd);
|
|
59
|
+
const manifest = buildAuditManifest(result, files, {
|
|
60
|
+
cwd,
|
|
61
|
+
outputDir: resolvedOutputDir,
|
|
62
|
+
failOn,
|
|
63
|
+
artifacts
|
|
64
|
+
});
|
|
62
65
|
await fs.writeFile(files.manifest, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
63
66
|
|
|
64
67
|
return {
|
|
@@ -69,6 +72,76 @@ export async function writeAuditPack({
|
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
|
|
75
|
+
export async function verifyAuditPack({ cwd, manifestPath = "" }) {
|
|
76
|
+
const resolvedManifestPath = path.resolve(cwd, manifestPath || path.join("mcp-guard-audit", AUDIT_FILENAMES.manifest));
|
|
77
|
+
const manifestDir = path.dirname(resolvedManifestPath);
|
|
78
|
+
const manifest = JSON.parse(await fs.readFile(resolvedManifestPath, "utf8"));
|
|
79
|
+
const algorithm = manifest.integrity?.algorithm;
|
|
80
|
+
const artifacts = Array.isArray(manifest.integrity?.artifacts) ? manifest.integrity.artifacts : [];
|
|
81
|
+
const results = [];
|
|
82
|
+
|
|
83
|
+
if (algorithm !== "sha256") {
|
|
84
|
+
results.push({
|
|
85
|
+
key: "manifest",
|
|
86
|
+
path: displayPath(resolvedManifestPath, cwd),
|
|
87
|
+
status: "failed",
|
|
88
|
+
reason: `Unsupported or missing integrity algorithm: ${algorithm || "none"}`
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (artifacts.length === 0) {
|
|
93
|
+
results.push({
|
|
94
|
+
key: "manifest",
|
|
95
|
+
path: displayPath(resolvedManifestPath, cwd),
|
|
96
|
+
status: "failed",
|
|
97
|
+
reason: "No integrity artifacts found in manifest."
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const artifact of artifacts) {
|
|
102
|
+
results.push(await verifyAuditArtifact(artifact, { cwd, manifestDir }));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const failedCount = results.filter((result) => result.status !== "passed").length;
|
|
106
|
+
const checkedCount = artifacts.length;
|
|
107
|
+
return {
|
|
108
|
+
manifestPath: resolvedManifestPath,
|
|
109
|
+
status: failedCount === 0 ? "passed" : "failed",
|
|
110
|
+
checkedCount,
|
|
111
|
+
passedCount: results.filter((result) => result.status === "passed").length,
|
|
112
|
+
failedCount,
|
|
113
|
+
results
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function generateAuditVerificationSummary(verification, cwd) {
|
|
118
|
+
const lines = [];
|
|
119
|
+
lines.push("mcp-guard audit verification");
|
|
120
|
+
lines.push(`Manifest: ${displayPath(verification.manifestPath, cwd)}`);
|
|
121
|
+
lines.push(`Status: ${verification.status}`);
|
|
122
|
+
lines.push(`Artifacts checked: ${verification.checkedCount}`);
|
|
123
|
+
lines.push(`Passed: ${verification.passedCount}`);
|
|
124
|
+
lines.push(`Failed: ${verification.failedCount}`);
|
|
125
|
+
lines.push("");
|
|
126
|
+
|
|
127
|
+
for (const result of verification.results) {
|
|
128
|
+
lines.push(`- [${result.status === "passed" ? "ok" : "failed"}] ${result.key}: ${result.path}`);
|
|
129
|
+
if (result.status !== "passed") {
|
|
130
|
+
lines.push(` Reason: ${result.reason}`);
|
|
131
|
+
if (result.expectedSha256 || result.actualSha256) {
|
|
132
|
+
lines.push(` Expected sha256: ${result.expectedSha256 || "-"}`);
|
|
133
|
+
lines.push(` Actual sha256: ${result.actualSha256 || "-"}`);
|
|
134
|
+
}
|
|
135
|
+
if (Number.isInteger(result.expectedBytes) || Number.isInteger(result.actualBytes)) {
|
|
136
|
+
lines.push(` Expected bytes: ${result.expectedBytes ?? "-"}`);
|
|
137
|
+
lines.push(` Actual bytes: ${result.actualBytes ?? "-"}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return `${lines.join("\n")}\n`;
|
|
143
|
+
}
|
|
144
|
+
|
|
72
145
|
export function auditFilePaths(outputDir) {
|
|
73
146
|
return Object.fromEntries(
|
|
74
147
|
Object.entries(AUDIT_FILENAMES).map(([key, filename]) => [key, path.join(outputDir, filename)])
|
|
@@ -246,7 +319,7 @@ function generateRemediationChecklist(result) {
|
|
|
246
319
|
return `${lines.join("\n")}\n`;
|
|
247
320
|
}
|
|
248
321
|
|
|
249
|
-
function buildAuditManifest(result, files, { cwd, outputDir, failOn }) {
|
|
322
|
+
function buildAuditManifest(result, files, { cwd, outputDir, failOn, artifacts }) {
|
|
250
323
|
return {
|
|
251
324
|
version: 1,
|
|
252
325
|
tool: {
|
|
@@ -268,10 +341,92 @@ function buildAuditManifest(result, files, { cwd, outputDir, failOn }) {
|
|
|
268
341
|
baseline: result.baseline || { enabled: false },
|
|
269
342
|
files: Object.fromEntries(
|
|
270
343
|
Object.entries(files).map(([key, filePath]) => [key, displayPath(filePath, cwd)])
|
|
271
|
-
)
|
|
344
|
+
),
|
|
345
|
+
integrity: {
|
|
346
|
+
algorithm: "sha256",
|
|
347
|
+
artifacts
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async function auditArtifacts(files, cwd) {
|
|
353
|
+
const artifacts = [];
|
|
354
|
+
for (const [key, filePath] of Object.entries(files)) {
|
|
355
|
+
if (key === "manifest") continue;
|
|
356
|
+
const content = await fs.readFile(filePath);
|
|
357
|
+
artifacts.push({
|
|
358
|
+
key,
|
|
359
|
+
path: displayPath(filePath, cwd),
|
|
360
|
+
bytes: content.byteLength,
|
|
361
|
+
sha256: crypto.createHash("sha256").update(content).digest("hex")
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
return artifacts;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function verifyAuditArtifact(artifact, { cwd, manifestDir }) {
|
|
368
|
+
if (!artifact?.key || !artifact?.path) {
|
|
369
|
+
return {
|
|
370
|
+
key: artifact?.key || "<unknown>",
|
|
371
|
+
path: artifact?.path || "<missing>",
|
|
372
|
+
status: "failed",
|
|
373
|
+
reason: "Artifact entry is missing key or path."
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const resolvedPath = await resolveAuditArtifactPath(artifact.path, { cwd, manifestDir });
|
|
378
|
+
let content;
|
|
379
|
+
try {
|
|
380
|
+
content = await fs.readFile(resolvedPath);
|
|
381
|
+
} catch {
|
|
382
|
+
return {
|
|
383
|
+
key: artifact.key,
|
|
384
|
+
path: artifact.path,
|
|
385
|
+
resolvedPath: displayPath(resolvedPath, cwd),
|
|
386
|
+
status: "failed",
|
|
387
|
+
reason: "Artifact file is missing.",
|
|
388
|
+
expectedBytes: artifact.bytes,
|
|
389
|
+
expectedSha256: artifact.sha256
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const actualBytes = content.byteLength;
|
|
394
|
+
const actualSha256 = crypto.createHash("sha256").update(content).digest("hex");
|
|
395
|
+
const bytesMatch = actualBytes === artifact.bytes;
|
|
396
|
+
const hashMatch = actualSha256 === artifact.sha256;
|
|
397
|
+
return {
|
|
398
|
+
key: artifact.key,
|
|
399
|
+
path: artifact.path,
|
|
400
|
+
resolvedPath: displayPath(resolvedPath, cwd),
|
|
401
|
+
status: bytesMatch && hashMatch ? "passed" : "failed",
|
|
402
|
+
reason: bytesMatch && hashMatch ? "" : "Artifact content does not match manifest integrity metadata.",
|
|
403
|
+
expectedBytes: artifact.bytes,
|
|
404
|
+
actualBytes,
|
|
405
|
+
expectedSha256: artifact.sha256,
|
|
406
|
+
actualSha256
|
|
272
407
|
};
|
|
273
408
|
}
|
|
274
409
|
|
|
410
|
+
async function resolveAuditArtifactPath(artifactPath, { cwd, manifestDir }) {
|
|
411
|
+
if (path.isAbsolute(artifactPath)) return artifactPath;
|
|
412
|
+
|
|
413
|
+
const candidates = [
|
|
414
|
+
path.join(manifestDir, path.basename(artifactPath)),
|
|
415
|
+
path.resolve(cwd, artifactPath)
|
|
416
|
+
];
|
|
417
|
+
|
|
418
|
+
for (const candidate of candidates) {
|
|
419
|
+
try {
|
|
420
|
+
await fs.access(candidate);
|
|
421
|
+
return candidate;
|
|
422
|
+
} catch {
|
|
423
|
+
// Try the next candidate.
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return candidates[0];
|
|
428
|
+
}
|
|
429
|
+
|
|
275
430
|
function decisionGuidance(result) {
|
|
276
431
|
if (result.findings.length === 0) {
|
|
277
432
|
return ["No active findings were detected. Continue reviewing new MCP servers before adding them."];
|
package/src/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { generateAuditSummary, writeAuditPack } from "./audit.js";
|
|
3
|
+
import { generateAuditSummary, generateAuditVerificationSummary, verifyAuditPack, writeAuditPack } from "./audit.js";
|
|
4
4
|
import { applyBaseline, loadBaselineFile, writeBaselineFile } from "./baseline.js";
|
|
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
8
|
import { compareSeverity, severityRank } from "./severity.js";
|
|
9
9
|
|
|
10
|
-
const VERSION = "0.4.
|
|
10
|
+
const VERSION = "0.4.8";
|
|
11
11
|
|
|
12
12
|
export async function runCli(argv, io) {
|
|
13
13
|
const args = argv.slice(2);
|
|
@@ -84,6 +84,25 @@ export async function runCli(argv, io) {
|
|
|
84
84
|
return 0;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
if (command === "verify-audit") {
|
|
88
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
89
|
+
io.stdout.write(helpText());
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const options = parseVerifyAuditArgs(args.slice(1), io.cwd);
|
|
94
|
+
const verification = await verifyAuditPack({
|
|
95
|
+
cwd: options.cwd,
|
|
96
|
+
manifestPath: options.manifestPath
|
|
97
|
+
});
|
|
98
|
+
io.stdout.write(generateAuditVerificationSummary(verification, options.cwd));
|
|
99
|
+
if (verification.status !== "passed") {
|
|
100
|
+
process.exitCode = 2;
|
|
101
|
+
return 2;
|
|
102
|
+
}
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
87
106
|
if (command !== "scan") {
|
|
88
107
|
io.stderr.write(`Unknown command: ${command}\n\n`);
|
|
89
108
|
io.stderr.write(helpText());
|
|
@@ -329,6 +348,30 @@ function parseAuditArgs(args, defaultCwd) {
|
|
|
329
348
|
return options;
|
|
330
349
|
}
|
|
331
350
|
|
|
351
|
+
function parseVerifyAuditArgs(args, defaultCwd) {
|
|
352
|
+
const options = {
|
|
353
|
+
cwd: defaultCwd,
|
|
354
|
+
manifestPath: ""
|
|
355
|
+
};
|
|
356
|
+
let manifestValue = "";
|
|
357
|
+
|
|
358
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
359
|
+
const arg = args[index];
|
|
360
|
+
if (arg === "--manifest" || arg === "-m") {
|
|
361
|
+
manifestValue = readValue(args, index, arg);
|
|
362
|
+
index += 1;
|
|
363
|
+
} else if (arg === "--cwd") {
|
|
364
|
+
options.cwd = path.resolve(readValue(args, index, arg));
|
|
365
|
+
index += 1;
|
|
366
|
+
} else {
|
|
367
|
+
throw new Error(`Unknown verify-audit option: ${arg}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
options.manifestPath = manifestValue ? resolveInputPath(manifestValue, options.cwd) : "";
|
|
372
|
+
return options;
|
|
373
|
+
}
|
|
374
|
+
|
|
332
375
|
function readValue(args, index, optionName) {
|
|
333
376
|
const value = args[index + 1];
|
|
334
377
|
if (!value || value.startsWith("--")) {
|
|
@@ -370,6 +413,7 @@ Open-source scanner for risky MCP server and AI agent tool configuration.
|
|
|
370
413
|
Usage:
|
|
371
414
|
mcp-guard scan [options]
|
|
372
415
|
mcp-guard audit [options]
|
|
416
|
+
mcp-guard verify-audit [options]
|
|
373
417
|
mcp-guard init [options]
|
|
374
418
|
mcp-guard version
|
|
375
419
|
mcp-guard help
|
|
@@ -420,11 +464,17 @@ Audit options:
|
|
|
420
464
|
--no-policy Do not auto-load .mcp-guard-policy.json.
|
|
421
465
|
--no-defaults Only scan paths passed with --config.
|
|
422
466
|
|
|
467
|
+
Verify audit options:
|
|
468
|
+
-m, --manifest <path> Audit manifest to verify.
|
|
469
|
+
Default: mcp-guard-audit/mcp-guard-audit-manifest.json.
|
|
470
|
+
--cwd <path> Working directory for resolving relative artifact paths.
|
|
471
|
+
|
|
423
472
|
Examples:
|
|
424
473
|
mcp-guard init
|
|
425
474
|
mcp-guard init --write-baseline --upload-sarif
|
|
426
475
|
mcp-guard scan
|
|
427
476
|
mcp-guard audit --config .mcp.json --output-dir mcp-guard-audit
|
|
477
|
+
mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
|
|
428
478
|
mcp-guard audit --config .mcp.json --policy .mcp-guard-policy.json --fail-on high
|
|
429
479
|
mcp-guard scan --format markdown --output mcp-guard-report.md
|
|
430
480
|
mcp-guard scan --format html --output mcp-guard-report.html
|