security-mcp 1.1.3 → 1.1.4
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 +134 -7
- package/defaults/control-catalog.json +200 -0
- package/dist/cli/index.js +82 -5
- package/dist/cli/install.js +36 -6
- package/dist/cli/onboarding.js +6 -0
- package/dist/gate/checks/auth-deep.js +231 -0
- package/dist/gate/checks/injection-deep.js +205 -0
- package/dist/gate/policy.js +5 -1
- package/dist/mcp/server.js +271 -41
- package/package.json +1 -1
- package/prompts/SECURITY_PROMPT.md +73 -0
- package/skills/appsec-code-auditor/SKILL.md +35 -0
- package/skills/auth-session-hacker/SKILL.md +26 -0
- package/skills/injection-specialist/SKILL.md +29 -0
- package/skills/pentest-infra/SKILL.md +43 -0
- package/skills/pentest-team/SKILL.md +47 -0
- package/skills/pentest-web-api/SKILL.md +53 -0
- package/skills/senior-security-engineer/SKILL.md +203 -2
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ Works with **Claude Code, GitHub Copilot, Cursor, Codex, Replit**, and any MCP-c
|
|
|
17
17
|
|
|
18
18
|
## Table of Contents
|
|
19
19
|
|
|
20
|
+
- [What's New in 1.1.4](#whats-new-in-114)
|
|
20
21
|
- [What Problem Does This Solve?](#what-problem-does-this-solve)
|
|
21
22
|
- [Who Is This For?](#who-is-this-for)
|
|
22
23
|
- [Two Modes - Pick Your Depth](#two-modes---pick-your-depth)
|
|
@@ -25,7 +26,9 @@ Works with **Claude Code, GitHub Copilot, Cursor, Codex, Replit**, and any MCP-c
|
|
|
25
26
|
- [Claude Code](#step-by-step-claude-code)
|
|
26
27
|
- [Cursor](#step-by-step-cursor)
|
|
27
28
|
- [VS Code / GitHub Copilot](#step-by-step-vs-code--github-copilot)
|
|
29
|
+
- [Windsurf](#step-by-step-windsurf)
|
|
28
30
|
- [Manual Configuration](#manual-configuration-any-mcp-editor)
|
|
31
|
+
- [Verify Your Installation](#verify-your-installation)
|
|
29
32
|
- [How to Run Your First Security Review](#how-to-run-your-first-security-review)
|
|
30
33
|
- [CI/CD Security Gate](#cicd-security-gate)
|
|
31
34
|
- [What Gets Fixed Automatically](#what-gets-fixed-automatically)
|
|
@@ -40,6 +43,57 @@ Works with **Claude Code, GitHub Copilot, Cursor, Codex, Replit**, and any MCP-c
|
|
|
40
43
|
|
|
41
44
|
---
|
|
42
45
|
|
|
46
|
+
## What's New in 1.1.4
|
|
47
|
+
|
|
48
|
+
### 20 Checks (up from 18) - Deep Injection + Deep Auth Modules
|
|
49
|
+
|
|
50
|
+
Two new deep-check modules run automatically for web and API surfaces:
|
|
51
|
+
|
|
52
|
+
**`checkInjectionDeep`** — 11 new patterns: XXE (CWE-611), SSTI (CWE-94), prototype pollution (CWE-1321), open redirect (CWE-601), NoSQL operator injection (CWE-943), CRLF injection (CWE-113), unsafe YAML load (CWE-502), unsafe deserialization, path traversal (CWE-22), log injection (CWE-117), SSRF (CWE-918).
|
|
53
|
+
|
|
54
|
+
**`checkAuthDeep`** — 12 new patterns: JWT algorithm confusion / `alg:none` (CWE-327), session fixation (CWE-384), OAuth missing state parameter (CWE-352), OAuth `redirect_uri` open redirect (CWE-601), PKCE not enforced (RFC 7636), hardcoded JWT secret (CWE-798), missing rate limit on auth endpoints (CWE-307), plaintext password comparison (CWE-256), SAML signature validation disabled (CWE-347), insecure cookie flags (CWE-1004/614), refresh token not rotated (CWE-613), JWT HS/RS confusion (CVE-2015-9235 pattern).
|
|
55
|
+
|
|
56
|
+
### Coverage Completeness Protocol (§0)
|
|
57
|
+
|
|
58
|
+
Every security review now runs a mandatory 5-step protocol before reporting any result:
|
|
59
|
+
|
|
60
|
+
1. **Complete file inventory** — enumerate all source files into `coverage-manifest.json`; no attack class can be called CLEAN without checking every file.
|
|
61
|
+
2. **Taint tracking** — trace every user-controlled input (`req.body`, `req.query`, WebSocket, env, file uploads, external API responses) to all downstream sinks, classifying each SAFE / UNSAFE / UNRESOLVED.
|
|
62
|
+
3. **Negative assertions** — after each attack class: `ATTACK CLASS: {name} | FILES: N/N | PATTERNS: {list} | RESULT: CLEAN`.
|
|
63
|
+
4. **Fix verification loop** — after every fix, re-run the triggering check and confirm it no longer fires before advancing.
|
|
64
|
+
5. **All-or-nothing mandate** — every HIGH/CRITICAL finding is either FIXED (verified clean) or BLOCKED (risk-accepted, gate failing, remediation plan written to `deferred-fixes.json`).
|
|
65
|
+
|
|
66
|
+
### Enhanced Threat Model Template
|
|
67
|
+
|
|
68
|
+
The `security.threat_model` tool now generates a more complete template including LINDDUN privacy threat analysis (Linking, Identifying, Non-repudiation, Detecting, Data Disclosure, Unawareness, Non-compliance), TRIKE risk matrix (actor-action-asset-risk), DREAD scoring, attack trees for the top 3 critical paths, adversary profiles mapped to ATT&CK techniques, and supply chain threat enumeration.
|
|
69
|
+
|
|
70
|
+
### Expanded Release Checklists
|
|
71
|
+
|
|
72
|
+
All domain-specific release checklists now include:
|
|
73
|
+
|
|
74
|
+
- **OAuth/OIDC** — PKCE with S256, state/nonce verification, exact-match `redirect_uri`, code reuse prevention, audience validation
|
|
75
|
+
- **Business Logic** — idempotency keys on payment mutations, negative input validation, race condition testing for balance/quota/inventory
|
|
76
|
+
- **Serialization/Injection** — XXE, SSTI, unsafe YAML, deserialization, prototype pollution, open redirect, CRLF in every checklist
|
|
77
|
+
- **AI/LLM** — system prompt extraction resistance, multi-turn attack chains, multimodal injection, agentic tool allowlist, AML.T0054/T0057 mitigations
|
|
78
|
+
- **Payments (PCI DSS 4.0)** — PAN masking, DOM mutation monitoring, EMV 3DS 2.2+, Magecart prevention (SRI on checkout pages)
|
|
79
|
+
- **Observability Gate** (new) — anomaly detection baselines, SLO definitions for security events, alert fatigue review, runbook coverage
|
|
80
|
+
|
|
81
|
+
### Windsurf Support
|
|
82
|
+
|
|
83
|
+
The installer now detects and configures Windsurf (`~/.windsurf/mcp.json`) automatically alongside Claude Code, Cursor, and VS Code.
|
|
84
|
+
|
|
85
|
+
### `doctor` Command
|
|
86
|
+
|
|
87
|
+
Verify your installation health at any time:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npx -y security-mcp@latest doctor
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Checks Node.js version, editor configs, and skill files — prints PASS/FAIL per check with actionable fix commands.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
43
97
|
## What Problem Does This Solve?
|
|
44
98
|
|
|
45
99
|
When you use an AI coding assistant to build features fast, security is easy to skip - not because you don't care, but because:
|
|
@@ -257,6 +311,45 @@ You should see:
|
|
|
257
311
|
|
|
258
312
|
---
|
|
259
313
|
|
|
314
|
+
### Step-by-Step: Windsurf
|
|
315
|
+
|
|
316
|
+
**Step 1 - Run the installer:**
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
npx -y security-mcp@latest install
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
This auto-detects Windsurf and writes to `~/.windsurf/mcp.json`.
|
|
323
|
+
|
|
324
|
+
**Step 2 - Verify:**
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
cat ~/.windsurf/mcp.json
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Expected output:
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"mcpServers": {
|
|
335
|
+
"security-mcp": {
|
|
336
|
+
"command": "npx",
|
|
337
|
+
"args": ["-y", "security-mcp@latest", "serve"]
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Step 3 - Restart Windsurf.**
|
|
344
|
+
|
|
345
|
+
**Step 4 - In the Windsurf AI chat, type:**
|
|
346
|
+
|
|
347
|
+
```text
|
|
348
|
+
Use /senior-security-engineer to review my recent changes
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
260
353
|
### Manual Configuration (Any MCP Editor)
|
|
261
354
|
|
|
262
355
|
If the installer doesn't detect your editor, or you prefer to configure manually:
|
|
@@ -343,6 +436,28 @@ npx -y security-mcp@latest install --dry-run
|
|
|
343
436
|
|
|
344
437
|
---
|
|
345
438
|
|
|
439
|
+
## Verify Your Installation
|
|
440
|
+
|
|
441
|
+
After installing, confirm everything is wired up correctly:
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
npx -y security-mcp@latest doctor
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
This checks your Node.js version, editor config files, and installed skills — and prints `[PASS]` or `[FAIL]` per check with a fix command if anything is missing.
|
|
448
|
+
|
|
449
|
+
Example output:
|
|
450
|
+
|
|
451
|
+
```text
|
|
452
|
+
[PASS] Node.js 22.x
|
|
453
|
+
[PASS] Claude Code config (~/.claude/settings.json)
|
|
454
|
+
[PASS] senior-security-engineer skill (~/.claude/skills/senior-security-engineer/SKILL.md)
|
|
455
|
+
|
|
456
|
+
All checks passed. Restart your editor, then type /senior-security-engineer.
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
346
461
|
## How to Run Your First Security Review
|
|
347
462
|
|
|
348
463
|
### Daily Workflow: `/senior-security-engineer`
|
|
@@ -365,7 +480,7 @@ npx -y security-mcp@latest install --dry-run
|
|
|
365
480
|
|
|
366
481
|
1. Call `security.start_review` to create a tracked run
|
|
367
482
|
2. Build a scan plan covering all relevant OWASP/NIST/ATT&CK controls
|
|
368
|
-
3. Run
|
|
483
|
+
3. Run 20 security checks in parallel across secrets, dependencies, crypto, auth, injection, cloud config, AI/LLM, mobile, and more
|
|
369
484
|
4. Write fixes directly into your code for every finding it can remediate
|
|
370
485
|
5. Generate a SHA-256 attested report at `.mcp/reports/{runId}.attestation.json`
|
|
371
486
|
|
|
@@ -463,7 +578,7 @@ jobs:
|
|
|
463
578
|
|
|
464
579
|
### What the CI Gate Checks
|
|
465
580
|
|
|
466
|
-
The gate runs **
|
|
581
|
+
The gate runs **20 checks in parallel** against your diff:
|
|
467
582
|
|
|
468
583
|
| Category | What It Catches |
|
|
469
584
|
| --- | --- |
|
|
@@ -486,6 +601,8 @@ The gate runs **18 checks in parallel** against your diff:
|
|
|
486
601
|
| **AI red-team** | Static + optional dynamic probes against AI endpoints |
|
|
487
602
|
| **Exceptions** | Validates any active security exceptions are non-expired and properly approved |
|
|
488
603
|
| **Baseline regression** | Detects when previously-satisfied controls go missing (BASELINE_REGRESSION HIGH finding injected on regression) |
|
|
604
|
+
| **Deep injection** | XXE, SSTI, prototype pollution, open redirect, NoSQL operator injection, CRLF, unsafe YAML load, deserialization, path traversal, log injection, SSRF (11 new patterns) |
|
|
605
|
+
| **Deep auth** | JWT algorithm confusion, session fixation, OAuth missing state, OAuth open redirect_uri, PKCE not enforced, hardcoded JWT secret, missing rate limit on auth endpoints, plaintext password compare, SAML signature disabled, insecure cookie flags, refresh token not rotated, JWT HS/RS confusion (12 new patterns) |
|
|
489
606
|
|
|
490
607
|
### Customize the Gate Policy
|
|
491
608
|
|
|
@@ -652,15 +769,16 @@ app.use(helmet({
|
|
|
652
769
|
┌──────────────────────────────────────────────────────────────┐
|
|
653
770
|
│ Policy Gate Engine (src/gate/policy.ts) │
|
|
654
771
|
│ │
|
|
655
|
-
│
|
|
772
|
+
│ 20 checks run in parallel: │
|
|
656
773
|
│ checkSecrets checkDependencies checkApi checkInfra │
|
|
657
774
|
│ checkCrypto checkMobileIos checkMobileAndroid │
|
|
658
775
|
│ checkAi checkGraphQL checkKubernetes │
|
|
659
776
|
│ checkDatabase checkDlp checkWebNextjs │
|
|
660
|
-
│ runSbomChecks runAiRedteamChecks runRuntimeChecks
|
|
777
|
+
│ runSbomChecks runAiRedteamChecks runRuntimeChecks │
|
|
778
|
+
│ checkInjectionDeep (11 patterns) checkAuthDeep (12 patterns)│
|
|
661
779
|
│ │
|
|
662
780
|
│ Surface detection -> Control catalog -> Exception handling -> │
|
|
663
|
-
│ Confidence scoring -> PASS / FAIL
|
|
781
|
+
│ Coverage manifest -> Taint map -> Confidence scoring -> PASS / FAIL │
|
|
664
782
|
└──────────────────────────────────────────────────────────────┘
|
|
665
783
|
```
|
|
666
784
|
|
|
@@ -684,10 +802,17 @@ User: /senior-security-engineer
|
|
|
684
802
|
└── STRIDE + PASTA + ATT&CK template for changed surface
|
|
685
803
|
│
|
|
686
804
|
▼
|
|
805
|
+
§0 Coverage Completeness Protocol (runs first)
|
|
806
|
+
├── enumerate ALL source files → coverage-manifest.json
|
|
807
|
+
├── taint-trace every user-controlled input → taint-map.json
|
|
808
|
+
├── negative assertion per attack class: "FILES: N/N | RESULT: CLEAN"
|
|
809
|
+
└── fix verification loop: re-run check after every fix, confirm CLEAN
|
|
810
|
+
│
|
|
811
|
+
▼
|
|
687
812
|
security.run_pr_gate(runId, mode, targets)
|
|
688
813
|
├── git diff / glob targets -> changed files list
|
|
689
814
|
├── detectSurfaces() -> web? api? infra? mobile? ai?
|
|
690
|
-
├──
|
|
815
|
+
├── 20 checks in parallel (incl. deep injection + deep auth)
|
|
691
816
|
├── apply exceptions from .mcp/exceptions/
|
|
692
817
|
├── compute confidence score
|
|
693
818
|
└── returns PASS/FAIL + findings[]
|
|
@@ -695,6 +820,8 @@ User: /senior-security-engineer
|
|
|
695
820
|
▼
|
|
696
821
|
Claude writes inline fixes for every finding
|
|
697
822
|
(production-ready secure code, not suggestions)
|
|
823
|
+
Every HIGH/CRITICAL: FIXED with verified-clean re-run,
|
|
824
|
+
OR formally blocked with risk-acceptance record
|
|
698
825
|
│
|
|
699
826
|
▼
|
|
700
827
|
security.attest_review(runId)
|
|
@@ -853,7 +980,7 @@ Your AI uses these automatically. You don't call them directly, but understandin
|
|
|
853
980
|
| Tool | What It Does |
|
|
854
981
|
| --- | --- |
|
|
855
982
|
| `security.start_review` | Starts a stateful review run; returns `runId` used to track all subsequent steps and produce the final attestation |
|
|
856
|
-
| `security.run_pr_gate` | Runs
|
|
983
|
+
| `security.run_pr_gate` | Runs 20 security checks in parallel; returns PASS/FAIL, findings with severity, and required actions |
|
|
857
984
|
| `security.threat_model` | Generates a STRIDE + PASTA + ATT&CK threat model template for a specific feature or surface |
|
|
858
985
|
| `security.checklist` | Returns the pre-release security checklist, optionally filtered by surface (web / api / mobile / ai / infra / payments) |
|
|
859
986
|
| `security.scan_strategy` | Builds an exhaustive scan plan mapping every check to OWASP, NIST, ATT&CK, and compliance controls |
|
|
@@ -544,6 +544,206 @@
|
|
|
544
544
|
"surfaces": ["all"],
|
|
545
545
|
"frameworks": ["GDPR", "HIPAA", "PCI DSS 4.0"],
|
|
546
546
|
"required_scanners": ["semgrep"]
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"id": "OAUTH_PKCE_ENFORCED",
|
|
550
|
+
"description": "All OAuth 2.0 public clients use PKCE with S256 code challenge method.",
|
|
551
|
+
"automation": "tooling",
|
|
552
|
+
"surfaces": ["web", "api", "mobile"],
|
|
553
|
+
"frameworks": ["RFC 7636", "OWASP ASVS 3.5.3"],
|
|
554
|
+
"required_scanners": ["semgrep"]
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
"id": "JWT_ALG_CONFUSION_PREVENTED",
|
|
558
|
+
"description": "All jwt.verify() calls pin the algorithm to RS256/ES256 — algorithm confusion attack prevented.",
|
|
559
|
+
"automation": "tooling",
|
|
560
|
+
"surfaces": ["web", "api"],
|
|
561
|
+
"frameworks": ["CWE-327", "OWASP A07"],
|
|
562
|
+
"required_scanners": ["semgrep"]
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
"id": "SESSION_FIXATION_PREVENTED",
|
|
566
|
+
"description": "Session is regenerated after authentication to prevent session fixation attacks.",
|
|
567
|
+
"automation": "tooling",
|
|
568
|
+
"surfaces": ["web", "api"],
|
|
569
|
+
"frameworks": ["CWE-384", "OWASP A07"],
|
|
570
|
+
"required_scanners": ["semgrep"]
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
"id": "BUSINESS_LOGIC_REVIEWED",
|
|
574
|
+
"description": "Business logic abuse scenarios (state skipping, negative values, race conditions) are tested before release.",
|
|
575
|
+
"automation": "evidence",
|
|
576
|
+
"surfaces": ["all"],
|
|
577
|
+
"frameworks": ["OWASP Testing Guide OTG-BUSLOGIC", "NIST SP 800-53 SA-11"],
|
|
578
|
+
"evidence": ["pentest-report.json", "threat-model.json"]
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
"id": "RACE_CONDITION_MITIGATED",
|
|
582
|
+
"description": "All limit-once invariants (balance, quota, inventory, coupons) are protected by atomic DB operations.",
|
|
583
|
+
"automation": "tooling",
|
|
584
|
+
"surfaces": ["api"],
|
|
585
|
+
"frameworks": ["CWE-362", "OWASP Testing Guide OTG-BUSLOGIC-009"],
|
|
586
|
+
"required_scanners": ["semgrep"]
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"id": "PROTOTYPE_POLLUTION_MITIGATED",
|
|
590
|
+
"description": "All object merges of user-controlled data are protected by schema validation before merge.",
|
|
591
|
+
"automation": "tooling",
|
|
592
|
+
"surfaces": ["web", "api"],
|
|
593
|
+
"frameworks": ["CWE-1321", "OWASP A03"],
|
|
594
|
+
"required_scanners": ["semgrep"]
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
"id": "HTTP_SMUGGLING_MITIGATED",
|
|
598
|
+
"description": "Proxy and origin servers normalize CL/TE headers; H2C upgrade disabled.",
|
|
599
|
+
"automation": "evidence",
|
|
600
|
+
"surfaces": ["web", "api"],
|
|
601
|
+
"frameworks": ["CWE-444", "OWASP Testing Guide OTG-INPVAL-016"],
|
|
602
|
+
"evidence": ["pentest-report.json"]
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
"id": "XXE_PREVENTED",
|
|
606
|
+
"description": "XML parsers disable external entity processing (processEntities:false).",
|
|
607
|
+
"automation": "tooling",
|
|
608
|
+
"surfaces": ["api"],
|
|
609
|
+
"frameworks": ["CWE-611", "OWASP A03"],
|
|
610
|
+
"required_scanners": ["semgrep"]
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
"id": "SSTI_PREVENTED",
|
|
614
|
+
"description": "Server-side templates are never compiled from user-controlled input.",
|
|
615
|
+
"automation": "tooling",
|
|
616
|
+
"surfaces": ["web", "api"],
|
|
617
|
+
"frameworks": ["CWE-94", "OWASP A03"],
|
|
618
|
+
"required_scanners": ["semgrep"]
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"id": "DESERIALIZATION_SAFE",
|
|
622
|
+
"description": "Untrusted data is never passed to unsafe deserialization functions (node-serialize, eval, new Function).",
|
|
623
|
+
"automation": "tooling",
|
|
624
|
+
"surfaces": ["api"],
|
|
625
|
+
"frameworks": ["CWE-502", "OWASP A08"],
|
|
626
|
+
"required_scanners": ["semgrep"]
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
"id": "NOSQL_INJECTION_PREVENTED",
|
|
630
|
+
"description": "MongoDB/DynamoDB queries are built from validated fields, not raw user input.",
|
|
631
|
+
"automation": "tooling",
|
|
632
|
+
"surfaces": ["api"],
|
|
633
|
+
"frameworks": ["CWE-943", "OWASP A03"],
|
|
634
|
+
"required_scanners": ["semgrep"]
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
"id": "HTTP_VERB_TAMPERING_PREVENTED",
|
|
638
|
+
"description": "Read-only endpoints return 405 on PUT/DELETE — HTTP verb tampering blocked.",
|
|
639
|
+
"automation": "tooling",
|
|
640
|
+
"surfaces": ["api"],
|
|
641
|
+
"frameworks": ["OWASP Testing Guide OTG-INPVAL-003"],
|
|
642
|
+
"required_scanners": ["semgrep"]
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
"id": "OPEN_REDIRECT_BLOCKED",
|
|
646
|
+
"description": "All res.redirect() targets are validated against an allowlist or enforced relative-only.",
|
|
647
|
+
"automation": "tooling",
|
|
648
|
+
"surfaces": ["web", "api"],
|
|
649
|
+
"frameworks": ["CWE-601", "OWASP A01"],
|
|
650
|
+
"required_scanners": ["semgrep"]
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
"id": "SUBDOMAIN_TAKEOVER_MITIGATED",
|
|
654
|
+
"description": "DNS audit confirms no dangling CNAME records pointing to unprovisioned services.",
|
|
655
|
+
"automation": "evidence",
|
|
656
|
+
"surfaces": ["web"],
|
|
657
|
+
"frameworks": ["OWASP Testing Guide OTG-CONFIG-010"],
|
|
658
|
+
"evidence": ["dns-audit.json"]
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
"id": "GRAPHQL_FULL_HARDENING",
|
|
662
|
+
"description": "GraphQL introspection disabled, depth/complexity limits enforced, batching limited, field-level auth present.",
|
|
663
|
+
"automation": "tooling",
|
|
664
|
+
"surfaces": ["api"],
|
|
665
|
+
"frameworks": ["OWASP API Security Top 10", "OWASP Testing Guide"],
|
|
666
|
+
"required_scanners": ["semgrep"]
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
"id": "AI_MULTIMODAL_INJECTION_PREVENTED",
|
|
670
|
+
"description": "Multimodal inputs (image, audio, document) are treated as untrusted and cannot inject instructions.",
|
|
671
|
+
"automation": "evidence",
|
|
672
|
+
"surfaces": ["ai"],
|
|
673
|
+
"frameworks": ["OWASP LLM01", "MITRE ATLAS AML.T0054"],
|
|
674
|
+
"evidence": ["ai-redteam-report.json"]
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
"id": "AI_MEMORY_POISONING_PREVENTED",
|
|
678
|
+
"description": "Agent memory and scratchpad content is validated before use in downstream instructions.",
|
|
679
|
+
"automation": "evidence",
|
|
680
|
+
"surfaces": ["ai"],
|
|
681
|
+
"frameworks": ["OWASP LLM08", "MITRE ATLAS"],
|
|
682
|
+
"evidence": ["ai-redteam-report.json"]
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
"id": "AI_MODEL_INVERSION_MITIGATED",
|
|
686
|
+
"description": "Model cannot be probed to recite training PII or memorized secrets.",
|
|
687
|
+
"automation": "evidence",
|
|
688
|
+
"surfaces": ["ai"],
|
|
689
|
+
"frameworks": ["OWASP LLM06", "MITRE ATLAS AML.T0057"],
|
|
690
|
+
"evidence": ["ai-redteam-report.json"]
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
"id": "MASVS_ANTI_REVERSING",
|
|
694
|
+
"description": "Mobile release binary uses code obfuscation and anti-instrumentation controls (MASVS-RESILIENCE).",
|
|
695
|
+
"automation": "evidence",
|
|
696
|
+
"surfaces": ["mobile"],
|
|
697
|
+
"frameworks": ["OWASP MASVS 2.0 RESILIENCE"],
|
|
698
|
+
"evidence": ["mobile-binary-review.json"]
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
"id": "IMDS_V2_ENFORCED",
|
|
702
|
+
"description": "EC2 instances enforce IMDSv2 (HttpTokens=required) — IMDSv1 disabled.",
|
|
703
|
+
"automation": "tooling",
|
|
704
|
+
"surfaces": ["infra"],
|
|
705
|
+
"frameworks": ["CIS AWS Foundations Benchmark 5.6", "NIST SP 800-53 SC-7"],
|
|
706
|
+
"required_scanners": ["checkov"]
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
"id": "CLOUD_THREAT_DETECTION_ENABLED",
|
|
710
|
+
"description": "AWS GuardDuty / GCP SCC / Azure Defender for Cloud is enabled across all accounts.",
|
|
711
|
+
"automation": "tooling",
|
|
712
|
+
"surfaces": ["infra"],
|
|
713
|
+
"frameworks": ["CIS Benchmark", "NIST SP 800-53 SI-3"],
|
|
714
|
+
"required_scanners": ["checkov"]
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
"id": "MAGECART_PROTECTION",
|
|
718
|
+
"description": "Payment pages enforce extra-strict CSP and SRI on all scripts — Magecart e-skimming prevented.",
|
|
719
|
+
"automation": "tooling",
|
|
720
|
+
"surfaces": ["payments", "web"],
|
|
721
|
+
"frameworks": ["PCI DSS 4.0 Req 6.4.3", "OWASP ASVS 14.4"],
|
|
722
|
+
"required_scanners": ["semgrep"]
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
"id": "PCI_3DS_V2_ENFORCED",
|
|
726
|
+
"description": "EMV 3DS version 2.2+ is used for all card-not-present high-risk transactions.",
|
|
727
|
+
"automation": "evidence",
|
|
728
|
+
"surfaces": ["payments"],
|
|
729
|
+
"frameworks": ["PCI DSS 4.0 Req 8", "EMV 3DS 2.2"],
|
|
730
|
+
"evidence": ["payment-integration-test-results.json"]
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
"id": "HIPAA_PHI_CONTROLS",
|
|
734
|
+
"description": "Protected Health Information (PHI) is encrypted at rest and in transit; access is limited and audited.",
|
|
735
|
+
"automation": "evidence",
|
|
736
|
+
"surfaces": ["all"],
|
|
737
|
+
"frameworks": ["HIPAA §164.312", "NIST SP 800-53 SC-28"],
|
|
738
|
+
"evidence": ["hipaa-phi-audit.json"]
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
"id": "SUPPLY_CHAIN_CICD_HARDENED",
|
|
742
|
+
"description": "All CI/CD GitHub Actions are SHA-pinned; GITHUB_TOKEN has minimal permissions; no pwn-request risk.",
|
|
743
|
+
"automation": "tooling",
|
|
744
|
+
"surfaces": ["infra"],
|
|
745
|
+
"frameworks": ["SLSA Level 3", "NIST SP 800-161", "CIS Software Supply Chain Benchmark"],
|
|
746
|
+
"required_scanners": ["semgrep", "checkov"]
|
|
547
747
|
}
|
|
548
748
|
]
|
|
549
749
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* --version
|
|
10
10
|
* --help
|
|
11
11
|
*/
|
|
12
|
-
import { createRequire } from "module";
|
|
13
|
-
import { fileURLToPath } from "url";
|
|
14
|
-
import { dirname, resolve } from "path";
|
|
12
|
+
import { createRequire } from "node:module";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { dirname, resolve } from "node:path";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { homedir, platform } from "node:os";
|
|
15
17
|
import { runInstall } from "./install.js";
|
|
16
18
|
import { main as runServer } from "../mcp/server.js";
|
|
17
19
|
import { notifyIfUpdateAvailable } from "./update.js";
|
|
@@ -55,6 +57,7 @@ COMMANDS
|
|
|
55
57
|
install Auto-detect installed editors and write MCP configs
|
|
56
58
|
install-global Install using the globally installed security-mcp binary
|
|
57
59
|
config Print MCP config JSON for manual editor setup
|
|
60
|
+
doctor Verify the installation is working correctly
|
|
58
61
|
|
|
59
62
|
OPTIONS (install)
|
|
60
63
|
--claude-code Write config for Claude Code only
|
|
@@ -87,6 +90,9 @@ EXAMPLES
|
|
|
87
90
|
# Preview install without writing:
|
|
88
91
|
npx -y security-mcp@latest install --dry-run
|
|
89
92
|
|
|
93
|
+
# Verify installation health:
|
|
94
|
+
npx -y security-mcp@latest doctor
|
|
95
|
+
|
|
90
96
|
# Print JSON config snippet:
|
|
91
97
|
npx -y security-mcp@latest config
|
|
92
98
|
security-mcp config --use-global-binary
|
|
@@ -103,11 +109,76 @@ EDITOR CONFIG (add manually if install fails):
|
|
|
103
109
|
|
|
104
110
|
Claude Code: ~/.claude/settings.json
|
|
105
111
|
Cursor: ~/.cursor/mcp.json or .cursor/mcp.json
|
|
106
|
-
VS Code: .
|
|
112
|
+
VS Code: User settings.json (via Preferences > Open User Settings JSON)
|
|
113
|
+
Windsurf: ~/.windsurf/mcp.json
|
|
107
114
|
|
|
108
115
|
MORE INFO
|
|
109
116
|
https://github.com/AbrahamOO/security-mcp
|
|
110
117
|
`;
|
|
118
|
+
function resolveHome(p) {
|
|
119
|
+
return p.replace(/^~/, homedir());
|
|
120
|
+
}
|
|
121
|
+
function getVsCodeSettingsPath() {
|
|
122
|
+
const os = platform();
|
|
123
|
+
if (os === "win32")
|
|
124
|
+
return `${process.env["APPDATA"] ?? ""}\\Code\\User\\settings.json`;
|
|
125
|
+
if (os === "darwin")
|
|
126
|
+
return `${homedir()}/Library/Application Support/Code/User/settings.json`;
|
|
127
|
+
return `${homedir()}/.config/Code/User/settings.json`;
|
|
128
|
+
}
|
|
129
|
+
function runDoctor() {
|
|
130
|
+
const checks = [];
|
|
131
|
+
// Node.js version
|
|
132
|
+
const nodeVer = process.versions.node.split(".").map(Number);
|
|
133
|
+
const nodeOk = (nodeVer[0] ?? 0) >= 20;
|
|
134
|
+
checks.push({ label: `Node.js ${process.versions.node}`, ok: nodeOk, hint: nodeOk ? undefined : "Node.js 20+ required. Download from https://nodejs.org" });
|
|
135
|
+
// Claude Code config
|
|
136
|
+
const claudeConfig = resolveHome("~/.claude/settings.json");
|
|
137
|
+
const claudeOk = existsSync(claudeConfig);
|
|
138
|
+
checks.push({ label: `Claude Code config (${claudeConfig})`, ok: claudeOk, hint: claudeOk ? undefined : "Run: npx -y security-mcp@latest install --claude-code" });
|
|
139
|
+
// Claude Code skill
|
|
140
|
+
const skillPath = resolveHome("~/.claude/skills/senior-security-engineer/SKILL.md");
|
|
141
|
+
const skillOk = existsSync(skillPath);
|
|
142
|
+
checks.push({ label: `senior-security-engineer skill (${skillPath})`, ok: skillOk, hint: skillOk ? undefined : "Run: npx -y security-mcp@latest install --claude-code" });
|
|
143
|
+
// Cursor global config
|
|
144
|
+
const cursorConfig = resolveHome("~/.cursor/mcp.json");
|
|
145
|
+
if (existsSync(resolveHome("~/.cursor"))) {
|
|
146
|
+
const cursorOk = existsSync(cursorConfig);
|
|
147
|
+
checks.push({ label: `Cursor config (${cursorConfig})`, ok: cursorOk, hint: cursorOk ? undefined : "Run: npx -y security-mcp@latest install --cursor" });
|
|
148
|
+
}
|
|
149
|
+
// VS Code config
|
|
150
|
+
const vscodePath = getVsCodeSettingsPath();
|
|
151
|
+
if (existsSync(vscodePath)) {
|
|
152
|
+
checks.push({ label: `VS Code config (${vscodePath})`, ok: true });
|
|
153
|
+
}
|
|
154
|
+
// Windsurf config
|
|
155
|
+
const windsurfConfig = resolveHome("~/.windsurf/mcp.json");
|
|
156
|
+
if (existsSync(resolveHome("~/.windsurf"))) {
|
|
157
|
+
const windsurfOk = existsSync(windsurfConfig);
|
|
158
|
+
checks.push({ label: `Windsurf config (${windsurfConfig})`, ok: windsurfOk, hint: windsurfOk ? undefined : "Run: npx -y security-mcp@latest install" });
|
|
159
|
+
}
|
|
160
|
+
process.stdout.write(`\nsecurity-mcp doctor v${VERSION}\n`);
|
|
161
|
+
process.stdout.write("=".repeat(40) + "\n\n");
|
|
162
|
+
let allOk = true;
|
|
163
|
+
for (const check of checks) {
|
|
164
|
+
const status = check.ok ? "PASS" : "FAIL";
|
|
165
|
+
process.stdout.write(` [${status}] ${check.label}\n`);
|
|
166
|
+
if (!check.ok) {
|
|
167
|
+
allOk = false;
|
|
168
|
+
if (check.hint)
|
|
169
|
+
process.stdout.write(` Fix: ${check.hint}\n`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
process.stdout.write("\n");
|
|
173
|
+
if (allOk) {
|
|
174
|
+
process.stdout.write("All checks passed. security-mcp is installed correctly.\n");
|
|
175
|
+
process.stdout.write("Restart your editor if you haven't already, then type /senior-security-engineer.\n\n");
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
process.stdout.write("Some checks failed. Run the suggested fix commands above, then re-run: npx -y security-mcp@latest doctor\n\n");
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
111
182
|
async function main() {
|
|
112
183
|
const args = process.argv.slice(2);
|
|
113
184
|
const useGlobalBinary = args.includes("--use-global-binary");
|
|
@@ -165,7 +236,13 @@ async function main() {
|
|
|
165
236
|
process.stdout.write("\nAdd the above to your editor's MCP config file.\n");
|
|
166
237
|
process.stdout.write(" Claude Code: ~/.claude/settings.json\n");
|
|
167
238
|
process.stdout.write(" Cursor: ~/.cursor/mcp.json\n");
|
|
168
|
-
process.stdout.write(" VS Code: .
|
|
239
|
+
process.stdout.write(" VS Code: User settings.json (Preferences > Open User Settings JSON)\n");
|
|
240
|
+
process.stdout.write(" Windsurf: ~/.windsurf/mcp.json\n");
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case "doctor":
|
|
244
|
+
case "verify": {
|
|
245
|
+
runDoctor();
|
|
169
246
|
break;
|
|
170
247
|
}
|
|
171
248
|
default: {
|
package/dist/cli/install.js
CHANGED
|
@@ -29,6 +29,7 @@ function getEditorTargets(opts) {
|
|
|
29
29
|
const cursorGlobalPath = resolveHome("~/.cursor/mcp.json");
|
|
30
30
|
const cursorLocalPath = ".cursor/mcp.json";
|
|
31
31
|
const vscodePath = getVsCodeSettingsPath();
|
|
32
|
+
const windsurfPath = resolveHome("~/.windsurf/mcp.json");
|
|
32
33
|
const all = [
|
|
33
34
|
{
|
|
34
35
|
name: "Claude Code",
|
|
@@ -53,6 +54,12 @@ function getEditorTargets(opts) {
|
|
|
53
54
|
configPath: vscodePath,
|
|
54
55
|
type: "vscode-settings",
|
|
55
56
|
detected: existsSync(vscodePath)
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "Windsurf",
|
|
60
|
+
configPath: windsurfPath,
|
|
61
|
+
type: "mcp-servers-json",
|
|
62
|
+
detected: existsSync(resolveHome("~/.windsurf"))
|
|
56
63
|
}
|
|
57
64
|
];
|
|
58
65
|
if (opts.all) {
|
|
@@ -166,11 +173,24 @@ function installSkill(dryRun) {
|
|
|
166
173
|
*/
|
|
167
174
|
// CWE-22: only alphanumeric, hyphens, and dots allowed in skill names
|
|
168
175
|
const SAFE_SKILL_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
|
176
|
+
// CWE-918: allowlist for skill download hosts — no user-controlled URLs reach the network
|
|
177
|
+
const ALLOWED_SKILL_HOSTS = new Set(["raw.githubusercontent.com", "github.com"]);
|
|
169
178
|
export async function downloadSkill(skillName, url, dryRun = false) {
|
|
170
179
|
if (!SAFE_SKILL_NAME_RE.test(skillName)) {
|
|
171
180
|
process.stdout.write(` [error] invalid skill name "${skillName}" — skipping download\n`);
|
|
172
181
|
return;
|
|
173
182
|
}
|
|
183
|
+
try {
|
|
184
|
+
const { hostname } = new URL(url);
|
|
185
|
+
if (!ALLOWED_SKILL_HOSTS.has(hostname)) {
|
|
186
|
+
process.stdout.write(` [error] blocked skill download from unauthorized host "${hostname}"\n`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
process.stdout.write(` [error] invalid skill URL "${url}" — skipping download\n`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
174
194
|
const skillDest = resolveHome(`~/.claude/skills/${skillName}/SKILL.md`);
|
|
175
195
|
if (dryRun) {
|
|
176
196
|
process.stdout.write(` [dry-run] would download skill "${skillName}" from ${url} → ${skillDest}\n`);
|
|
@@ -271,12 +291,22 @@ export async function runInstall(opts) {
|
|
|
271
291
|
process.stdout.write("\nInstalling security policy...\n");
|
|
272
292
|
installPolicy(dryRun);
|
|
273
293
|
process.stdout.write("\n");
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
294
|
+
if (dryRun) {
|
|
295
|
+
process.stdout.write("Dry-run complete. Re-run without --dry-run to apply.\n\n");
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
process.stdout.write("Installation complete!\n");
|
|
277
299
|
process.stdout.write(`Install mode: ${opts.useGlobalBinary ? "global binary (security-mcp serve)" : "npx (npx -y security-mcp@latest serve)"}\n`);
|
|
278
300
|
process.stdout.write("\nNext steps:\n");
|
|
279
|
-
process.stdout.write(" 1. Restart your editor.\n");
|
|
280
|
-
process.stdout.write(
|
|
281
|
-
process.stdout.write(
|
|
301
|
+
process.stdout.write(" 1. Restart your editor (fully quit and reopen — not just reload window).\n");
|
|
302
|
+
process.stdout.write(" 2. Verify the server loaded:\n");
|
|
303
|
+
process.stdout.write(" Claude Code: type /mcp and confirm security-mcp is listed as Connected.\n");
|
|
304
|
+
process.stdout.write(" Cursor: Settings > MCP > security-mcp should show as active.\n");
|
|
305
|
+
process.stdout.write(" 3. Run your first security review:\n");
|
|
306
|
+
process.stdout.write(" /senior-security-engineer\n");
|
|
307
|
+
process.stdout.write(" The agent will ask you to choose a scan scope (recent changes / full codebase / specific files).\n");
|
|
308
|
+
process.stdout.write(" 4. For a deep 39-agent audit before a release:\n");
|
|
309
|
+
process.stdout.write(" /ciso-orchestrator\n");
|
|
310
|
+
process.stdout.write("\nVerify this install at any time:\n");
|
|
311
|
+
process.stdout.write(" npx -y security-mcp@latest --version\n\n");
|
|
282
312
|
}
|
package/dist/cli/onboarding.js
CHANGED
|
@@ -275,8 +275,14 @@ async function fetchLatestRelease(repo) {
|
|
|
275
275
|
function pickAsset(assets, pattern) {
|
|
276
276
|
return assets.find((a) => a.name.toLowerCase().includes(pattern.toLowerCase()))?.browser_download_url;
|
|
277
277
|
}
|
|
278
|
+
const ALLOWED_BINARY_HOSTS = new Set([
|
|
279
|
+
"github.com", "objects.githubusercontent.com", "github-releases.githubusercontent.com"
|
|
280
|
+
]);
|
|
278
281
|
async function downloadBinary(url, dest) {
|
|
279
282
|
try {
|
|
283
|
+
const { hostname } = new URL(url);
|
|
284
|
+
if (!ALLOWED_BINARY_HOSTS.has(hostname))
|
|
285
|
+
return false;
|
|
280
286
|
const res = await fetch(url);
|
|
281
287
|
if (!res.ok || !res.body)
|
|
282
288
|
return false;
|