security-mcp 1.1.3 → 1.3.1

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 (133) hide show
  1. package/README.md +164 -185
  2. package/defaults/checklists/ai.json +20 -1
  3. package/defaults/checklists/api.json +35 -1
  4. package/defaults/checklists/infra.json +34 -1
  5. package/defaults/checklists/mobile.json +23 -1
  6. package/defaults/checklists/payments.json +15 -1
  7. package/defaults/checklists/web.json +11 -1
  8. package/defaults/control-catalog.json +200 -0
  9. package/defaults/security-policy.json +2 -2
  10. package/dist/cli/index.js +82 -5
  11. package/dist/cli/install.js +36 -6
  12. package/dist/cli/onboarding.js +6 -0
  13. package/dist/gate/baseline.js +82 -7
  14. package/dist/gate/catalog.js +10 -2
  15. package/dist/gate/checks/ai.js +757 -39
  16. package/dist/gate/checks/auth-deep.js +935 -0
  17. package/dist/gate/checks/business-logic.js +751 -0
  18. package/dist/gate/checks/ci-pipeline.js +399 -4
  19. package/dist/gate/checks/crypto.js +423 -2
  20. package/dist/gate/checks/dependencies.js +571 -15
  21. package/dist/gate/checks/graphql.js +201 -19
  22. package/dist/gate/checks/infra.js +246 -1
  23. package/dist/gate/checks/injection-deep.js +848 -0
  24. package/dist/gate/checks/k8s.js +114 -1
  25. package/dist/gate/checks/mobile-android.js +917 -3
  26. package/dist/gate/checks/mobile-ios.js +797 -5
  27. package/dist/gate/checks/required-artifacts.js +194 -0
  28. package/dist/gate/checks/runtime.js +178 -0
  29. package/dist/gate/checks/secrets.js +244 -13
  30. package/dist/gate/checks/supply-chain-deep.js +787 -0
  31. package/dist/gate/checks/web-nextjs.js +572 -48
  32. package/dist/gate/diff.js +17 -5
  33. package/dist/gate/evidence.js +8 -1
  34. package/dist/gate/exceptions.js +131 -9
  35. package/dist/gate/policy.js +282 -129
  36. package/dist/mcp/audit-chain.js +122 -28
  37. package/dist/mcp/auth.js +169 -0
  38. package/dist/mcp/learning.js +129 -4
  39. package/dist/mcp/model-router.js +158 -21
  40. package/dist/mcp/orchestration.js +186 -51
  41. package/dist/mcp/server.js +608 -94
  42. package/dist/repo/fs.js +24 -1
  43. package/dist/repo/search.js +31 -6
  44. package/dist/review/store.js +52 -1
  45. package/package.json +7 -7
  46. package/prompts/SECURITY_PROMPT.md +73 -0
  47. package/skills/_TEMPLATE/SKILL.md +99 -0
  48. package/skills/advanced-dos-tester/SKILL.md +109 -0
  49. package/skills/agentic-loop-exploiter/SKILL.md +368 -0
  50. package/skills/ai-llm-redteam/SKILL.md +104 -0
  51. package/skills/ai-model-supply-chain-agent/SKILL.md +103 -0
  52. package/skills/algorithm-implementation-reviewer/SKILL.md +98 -0
  53. package/skills/android-penetration-tester/SKILL.md +455 -46
  54. package/skills/anti-replay-tester/SKILL.md +106 -0
  55. package/skills/appsec-code-auditor/SKILL.md +120 -0
  56. package/skills/artifact-integrity-analyst/SKILL.md +441 -0
  57. package/skills/attack-navigator/SKILL.md +467 -8
  58. package/skills/auth-session-hacker/SKILL.md +128 -0
  59. package/skills/aws-penetration-tester/SKILL.md +456 -0
  60. package/skills/azure-penetration-tester/SKILL.md +490 -3
  61. package/skills/binary-auth-validator/SKILL.md +111 -0
  62. package/skills/bot-detection-specialist/SKILL.md +109 -0
  63. package/skills/business-logic-attacker/SKILL.md +231 -0
  64. package/skills/capec-code-mapper/SKILL.md +84 -0
  65. package/skills/cert-pin-rotation-specialist/SKILL.md +112 -0
  66. package/skills/cicd-pipeline-hijacker/SKILL.md +405 -0
  67. package/skills/ciso-orchestrator/SKILL.md +454 -43
  68. package/skills/cloud-infra-specialist/SKILL.md +118 -0
  69. package/skills/compliance-gap-analyst/SKILL.md +422 -0
  70. package/skills/compliance-grc/SKILL.md +85 -0
  71. package/skills/compliance-lifecycle-tracker/SKILL.md +84 -0
  72. package/skills/credential-stuffing-specialist/SKILL.md +102 -0
  73. package/skills/crypto-pki-specialist/SKILL.md +87 -0
  74. package/skills/csa-ccm-mapper/SKILL.md +84 -0
  75. package/skills/csf2-governance-mapper/SKILL.md +84 -0
  76. package/skills/deep-link-fuzzer/SKILL.md +109 -0
  77. package/skills/dependency-confusion-attacker/SKILL.md +415 -0
  78. package/skills/device-integrity-aggregator/SKILL.md +108 -0
  79. package/skills/dos-resilience-tester/SKILL.md +97 -0
  80. package/skills/dread-scorer/SKILL.md +84 -0
  81. package/skills/egress-policy-enforcer/SKILL.md +99 -0
  82. package/skills/evidence-collector/SKILL.md +98 -0
  83. package/skills/file-upload-attacker/SKILL.md +109 -0
  84. package/skills/gcp-penetration-tester/SKILL.md +459 -2
  85. package/skills/git-history-secret-scanner/SKILL.md +106 -0
  86. package/skills/iam-privesc-graph-builder/SKILL.md +152 -0
  87. package/skills/incident-responder/SKILL.md +111 -0
  88. package/skills/injection-specialist/SKILL.md +131 -0
  89. package/skills/ios-security-auditor/SKILL.md +282 -0
  90. package/skills/json-ambiguity-tester/SKILL.md +0 -0
  91. package/skills/k8s-container-escaper/SKILL.md +384 -0
  92. package/skills/key-management-lifecycle-analyst/SKILL.md +98 -0
  93. package/skills/kill-switch-engineer/SKILL.md +102 -0
  94. package/skills/linddun-privacy-analyst/SKILL.md +102 -0
  95. package/skills/logic-race-fuzzer/SKILL.md +443 -0
  96. package/skills/mobile-api-network-attacker/SKILL.md +421 -0
  97. package/skills/mobile-binary-hardener/SKILL.md +102 -0
  98. package/skills/mobile-security-specialist/SKILL.md +85 -0
  99. package/skills/mobile-webview-auditor/SKILL.md +96 -0
  100. package/skills/model-extraction-attacker/SKILL.md +219 -0
  101. package/skills/multipart-abuse-tester/SKILL.md +84 -0
  102. package/skills/oauth-pkce-specialist/SKILL.md +104 -0
  103. package/skills/parser-exhaustion-tester/SKILL.md +142 -0
  104. package/skills/pentest-infra/SKILL.md +141 -0
  105. package/skills/pentest-social/SKILL.md +201 -0
  106. package/skills/pentest-team/SKILL.md +134 -0
  107. package/skills/pentest-web-api/SKILL.md +151 -0
  108. package/skills/privacy-flow-analyst/SKILL.md +234 -0
  109. package/skills/prompt-injection-specialist/SKILL.md +394 -0
  110. package/skills/quantum-migration-planner/SKILL.md +96 -0
  111. package/skills/rag-poisoning-specialist/SKILL.md +358 -0
  112. package/skills/registry-mirror-enforcer/SKILL.md +84 -0
  113. package/skills/rotation-validation-agent/SKILL.md +112 -0
  114. package/skills/samm-assessor/SKILL.md +85 -0
  115. package/skills/secrets-mask-bypass-tester/SKILL.md +100 -0
  116. package/skills/senior-security-engineer/SKILL.md +370 -2
  117. package/skills/serialization-memory-attacker/SKILL.md +332 -0
  118. package/skills/session-timeout-tester/SKILL.md +161 -0
  119. package/skills/slsa-level3-enforcer/SKILL.md +112 -0
  120. package/skills/slsa-provenance-enforcer/SKILL.md +102 -0
  121. package/skills/ssrf-detection-validator/SKILL.md +108 -0
  122. package/skills/step-up-auth-enforcer/SKILL.md +84 -0
  123. package/skills/stride-pasta-analyst/SKILL.md +420 -0
  124. package/skills/supply-chain-devsecops/SKILL.md +98 -0
  125. package/skills/threat-infrastructure-analyst/SKILL.md +84 -0
  126. package/skills/threat-modeler/SKILL.md +85 -0
  127. package/skills/tls-certificate-auditor/SKILL.md +573 -18
  128. package/skills/token-reuse-detector/SKILL.md +95 -0
  129. package/skills/trike-risk-modeler/SKILL.md +84 -0
  130. package/skills/unicode-homograph-tester/SKILL.md +84 -0
  131. package/skills/waf-rule-lifecycle-agent/SKILL.md +97 -0
  132. package/skills/webhook-security-tester/SKILL.md +102 -0
  133. package/skills/zero-trust-architect/SKILL.md +109 -0
@@ -20,6 +20,28 @@
20
20
  { "id": "mobile_threat_model", "description": "Threat model completed and reviewed for this mobile surface change", "critical": true },
21
21
  { "id": "mobile_data_residency", "description": "Data residency requirements met — no user data stored on device beyond session", "critical": false },
22
22
  { "id": "mobile_backup_prevention", "description": "allowBackup=false in Android manifest — sensitive data not included in backups", "critical": true },
23
- { "id": "mobile_logging", "description": "No sensitive data logged in production builds — crash reporting sanitized", "critical": true }
23
+ { "id": "mobile_logging", "description": "No sensitive data logged in production builds — crash reporting sanitized", "critical": true },
24
+ { "id": "mobile_tapjacking_prevention", "description": "Android tapjacking prevention: FLAG_SECURE set on sensitive screens; filterTouchesWhenObscured=true on clickable elements handling auth or payments", "critical": true },
25
+ { "id": "mobile_memory_zeroing", "description": "Sensitive data (passwords, keys, PAN) explicitly zeroed from memory after use — not relying on GC; no sensitive data in String objects (use char[] or SecureString)", "critical": false },
26
+ { "id": "mobile_anti_debugging", "description": "Anti-debugging and anti-tampering controls in place for high-risk operations; ptrace detection and integrity attestation verified before sensitive operations", "critical": false },
27
+ { "id": "mobile_ios_temp_sensitive", "title": "iOS: Sensitive data not written to NSTemporaryDirectory or NSCachesDirectory without explicit cleanup", "severity": "high", "automated": true },
28
+ { "id": "mobile_ios_file_protection_none", "title": "iOS: NSFileProtectionNone not set on any file containing sensitive data", "severity": "critical", "automated": true },
29
+ { "id": "mobile_ios_appstorage_sensitive", "title": "iOS: @AppStorage not used for sensitive credentials or tokens", "severity": "high", "automated": true },
30
+ { "id": "mobile_ios_sqlite_unencrypted", "title": "iOS: SQLite databases encrypted with SQLCipher when storing sensitive data", "severity": "high", "automated": true },
31
+ { "id": "mobile_ios_webview_http", "title": "iOS: WKWebView does not load http:// URLs when JavaScript is enabled", "severity": "critical", "automated": true },
32
+ { "id": "mobile_ios_universal_links", "title": "iOS: Universal Links configured with HTTPS AASA and restrictive path patterns", "severity": "high", "automated": true },
33
+ { "id": "mobile_android_root_detection", "title": "Android: Root detection implemented for high-risk operations", "severity": "medium", "automated": true },
34
+ { "id": "mobile_android_frida_magisk_detection", "title": "Android: Frida/Magisk/Xposed detection in place for high-risk app flows", "severity": "medium", "automated": true },
35
+ { "id": "mobile_android_webview_ssl_error", "title": "Android: WebViewClient.onReceivedSslError does not call proceed() unconditionally", "severity": "critical", "automated": true },
36
+ { "id": "mobile_android_firebase_rules", "title": "Android: Firebase Realtime Database and Firestore rules deny unauthenticated access", "severity": "critical", "automated": true },
37
+ { "id": "mobile_android_maps_api_key", "title": "Android: Google Maps API key not hardcoded in manifest or resource files", "severity": "high", "automated": true },
38
+ { "id": "mobile_android_deeplink_traversal", "title": "Android: Deep link path parameters validated and sanitized before use in file or URL operations", "severity": "high", "automated": true },
39
+ { "id": "mobile_android_sharedprefs_world", "title": "Android: SharedPreferences not opened with MODE_WORLD_READABLE or MODE_WORLD_WRITEABLE", "severity": "critical", "automated": true },
40
+ { "id": "mobile_android_content_provider_permission", "title": "Android: ContentProvider with exported=true has explicit readPermission and writePermission", "severity": "high", "automated": true },
41
+ { "id": "mobile_rn_async_storage_sensitive", "title": "React Native: AsyncStorage not used for credentials or tokens — use react-native-keychain or Expo SecureStore", "severity": "high", "automated": true },
42
+ { "id": "mobile_rn_codepush_integrity", "title": "React Native: OTA bundle updates verified with code signing before execution", "severity": "high", "automated": true },
43
+ { "id": "mobile_flutter_sharedprefs_sensitive", "title": "Flutter: Sensitive data stored in flutter_secure_storage, not shared_preferences", "severity": "high", "automated": true },
44
+ { "id": "mobile_expo_async_vs_secure", "title": "Expo: Credentials and tokens stored in SecureStore, not AsyncStorage", "severity": "high", "automated": true },
45
+ { "id": "mobile_certificate_transparency", "title": "Certificate Transparency enforcement enabled for production domains", "severity": "medium", "automated": true }
24
46
  ]
25
47
  }
@@ -20,6 +20,20 @@
20
20
  { "id": "pci_chargeback_monitoring", "description": "Chargeback monitoring and alerting configured with defined response process", "critical": false },
21
21
  { "id": "pci_data_retention", "description": "Payment data retention policy enforced — data purged per PCI DSS schedule", "critical": true },
22
22
  { "id": "pci_ir_playbook", "description": "Payment fraud and PCI breach IR playbooks exist and are current", "critical": true },
23
- { "id": "pci_threat_model", "description": "Threat model completed and reviewed for this payment surface change", "critical": true }
23
+ { "id": "pci_threat_model", "description": "Threat model completed and reviewed for this payment surface change", "critical": true },
24
+ { "id": "pci_magecart_prevention", "description": "Magecart/digital-skimming prevention: SRI hashes on all checkout page scripts; CSP blocks unauthorized exfiltration destinations; DOM mutation monitoring detects injected form skimmers", "critical": true },
25
+ { "id": "pci_3ds_enforced", "description": "EMV 3D Secure 2.2+ enforced for card-not-present transactions above risk threshold; step-up authentication triggered by anomaly score; 3DS bypass attempts logged and alerted", "critical": true },
26
+ { "id": "pci_currency_validation", "title": "Payment currency fixed server-side — client-supplied currency code rejected or validated against strict allowlist", "severity": "critical", "automated": true },
27
+ { "id": "pci_discount_stacking", "title": "Discount/coupon stacking limited server-side — maximum one promotion per order enforced atomically", "severity": "high", "automated": true },
28
+ { "id": "pci_payment_confirmation_server_side", "title": "Order/fulfillment status derived exclusively from payment processor API response — never from client-supplied status field", "severity": "critical", "automated": true },
29
+ { "id": "pci_webhook_timestamp_tolerance", "title": "Webhook timestamp tolerance ≤ 300 seconds — stale events rejected", "severity": "high", "automated": true },
30
+ { "id": "pci_server_side_totals", "title": "Tax, shipping, and discount amounts computed server-side — never sourced from client request body", "severity": "high", "automated": true },
31
+ { "id": "pci_total_revalidation", "title": "Final charge amount re-computed server-side from canonical item prices — client-supplied total field never used as charge amount", "severity": "critical", "automated": true },
32
+ { "id": "pci_referral_abuse_prevention", "title": "Referral/signup bonus protected against self-referral and multi-account abuse with deduplication", "severity": "high", "automated": true },
33
+ { "id": "biz_email_normalization", "title": "Email addresses normalised before uniqueness check — duplicate account creation via aliasing prevented", "severity": "high", "automated": true },
34
+ { "id": "biz_feature_flag_server_side", "title": "Feature entitlements derived from server-side session/database record — not from client-supplied plan/tier/featureFlag parameters", "severity": "high", "automated": true },
35
+ { "id": "biz_api_version_controls_parity", "title": "All security controls applied uniformly across all live API versions — deprecated versions sunset or mirrored", "severity": "high", "automated": false },
36
+ { "id": "pci_trial_abuse_prevention", "title": "Free trial creation checked for velocity on payment method fingerprint, BIN prefix, IP, and device identifier", "severity": "high", "automated": true },
37
+ { "id": "pci_payment_intent_idempotency", "title": "Payment intent charge guarded by distributed lock or unique constraint — concurrent double-charge prevented", "severity": "critical", "automated": true }
24
38
  ]
25
39
  }
@@ -25,6 +25,16 @@
25
25
  { "id": "web_sast_pass", "description": "SAST scan passed with no CRITICAL findings", "critical": true },
26
26
  { "id": "web_secrets_scan", "description": "Secrets scan clean — no credentials or tokens in source code", "critical": true },
27
27
  { "id": "web_logging", "description": "Required security events logged — no PII, tokens, or secrets in logs", "critical": false },
28
- { "id": "web_staging_verified", "description": "Security headers verified in staging environment with automated check", "critical": false }
28
+ { "id": "web_staging_verified", "description": "Security headers verified in staging environment with automated check", "critical": false },
29
+ { "id": "web_dom_clobbering", "description": "DOM clobbering prevention reviewed — named form inputs, anchors, and embeds do not shadow window/document properties used in application logic", "critical": true },
30
+ { "id": "web_cache_poisoning", "description": "Web cache poisoning prevented — unkeyed headers (X-Forwarded-Host, X-Original-URL, X-Rewrite-URL) are not reflected in cached responses or redirects", "critical": true },
31
+ { "id": "web_websocket_auth", "description": "WebSocket upgrade validates authentication independently (token in first message or query param signed); does not rely solely on session cookies which bypass CORS", "critical": true },
32
+ { "id": "web_postmessage_origin", "description": "postMessage listeners validate event.origin against an explicit allowlist before processing any message data — no wildcard '*' as trusted origin", "critical": true },
33
+ { "id": "web_dangling_markup", "description": "Dangling markup injection reviewed — no user-controlled partial HTML tags output across multi-step page generation; prevents attribute-injection data exfiltration", "critical": false },
34
+ { "id": "web_css_injection", "title": "User input not written into CSS style attributes, CSS-in-JS template literals, or <style> blocks", "severity": "high", "automated": true },
35
+ { "id": "web_dangling_markup_check", "title": "Dangling markup injection prevention — no user-controlled partial HTML tags in server-rendered output", "severity": "high", "automated": true },
36
+ { "id": "web_postmessage_wildcard", "title": "postMessage sender does not use wildcard '*' as targetOrigin for sensitive data", "severity": "medium", "automated": true },
37
+ { "id": "web_cache_poisoning_headers", "title": "Unkeyed headers (X-Forwarded-Host, X-Original-URL) not reflected in cached responses or redirects", "severity": "medium", "automated": true },
38
+ { "id": "web_missing_sri", "title": "All external scripts and stylesheets loaded with Subresource Integrity (SRI) integrity= attribute", "severity": "medium", "automated": true }
29
39
  ]
30
40
  }
@@ -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
  }
@@ -4,8 +4,8 @@
4
4
  "description": "Default security gate policy for security-mcp. Copy to .mcp/policies/security-policy.json and customize for your project.",
5
5
  "required_checks": {
6
6
  "secrets_scan": { "severity_block": ["HIGH", "CRITICAL"] },
7
- "dependency_scan": { "severity_block": ["CRITICAL"] },
8
- "sast": { "severity_block": ["CRITICAL"] },
7
+ "dependency_scan": { "severity_block": ["HIGH", "CRITICAL"] },
8
+ "sast": { "severity_block": ["HIGH", "CRITICAL"] },
9
9
  "iac_scan": { "severity_block": ["HIGH", "CRITICAL"] }
10
10
  },
11
11
  "environments": {
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: .vscode/mcp.json (workspace)
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: .vscode/mcp.json\n");
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: {
@@ -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
- process.stdout.write(dryRun
275
- ? "Dry-run complete. Re-run without --dry-run to apply.\n"
276
- : "Done! Restart your editor to activate the security-mcp server.\n");
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(' 2. In Claude Code, type /senior-security-engineer to activate the security persona.\n');
281
- process.stdout.write(' 3. Ask your AI: "Run security.start_review with mode=recent_changes" to begin an auditable review.\n\n');
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
  }
@@ -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;