security-mcp 1.0.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.
@@ -0,0 +1,463 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { readFileSync, existsSync } from "fs";
4
+ import { dirname, join, resolve } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { z } from "zod";
7
+ import { runPrGate } from "../gate/policy.js";
8
+ import { readFileSafe } from "../repo/fs.js";
9
+ import { searchRepo } from "../repo/search.js";
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const PKG_ROOT = resolve(__dirname, "../..");
12
+ const PROMPTS_DIR = join(PKG_ROOT, "prompts");
13
+ // Load the generalized security prompt at startup.
14
+ // Falls back to a short notice if the file has not been built yet.
15
+ function loadPromptFile(name) {
16
+ const path = join(PROMPTS_DIR, name);
17
+ if (existsSync(path)) {
18
+ return readFileSync(path, "utf-8");
19
+ }
20
+ return `[security-mcp] Prompt file not found: ${name}. Run "npm run build" from the package root.`;
21
+ }
22
+ const SECURITY_PROMPT = loadPromptFile("SECURITY_PROMPT.md");
23
+ /* eslint-disable deprecation/deprecation */
24
+ const server = new McpServer({
25
+ name: "security-mcp",
26
+ version: "1.0.0"
27
+ });
28
+ const tool = server.tool.bind(server);
29
+ // ---------------------------------------------------------------------------
30
+ // Helper
31
+ // ---------------------------------------------------------------------------
32
+ function asTextResponse(data) {
33
+ const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
34
+ return { content: [{ type: "text", text }] };
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // Existing tools (unchanged)
38
+ // ---------------------------------------------------------------------------
39
+ const RunPrGateParams = {
40
+ baseRef: z.string().optional().describe("Base git ref for diff (e.g. origin/main). Optional."),
41
+ headRef: z.string().optional().describe("Head git ref for diff (e.g. HEAD). Optional."),
42
+ policyPath: z.string().optional().describe("Override policy path. Default: .mcp/policies/security-policy.json")
43
+ };
44
+ const RunPrGateSchema = z.object(RunPrGateParams);
45
+ tool("security.run_pr_gate", "Run the security policy gate against the current workspace. Returns PASS/FAIL plus findings and required actions.", RunPrGateParams, async (args, _extra) => {
46
+ const { baseRef, headRef, policyPath } = RunPrGateSchema.parse(args);
47
+ const result = await runPrGate({
48
+ baseRef,
49
+ headRef,
50
+ policyPath: policyPath ?? ".mcp/policies/security-policy.json"
51
+ });
52
+ return asTextResponse(result);
53
+ });
54
+ const ReadFileParams = {
55
+ path: z.string().describe("Relative path in the repo.")
56
+ };
57
+ const ReadFileSchema = z.object(ReadFileParams);
58
+ tool("repo.read_file", "Read a file from the repo workspace.", ReadFileParams, async (args, _extra) => {
59
+ const { path } = ReadFileSchema.parse(args);
60
+ const data = await readFileSafe(path);
61
+ return asTextResponse(data);
62
+ });
63
+ const SearchParams = {
64
+ query: z.string().describe("Plain string or regex pattern."),
65
+ isRegex: z.boolean().optional().describe("Treat query as regex. Default false."),
66
+ maxMatches: z.number().int().min(1).max(500).optional().describe("Default 200.")
67
+ };
68
+ const SearchSchema = z.object(SearchParams);
69
+ tool("repo.search", "Search the repo for a regex or string. Returns matches with file + line numbers.", SearchParams, async (args, _extra) => {
70
+ const { query, isRegex, maxMatches } = SearchSchema.parse(args);
71
+ const matches = await searchRepo({ query, isRegex: !!isRegex, maxMatches: maxMatches ?? 200 });
72
+ return asTextResponse(matches);
73
+ });
74
+ // ---------------------------------------------------------------------------
75
+ // New tool: security.get_system_prompt
76
+ // ---------------------------------------------------------------------------
77
+ const GetSystemPromptParams = {
78
+ stack: z.string().optional().describe("Your tech stack, e.g. 'Next.js, TypeScript, PostgreSQL, AWS Lambda'. " +
79
+ "Appended as a Scope section to the prompt."),
80
+ cloud: z.string().optional().describe("Primary cloud provider(s), e.g. 'AWS', 'GCP', 'Azure', 'multi-cloud'."),
81
+ payment_processor: z.string().optional().describe("Payment processor in use, e.g. 'Stripe', 'Braintree', 'Adyen', or 'none'.")
82
+ };
83
+ const GetSystemPromptSchema = z.object(GetSystemPromptParams);
84
+ tool("security.get_system_prompt", "Return the full security engineering system prompt. Optionally customized with your stack, cloud provider, and payment processor. Use this as the system prompt to configure Claude as an elite security engineer for your project.", GetSystemPromptParams, async (args, _extra) => {
85
+ const { stack, cloud, payment_processor } = GetSystemPromptSchema.parse(args);
86
+ let prompt = SECURITY_PROMPT;
87
+ // Append a project-specific scope section if any context was provided
88
+ if (stack ?? cloud ?? payment_processor) {
89
+ const scopeLines = [
90
+ "",
91
+ "---",
92
+ "",
93
+ "## PROJECT SCOPE (user-defined)",
94
+ ""
95
+ ];
96
+ if (stack)
97
+ scopeLines.push(`- **Stack**: ${stack}`);
98
+ if (cloud)
99
+ scopeLines.push(`- **Primary cloud**: ${cloud}`);
100
+ if (payment_processor)
101
+ scopeLines.push(`- **Payment processor**: ${payment_processor}`);
102
+ scopeLines.push("");
103
+ prompt = prompt + scopeLines.join("\n");
104
+ }
105
+ return asTextResponse(prompt);
106
+ });
107
+ // ---------------------------------------------------------------------------
108
+ // New tool: security.threat_model
109
+ // ---------------------------------------------------------------------------
110
+ const ThreatModelParams = {
111
+ feature: z.string().describe("One or two sentences describing the feature or component to threat-model. " +
112
+ "Example: 'OAuth 2.0 login flow with PKCE and session cookies'."),
113
+ surfaces: z.array(z.enum(["web", "api", "mobile", "ai", "infra", "data"])).optional().describe("Attack surfaces involved. Defaults to all.")
114
+ };
115
+ const ThreatModelSchema = z.object(ThreatModelParams);
116
+ tool("security.threat_model", "Generate a STRIDE + PASTA + ATT&CK threat model template for a described feature or component. Returns a structured Markdown document ready to fill in.", ThreatModelParams, async (args, _extra) => {
117
+ const { feature, surfaces } = ThreatModelSchema.parse(args);
118
+ const surfaceList = surfaces ?? ["web", "api", "mobile", "ai", "infra", "data"];
119
+ const template = `# Threat Model: ${feature}
120
+
121
+ **Date**: ${new Date().toISOString().slice(0, 10)}
122
+ **Status**: DRAFT
123
+ **Surfaces**: ${surfaceList.join(", ")}
124
+
125
+ ---
126
+
127
+ ## 1. Asset Inventory
128
+
129
+ | Asset | Sensitivity | Owner |
130
+ |---|---|---|
131
+ | _e.g. User session tokens_ | HIGH | |
132
+ | _e.g. PII records_ | CRITICAL | |
133
+
134
+ ## 2. Trust Boundaries
135
+
136
+ List every point where the trust level changes (e.g. browser -> API server, API -> DB, service A -> service B).
137
+
138
+ - [ ] Boundary 1:
139
+ - [ ] Boundary 2:
140
+
141
+ ## 3. Data Flow Diagram (DFD)
142
+
143
+ Describe Level 0 (context) and Level 1 (process) flows in prose or embed a diagram link.
144
+
145
+ ## 4. STRIDE Analysis
146
+
147
+ | Component | Spoofing | Tampering | Repudiation | Info Disclosure | DoS | Elevation of Privilege |
148
+ |---|---|---|---|---|---|---|
149
+ | _component_ | | | | | | |
150
+
151
+ ## 5. PASTA Risk Assessment
152
+
153
+ **Stage 1 - Business objectives at risk**:
154
+
155
+ **Stage 2 - Technical scope**:
156
+
157
+ **Stage 3 - Application decomposition** (key entry points, APIs, data stores):
158
+
159
+ **Stage 4 - Threat analysis** (attacker profile, motivation):
160
+
161
+ **Stage 5 - Vulnerability analysis**:
162
+
163
+ **Stage 6 - Attack modeling** (attack trees for top 3 risks):
164
+
165
+ **Stage 7 - Risk and impact analysis**:
166
+
167
+ ## 6. MITRE ATT&CK Mapping
168
+
169
+ | Tactic | Technique ID | Technique Name | Applicable? | D3FEND Countermeasure |
170
+ |---|---|---|---|---|
171
+ | Initial Access | T1190 | Exploit Public-Facing Application | | |
172
+ | Credential Access | T1110 | Brute Force | | |
173
+ | Exfiltration | T1041 | Exfiltration Over C2 Channel | | |
174
+ | Collection | T1530 | Data from Cloud Storage | | |
175
+
176
+ ## 7. Controls
177
+
178
+ ### Preventive
179
+ - [ ]
180
+
181
+ ### Detective
182
+ - [ ]
183
+
184
+ ### Corrective / Recovery
185
+ - [ ]
186
+
187
+ ### Compensating (if primary control is not feasible)
188
+ - [ ]
189
+
190
+ ## 8. NIST 800-53 Control Mapping
191
+
192
+ | Control ID | Control Name | Implemented? | Evidence |
193
+ |---|---|---|---|
194
+ | AC-3 | Access Enforcement | | |
195
+ | AU-2 | Event Logging | | |
196
+ | SC-8 | Transmission Confidentiality and Integrity | | |
197
+ | SI-10 | Information Input Validation | | |
198
+
199
+ ## 9. Residual Risks
200
+
201
+ | Risk | Likelihood | Impact | Owner | Review Date | Acceptance Rationale |
202
+ |---|---|---|---|---|---|
203
+ | | | | | | |
204
+
205
+ ## 10. Security Test Cases (from threat model)
206
+
207
+ | Test ID | Threat | Test Scenario | Expected Result | Status |
208
+ |---|---|---|---|---|
209
+ | TM-001 | | | | PENDING |
210
+
211
+ ## 11. Pre-Release Checklist (Section 22E)
212
+
213
+ - [ ] Threat model reviewed by security-designated reviewer
214
+ - [ ] All SAST/SCA/IaC/container scan gates pass
215
+ - [ ] Auth and authorization logic reviewed
216
+ - [ ] Secrets handling reviewed - no hardcoded secrets
217
+ - [ ] Input validation present on all new inputs (server-side confirmed)
218
+ - [ ] Error messages reviewed - no information leakage
219
+ - [ ] Logging confirmed - required events logged, no PII in logs
220
+ - [ ] Security headers verified in staging
221
+ - [ ] Rate limiting confirmed on all new endpoints
222
+ - [ ] CORS configuration reviewed
223
+ - [ ] Dependencies reviewed for new CVEs
224
+ - [ ] Network rules reviewed - no 0.0.0.0/0, all traffic via private paths
225
+ - [ ] IR playbook updated if new attack surface introduced
226
+ - [ ] Compliance requirements addressed and documented
227
+ `;
228
+ return asTextResponse(template);
229
+ });
230
+ // ---------------------------------------------------------------------------
231
+ // New tool: security.checklist
232
+ // ---------------------------------------------------------------------------
233
+ const ChecklistParams = {
234
+ surface: z.enum(["web", "api", "mobile", "ai", "infra", "payments", "all"]).optional()
235
+ .describe("Filter checklist by attack surface. Default: all.")
236
+ };
237
+ const ChecklistSchema = z.object(ChecklistParams);
238
+ const CHECKLIST_ALL = `# Pre-Release Security Checklist
239
+
240
+ Use before every production release. All items must be checked or explicitly risk-accepted.
241
+
242
+ ## All Surfaces
243
+
244
+ - [ ] Threat model completed and reviewed by security-designated reviewer
245
+ - [ ] SAST scan results reviewed - all CRITICAL/HIGH findings resolved
246
+ - [ ] SCA scan - no CRITICAL CVEs in dependencies; HIGH CVEs triaged
247
+ - [ ] Secrets scan clean (Trufflehog / Gitleaks)
248
+ - [ ] IaC scan - no HIGH/CRITICAL misconfigurations (Checkov / tfsec)
249
+ - [ ] Container scan - no CRITICAL CVEs with available fix (Trivy / Grype)
250
+ - [ ] Error messages reviewed - no stack traces, schema details, or enum leakage
251
+ - [ ] Logging reviewed - all required events logged; no PII, secrets, or tokens in logs
252
+ - [ ] Dependencies reviewed for new CVEs introduced by this change
253
+ - [ ] SBOM generated for this release artifact
254
+ - [ ] Rollback plan documented and tested
255
+ - [ ] IR playbook updated if a new attack surface was introduced
256
+
257
+ ## Web / Frontend
258
+
259
+ - [ ] Content-Security-Policy header present with nonce-based script control (no unsafe-inline)
260
+ - [ ] HSTS header with includeSubDomains and preload
261
+ - [ ] X-Frame-Options: DENY
262
+ - [ ] X-Content-Type-Options: nosniff
263
+ - [ ] Referrer-Policy: strict-origin-when-cross-origin
264
+ - [ ] Permissions-Policy set
265
+ - [ ] No inline JavaScript or inline event handlers
266
+ - [ ] Subresource Integrity (SRI) on any third-party scripts
267
+ - [ ] CSRF protection on all state-changing endpoints
268
+ - [ ] XSS: no dangerouslySetInnerHTML without sanitization
269
+
270
+ ## API
271
+
272
+ - [ ] All new endpoints require authentication (JWT RS256/ES256 validated)
273
+ - [ ] Authorization checked server-side for every resource operation (IDOR prevention)
274
+ - [ ] Input validation present on all new inputs - server-side schema validation confirmed
275
+ - [ ] Rate limiting configured on all new endpoints
276
+ - [ ] CORS origin allowlist reviewed (no wildcard on authenticated endpoints)
277
+ - [ ] Request size limits enforced
278
+ - [ ] SSRF protection on any server-side HTTP client (block private IPs, metadata endpoints)
279
+ - [ ] Webhook signatures verified (HMAC-SHA256 + replay protection)
280
+ - [ ] OpenAPI spec updated
281
+
282
+ ## Infrastructure / Cloud
283
+
284
+ - [ ] No 0.0.0.0/0 ingress or egress rules in any firewall / security group
285
+ - [ ] All managed services accessed via VPC endpoints / private connectivity
286
+ - [ ] No world-readable storage buckets
287
+ - [ ] Secrets stored in secret manager - not in env files, CI logs, or container images
288
+ - [ ] IAM roles follow least privilege - no wildcard permissions
289
+ - [ ] Network segmentation reviewed (web tier, app tier, data tier isolated)
290
+ - [ ] WAF rules updated if new public endpoints added
291
+ - [ ] Cloud audit logging confirmed for new resources
292
+
293
+ ## Mobile
294
+
295
+ - [ ] iOS: NSAllowsArbitraryLoads is false (ATS enforced)
296
+ - [ ] Android: android:debuggable="false" in release build
297
+ - [ ] Android: cleartext traffic disabled (usesCleartextTraffic="false")
298
+ - [ ] Certificate pinning verified for high-value API calls
299
+ - [ ] Sensitive data not stored in shared preferences or external storage
300
+
301
+ ## AI / LLM
302
+
303
+ - [ ] All AI inputs sanitized and validated
304
+ - [ ] System prompt structurally separated from user content (no string concatenation)
305
+ - [ ] Indirect prompt injection: retrieved context (RAG, external data) treated as untrusted
306
+ - [ ] Model outputs validated against JSON schema before acting on them
307
+ - [ ] Output PII scan: no SSN, card numbers, tokens in model responses
308
+ - [ ] AI endpoints rate-limited independently from regular API
309
+ - [ ] Model access logging enabled (user, timestamp, token counts)
310
+ - [ ] Red-team test cases executed and results reviewed
311
+
312
+ ## Payments (PCI DSS 4.0)
313
+
314
+ - [ ] No card numbers, CVV, or PAN in any log, database, cache, or error message
315
+ - [ ] Stripe / payment processor webhook verified (HMAC-SHA256)
316
+ - [ ] PCI scope clearly defined and documented
317
+ - [ ] Payment-adjacent systems network-segmented from non-payment systems
318
+ - [ ] Audit trail maintained for all payment operations
319
+ `;
320
+ tool("security.checklist", "Return the pre-release security checklist, optionally filtered by attack surface (web, api, mobile, ai, infra, payments, all).", ChecklistParams, async (args, _extra) => {
321
+ const { surface } = ChecklistSchema.parse(args);
322
+ if (!surface || surface === "all") {
323
+ return asTextResponse(CHECKLIST_ALL);
324
+ }
325
+ // Extract the relevant section
326
+ const sectionMap = {
327
+ web: "## Web / Frontend",
328
+ api: "## API",
329
+ infra: "## Infrastructure / Cloud",
330
+ mobile: "## Mobile",
331
+ ai: "## AI / LLM",
332
+ payments: "## Payments (PCI DSS 4.0)"
333
+ };
334
+ const header = sectionMap[surface];
335
+ const lines = CHECKLIST_ALL.split("\n");
336
+ const start = lines.findIndex((l) => l === header);
337
+ if (start === -1) {
338
+ return asTextResponse(CHECKLIST_ALL);
339
+ }
340
+ // Include "All Surfaces" section + the requested section
341
+ const allSurfacesEnd = lines.findIndex((l, i) => i > 0 && l.startsWith("## ") && l !== "## All Surfaces");
342
+ const allSurfaces = lines.slice(0, allSurfacesEnd).join("\n");
343
+ const sectionEnd = lines.findIndex((l, i) => i > start + 1 && l.startsWith("## "));
344
+ const section = lines.slice(start, sectionEnd === -1 ? undefined : sectionEnd).join("\n");
345
+ return asTextResponse(`# Pre-Release Security Checklist (${surface})\n\n${allSurfaces}\n\n${section}`);
346
+ });
347
+ // ---------------------------------------------------------------------------
348
+ // New tool: security.generate_policy
349
+ // ---------------------------------------------------------------------------
350
+ const GeneratePolicyParams = {
351
+ surfaces: z.array(z.enum(["web", "api", "mobile", "ai", "infra"])).optional().describe("Active surfaces in your project. Determines which gate requirements are included."),
352
+ cloud: z.enum(["gcp", "aws", "azure", "multi", "none"]).optional()
353
+ .describe("Primary cloud provider. Adjusts cloud-specific evidence expectations.")
354
+ };
355
+ const GeneratePolicySchema = z.object(GeneratePolicyParams);
356
+ tool("security.generate_policy", "Generate a security-policy.json for your project based on your active surfaces and cloud provider. Save the output to .mcp/policies/security-policy.json.", GeneratePolicyParams, async (args, _extra) => {
357
+ const { surfaces, cloud } = GeneratePolicySchema.parse(args);
358
+ const activeSurfaces = surfaces ?? ["web", "api", "infra"];
359
+ const requirements = [
360
+ { id: "ZERO_TRUST", type: "gate", evidence: ["deny_by_default_authz", "service_to_service_auth"] },
361
+ { id: "SECRET_MANAGER_ONLY", type: "gate", evidence: ["no_hardcoded_secrets", "secret_manager_refs"] },
362
+ { id: "TLS_13", type: "gate", evidence: ["tls_config_verified"] }
363
+ ];
364
+ if (activeSurfaces.includes("web") || activeSurfaces.includes("api")) {
365
+ requirements.push({ id: "CSP_NO_INLINE", type: "gate", evidence: ["security_headers_present"] });
366
+ requirements.push({ id: "CSRF", type: "gate", evidence: ["csrf_protection_present", "csrf_tests_present"] });
367
+ requirements.push({ id: "SSRF", type: "gate", evidence: ["ssrf_guard_present", "ssrf_tests_present"] });
368
+ }
369
+ if (activeSurfaces.includes("mobile")) {
370
+ requirements.push({
371
+ id: "MOBILE_MASVS",
372
+ type: "gate",
373
+ evidence: ["ios_ats_strict", "android_nsc_strict", "release_not_debuggable"]
374
+ });
375
+ }
376
+ if (activeSurfaces.includes("ai")) {
377
+ requirements.push({
378
+ id: "AI_BOUNDED_OUTPUTS",
379
+ type: "gate",
380
+ evidence: ["json_schema_validation", "tool_allowlist_router"]
381
+ });
382
+ }
383
+ const onChanges = ["src/**", "api/**"];
384
+ if (activeSurfaces.includes("infra"))
385
+ onChanges.push("infra/**", "terraform/**", "k8s/**");
386
+ if (activeSurfaces.includes("mobile"))
387
+ onChanges.push("ios/**", "android/**");
388
+ if (activeSurfaces.includes("ai"))
389
+ onChanges.push("ai/**");
390
+ const policy = {
391
+ name: "security-policy",
392
+ version: "1.0.0",
393
+ required_checks: {
394
+ secrets_scan: { severity_block: ["HIGH", "CRITICAL"] },
395
+ dependency_scan: { severity_block: ["CRITICAL"] },
396
+ sast: { severity_block: ["CRITICAL"] },
397
+ ...(activeSurfaces.includes("infra") ? { iac_scan: { severity_block: ["HIGH", "CRITICAL"] } } : {})
398
+ },
399
+ requirements,
400
+ artifacts_required: [
401
+ {
402
+ pattern: "security/threat-models/*.md",
403
+ on_changes: onChanges
404
+ }
405
+ ],
406
+ exceptions: {
407
+ require_ticket: true,
408
+ approval_roles: ["SecurityLead", "GRC", "CTO"]
409
+ },
410
+ _meta: {
411
+ generated_by: "security-mcp",
412
+ surfaces: activeSurfaces,
413
+ cloud: cloud ?? "unspecified"
414
+ }
415
+ };
416
+ const comment = "// Save this to .mcp/policies/security-policy.json and customize as needed.\n" +
417
+ "// See https://github.com/AbrahamOO/security-mcp for full documentation.\n\n";
418
+ return asTextResponse(comment + JSON.stringify(policy, null, 2));
419
+ });
420
+ // ---------------------------------------------------------------------------
421
+ // MCP Prompts capability
422
+ // ---------------------------------------------------------------------------
423
+ server.prompt("security-engineer", "Activate the security-mcp system prompt. Sets up the model as an elite, threat-informed security engineer applying OWASP, MITRE ATT&CK, NIST 800-53, Zero Trust, PCI DSS, SOC 2, and ISO 27001 to every code and architecture decision.", async () => ({
424
+ messages: [
425
+ {
426
+ role: "user",
427
+ content: {
428
+ type: "text",
429
+ text: SECURITY_PROMPT
430
+ }
431
+ }
432
+ ]
433
+ }));
434
+ server.prompt("threat-model-template", "Generate a blank STRIDE + PASTA + MITRE ATT&CK threat model template for a feature.", { feature: z.string().describe("Name or brief description of the feature to threat-model.") }, async ({ feature }) => ({
435
+ messages: [
436
+ {
437
+ role: "user",
438
+ content: {
439
+ type: "text",
440
+ text: `You are a principal security engineer. Produce a complete, filled-out STRIDE + PASTA + ` +
441
+ `MITRE ATT&CK threat model for the following feature:\n\n**${feature}**\n\n` +
442
+ `Use the Section 22 output format from the security-mcp system prompt: ` +
443
+ `Threat Model, Controls (preventive/detective/corrective), Compliance Mapping, ` +
444
+ `Residual Risks, and a Security Checklist. Be specific and actionable.`
445
+ }
446
+ }
447
+ ]
448
+ }));
449
+ // ---------------------------------------------------------------------------
450
+ // Server startup
451
+ // ---------------------------------------------------------------------------
452
+ export async function main() {
453
+ const transport = new StdioServerTransport();
454
+ await server.connect(transport);
455
+ }
456
+ // Only auto-start when this file is the direct entry point (not imported by CLI)
457
+ const isMain = process.argv[1]?.endsWith("server.js") || process.argv[1]?.endsWith("server.ts");
458
+ if (isMain) {
459
+ main().catch((err) => {
460
+ console.error("MCP server crashed:", err);
461
+ process.exit(1);
462
+ });
463
+ }
@@ -0,0 +1,9 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ const ROOT = process.cwd();
4
+ export async function readFileSafe(relPath) {
5
+ const p = path.resolve(ROOT, relPath);
6
+ if (!p.startsWith(ROOT))
7
+ throw new Error("Path traversal blocked");
8
+ return await readFile(p, "utf8");
9
+ }
@@ -0,0 +1,41 @@
1
+ import fg from "fast-glob";
2
+ import { readFileSafe } from "./fs.js";
3
+ const MAX_PREVIEW_LEN = 240;
4
+ function isHit(line, query, re) {
5
+ return re ? re.test(line) : line.includes(query);
6
+ }
7
+ function scanLines(file, lines, opts, re, matches) {
8
+ for (let i = 0; i < lines.length; i++) {
9
+ if (matches.length >= opts.maxMatches)
10
+ return;
11
+ const line = lines[i];
12
+ if (!isHit(line, opts.query, re))
13
+ continue;
14
+ matches.push({
15
+ file,
16
+ line: i + 1,
17
+ preview: line.slice(0, MAX_PREVIEW_LEN)
18
+ });
19
+ }
20
+ }
21
+ export async function searchRepo(opts) {
22
+ const files = await fg(["**/*.*"], {
23
+ dot: true,
24
+ ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"]
25
+ });
26
+ const re = opts.isRegex ? new RegExp(opts.query, "i") : null;
27
+ const matches = [];
28
+ for (const file of files) {
29
+ if (matches.length >= opts.maxMatches)
30
+ break;
31
+ let text = "";
32
+ try {
33
+ text = await readFileSafe(file);
34
+ }
35
+ catch {
36
+ continue;
37
+ }
38
+ scanLines(file, text.split("\n"), opts, re, matches);
39
+ }
40
+ return matches;
41
+ }
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "security-mcp",
3
+ "version": "1.0.0",
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
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "security-mcp contributors",
8
+ "homepage": "https://github.com/AbrahamOO/security-mcp#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/AbrahamOO/security-mcp.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/AbrahamOO/security-mcp/issues"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "security",
19
+ "claude-code",
20
+ "cursor",
21
+ "copilot",
22
+ "codex",
23
+ "replit",
24
+ "owasp",
25
+ "devsecops",
26
+ "security-gate",
27
+ "ai-security",
28
+ "threat-model",
29
+ "zero-trust",
30
+ "nist",
31
+ "mitre",
32
+ "mitre-attack",
33
+ "sast",
34
+ "supply-chain",
35
+ "pci-dss",
36
+ "soc2",
37
+ "iso27001",
38
+ "security-review",
39
+ "code-review",
40
+ "llm-security",
41
+ "model-context-protocol"
42
+ ],
43
+ "bin": {
44
+ "security-mcp": "./dist/cli/index.js"
45
+ },
46
+ "files": [
47
+ "dist/",
48
+ "prompts/",
49
+ "skills/",
50
+ "defaults/",
51
+ "README.md",
52
+ "LICENSE"
53
+ ],
54
+ "scripts": {
55
+ "build": "tsc -p tsconfig.json",
56
+ "prepublishOnly": "npm run build",
57
+ "start": "node dist/cli/index.js serve",
58
+ "mcp:server": "node dist/mcp/server.js",
59
+ "ci:pr-gate": "node dist/ci/pr-gate.js"
60
+ },
61
+ "dependencies": {
62
+ "@modelcontextprotocol/sdk": "^1.26.0",
63
+ "execa": "^9.5.2",
64
+ "fast-glob": "^3.3.3",
65
+ "picomatch": "^3.0.1",
66
+ "zod": "^3.24.1"
67
+ },
68
+ "devDependencies": {
69
+ "@types/node": "^22.13.5",
70
+ "@types/picomatch": "^2.3.4",
71
+ "typescript": "^5.7.3"
72
+ },
73
+ "engines": {
74
+ "node": ">=20"
75
+ }
76
+ }