@srhot/mcp-guard 0.1.0

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 (78) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/CONTRIBUTING.md +22 -0
  3. package/LICENSE +21 -0
  4. package/README.md +202 -0
  5. package/SECURITY.md +11 -0
  6. package/dist/cli.d.ts +7 -0
  7. package/dist/cli.js +73 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/index.d.ts +5 -0
  10. package/dist/index.js +5 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/policy/loadPolicy.d.ts +3 -0
  13. package/dist/policy/loadPolicy.js +67 -0
  14. package/dist/policy/loadPolicy.js.map +1 -0
  15. package/dist/policy/policyEngine.d.ts +3 -0
  16. package/dist/policy/policyEngine.js +30 -0
  17. package/dist/policy/policyEngine.js.map +1 -0
  18. package/dist/reporters/consoleReporter.d.ts +2 -0
  19. package/dist/reporters/consoleReporter.js +23 -0
  20. package/dist/reporters/consoleReporter.js.map +1 -0
  21. package/dist/reporters/jsonReporter.d.ts +2 -0
  22. package/dist/reporters/jsonReporter.js +4 -0
  23. package/dist/reporters/jsonReporter.js.map +1 -0
  24. package/dist/reporters/markdownReporter.d.ts +2 -0
  25. package/dist/reporters/markdownReporter.js +33 -0
  26. package/dist/reporters/markdownReporter.js.map +1 -0
  27. package/dist/rules/MCP01_SECRET_EXPOSURE.d.ts +2 -0
  28. package/dist/rules/MCP01_SECRET_EXPOSURE.js +48 -0
  29. package/dist/rules/MCP01_SECRET_EXPOSURE.js.map +1 -0
  30. package/dist/rules/MCP03_TOOL_POISONING.d.ts +2 -0
  31. package/dist/rules/MCP03_TOOL_POISONING.js +37 -0
  32. package/dist/rules/MCP03_TOOL_POISONING.js.map +1 -0
  33. package/dist/rules/MCP05_COMMAND_EXECUTION.d.ts +2 -0
  34. package/dist/rules/MCP05_COMMAND_EXECUTION.js +39 -0
  35. package/dist/rules/MCP05_COMMAND_EXECUTION.js.map +1 -0
  36. package/dist/rules/MCP07_INSUFFICIENT_AUTH.d.ts +2 -0
  37. package/dist/rules/MCP07_INSUFFICIENT_AUTH.js +63 -0
  38. package/dist/rules/MCP07_INSUFFICIENT_AUTH.js.map +1 -0
  39. package/dist/rules/MCP08_AUDIT_GAP.d.ts +2 -0
  40. package/dist/rules/MCP08_AUDIT_GAP.js +30 -0
  41. package/dist/rules/MCP08_AUDIT_GAP.js.map +1 -0
  42. package/dist/rules/MCP09_SHADOW_SERVER.d.ts +2 -0
  43. package/dist/rules/MCP09_SHADOW_SERVER.js +64 -0
  44. package/dist/rules/MCP09_SHADOW_SERVER.js.map +1 -0
  45. package/dist/rules/MCP10_CONTEXT_OVERSHARING.d.ts +2 -0
  46. package/dist/rules/MCP10_CONTEXT_OVERSHARING.js +39 -0
  47. package/dist/rules/MCP10_CONTEXT_OVERSHARING.js.map +1 -0
  48. package/dist/rules/index.d.ts +10 -0
  49. package/dist/rules/index.js +18 -0
  50. package/dist/rules/index.js.map +1 -0
  51. package/dist/scanner/fileLoader.d.ts +3 -0
  52. package/dist/scanner/fileLoader.js +48 -0
  53. package/dist/scanner/fileLoader.js.map +1 -0
  54. package/dist/scanner/scanTarget.d.ts +2 -0
  55. package/dist/scanner/scanTarget.js +21 -0
  56. package/dist/scanner/scanTarget.js.map +1 -0
  57. package/dist/types.d.ts +58 -0
  58. package/dist/types.js +2 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/utils/paths.d.ts +2 -0
  61. package/dist/utils/paths.js +8 -0
  62. package/dist/utils/paths.js.map +1 -0
  63. package/dist/utils/severity.d.ts +4 -0
  64. package/dist/utils/severity.js +20 -0
  65. package/dist/utils/severity.js.map +1 -0
  66. package/docs/architecture.md +11 -0
  67. package/docs/release.md +42 -0
  68. package/docs/roadmap.md +34 -0
  69. package/docs/rules.md +21 -0
  70. package/examples/bad-mcp-server/.env.example +4 -0
  71. package/examples/bad-mcp-server/mcp.json +11 -0
  72. package/examples/bad-mcp-server/server.ts +13 -0
  73. package/examples/bad-mcp-server/tools.json +17 -0
  74. package/examples/safe-mcp-server/mcp.json +13 -0
  75. package/examples/safe-mcp-server/server.ts +9 -0
  76. package/examples/safe-mcp-server/tools.json +17 -0
  77. package/mcpguard.config.yml +29 -0
  78. package/package.json +80 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial TypeScript CLI scanner.
6
+ - Added policy engine with `off`, `warn`, and `error` modes.
7
+ - Added seven initial MCP security rules.
8
+ - Added safe and bad local MCP examples.
9
+ - Added console, JSON, and Markdown reporters.
10
+ - Added Vitest coverage and GitHub Actions CI.
@@ -0,0 +1,22 @@
1
+ # Contributing
2
+
3
+ Thanks for helping improve MCP-Guard.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ npm install
9
+ npm test
10
+ npm run build
11
+ ```
12
+
13
+ ## Rule Contributions
14
+
15
+ Good v0.1 rules should be:
16
+
17
+ - Deterministic and locally testable.
18
+ - Focused on MCP security risks.
19
+ - Backed by safe synthetic examples.
20
+ - Configurable through the policy engine.
21
+
22
+ Avoid rules that call external services, require credentials, or need a live MCP server.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MCP-Guard 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.
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # MCP-Guard
2
+
3
+ Open-source security scanner and policy engine for Model Context Protocol server projects.
4
+
5
+ MCP-Guard helps detect tool poisoning, secret exposure, excessive permissions, risky command execution, missing audit controls, shadow MCP servers, and context over-sharing before an AI agent connects to them.
6
+
7
+ ## Quick Start
8
+
9
+ Run MCP-Guard without installing it:
10
+
11
+ ```bash
12
+ npx @srhot/mcp-guard scan .
13
+ ```
14
+
15
+ Install it globally:
16
+
17
+ ```bash
18
+ npm install -g @srhot/mcp-guard
19
+ mcp-guard scan .
20
+ ```
21
+
22
+ Write a Markdown report:
23
+
24
+ ```bash
25
+ npx @srhot/mcp-guard scan . --format markdown --output mcp-guard-report.md
26
+ ```
27
+
28
+ Write a JSON report:
29
+
30
+ ```bash
31
+ npx @srhot/mcp-guard scan . --format json --output mcp-guard-report.json
32
+ ```
33
+
34
+ Expected behavior:
35
+
36
+ - Safe targets exit `0` and report no error-level findings.
37
+ - Bad targets exit `1` when error-level findings are present.
38
+ - Use `--no-fail` to print findings while exiting `0`, which is useful for demos and non-blocking CI checks.
39
+
40
+ MCP-Guard v0.1 is a static scanner and policy engine. It is not a runtime MCP firewall or proxy yet.
41
+
42
+ ## What Can I Scan?
43
+
44
+ You can scan:
45
+
46
+ - MCP server repositories.
47
+ - Folders containing `mcp.json`.
48
+ - Folders containing `tools.json`.
49
+ - TypeScript or JavaScript MCP server implementations.
50
+ - Config folders used by MCP clients.
51
+
52
+ MCP-Guard v0.1 performs static scanning and policy checks. It does not yet inspect live MCP JSON-RPC traffic.
53
+
54
+ ## Status
55
+
56
+ MCP-Guard v0.1 is a scanner, policy engine, and local test laboratory. It is not a runtime firewall or proxy yet.
57
+
58
+ ## Why It Exists
59
+
60
+ MCP servers can expose tools, files, credentials, commands, and instructions to AI agents. That makes small configuration mistakes unusually powerful. MCP-Guard gives maintainers a fast local check for common MCP security risks before those servers are used by agents or committed to shared repositories.
61
+
62
+ ## What It Detects
63
+
64
+ - Likely hardcoded secrets and private keys.
65
+ - Suspicious tool descriptions that look like hidden instructions.
66
+ - Risky command execution surfaces.
67
+ - Remote HTTP endpoints without clear authentication.
68
+ - Missing audit logging when policy requires it.
69
+ - MCP server names outside the allowlist.
70
+ - Broad filesystem and context exposure.
71
+
72
+ ## Usage
73
+
74
+ ```bash
75
+ mcp-guard scan <target>
76
+ mcp-guard scan <target> --format json
77
+ mcp-guard scan <target> --format markdown
78
+ mcp-guard scan <target> --output report.md
79
+ mcp-guard scan <target> --policy mcpguard.config.yml
80
+ mcp-guard scan <target> --no-fail
81
+ ```
82
+
83
+ Exit behavior:
84
+
85
+ - Exit code `0` when there are no error-level findings.
86
+ - Exit code `1` when there are error-level findings.
87
+ - `--no-fail` always exits `0` while still printing findings.
88
+
89
+ ## Example Output
90
+
91
+ ```text
92
+ MCP-Guard scan FAILED
93
+ Target: /path/to/examples/bad-mcp-server
94
+ Findings: 30 total, 27 error-level
95
+ Severity: 1 critical, 26 high, 3 medium, 0 low, 0 info
96
+
97
+ [HIGH] MCP05_COMMAND_EXECUTION Risky command execution surface
98
+ Policy: error (fails scan)
99
+ File: examples/bad-mcp-server/server.ts:4
100
+ Evidence: exec(
101
+ ```
102
+
103
+ ## Policy Configuration
104
+
105
+ `mcpguard.config.yml` controls rule behavior and project expectations:
106
+
107
+ ```yaml
108
+ rules:
109
+ MCP01_SECRET_EXPOSURE: error
110
+ MCP03_TOOL_POISONING: error
111
+ MCP05_COMMAND_EXECUTION: error
112
+ MCP07_INSUFFICIENT_AUTH: warn
113
+ MCP08_AUDIT_GAP: warn
114
+ MCP09_SHADOW_SERVER: warn
115
+ MCP10_CONTEXT_OVERSHARING: error
116
+
117
+ allowlist:
118
+ servers:
119
+ - safe-filesystem-server
120
+ - safe-github-server
121
+
122
+ require:
123
+ auditLogging: true
124
+ explicitToolConsent: true
125
+ ```
126
+
127
+ Rule modes:
128
+
129
+ - `off` suppresses a rule.
130
+ - `warn` reports findings without failing the scan.
131
+ - `error` fails the scan when the rule finds an issue.
132
+
133
+ Without a policy override, high and critical findings fail the scan.
134
+
135
+ ## Try the Test Laboratory
136
+
137
+ Clone the repository and run the local examples:
138
+
139
+ ```bash
140
+ npm install
141
+ npm run scan:safe
142
+ npm run demo
143
+ ```
144
+
145
+ `examples/safe-mcp-server` should pass. `examples/bad-mcp-server` intentionally contains risky patterns and should produce findings across the initial rule set.
146
+
147
+ ## GitHub Action
148
+
149
+ Add MCP-Guard to another project with a workflow like this:
150
+
151
+ ```yaml
152
+ name: MCP Guard
153
+
154
+ on: [push, pull_request]
155
+
156
+ jobs:
157
+ mcp-guard:
158
+ runs-on: ubuntu-latest
159
+ steps:
160
+ - uses: actions/checkout@v5
161
+ - uses: actions/setup-node@v5
162
+ with:
163
+ node-version: 24
164
+ package-manager-cache: false
165
+ - run: npx @srhot/mcp-guard scan .
166
+ ```
167
+
168
+ ## Development
169
+
170
+ ```bash
171
+ npm install
172
+ npm run lint
173
+ npm test
174
+ npm run build
175
+ npm run format
176
+ npm run pack:check
177
+ ```
178
+
179
+ ## Local Package Smoke Test
180
+
181
+ ```bash
182
+ npm pack
183
+ npm install -g ./srhot-mcp-guard-0.1.0.tgz
184
+ mcp-guard scan examples/safe-mcp-server
185
+ mcp-guard scan examples/bad-mcp-server --no-fail
186
+ npm uninstall -g @srhot/mcp-guard
187
+ ```
188
+
189
+ ## Roadmap
190
+
191
+ - v0.1: static scanner + policy engine + test lab.
192
+ - v0.2: SARIF output + stronger CI integration.
193
+ - v0.3: runtime MCP proxy/firewall.
194
+ - v0.4: dashboard and enterprise policy controls.
195
+
196
+ ## Disclaimer
197
+
198
+ MCP-Guard is an early open-source security tool and does not replace a full security review.
199
+
200
+ ## Contributing
201
+
202
+ Issues and pull requests are welcome. Please keep rules deterministic, locally testable, and free of external API requirements.
package/SECURITY.md ADDED
@@ -0,0 +1,11 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ MCP-Guard is currently in v0.1. Security fixes target the latest release.
6
+
7
+ ## Reporting a Vulnerability
8
+
9
+ Please report suspected vulnerabilities privately through the repository security advisory workflow when available. If that is not available, open a minimal issue that does not include exploitable details and ask the maintainers for a private contact path.
10
+
11
+ Do not include real secrets, credentials, or private MCP server configuration in public issues.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ interface CliIo {
3
+ stdout: Pick<NodeJS.WriteStream, "write">;
4
+ stderr: Pick<NodeJS.WriteStream, "write">;
5
+ }
6
+ export declare function runCli(argv?: string[], io?: CliIo): Promise<number>;
7
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Command, CommanderError } from "commander";
6
+ import { scanTarget } from "./scanner/scanTarget.js";
7
+ import { renderConsoleReport } from "./reporters/consoleReporter.js";
8
+ import { renderJsonReport } from "./reporters/jsonReporter.js";
9
+ import { renderMarkdownReport } from "./reporters/markdownReporter.js";
10
+ export async function runCli(argv = process.argv.slice(2), io = process) {
11
+ let exitCode = 0;
12
+ const program = new Command();
13
+ program
14
+ .name("mcp-guard")
15
+ .description("Security scanner and policy engine for MCP server projects")
16
+ .version("0.1.0")
17
+ .exitOverride();
18
+ program
19
+ .command("scan")
20
+ .argument("<target>", "MCP server project or config to scan")
21
+ .option("--format <format>", "report format: console, json, markdown", "console")
22
+ .option("--output <path>", "write report to a file")
23
+ .option("--policy <path>", "path to mcpguard.config.yml")
24
+ .option("--no-fail", "always exit 0 even when error-level findings are present")
25
+ .action(async (target, options) => {
26
+ const format = validateFormat(String(options.format ?? "console"));
27
+ const result = await scanTarget({
28
+ target,
29
+ policyPath: typeof options.policy === "string" ? options.policy : undefined
30
+ });
31
+ const report = renderReport(format, result);
32
+ if (typeof options.output === "string") {
33
+ await fs.mkdir(path.dirname(path.resolve(options.output)), { recursive: true });
34
+ await fs.writeFile(options.output, report, "utf8");
35
+ }
36
+ else {
37
+ io.stdout.write(report);
38
+ }
39
+ exitCode = result.shouldFail && options.fail !== false ? 1 : 0;
40
+ });
41
+ try {
42
+ await program.parseAsync(argv, { from: "user" });
43
+ }
44
+ catch (error) {
45
+ if (error instanceof CommanderError) {
46
+ return error.exitCode;
47
+ }
48
+ io.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
49
+ return 1;
50
+ }
51
+ return exitCode;
52
+ }
53
+ function validateFormat(format) {
54
+ if (format === "console" || format === "json" || format === "markdown") {
55
+ return format;
56
+ }
57
+ throw new Error(`Unsupported format "${format}". Use console, json, or markdown.`);
58
+ }
59
+ function renderReport(format, result) {
60
+ if (format === "json") {
61
+ return renderJsonReport(result);
62
+ }
63
+ if (format === "markdown") {
64
+ return renderMarkdownReport(result);
65
+ }
66
+ return renderConsoleReport(result);
67
+ }
68
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
69
+ runCli().then((code) => {
70
+ process.exitCode = code;
71
+ });
72
+ }
73
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { scanTarget } from "./scanner/scanTarget.js";
2
+ export { loadPolicy } from "./policy/loadPolicy.js";
3
+ export { applyPolicy, summarizeFindings } from "./policy/policyEngine.js";
4
+ export { rules } from "./rules/index.js";
5
+ export type { Finding, LoadedFile, Policy, Rule, RuleContext, ScanOptions, ScanResult, Severity } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { scanTarget } from "./scanner/scanTarget.js";
2
+ export { loadPolicy } from "./policy/loadPolicy.js";
3
+ export { applyPolicy, summarizeFindings } from "./policy/policyEngine.js";
4
+ export { rules } from "./rules/index.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Policy } from "../types.js";
2
+ export declare const defaultPolicy: Policy;
3
+ export declare function loadPolicy(policyPath?: string): Promise<Policy>;
@@ -0,0 +1,67 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parse } from "yaml";
4
+ import { z } from "zod";
5
+ const ruleModeSchema = z.enum(["off", "warn", "error"]);
6
+ const policySchema = z
7
+ .object({
8
+ rules: z.record(ruleModeSchema).default({}),
9
+ allowlist: z
10
+ .object({
11
+ servers: z.array(z.string()).default([])
12
+ })
13
+ .default({ servers: [] }),
14
+ deny: z
15
+ .object({
16
+ filesystemRoots: z.array(z.string()).default([]),
17
+ commands: z.array(z.string()).default([])
18
+ })
19
+ .default({ filesystemRoots: [], commands: [] }),
20
+ require: z
21
+ .object({
22
+ auditLogging: z.boolean().default(false),
23
+ explicitToolConsent: z.boolean().default(false)
24
+ })
25
+ .default({ auditLogging: false, explicitToolConsent: false })
26
+ })
27
+ .default({});
28
+ export const defaultPolicy = {
29
+ rules: {},
30
+ allowlist: {
31
+ servers: []
32
+ },
33
+ deny: {
34
+ filesystemRoots: ["/", "/home", "C:\\Users"],
35
+ commands: ["rm -rf", "curl | sh", "powershell -enc", "wget", "scp"]
36
+ },
37
+ require: {
38
+ auditLogging: false,
39
+ explicitToolConsent: false
40
+ }
41
+ };
42
+ export async function loadPolicy(policyPath) {
43
+ if (!policyPath) {
44
+ const candidate = path.resolve(process.cwd(), "mcpguard.config.yml");
45
+ try {
46
+ await fs.access(candidate);
47
+ return loadPolicy(candidate);
48
+ }
49
+ catch {
50
+ return defaultPolicy;
51
+ }
52
+ }
53
+ const raw = await fs.readFile(path.resolve(policyPath), "utf8");
54
+ const parsed = policySchema.parse(parse(raw) ?? {});
55
+ return {
56
+ rules: parsed.rules,
57
+ allowlist: parsed.allowlist,
58
+ deny: {
59
+ filesystemRoots: parsed.deny.filesystemRoots.length
60
+ ? parsed.deny.filesystemRoots
61
+ : defaultPolicy.deny.filesystemRoots,
62
+ commands: parsed.deny.commands.length ? parsed.deny.commands : defaultPolicy.deny.commands
63
+ },
64
+ require: parsed.require
65
+ };
66
+ }
67
+ //# sourceMappingURL=loadPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadPolicy.js","sourceRoot":"","sources":["../../src/policy/loadPolicy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Finding, Policy, ScanSummary } from "../types.js";
2
+ export declare function applyPolicy(findings: Finding[], policy: Policy): Finding[];
3
+ export declare function summarizeFindings(findings: Finding[]): ScanSummary;
@@ -0,0 +1,30 @@
1
+ import { createSeverityCounts, shouldFailByDefault } from "../utils/severity.js";
2
+ export function applyPolicy(findings, policy) {
3
+ return findings.flatMap((finding) => {
4
+ const mode = policy.rules[finding.id];
5
+ if (mode === "off") {
6
+ return [];
7
+ }
8
+ const failsScan = mode === "error" ? true : mode === "warn" ? false : shouldFailByDefault(finding.severity);
9
+ const policyLevel = mode === "error" || (mode === undefined && failsScan) ? "error" : "warn";
10
+ return [
11
+ {
12
+ ...finding,
13
+ policyLevel,
14
+ failsScan
15
+ }
16
+ ];
17
+ });
18
+ }
19
+ export function summarizeFindings(findings) {
20
+ const bySeverity = createSeverityCounts();
21
+ for (const finding of findings) {
22
+ bySeverity[finding.severity] += 1;
23
+ }
24
+ return {
25
+ total: findings.length,
26
+ failCount: findings.filter((finding) => finding.failsScan).length,
27
+ bySeverity
28
+ };
29
+ }
30
+ //# sourceMappingURL=policyEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policyEngine.js","sourceRoot":"","sources":["../../src/policy/policyEngine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,MAAM,UAAU,WAAW,CAAC,QAAmB,EAAE,MAAc;IAC7D,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAEtC,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACnB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,SAAS,GACb,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5F,MAAM,WAAW,GAAG,IAAI,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;QAE7F,OAAO;YACL;gBACE,GAAG,OAAO;gBACV,WAAW;gBACX,SAAS;aACV;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAmB;IACnD,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,MAAM;QACtB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM;QACjE,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ScanResult } from "../types.js";
2
+ export declare function renderConsoleReport(result: ScanResult): string;
@@ -0,0 +1,23 @@
1
+ import { displayPath } from "../utils/paths.js";
2
+ export function renderConsoleReport(result) {
3
+ const status = result.shouldFail ? "FAILED" : "PASSED";
4
+ const lines = [
5
+ `MCP-Guard scan ${status}`,
6
+ `Target: ${result.targetPath}`,
7
+ `Findings: ${result.summary.total} total, ${result.summary.failCount} error-level`,
8
+ `Severity: ${result.summary.bySeverity.CRITICAL} critical, ${result.summary.bySeverity.HIGH} high, ${result.summary.bySeverity.MEDIUM} medium, ${result.summary.bySeverity.LOW} low, ${result.summary.bySeverity.INFO} info`,
9
+ ""
10
+ ];
11
+ if (result.findings.length === 0) {
12
+ lines.push("No findings.");
13
+ return `${lines.join("\n")}\n`;
14
+ }
15
+ for (const finding of result.findings) {
16
+ const location = finding.lineNumber
17
+ ? `${displayPath(finding.filePath)}:${finding.lineNumber}`
18
+ : displayPath(finding.filePath);
19
+ lines.push(`[${finding.severity}] ${finding.id} ${finding.title}`, ` Policy: ${finding.policyLevel ?? "warn"}${finding.failsScan ? " (fails scan)" : ""}`, ` File: ${location}`, ` Evidence: ${finding.evidence}`, ` Message: ${finding.message}`, ` Recommendation: ${finding.recommendation}`, "");
20
+ }
21
+ return `${lines.join("\n")}\n`;
22
+ }
23
+ //# sourceMappingURL=consoleReporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consoleReporter.js","sourceRoot":"","sources":["../../src/reporters/consoleReporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,UAAU,mBAAmB,CAAC,MAAkB;IACpD,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IACvD,MAAM,KAAK,GAAG;QACZ,kBAAkB,MAAM,EAAE;QAC1B,WAAW,MAAM,CAAC,UAAU,EAAE;QAC9B,aAAa,MAAM,CAAC,OAAO,CAAC,KAAK,WAAW,MAAM,CAAC,OAAO,CAAC,SAAS,cAAc;QAClF,aAAa,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,cAAc,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,YAAY,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,SAAS,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,OAAO;QAC5N,EAAE;KACH,CAAC;IAEF,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3B,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACjC,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU;YACjC,CAAC,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,UAAU,EAAE;YAC1D,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAElC,KAAK,CAAC,IAAI,CACR,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,EAAE,IAAI,OAAO,CAAC,KAAK,EAAE,EACtD,aAAa,OAAO,CAAC,WAAW,IAAI,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,EACvF,WAAW,QAAQ,EAAE,EACrB,eAAe,OAAO,CAAC,QAAQ,EAAE,EACjC,cAAc,OAAO,CAAC,OAAO,EAAE,EAC/B,qBAAqB,OAAO,CAAC,cAAc,EAAE,EAC7C,EAAE,CACH,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ScanResult } from "../types.js";
2
+ export declare function renderJsonReport(result: ScanResult): string;
@@ -0,0 +1,4 @@
1
+ export function renderJsonReport(result) {
2
+ return `${JSON.stringify(result, null, 2)}\n`;
3
+ }
4
+ //# sourceMappingURL=jsonReporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonReporter.js","sourceRoot":"","sources":["../../src/reporters/jsonReporter.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IACjD,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ScanResult } from "../types.js";
2
+ export declare function renderMarkdownReport(result: ScanResult): string;
@@ -0,0 +1,33 @@
1
+ import { displayPath } from "../utils/paths.js";
2
+ export function renderMarkdownReport(result) {
3
+ const lines = [
4
+ "# MCP-Guard Scan Report",
5
+ "",
6
+ `Target: \`${result.targetPath}\``,
7
+ `Status: **${result.shouldFail ? "failed" : "passed"}**`,
8
+ "",
9
+ "## Summary",
10
+ "",
11
+ `- Total findings: ${result.summary.total}`,
12
+ `- Error-level findings: ${result.summary.failCount}`,
13
+ `- Critical: ${result.summary.bySeverity.CRITICAL}`,
14
+ `- High: ${result.summary.bySeverity.HIGH}`,
15
+ `- Medium: ${result.summary.bySeverity.MEDIUM}`,
16
+ `- Low: ${result.summary.bySeverity.LOW}`,
17
+ `- Info: ${result.summary.bySeverity.INFO}`,
18
+ ""
19
+ ];
20
+ if (result.findings.length === 0) {
21
+ lines.push("No findings.");
22
+ return `${lines.join("\n")}\n`;
23
+ }
24
+ lines.push("## Findings", "");
25
+ for (const finding of result.findings) {
26
+ const location = finding.lineNumber
27
+ ? `${displayPath(finding.filePath)}:${finding.lineNumber}`
28
+ : displayPath(finding.filePath);
29
+ lines.push(`### ${finding.id}: ${finding.title}`, "", `- Severity: ${finding.severity}`, `- Policy: ${finding.policyLevel ?? "warn"}`, `- Location: \`${location}\``, `- Evidence: \`${finding.evidence}\``, `- Message: ${finding.message}`, `- Recommendation: ${finding.recommendation}`, "");
30
+ }
31
+ return `${lines.join("\n")}\n`;
32
+ }
33
+ //# sourceMappingURL=markdownReporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownReporter.js","sourceRoot":"","sources":["../../src/reporters/markdownReporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,UAAU,oBAAoB,CAAC,MAAkB;IACrD,MAAM,KAAK,GAAG;QACZ,yBAAyB;QACzB,EAAE;QACF,aAAa,MAAM,CAAC,UAAU,IAAI;QAClC,aAAa,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,IAAI;QACxD,EAAE;QACF,YAAY;QACZ,EAAE;QACF,qBAAqB,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE;QAC3C,2BAA2B,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE;QACrD,eAAe,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE;QACnD,WAAW,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE;QAC3C,aAAa,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE;QAC/C,UAAU,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE;QACzC,WAAW,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE;QAC3C,EAAE;KACH,CAAC;IAEF,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3B,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAE9B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU;YACjC,CAAC,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,UAAU,EAAE;YAC1D,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClC,KAAK,CAAC,IAAI,CACR,OAAO,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,KAAK,EAAE,EACrC,EAAE,EACF,eAAe,OAAO,CAAC,QAAQ,EAAE,EACjC,aAAa,OAAO,CAAC,WAAW,IAAI,MAAM,EAAE,EAC5C,iBAAiB,QAAQ,IAAI,EAC7B,iBAAiB,OAAO,CAAC,QAAQ,IAAI,EACrC,cAAc,OAAO,CAAC,OAAO,EAAE,EAC/B,qBAAqB,OAAO,CAAC,cAAc,EAAE,EAC7C,EAAE,CACH,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AACjC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "../types.js";
2
+ export declare const MCP01_SECRET_EXPOSURE: Rule;
@@ -0,0 +1,48 @@
1
+ import { lineNumberForIndex } from "../scanner/fileLoader.js";
2
+ const secretPatterns = [
3
+ {
4
+ label: "named secret assignment",
5
+ regex: /\b(?:OPENAI_API_KEY|ANTHROPIC_API_KEY|GITHUB_TOKEN|API_KEY|SECRET|PASSWORD)\b\s*[:=]\s*["']?[A-Za-z0-9_./+=-]{12,}/gi
6
+ },
7
+ {
8
+ label: "bearer token",
9
+ regex: /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi,
10
+ critical: true
11
+ },
12
+ {
13
+ label: "private key block",
14
+ regex: /-----BEGIN\s+(?:RSA\s+|EC\s+|OPENSSH\s+)?PRIVATE KEY-----/gi,
15
+ critical: true
16
+ }
17
+ ];
18
+ export const MCP01_SECRET_EXPOSURE = {
19
+ id: "MCP01_SECRET_EXPOSURE",
20
+ scan(context) {
21
+ const findings = [];
22
+ for (const file of context.files) {
23
+ for (const pattern of secretPatterns) {
24
+ for (const match of file.content.matchAll(pattern.regex)) {
25
+ findings.push({
26
+ id: "MCP01_SECRET_EXPOSURE",
27
+ title: "Possible secret exposure",
28
+ severity: pattern.critical ? "CRITICAL" : "HIGH",
29
+ category: "Secrets",
30
+ message: `Detected a likely ${pattern.label} in a scanned file.`,
31
+ filePath: file.filePath,
32
+ lineNumber: lineNumberForIndex(file.content, match.index ?? 0),
33
+ evidence: redactEvidence(match[0]),
34
+ recommendation: "Move secrets to a secure secret manager or environment variable and rotate any exposed credentials."
35
+ });
36
+ }
37
+ }
38
+ }
39
+ return findings;
40
+ }
41
+ };
42
+ function redactEvidence(value) {
43
+ if (value.length <= 16) {
44
+ return "[redacted]";
45
+ }
46
+ return `${value.slice(0, 12)}...[redacted]`;
47
+ }
48
+ //# sourceMappingURL=MCP01_SECRET_EXPOSURE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MCP01_SECRET_EXPOSURE.js","sourceRoot":"","sources":["../../src/rules/MCP01_SECRET_EXPOSURE.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,cAAc,GAAgE;IAClF;QACE,KAAK,EAAE,yBAAyB;QAChC,KAAK,EACH,sHAAsH;KACzH;IACD;QACE,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,sCAAsC;QAC7C,QAAQ,EAAE,IAAI;KACf;IACD;QACE,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,6DAA6D;QACpE,QAAQ,EAAE,IAAI;KACf;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAS;IACzC,EAAE,EAAE,uBAAuB;IAC3B,IAAI,CAAC,OAAO;QACV,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;gBACrC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACzD,QAAQ,CAAC,IAAI,CAAC;wBACZ,EAAE,EAAE,uBAAuB;wBAC3B,KAAK,EAAE,0BAA0B;wBACjC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;wBAChD,QAAQ,EAAE,SAAS;wBACnB,OAAO,EAAE,qBAAqB,OAAO,CAAC,KAAK,qBAAqB;wBAChE,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,UAAU,EAAE,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;wBAC9D,QAAQ,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;wBAClC,cAAc,EACZ,qGAAqG;qBACxG,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QACvB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "../types.js";
2
+ export declare const MCP03_TOOL_POISONING: Rule;