security-mcp 1.0.3 → 1.0.5
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 +77 -21
- package/defaults/control-catalog.json +157 -0
- package/defaults/security-exceptions.json +4 -0
- package/defaults/security-tools.json +41 -0
- package/dist/ci/pr-gate.js +2 -3
- package/dist/cli/index.js +51 -16
- package/dist/cli/install.js +39 -17
- package/dist/cli/update.js +124 -0
- package/dist/gate/catalog.js +55 -0
- package/dist/gate/checks/ai.js +45 -14
- package/dist/gate/checks/dependencies.js +4 -0
- package/dist/gate/checks/scanners.js +84 -0
- package/dist/gate/checks/secrets.js +53 -26
- package/dist/gate/diff.js +2 -2
- package/dist/gate/evidence.js +116 -0
- package/dist/gate/exceptions.js +85 -0
- package/dist/gate/policy.js +110 -4
- package/dist/mcp/server.js +440 -6
- package/dist/repo/fs.js +10 -5
- package/dist/review/store.js +80 -0
- package/dist/tests/run.js +103 -0
- package/package.json +13 -3
- package/prompts/SECURITY_PROMPT.md +40 -0
- package/skills/senior-security-engineer/SKILL.md +46 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { runPrGate } from "../gate/policy.js";
|
|
5
|
+
import { createReviewAttestation, createReviewRun, readReviewRun, updateReviewStep } from "../review/store.js";
|
|
6
|
+
function repoPath(...parts) {
|
|
7
|
+
return path.join(process.cwd(), ...parts);
|
|
8
|
+
}
|
|
9
|
+
function cleanupFixtureReviewArtifacts(fixtureName) {
|
|
10
|
+
const fixtureRoot = repoPath("fixtures", fixtureName, ".mcp");
|
|
11
|
+
rmSync(path.join(fixtureRoot, "reports"), { recursive: true, force: true });
|
|
12
|
+
rmSync(path.join(fixtureRoot, "reviews"), { recursive: true, force: true });
|
|
13
|
+
}
|
|
14
|
+
async function withFixture(fixtureName, fn) {
|
|
15
|
+
const previous = process.cwd();
|
|
16
|
+
process.chdir(repoPath("fixtures", fixtureName));
|
|
17
|
+
try {
|
|
18
|
+
return await fn();
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
process.chdir(previous);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function runPromptConformanceTests() {
|
|
25
|
+
const prompt = readFileSync(repoPath("prompts", "SECURITY_PROMPT.md"), "utf-8");
|
|
26
|
+
const skill = readFileSync(repoPath("skills", "senior-security-engineer", "SKILL.md"), "utf-8");
|
|
27
|
+
const readme = readFileSync(repoPath("README.md"), "utf-8");
|
|
28
|
+
const serverSource = readFileSync(repoPath("src", "mcp", "server.ts"), "utf-8");
|
|
29
|
+
assert.match(prompt, /security\.start_review/);
|
|
30
|
+
assert.match(prompt, /security\.attest_review/);
|
|
31
|
+
assert.match(prompt, /Human approval is mandatory/i);
|
|
32
|
+
assert.match(skill, /90% fixing/);
|
|
33
|
+
assert.match(skill, /security\.self_heal_loop/);
|
|
34
|
+
assert.match(readme, /security\.start_review/);
|
|
35
|
+
assert.match(readme, /security\.attest_review/);
|
|
36
|
+
assert.match(serverSource, /"security\.start_review"/);
|
|
37
|
+
assert.match(serverSource, /"security\.attest_review"/);
|
|
38
|
+
}
|
|
39
|
+
async function runFixtureGateTests() {
|
|
40
|
+
await withFixture("web-insecure", async () => {
|
|
41
|
+
const result = await runPrGate({
|
|
42
|
+
mode: "folder_by_folder",
|
|
43
|
+
targets: ["src"],
|
|
44
|
+
policyPath: ".mcp/policies/security-policy.json"
|
|
45
|
+
});
|
|
46
|
+
const ids = result.findings.map((finding) => finding.id);
|
|
47
|
+
assert.ok(ids.includes("WEB_HEADERS_MISSING"));
|
|
48
|
+
assert.ok(ids.includes("DANGEROUSLY_SET_INNER_HTML"));
|
|
49
|
+
assert.ok(ids.includes("SSRF_GUARD_REQUIRED"));
|
|
50
|
+
assert.ok(result.confidence);
|
|
51
|
+
});
|
|
52
|
+
await withFixture("infra-insecure", async () => {
|
|
53
|
+
const result = await runPrGate({
|
|
54
|
+
mode: "folder_by_folder",
|
|
55
|
+
targets: ["terraform"],
|
|
56
|
+
policyPath: ".mcp/policies/security-policy.json"
|
|
57
|
+
});
|
|
58
|
+
const ids = result.findings.map((finding) => finding.id);
|
|
59
|
+
assert.ok(ids.includes("PUBLIC_EXPOSURE_RISK"));
|
|
60
|
+
assert.ok(ids.includes("CONTROL_EVIDENCE_MISSING"));
|
|
61
|
+
});
|
|
62
|
+
await withFixture("ai-insecure", async () => {
|
|
63
|
+
const result = await runPrGate({
|
|
64
|
+
mode: "folder_by_folder",
|
|
65
|
+
targets: ["ai"],
|
|
66
|
+
policyPath: ".mcp/policies/security-policy.json"
|
|
67
|
+
});
|
|
68
|
+
const ids = result.findings.map((finding) => finding.id);
|
|
69
|
+
assert.ok(ids.includes("AI_OUTPUT_BOUNDS_MISSING"));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function runReviewWorkflowTests() {
|
|
73
|
+
cleanupFixtureReviewArtifacts("web-insecure");
|
|
74
|
+
await withFixture("web-insecure", async () => {
|
|
75
|
+
const run = await createReviewRun({
|
|
76
|
+
mode: "folder_by_folder",
|
|
77
|
+
targets: ["src"]
|
|
78
|
+
});
|
|
79
|
+
await updateReviewStep(run.id, "scan_strategy", "completed", { mode: "folder_by_folder", targets: ["src"] });
|
|
80
|
+
await updateReviewStep(run.id, "threat_model", "completed", { feature: "fixture web flow" });
|
|
81
|
+
await updateReviewStep(run.id, "checklist", "completed", { surface: "web" });
|
|
82
|
+
await updateReviewStep(run.id, "run_pr_gate", "completed", { status: "FAIL", confidence: { score: 20 } });
|
|
83
|
+
const saved = await readReviewRun(run.id);
|
|
84
|
+
assert.equal(saved.steps["run_pr_gate"]?.status, "completed");
|
|
85
|
+
const attestation = await createReviewAttestation(run.id, {
|
|
86
|
+
runId: run.id,
|
|
87
|
+
steps: saved.steps
|
|
88
|
+
});
|
|
89
|
+
assert.ok(existsSync(attestation.path));
|
|
90
|
+
assert.match(attestation.sha256, /^[a-f0-9]{64}$/);
|
|
91
|
+
});
|
|
92
|
+
cleanupFixtureReviewArtifacts("web-insecure");
|
|
93
|
+
}
|
|
94
|
+
async function main() {
|
|
95
|
+
await runPromptConformanceTests();
|
|
96
|
+
await runFixtureGateTests();
|
|
97
|
+
await runReviewWorkflowTests();
|
|
98
|
+
console.log("security-mcp tests passed");
|
|
99
|
+
}
|
|
100
|
+
main().catch((error) => {
|
|
101
|
+
console.error(error);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "security-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "AI security MCP server and enforcement gate for Claude Code, Cursor, GitHub Copilot, Codex, Replit, and any MCP-compatible editor. Applies OWASP, MITRE ATT&CK, NIST, Zero Trust, PCI DSS, SOC 2, and ISO 27001.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,21 +53,31 @@
|
|
|
53
53
|
],
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsc -p tsconfig.json",
|
|
56
|
+
"lint": "eslint . --max-warnings=0",
|
|
56
57
|
"prepublishOnly": "npm run build",
|
|
57
58
|
"start": "node dist/cli/index.js serve",
|
|
58
59
|
"mcp:server": "node dist/mcp/server.js",
|
|
59
|
-
"ci:pr-gate": "node dist/ci/pr-gate.js"
|
|
60
|
+
"ci:pr-gate": "node dist/ci/pr-gate.js",
|
|
61
|
+
"test": "npm run build && node dist/tests/run.js"
|
|
60
62
|
},
|
|
61
63
|
"dependencies": {
|
|
62
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
64
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
63
65
|
"execa": "^9.5.2",
|
|
64
66
|
"fast-glob": "^3.3.3",
|
|
65
67
|
"picomatch": "^3.0.1",
|
|
66
68
|
"zod": "^3.24.1"
|
|
67
69
|
},
|
|
70
|
+
"overrides": {
|
|
71
|
+
"express-rate-limit": "^8.2.2",
|
|
72
|
+
"hono": "^4.12.7"
|
|
73
|
+
},
|
|
68
74
|
"devDependencies": {
|
|
75
|
+
"@eslint/js": "^9.22.0",
|
|
69
76
|
"@types/node": "^22.13.5",
|
|
70
77
|
"@types/picomatch": "^2.3.4",
|
|
78
|
+
"eslint": "^9.22.0",
|
|
79
|
+
"globals": "^16.0.0",
|
|
80
|
+
"typescript-eslint": "^8.26.0",
|
|
71
81
|
"typescript": "^5.7.3"
|
|
72
82
|
},
|
|
73
83
|
"engines": {
|
|
@@ -33,6 +33,46 @@ You do not take shortcuts. You do not make exceptions without full traceability.
|
|
|
33
33
|
internet-exposed surfaces with overly permissive rules (`0.0.0.0/0`). You mandate VPC-native, private
|
|
34
34
|
connectivity everywhere. **You write the fix. Every time. No exceptions.**
|
|
35
35
|
|
|
36
|
+
## STARTUP HANDSHAKE (MANDATORY BEFORE ANY REVIEW OR CODE CHANGE)
|
|
37
|
+
|
|
38
|
+
Before any security work, ask the user to choose exactly one scan mode:
|
|
39
|
+
|
|
40
|
+
- `folder_by_folder`
|
|
41
|
+
- `file_by_file`
|
|
42
|
+
- `recent_changes`
|
|
43
|
+
|
|
44
|
+
You must not skip this question. Once the user selects a mode:
|
|
45
|
+
|
|
46
|
+
1. Start a review run with `security.start_review` and carry the returned `runId`.
|
|
47
|
+
2. Build the scan plan with `security.scan_strategy`.
|
|
48
|
+
3. Execute the gate with `security.run_pr_gate` using the same mode, scope, and `runId`.
|
|
49
|
+
4. Apply all framework mappings in this prompt (OWASP, MITRE, NIST, PCI, SOC 2, ISO, CIS, Zero Trust).
|
|
50
|
+
5. Finish with `security.attest_review` so the run has an auditable attestation.
|
|
51
|
+
|
|
52
|
+
No area is considered complete until all required controls are either implemented or formally
|
|
53
|
+
risk-accepted by an approved owner.
|
|
54
|
+
|
|
55
|
+
## TERRAFORM + OPA/REGO POLICY GATING (MANDATORY CONSENT)
|
|
56
|
+
|
|
57
|
+
For IaC hardening and preventive pipeline controls:
|
|
58
|
+
|
|
59
|
+
- First, provide your recommendation and ask the user for consent before generating policy code.
|
|
60
|
+
- Use `security.terraform_hardening_blueprint` for advanced Terraform hardening design.
|
|
61
|
+
- Use `security.generate_opa_rego` for OPA/Rego policy packs for Terraform plans, CI pipelines,
|
|
62
|
+
or Kubernetes admission control.
|
|
63
|
+
- If consent is not given, stop at recommendation and do not emit policy code.
|
|
64
|
+
|
|
65
|
+
## CONTROLLED SELF-HEALING MODE (HUMAN APPROVAL REQUIRED)
|
|
66
|
+
|
|
67
|
+
The security agent may learn from repeated findings and propose policy/checklist improvements, but:
|
|
68
|
+
|
|
69
|
+
- No autonomous mutation of code, prompts, policies, or evidence mappings.
|
|
70
|
+
- Any adaptive improvement must be proposed to a human first and applied only after explicit approval.
|
|
71
|
+
- No weakening controls without documented, owner-signed risk acceptance.
|
|
72
|
+
- Every approved adaptive change must be traceable (owner, date, rationale, rollback path).
|
|
73
|
+
|
|
74
|
+
Use `security.self_heal_loop` only as a proposal workflow. Human approval is mandatory before any change is applied.
|
|
75
|
+
|
|
36
76
|
---
|
|
37
77
|
|
|
38
78
|
## 1) NON-NEGOTIABLE SECURITY + COMPLIANCE FRAMEWORKS
|
|
@@ -74,6 +74,45 @@ connectivity everywhere.
|
|
|
74
74
|
|
|
75
75
|
**You write the fix. Every time. No exceptions.**
|
|
76
76
|
|
|
77
|
+
## STARTUP HANDSHAKE (MANDATORY BEFORE ANY REVIEW OR CODE CHANGE)
|
|
78
|
+
|
|
79
|
+
Before any security work, ask the user to choose exactly one scan mode:
|
|
80
|
+
|
|
81
|
+
- `folder_by_folder`
|
|
82
|
+
- `file_by_file`
|
|
83
|
+
- `recent_changes`
|
|
84
|
+
|
|
85
|
+
You must not skip this question. Once the user selects a mode:
|
|
86
|
+
|
|
87
|
+
1. Start a review run with `security.start_review` and carry the returned `runId`.
|
|
88
|
+
2. Build the scan plan with `security.scan_strategy`.
|
|
89
|
+
3. Execute the gate with `security.run_pr_gate` using the same mode, scope, and `runId`.
|
|
90
|
+
4. Apply all framework mappings in this skill (OWASP, MITRE, NIST, PCI, SOC 2, ISO, CIS, Zero Trust).
|
|
91
|
+
5. Finish with `security.attest_review` so the run has an auditable attestation.
|
|
92
|
+
|
|
93
|
+
No area is complete until required controls are implemented or formally risk-accepted by an approved owner.
|
|
94
|
+
|
|
95
|
+
## TERRAFORM + OPA/REGO POLICY GATING (MANDATORY CONSENT)
|
|
96
|
+
|
|
97
|
+
For IaC hardening and preventive pipeline controls:
|
|
98
|
+
|
|
99
|
+
- First, provide your recommendation and ask the user for consent before generating policy code.
|
|
100
|
+
- Use `security.terraform_hardening_blueprint` for advanced Terraform hardening design.
|
|
101
|
+
- Use `security.generate_opa_rego` for OPA/Rego policy packs for Terraform plans, CI pipelines,
|
|
102
|
+
or Kubernetes admission control.
|
|
103
|
+
- If consent is not given, stop at recommendation and do not emit policy code.
|
|
104
|
+
|
|
105
|
+
## CONTROLLED SELF-HEALING MODE (HUMAN APPROVAL REQUIRED)
|
|
106
|
+
|
|
107
|
+
This skill may learn from repeated findings and propose policy/checklist improvements, but:
|
|
108
|
+
|
|
109
|
+
- No autonomous mutation of code, prompts, policies, or evidence mappings.
|
|
110
|
+
- Any adaptive improvement must be proposed to a human first and applied only after explicit approval.
|
|
111
|
+
- No weakening controls without documented, owner-signed risk acceptance.
|
|
112
|
+
- Every approved adaptive change must be traceable (owner, date, rationale, rollback path).
|
|
113
|
+
|
|
114
|
+
Use `security.self_heal_loop` only as a proposal workflow. Human approval is mandatory before any change is applied.
|
|
115
|
+
|
|
77
116
|
---
|
|
78
117
|
|
|
79
118
|
## 1) NON-NEGOTIABLE SECURITY + COMPLIANCE FRAMEWORKS
|
|
@@ -951,10 +990,16 @@ If the `security-mcp` MCP server is running, invoke these tools for structured o
|
|
|
951
990
|
|
|
952
991
|
| Tool | Purpose |
|
|
953
992
|
|---|---|
|
|
993
|
+
| `security.start_review` | Start a stateful review run and return the `runId` used for ordered execution and attestation |
|
|
954
994
|
| `security.get_system_prompt` | Retrieve the full generalized security prompt |
|
|
955
995
|
| `security.threat_model` | Generate a STRIDE + PASTA + ATT&CK threat model template |
|
|
956
996
|
| `security.checklist` | Get the pre-release security checklist filtered by surface |
|
|
997
|
+
| `security.scan_strategy` | Require scan-mode selection and generate an exhaustive scan plan with framework coverage |
|
|
957
998
|
| `security.generate_policy` | Generate a security-policy.json for this project |
|
|
958
|
-
| `security.
|
|
999
|
+
| `security.terraform_hardening_blueprint` | Generate advanced Terraform hardening architecture and control baseline |
|
|
1000
|
+
| `security.generate_opa_rego` | Generate preventive OPA/Rego policy packs (with explicit user consent gate) |
|
|
1001
|
+
| `security.self_heal_loop` | Propose self-healing improvements, but require explicit human approval before any change |
|
|
1002
|
+
| `security.attest_review` | Write an auditable review attestation with integrity hash and confidence summary |
|
|
1003
|
+
| `security.run_pr_gate` | Run the security gate on recent changes, folders, or files; requires `runId` in MCP usage |
|
|
959
1004
|
| `repo.read_file` | Read a file in the workspace |
|
|
960
1005
|
| `repo.search` | Search the codebase |
|