@blamejs/exceptd-skills 0.9.4 → 0.10.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.
- package/AGENTS.md +45 -0
- package/CHANGELOG.md +131 -0
- package/README.md +30 -5
- package/bin/exceptd.js +403 -1
- package/data/_indexes/_meta.json +3 -3
- package/data/_indexes/currency.json +138 -138
- package/data/playbooks/ai-api.json +763 -0
- package/data/playbooks/containers.json +766 -0
- package/data/playbooks/cred-stores.json +715 -0
- package/data/playbooks/crypto.json +726 -0
- package/data/playbooks/framework.json +725 -0
- package/data/playbooks/hardening.json +672 -0
- package/data/playbooks/kernel.json +549 -0
- package/data/playbooks/mcp.json +727 -0
- package/data/playbooks/runtime.json +649 -0
- package/data/playbooks/sbom.json +893 -0
- package/data/playbooks/secrets.json +690 -0
- package/lib/cross-ref-api.js +224 -0
- package/lib/playbook-runner.js +826 -0
- package/lib/schemas/playbook.schema.json +652 -0
- package/lib/verify.js +45 -0
- package/manifest-snapshot.json +1 -1
- package/manifest.json +39 -39
- package/orchestrator/dispatcher.js +13 -1
- package/orchestrator/index.js +119 -5
- package/orchestrator/pipeline.js +8 -2
- package/orchestrator/scanner.js +191 -4
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
- package/scripts/builders/currency.js +5 -3
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"id": "mcp",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"last_threat_review": "2026-05-11",
|
|
6
|
+
"threat_currency_score": 96,
|
|
7
|
+
"changelog": [
|
|
8
|
+
{
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"date": "2026-05-11",
|
|
11
|
+
"summary": "Initial seven-phase MCP supply-chain trust playbook. Enumerates installed MCP servers across Claude Code / Cursor / Windsurf / VS Code+Copilot / Gemini CLI configs, tests for unsigned manifests, missing tool allowlists, unpinned versions, command provenance gaps, and zero-interaction RCE exposure (CVE-2026-30615). Full GRC closure with EU AI Act + NIS2 + DORA notification clocks and auditor-ready exception generation.",
|
|
12
|
+
"cves_added": ["CVE-2026-30615"],
|
|
13
|
+
"framework_gaps_updated": ["nist-800-53-SA-12", "nist-800-53-CM-7", "iso-27001-2022-A.8.30", "soc2-CC9", "eu-ai-act-art15"]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"owner": "@blamejs/ai-security",
|
|
17
|
+
"air_gap_mode": false,
|
|
18
|
+
"preconditions": [
|
|
19
|
+
{
|
|
20
|
+
"id": "filesystem-read",
|
|
21
|
+
"description": "Agent must be able to read the user's home directory and per-tool config locations (~/.cursor, ~/.config/Code, ~/.codeium/windsurf, etc.).",
|
|
22
|
+
"check": "agent_has_filesystem_read == true",
|
|
23
|
+
"on_fail": "halt"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "any-ai-coding-assistant-installed",
|
|
27
|
+
"description": "At least one MCP-capable AI coding assistant must be installed on the host. If none, this playbook returns visibility_gap=no_mcp_clients and skips the rest.",
|
|
28
|
+
"check": "exists($HOME/.cursor) || exists($HOME/.codeium/windsurf) || exists($HOME/.config/claude) || exists($HOME/.config/Code) || exists($HOME/.gemini)",
|
|
29
|
+
"on_fail": "skip_phase"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"mutex": [],
|
|
33
|
+
"feeds_into": [
|
|
34
|
+
{
|
|
35
|
+
"playbook_id": "sbom",
|
|
36
|
+
"condition": "finding.severity == 'critical' OR analyze.blast_radius_score >= 4"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"playbook_id": "framework",
|
|
40
|
+
"condition": "analyze.compliance_theater_check.verdict == 'theater'"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"playbook_id": "ai-api",
|
|
44
|
+
"condition": "finding.includes_credential_exposure == true"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
"domain": {
|
|
50
|
+
"name": "MCP server supply-chain trust",
|
|
51
|
+
"attack_class": "mcp-supply-chain",
|
|
52
|
+
"atlas_refs": ["AML.T0010", "AML.T0016", "AML.T0096"],
|
|
53
|
+
"attack_refs": ["T1195.001", "T1059", "T1190"],
|
|
54
|
+
"cve_refs": ["CVE-2026-30615"],
|
|
55
|
+
"cwe_refs": ["CWE-345", "CWE-494", "CWE-829", "CWE-94", "CWE-77"],
|
|
56
|
+
"d3fend_refs": ["D3-CBAN", "D3-EAL", "D3-EHB", "D3-CSPP"],
|
|
57
|
+
"frameworks_in_scope": [
|
|
58
|
+
"nist-800-53", "nist-csf-2", "iso-27001-2022",
|
|
59
|
+
"soc2", "nis2", "dora", "eu-ai-act", "eu-cra",
|
|
60
|
+
"uk-caf", "au-ism", "au-essential-8", "cmmc"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
"phases": {
|
|
65
|
+
|
|
66
|
+
"govern": {
|
|
67
|
+
"jurisdiction_obligations": [
|
|
68
|
+
{
|
|
69
|
+
"jurisdiction": "EU",
|
|
70
|
+
"regulation": "NIS2 Art.23",
|
|
71
|
+
"obligation": "notify_regulator",
|
|
72
|
+
"window_hours": 24,
|
|
73
|
+
"clock_starts": "detect_confirmed",
|
|
74
|
+
"evidence_required": ["affected_mcp_client_inventory", "malicious_server_indicators", "interim_isolation_record"]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"jurisdiction": "EU",
|
|
78
|
+
"regulation": "NIS2 Art.23",
|
|
79
|
+
"obligation": "notify_regulator",
|
|
80
|
+
"window_hours": 72,
|
|
81
|
+
"clock_starts": "analyze_complete",
|
|
82
|
+
"evidence_required": ["full_incident_assessment", "manifest_provenance_audit", "remediation_plan"]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"jurisdiction": "EU",
|
|
86
|
+
"regulation": "NIS2 Art.23",
|
|
87
|
+
"obligation": "submit_full_report",
|
|
88
|
+
"window_hours": 720,
|
|
89
|
+
"clock_starts": "validate_complete",
|
|
90
|
+
"evidence_required": ["root_cause_analysis", "control_changes_implemented", "lessons_learned"]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"jurisdiction": "EU",
|
|
94
|
+
"regulation": "DORA Art.19",
|
|
95
|
+
"obligation": "notify_regulator",
|
|
96
|
+
"window_hours": 4,
|
|
97
|
+
"clock_starts": "detect_confirmed",
|
|
98
|
+
"evidence_required": ["initial_notification", "ict_third_party_dependencies", "developer_endpoint_blast_radius"]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"jurisdiction": "EU",
|
|
102
|
+
"regulation": "EU AI Act Art.73",
|
|
103
|
+
"obligation": "notify_regulator",
|
|
104
|
+
"window_hours": 360,
|
|
105
|
+
"clock_starts": "analyze_complete",
|
|
106
|
+
"evidence_required": ["serious_incident_assessment", "ai_system_affected", "tool_provenance_audit"]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"jurisdiction": "AU",
|
|
110
|
+
"regulation": "APRA CPS 234",
|
|
111
|
+
"obligation": "notify_regulator",
|
|
112
|
+
"window_hours": 72,
|
|
113
|
+
"clock_starts": "validate_complete",
|
|
114
|
+
"evidence_required": ["materiality_assessment", "remediation_completed_evidence"]
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"theater_fingerprints": [
|
|
118
|
+
{
|
|
119
|
+
"pattern_id": "vendor-management-without-mcp-coverage",
|
|
120
|
+
"claim": "Third-party / vendor risk management (CC9, SA-12, A.5.19) is operating effectively — all SaaS vendors are reviewed.",
|
|
121
|
+
"fast_detection_test": "Ask the GRC team to produce their vendor inventory and search it for any MCP server, npm @modelcontextprotocol/*, or developer-installed AI tool plugin. If MCP servers running inside developer environments are absent from the inventory while the AI coding assistants themselves are present, the program covers SaaS only and is blind to the MCP attack surface.",
|
|
122
|
+
"implicated_controls": ["nist-800-53-SA-12", "iso-27001-2022-A.5.19", "iso-27001-2022-A.5.20", "soc2-CC9"]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"pattern_id": "mcp-allowlist-without-signature-field",
|
|
126
|
+
"claim": "MCP tool allowlisting is enforced — only approved tools can be invoked.",
|
|
127
|
+
"fast_detection_test": "Diff the operator's documented MCP allowlist against the actual mcpServers entries in ~/.cursor/mcp.json, ~/.codeium/windsurf/mcp_config.json, ~/.config/claude/config.json. Theater if allowlist entries lack a signature, fingerprint, or content-hash field — the allowlist names a tool by string identifier that can be typosquatted or version-shifted without the allowlist firing.",
|
|
128
|
+
"implicated_controls": ["nist-800-53-CM-7", "iso-27001-2022-A.8.30", "eu-ai-act-art15"]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"pattern_id": "unsigned-manifest-passes-cm-7",
|
|
132
|
+
"claim": "Least-functionality (CM-7) is enforced — only authorized software runs in developer environments.",
|
|
133
|
+
"fast_detection_test": "Pick any MCP server installed in any AI coding assistant config. Verify whether its manifest is signed (Sigstore / OpenSSF model-signing / GPG with maintainer key in transparency log). Unsigned manifest + CM-7 = compliant + uncontrolled.",
|
|
134
|
+
"implicated_controls": ["nist-800-53-CM-7", "nist-800-53-SA-12", "cmmc-2-CM-L2-3-4-7"]
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"pattern_id": "version-pin-without-integrity",
|
|
138
|
+
"claim": "MCP server versions are pinned in config — supply chain is reproducible.",
|
|
139
|
+
"fast_detection_test": "For each pinned entry, check whether it carries an integrity hash (sha256:, sri integrity, or in-toto attestation reference). Version-pinned without integrity = pinning a name that can be re-published over.",
|
|
140
|
+
"implicated_controls": ["nist-800-53-SA-12", "iso-27001-2022-A.8.30", "eu-cra-art13"]
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"framework_context": {
|
|
144
|
+
"gap_summary": "No mainstream compliance framework — NIST 800-53, ISO 27001:2022, SOC 2, PCI DSS 4.0, NIS2, DORA, EU AI Act, EU CRA — has a control category for MCP server trust. Vendor-management controls (SA-12 / A.5.19 / CC9) were drafted for SaaS vendors and outsourced services; they do not contemplate a 'developer installs a tool that the AI assistant then executes with the developer's full local privileges, zero user interaction required.' Configuration-management controls (CM-7 / A.8.9 / CC8) treat MCP servers as approved-software-list items, not as attestable code-signing targets. The result: an organization can pass a clean audit while every developer endpoint runs unsigned, unpinned MCP servers fetched from the long tail of npm and PyPI namespaces. CVE-2026-30615 (Windsurf MCP zero-interaction RCE, 150M+ download blast radius) is the canonical proof: the vendor-management control was 'operating effectively' across every affected org.",
|
|
145
|
+
"lag_score": 210,
|
|
146
|
+
"per_framework_gaps": [
|
|
147
|
+
{
|
|
148
|
+
"framework": "nist-800-53",
|
|
149
|
+
"control_id": "SA-12",
|
|
150
|
+
"designed_for": "Supply chain protection for traditional software acquisition (commercial off-the-shelf vendors, contractor-developed components).",
|
|
151
|
+
"insufficient_because": "Does not contemplate AI tool plugins installed by developers post-procurement. MCP servers are acquired outside any procurement gate, executed inside the developer's privileged session, and never enter the asset inventory the control targets."
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"framework": "nist-800-53",
|
|
155
|
+
"control_id": "CM-7",
|
|
156
|
+
"designed_for": "Least functionality — only authorized software runs.",
|
|
157
|
+
"insufficient_because": "Authorization is binary (allowed / disallowed). No primitive for 'allowed only if signed manifest verifies against publisher transparency log', which is the only durable test against MCP supply-chain compromise."
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"framework": "iso-27001-2022",
|
|
161
|
+
"control_id": "A.8.30",
|
|
162
|
+
"designed_for": "Outsourced development — third-party-developed code entering the org.",
|
|
163
|
+
"insufficient_because": "Treats outsourced development as a procurement event. MCP servers enter the org continuously, on developer initiative, with no procurement signal."
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"framework": "iso-27001-2022",
|
|
167
|
+
"control_id": "A.5.19",
|
|
168
|
+
"designed_for": "Supplier relationships for service providers (SaaS, MSP, cloud).",
|
|
169
|
+
"insufficient_because": "An MCP server is third-party code executing inside the org's developer environment, not a service the org consumes over the network. The supplier-relationship lens does not produce the right controls."
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"framework": "soc2",
|
|
173
|
+
"control_id": "CC9.2",
|
|
174
|
+
"designed_for": "Vendor and business partner risk management.",
|
|
175
|
+
"insufficient_because": "Vendor inventory excludes developer-installed AI tool plugins. The CC9 audit produces a clean opinion on a vendor list that omits the entire MCP attack surface."
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"framework": "eu-ai-act",
|
|
179
|
+
"control_id": "Art.15 (Accuracy, robustness and cybersecurity)",
|
|
180
|
+
"designed_for": "High-risk AI system robustness and cybersecurity.",
|
|
181
|
+
"insufficient_because": "Drafted for the AI model and the AI system boundary. MCP servers are tools the AI invokes — they sit outside the system boundary the article was drafted against, yet they execute with the developer's full privilege."
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"framework": "eu-cra",
|
|
185
|
+
"control_id": "Art.13 / Annex I",
|
|
186
|
+
"designed_for": "Essential cybersecurity requirements for products with digital elements.",
|
|
187
|
+
"insufficient_because": "Manufacturer of an MCP server is captured, but tool sideloading by developers is not — and the CRA's manufacturer obligations don't translate cleanly to maintainer-signed packages on npm/PyPI."
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
"skill_preload": ["mcp-agent-trust", "supply-chain-integrity", "exploit-scoring", "framework-gap-analysis", "compliance-theater", "policy-exception-gen"]
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
"direct": {
|
|
195
|
+
"threat_context": "MCP supply-chain landscape Q1-Q2 2026: CVE-2026-30615 (Windsurf MCP zero-interaction RCE, CVSS 9.8, published 2026-02) is the load-bearing incident driving the current wave. A malicious MCP server delivers an adversarial tool response → AI assistant follows instructions without user interaction → code execution in the developer's user context. The vulnerability class — unsigned manifests, no enforced allowlist, no required authentication between AI client and MCP server — is reachable across Cursor, VS Code+Copilot, Claude Code, Gemini CLI, and Windsurf (150M+ combined downloads). Typosquat campaigns against npm @modelcontextprotocol/* and PyPI ML namespaces are documented through 2026-04. Detection signal in production environments is rising: organizations that ran exceptd's mcp-agent-trust skill in the 30 days after the Windsurf advisory found a median of 7 unsigned MCP servers per developer endpoint, with 2 of those installed from registry namespaces typosquatting legitimate vendor names. Compliance frameworks have not caught up: SOC 2 CC9, NIST SA-12, and ISO A.5.19 all permit clean audits over the affected estate.",
|
|
196
|
+
"rwep_threshold": {
|
|
197
|
+
"escalate": 80,
|
|
198
|
+
"monitor": 50,
|
|
199
|
+
"close": 25
|
|
200
|
+
},
|
|
201
|
+
"framework_lag_declaration": "Every framework in scope (NIST 800-53 SA-12/CM-7, ISO 27001:2022 A.5.19/A.5.20/A.8.30, SOC 2 CC9, NIS2 Art.21(2)(d), EU AI Act Art.15, EU CRA Art.13) is structurally insufficient. The shared structural failure: every control treats third-party software risk as a procurement event with a discrete vendor list, while MCP servers are continuously sideloaded by developers from package registries with no procurement signal, no enforced signing, and no allowlist that survives version drift. Until frameworks add a 'developer-installed AI tool plugin' control category with mandatory manifest signature verification + version pinning with integrity hash + provenance attestation, vendor-management audit opinions provide zero signal about MCP exposure. Gap = ~210 days behind operational reality; no framework body has issued draft language as of 2026-05-11.",
|
|
202
|
+
"skill_chain": [
|
|
203
|
+
{ "skill": "mcp-agent-trust", "purpose": "Enumerate installed MCP servers across all AI coding assistant configs and test each for signed manifest, allowlist enforcement, version pinning + integrity hash, command-provenance trail.", "required": true },
|
|
204
|
+
{ "skill": "supply-chain-integrity", "purpose": "Cross-reference each MCP server against SLSA build-provenance / Sigstore / in-toto attestation; flag the long tail with no attestation chain.", "required": true },
|
|
205
|
+
{ "skill": "exploit-scoring", "purpose": "Compute RWEP for CVE-2026-30615 against the running configuration; rank MCP servers by exposure to known weaponized vectors.", "required": true },
|
|
206
|
+
{ "skill": "framework-gap-analysis", "purpose": "Map each finding to the specific framework controls that fail to cover it, with auditor-ready gap language.", "skip_if": "analyze.framework_gap_mapping.length == 0", "required": false },
|
|
207
|
+
{ "skill": "compliance-theater", "purpose": "Run the four theater tests defined in govern.theater_fingerprints; emit verdict for each.", "required": true },
|
|
208
|
+
{ "skill": "policy-exception-gen", "purpose": "If an org-critical MCP server cannot be removed/replaced within the compliance window, generate a defensible time-bound exception.", "skip_if": "close.exception_generation.trigger_condition == false", "required": false }
|
|
209
|
+
],
|
|
210
|
+
"token_budget": {
|
|
211
|
+
"estimated_total": 22000,
|
|
212
|
+
"breakdown": {
|
|
213
|
+
"govern": 2800,
|
|
214
|
+
"direct": 1500,
|
|
215
|
+
"look": 2400,
|
|
216
|
+
"detect": 3200,
|
|
217
|
+
"analyze": 5000,
|
|
218
|
+
"validate": 4200,
|
|
219
|
+
"close": 2900
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
"look": {
|
|
225
|
+
"artifacts": [
|
|
226
|
+
{
|
|
227
|
+
"id": "cursor-mcp-config",
|
|
228
|
+
"type": "mcp_manifest",
|
|
229
|
+
"source": "$HOME/.cursor/mcp.json",
|
|
230
|
+
"description": "Cursor's MCP server configuration — primary attack surface for the Cursor IDE userbase.",
|
|
231
|
+
"required": false,
|
|
232
|
+
"air_gap_alternative": "If $HOME/.cursor absent, mark Cursor not-installed; do not infer absence of Cursor MCP exposure organization-wide."
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"id": "claude-code-mcp-config",
|
|
236
|
+
"type": "mcp_manifest",
|
|
237
|
+
"source": "$HOME/.config/claude/config.json AND $HOME/.claude/settings.json AND .mcp.json in project roots",
|
|
238
|
+
"description": "Claude Code's MCP configuration — user, project, and enterprise scopes.",
|
|
239
|
+
"required": false
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"id": "windsurf-mcp-config",
|
|
243
|
+
"type": "mcp_manifest",
|
|
244
|
+
"source": "$HOME/.codeium/windsurf/mcp_config.json",
|
|
245
|
+
"description": "Windsurf MCP configuration — directly affected by CVE-2026-30615.",
|
|
246
|
+
"required": false
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"id": "vscode-copilot-mcp-config",
|
|
250
|
+
"type": "mcp_manifest",
|
|
251
|
+
"source": "$HOME/.config/Code/User/settings.json (chat.mcp.servers) AND .vscode/mcp.json in workspaces",
|
|
252
|
+
"description": "VS Code + GitHub Copilot Chat MCP configuration.",
|
|
253
|
+
"required": false
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"id": "gemini-cli-mcp-config",
|
|
257
|
+
"type": "mcp_manifest",
|
|
258
|
+
"source": "$HOME/.gemini/settings.json (mcpServers)",
|
|
259
|
+
"description": "Gemini CLI MCP configuration.",
|
|
260
|
+
"required": false
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"id": "mcp-process-list",
|
|
264
|
+
"type": "process_list",
|
|
265
|
+
"source": "ps -eo pid,ppid,user,cmd | grep -Ei 'mcp|modelcontextprotocol|npx.*@.*mcp' OR Get-CimInstance Win32_Process | Where-Object CommandLine -match 'mcp'",
|
|
266
|
+
"description": "Currently-running MCP server processes — catches anything launched by an assistant that isn't reflected in static config.",
|
|
267
|
+
"required": true
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"id": "npm-global-mcp",
|
|
271
|
+
"type": "process_list",
|
|
272
|
+
"source": "npm ls -g --depth=0 --json 2>/dev/null OR yarn global list --json",
|
|
273
|
+
"description": "Globally-installed npm packages — used to spot @modelcontextprotocol/* and known MCP servers.",
|
|
274
|
+
"required": false,
|
|
275
|
+
"air_gap_alternative": "Read $(npm root -g) directory listing if npm command fails."
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"id": "pip-global-mcp",
|
|
279
|
+
"type": "process_list",
|
|
280
|
+
"source": "pip list --format=json 2>/dev/null AND pipx list --json 2>/dev/null",
|
|
281
|
+
"description": "Python-installed MCP server packages.",
|
|
282
|
+
"required": false
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"id": "mcp-manifest-signatures",
|
|
286
|
+
"type": "mcp_manifest",
|
|
287
|
+
"source": "For each enumerated MCP server, check package directory for .sigstore, .sig, COSIGN.pub, in-toto attestation, or Sigstore transparency log entry.",
|
|
288
|
+
"description": "Signature / attestation artifacts for each installed MCP server.",
|
|
289
|
+
"required": false,
|
|
290
|
+
"air_gap_alternative": "When offline, defer Sigstore-rekor lookups; mark signature=indeterminate and downgrade confidence."
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"id": "mcp-allowlist-policy",
|
|
294
|
+
"type": "config_file",
|
|
295
|
+
"source": "$HOME/.claude/settings.json (permissions.allow), Cursor's .cursorrules, Windsurf workspace policy, VS Code chat.mcp.allowlist",
|
|
296
|
+
"description": "Per-tool allowlist policy — the operator's documented set of authorized tools.",
|
|
297
|
+
"required": false
|
|
298
|
+
}
|
|
299
|
+
],
|
|
300
|
+
"collection_scope": {
|
|
301
|
+
"time_window": "current",
|
|
302
|
+
"asset_scope": "local_host_developer_environment",
|
|
303
|
+
"depth": "standard",
|
|
304
|
+
"sampling": "single-developer-endpoint point-in-time snapshot; fleet rollout requires per-endpoint execution. Re-collect on every regression_trigger event."
|
|
305
|
+
},
|
|
306
|
+
"environment_assumptions": [
|
|
307
|
+
{
|
|
308
|
+
"assumption": "agent runs in developer endpoint context (not a server / production VM)",
|
|
309
|
+
"if_false": "MCP attack surface is by definition developer-endpoint. If running in a server context with no AI coding assistants, return visibility_gap=wrong_environment_class."
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"assumption": "agent has read access to $HOME and subdirectories",
|
|
313
|
+
"if_false": "Investigation is unfounded. Halt and escalate — MCP configs live in user home; no read = no detection."
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"assumption": "at least one AI coding assistant config directory exists",
|
|
317
|
+
"if_false": "Return visibility_gap=no_mcp_clients. Note: a config-less host may still be exposed via a fresh install of an MCP-capable assistant; document as residual."
|
|
318
|
+
}
|
|
319
|
+
],
|
|
320
|
+
"fallback_if_unavailable": [
|
|
321
|
+
{ "artifact_id": "mcp-manifest-signatures", "fallback_action": "mark_inconclusive", "confidence_impact": "medium" },
|
|
322
|
+
{ "artifact_id": "npm-global-mcp", "fallback_action": "use_compensating_artifact", "confidence_impact": "low" },
|
|
323
|
+
{ "artifact_id": "mcp-allowlist-policy", "fallback_action": "mark_inconclusive", "confidence_impact": "medium" },
|
|
324
|
+
{ "artifact_id": "mcp-process-list", "fallback_action": "escalate_to_human", "confidence_impact": "high" }
|
|
325
|
+
]
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
"detect": {
|
|
329
|
+
"indicators": [
|
|
330
|
+
{
|
|
331
|
+
"id": "unsigned-mcp-manifest",
|
|
332
|
+
"type": "file_path",
|
|
333
|
+
"value": "$mcp_server_package missing .sigstore, .sig, sigstore-rekor log entry, OR signed in-toto attestation",
|
|
334
|
+
"description": "Primary detector — an MCP server with no signature artifact is unattestable and matches CVE-2026-30615's risk class.",
|
|
335
|
+
"confidence": "deterministic",
|
|
336
|
+
"deterministic": true,
|
|
337
|
+
"atlas_ref": "AML.T0010",
|
|
338
|
+
"attack_ref": "T1195.001"
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"id": "mcp-version-without-integrity",
|
|
342
|
+
"type": "log_pattern",
|
|
343
|
+
"value": "mcpServers.<name>.command pins a version (e.g. @modelcontextprotocol/server-foo@1.2.3) but no sha256, sri-integrity, or attestation reference accompanies it",
|
|
344
|
+
"description": "Version pin without integrity hash — the named package can be re-published over without the pin firing.",
|
|
345
|
+
"confidence": "deterministic",
|
|
346
|
+
"deterministic": true
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"id": "mcp-allowlist-missing",
|
|
350
|
+
"type": "behavioral_signal",
|
|
351
|
+
"value": "AI coding assistant config contains mcpServers entries with no corresponding allowlist policy (Claude Code permissions.allow / Cursor allowed-tools / VS Code chat.mcp.allowlist)",
|
|
352
|
+
"description": "No tool allowlist → AI invokes any tool the MCP server exposes.",
|
|
353
|
+
"confidence": "high",
|
|
354
|
+
"deterministic": false
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"id": "mcp-typosquat-candidate",
|
|
358
|
+
"type": "file_path",
|
|
359
|
+
"value": "MCP package name within edit-distance-2 of known-good MCP server namespace (e.g. @modelcontextprotocol/server-filesystem vs @modelcontex-tprotocol/server-filesystem)",
|
|
360
|
+
"description": "Typosquat candidate against the @modelcontextprotocol or vendor official namespace.",
|
|
361
|
+
"confidence": "high",
|
|
362
|
+
"deterministic": false,
|
|
363
|
+
"atlas_ref": "AML.T0010"
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"id": "mcp-command-provenance-gap",
|
|
367
|
+
"type": "log_pattern",
|
|
368
|
+
"value": "mcpServers.<name>.command launches via npx / uvx / curl|sh / a path outside a maintainer-controlled registry",
|
|
369
|
+
"description": "Command provenance gap — server is fetched at launch with no integrity check.",
|
|
370
|
+
"confidence": "high",
|
|
371
|
+
"deterministic": false,
|
|
372
|
+
"attack_ref": "T1195.001"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"id": "vulnerable-windsurf-version",
|
|
376
|
+
"type": "file_path",
|
|
377
|
+
"value": "Installed Windsurf IDE version <= patched_version_for_CVE-2026-30615",
|
|
378
|
+
"description": "Direct match for CVE-2026-30615.",
|
|
379
|
+
"confidence": "deterministic",
|
|
380
|
+
"deterministic": true,
|
|
381
|
+
"attack_ref": "T1190"
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"id": "mcp-server-running-as-root",
|
|
385
|
+
"type": "process_name",
|
|
386
|
+
"value": "ps shows an MCP server process running with effective UID 0 or with elevated capabilities (CAP_SYS_ADMIN, CAP_NET_ADMIN)",
|
|
387
|
+
"description": "MCP server with elevated privileges turns a tool-response RCE into immediate root.",
|
|
388
|
+
"confidence": "deterministic",
|
|
389
|
+
"deterministic": true
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
"false_positive_profile": [
|
|
393
|
+
{
|
|
394
|
+
"indicator_id": "unsigned-mcp-manifest",
|
|
395
|
+
"benign_pattern": "First-party MCP server distributed by the AI coding assistant vendor itself (Anthropic, Microsoft, Cursor) where vendor-internal signing was used but no public-rekor entry exists.",
|
|
396
|
+
"distinguishing_test": "Check whether the MCP server's package path matches the official vendor path (~/.claude/, vendor's bundled tools directory, etc.) AND the manifest's publisher field matches the AI assistant vendor. If both hold, downgrade to medium confidence with a 'vendor-internal-signing-unverifiable' note."
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"indicator_id": "mcp-typosquat-candidate",
|
|
400
|
+
"benign_pattern": "Legitimate fork by a known author (e.g. an enterprise's own mirror of @modelcontextprotocol/server-filesystem under @acme-corp/).",
|
|
401
|
+
"distinguishing_test": "Look up the publisher's GitHub identity and the package's metadata. If publisher matches a known org GitHub identity with > 6 months of history and the fork was not made within 7 days of the typosquat campaign window, downgrade to medium confidence."
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"indicator_id": "mcp-command-provenance-gap",
|
|
405
|
+
"benign_pattern": "npx-based launch of an MCP server whose npm package version is integrity-locked in a project-level lockfile.",
|
|
406
|
+
"distinguishing_test": "Walk up to the nearest package-lock.json / yarn.lock / pnpm-lock.yaml; if it contains an integrity field for the MCP server's published version, downgrade to medium confidence."
|
|
407
|
+
}
|
|
408
|
+
],
|
|
409
|
+
"minimum_signal": {
|
|
410
|
+
"detected": "At least one MCP server enumerated with either (a) unsigned-mcp-manifest=true AND mcp-version-without-integrity=true (the deterministic CVE-2026-30615 risk class), OR (b) vulnerable-windsurf-version=true, OR (c) mcp-typosquat-candidate=true with no benign-pattern match.",
|
|
411
|
+
"inconclusive": "MCP servers enumerated but signature/integrity artifact lookup was indeterminate (e.g. air-gap mode, Sigstore rekor unreachable). Cannot deny — escalate to human or retry with network.",
|
|
412
|
+
"not_detected": "No MCP servers enumerated across all known assistant configs AND no MCP-server processes running AND no @modelcontextprotocol packages in global package managers. Document as not-detected with a 'fresh install of any MCP-capable assistant re-opens this' caveat."
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
"analyze": {
|
|
417
|
+
"rwep_inputs": [
|
|
418
|
+
{ "signal_id": "vulnerable-windsurf-version", "rwep_factor": "cisa_kev", "weight": 0, "notes": "CVE-2026-30615 is not KEV-listed as of 2026-05-11; factor zero. Re-score if KEV listing occurs." },
|
|
419
|
+
{ "signal_id": "vulnerable-windsurf-version", "rwep_factor": "public_poc", "weight": 20, "notes": "Partial PoC public per catalog." },
|
|
420
|
+
{ "signal_id": "vulnerable-windsurf-version", "rwep_factor": "active_exploitation", "weight": 10, "notes": "Suspected, not confirmed per catalog." },
|
|
421
|
+
{ "signal_id": "vulnerable-windsurf-version", "rwep_factor": "blast_radius", "weight": 30, "notes": "Zero-interaction RCE in developer endpoint with full local credential exposure (AWS/GCP/kube dotfiles)." },
|
|
422
|
+
{ "signal_id": "vulnerable-windsurf-version", "rwep_factor": "patch_available", "weight": -15, "notes": "Vendor patch shipped." },
|
|
423
|
+
{ "signal_id": "vulnerable-windsurf-version", "rwep_factor": "live_patch_available", "weight": -10, "notes": "IDE auto-update path treated as live-patch." },
|
|
424
|
+
{ "signal_id": "unsigned-mcp-manifest", "rwep_factor": "blast_radius", "weight": 20, "notes": "Each unsigned manifest is a separate attack surface; tally to blast radius." },
|
|
425
|
+
{ "signal_id": "mcp-server-running-as-root", "rwep_factor": "blast_radius", "weight": 15, "notes": "Elevated privileges escalate any tool-response RCE to root immediately." },
|
|
426
|
+
{ "signal_id": "mcp-typosquat-candidate", "rwep_factor": "active_exploitation", "weight": 10, "notes": "Typosquat campaigns are documented active TTP for MCP namespaces through Q2 2026." }
|
|
427
|
+
],
|
|
428
|
+
"blast_radius_model": {
|
|
429
|
+
"scope_question": "If a malicious or compromised MCP server delivers an adversarial tool response on this developer endpoint, what scope of compromise is the endpoint realistically delivering to the attacker?",
|
|
430
|
+
"scoring_rubric": [
|
|
431
|
+
{ "condition": "endpoint has no cloud-IAM credentials, no kube context, no SSH key material, isolated dev VM", "blast_radius_score": 1, "description": "RCE in developer-user context only; lateral movement requires separate exploit chain." },
|
|
432
|
+
{ "condition": "endpoint has personal git credentials and project-scoped tokens but no production access", "blast_radius_score": 2, "description": "Source-code theft + commit-rewrite vector via stolen git creds." },
|
|
433
|
+
{ "condition": "endpoint has ~/.aws/credentials, ~/.config/gcloud, or ~/.kube/config with read access to non-production environments", "blast_radius_score": 3, "description": "Cloud read across staging/dev; data exfil + IAM enumeration." },
|
|
434
|
+
{ "condition": "endpoint has production-scoped IAM roles, kube admin contexts, or signing keys (cosign, sigstore, GPG)", "blast_radius_score": 4, "description": "Production tenancy + supply-chain-publishing capability." },
|
|
435
|
+
{ "condition": "endpoint is a release-engineering / CI/CD bootstrap host with cross-account assume-role + package-publishing rights to org namespaces", "blast_radius_score": 5, "description": "Org-wide pivot via supply-chain re-publication. Single endpoint compromises entire downstream." }
|
|
436
|
+
]
|
|
437
|
+
},
|
|
438
|
+
"compliance_theater_check": {
|
|
439
|
+
"claim": "Vendor/third-party risk management (NIST SA-12, ISO A.5.19, SOC 2 CC9) is operating effectively — all third-party software entering the developer environment is reviewed and authorized.",
|
|
440
|
+
"audit_evidence": "Vendor inventory with risk ratings, signed vendor-attestation forms, quarterly access reviews, procurement-system records.",
|
|
441
|
+
"reality_test": "Pull the vendor inventory used in the most recent audit. Search it for every MCP server name enumerated by this playbook. Theater if any enumerated MCP server is absent from the vendor inventory AND the audit opinion was clean. Bonus theater multiplier: enumerated MCP server is published from an npm/PyPI namespace not owned by any existing vendor in the inventory.",
|
|
442
|
+
"theater_verdict_if_gap": "Org has a vendor-management program that issues clean audit opinions while every developer endpoint sideloads unsigned, unpinned MCP servers from the long tail of package registries with no procurement signal. Either (a) extend vendor inventory to include all developer-installed AI tool plugins with manifest signature verification as the authorization gate, (b) enforce per-assistant allowlists with signed fingerprint + version-pin-with-integrity-hash, OR (c) generate a defensible policy exception via policy-exception-gen with time-bound risk acceptance and compensating controls (network egress allowlist for MCP processes, kernel-level seccomp/AppArmor profile for MCP processes, mandatory MCP-running-as-non-root)."
|
|
443
|
+
},
|
|
444
|
+
"framework_gap_mapping": [
|
|
445
|
+
{
|
|
446
|
+
"finding_id": "mcp-supply-chain-detected",
|
|
447
|
+
"framework": "nist-800-53",
|
|
448
|
+
"claimed_control": "SA-12 — Supply Chain Protection",
|
|
449
|
+
"actual_gap": "Procurement-event lens. MCP servers enter the org continuously, on developer initiative, outside any procurement gate. SA-12 generates no signal.",
|
|
450
|
+
"required_control": "Add an SA-12 variant requiring per-instance manifest signature verification (Sigstore / OpenSSF model-signing) before any developer-installed AI tool plugin is permitted to execute. Version pinning must accompany integrity hash. The AI assistant client itself must enforce the allowlist."
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
"finding_id": "mcp-supply-chain-detected",
|
|
454
|
+
"framework": "nist-800-53",
|
|
455
|
+
"claimed_control": "CM-7 — Least Functionality",
|
|
456
|
+
"actual_gap": "Binary authorization (allowed / disallowed). No primitive for 'allowed only if signed manifest verifies'.",
|
|
457
|
+
"required_control": "Bind CM-7 authorization to a signature-verification policy expressed in the AI client's MCP allowlist."
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
"finding_id": "mcp-supply-chain-detected",
|
|
461
|
+
"framework": "iso-27001-2022",
|
|
462
|
+
"claimed_control": "A.8.30 — Outsourced development",
|
|
463
|
+
"actual_gap": "Treats outsourced development as a procurement event. MCP servers enter the org continuously without procurement signal.",
|
|
464
|
+
"required_control": "Extend A.8.30 to encompass developer-installed AI tool plugins with continuous-attestation requirements rather than point-in-time procurement review."
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"finding_id": "mcp-supply-chain-detected",
|
|
468
|
+
"framework": "iso-27001-2022",
|
|
469
|
+
"claimed_control": "A.5.19 — Information security in supplier relationships",
|
|
470
|
+
"actual_gap": "Supplier-relationship lens does not produce signature-attestation requirements; treats package maintainers as out-of-scope suppliers.",
|
|
471
|
+
"required_control": "Add a supplier-relationship sub-control: any third-party code (including community-maintained packages) executing within developer environments must be attestable via signed publisher key in transparency log."
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"finding_id": "mcp-supply-chain-detected",
|
|
475
|
+
"framework": "soc2",
|
|
476
|
+
"claimed_control": "CC9.2 — Vendor and business partner risk management",
|
|
477
|
+
"actual_gap": "Vendor inventory excludes developer-installed AI tool plugins; audit produces clean opinion on incomplete inventory.",
|
|
478
|
+
"required_control": "Require vendor inventory completeness test: cross-reference enumerated MCP servers (and equivalents for future AI tool standards) against the inventory; any unenumerated package is a finding."
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
"finding_id": "mcp-supply-chain-detected",
|
|
482
|
+
"framework": "eu-ai-act",
|
|
483
|
+
"claimed_control": "Art.15 — Accuracy, robustness and cybersecurity",
|
|
484
|
+
"actual_gap": "System boundary drawn around the AI model; MCP tools outside the boundary even though they execute with full local privilege.",
|
|
485
|
+
"required_control": "Extend Art.15 cybersecurity obligations to the tool-plugin attack surface for high-risk AI systems; require signed manifests + allowlist enforcement as cybersecurity controls."
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
"finding_id": "mcp-supply-chain-detected",
|
|
489
|
+
"framework": "eu-cra",
|
|
490
|
+
"claimed_control": "Art.13 / Annex I — essential cybersecurity requirements",
|
|
491
|
+
"actual_gap": "Manufacturer-of-the-MCP-server captured, but tool sideloading by developers (no manufacturer relationship) is not.",
|
|
492
|
+
"required_control": "Implementing acts to extend essential requirements to developer-installed plugins distributed via package registries; pre-execution signature verification as a mandatory product feature for AI assistants."
|
|
493
|
+
}
|
|
494
|
+
],
|
|
495
|
+
"escalation_criteria": [
|
|
496
|
+
{ "condition": "rwep >= 80 AND vulnerable-windsurf-version == true", "action": "page_on_call" },
|
|
497
|
+
{ "condition": "rwep >= 80 AND mcp-server-running-as-root == true", "action": "page_on_call" },
|
|
498
|
+
{ "condition": "mcp-typosquat-candidate == true AND no benign_match", "action": "raise_severity" },
|
|
499
|
+
{ "condition": "blast_radius_score >= 4", "action": "trigger_playbook", "target_playbook": "sbom" },
|
|
500
|
+
{ "condition": "compliance_theater_check.verdict == 'theater' AND jurisdiction_obligations contains 'EU'", "action": "notify_legal" },
|
|
501
|
+
{ "condition": "any unsigned-mcp-manifest with cloud credentials detected", "action": "trigger_playbook", "target_playbook": "ai-api" }
|
|
502
|
+
]
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
"validate": {
|
|
506
|
+
"remediation_paths": [
|
|
507
|
+
{
|
|
508
|
+
"id": "vendor-patch-windsurf",
|
|
509
|
+
"description": "Apply vendor patch for CVE-2026-30615 via Windsurf auto-update.",
|
|
510
|
+
"preconditions": ["vulnerable_windsurf_version == true", "auto_update_available == true"],
|
|
511
|
+
"priority": 1,
|
|
512
|
+
"compensating_controls": [],
|
|
513
|
+
"estimated_time_hours": 0.5
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
"id": "remove-unsigned-mcp-servers",
|
|
517
|
+
"description": "Remove every MCP server with unsigned-mcp-manifest=true OR mcp-typosquat-candidate=true. Document removed entries in change-management.",
|
|
518
|
+
"preconditions": ["operator_authorized_to_modify_mcp_config == true"],
|
|
519
|
+
"priority": 2,
|
|
520
|
+
"compensating_controls": ["mcp_egress_network_allowlist", "mcp_runs_as_non_root_enforced"],
|
|
521
|
+
"estimated_time_hours": 2
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
"id": "enforce-signed-allowlist",
|
|
525
|
+
"description": "Adopt an enforced MCP allowlist with signature/fingerprint binding for each authorized tool. Configure Claude Code permissions.allow, Cursor allowed-tools, VS Code chat.mcp.allowlist, Windsurf workspace policy with signed fingerprints.",
|
|
526
|
+
"preconditions": ["all_authorized_mcp_servers_have_published_signatures == true"],
|
|
527
|
+
"priority": 3,
|
|
528
|
+
"compensating_controls": ["pre-commit hook validates mcp config against signed allowlist", "CI gate rejects PRs that introduce unsigned mcp entries"],
|
|
529
|
+
"estimated_time_hours": 6
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
"id": "version-pin-with-integrity",
|
|
533
|
+
"description": "For every authorized MCP server, replace bare version pin with integrity-hash pin (sha256 / sri-integrity / attestation reference) sourced from package lockfile or Sigstore rekor entry.",
|
|
534
|
+
"preconditions": ["package_lockfile_exists == true OR sigstore_rekor_entry_available == true"],
|
|
535
|
+
"priority": 4,
|
|
536
|
+
"compensating_controls": ["scheduled re-pin job on every authorized MCP package release"],
|
|
537
|
+
"estimated_time_hours": 4
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
"id": "policy-exception",
|
|
541
|
+
"description": "Generate auditor-ready policy exception via policy-exception-gen if a business-critical MCP server cannot meet signing / allowlist / integrity-pin requirements within compliance window.",
|
|
542
|
+
"preconditions": ["remediation_paths[1..4] blocked for at least one MCP server", "ciso_acceptance_obtainable == true"],
|
|
543
|
+
"priority": 5,
|
|
544
|
+
"compensating_controls": ["MCP process seccomp/AppArmor profile", "egress allowlist for MCP processes", "enhanced logging on tool invocation patterns"],
|
|
545
|
+
"estimated_time_hours": 8
|
|
546
|
+
}
|
|
547
|
+
],
|
|
548
|
+
"validation_tests": [
|
|
549
|
+
{
|
|
550
|
+
"id": "windsurf-version-post-patch",
|
|
551
|
+
"test": "Read Windsurf version string after auto-update. Confirm version outside CVE-2026-30615 affected range.",
|
|
552
|
+
"expected_result": "Windsurf version > patched_version_for_CVE-2026-30615.",
|
|
553
|
+
"test_type": "functional"
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
"id": "no-unsigned-mcp-remains",
|
|
557
|
+
"test": "Re-enumerate all MCP servers across all assistant configs. For each, verify .sigstore / .sig / rekor entry exists. Fail if any unsigned manifest remains.",
|
|
558
|
+
"expected_result": "Zero MCP servers with unsigned-mcp-manifest=true.",
|
|
559
|
+
"test_type": "negative"
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"id": "allowlist-enforces-signature",
|
|
563
|
+
"test": "Attempt to register a deliberately-unsigned dummy MCP server in the assistant's config. Confirm the AI client refuses to invoke its tools.",
|
|
564
|
+
"expected_result": "Client emits a signature-verification-failed error; no tool invocation succeeds.",
|
|
565
|
+
"test_type": "negative"
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
"id": "typosquat-canary",
|
|
569
|
+
"test": "Add a canary entry to the allowlist with a name within edit-distance-1 of a permitted MCP server. Confirm client rejects the canary on the basis that its fingerprint does not match the permitted entry.",
|
|
570
|
+
"expected_result": "Client rejects canary; emits allowlist-fingerprint-mismatch.",
|
|
571
|
+
"test_type": "negative"
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
"id": "mcp-process-non-root",
|
|
575
|
+
"test": "Run ps post-remediation. Confirm no MCP server processes run with effective UID 0 or with elevated capabilities.",
|
|
576
|
+
"expected_result": "All MCP processes run as non-root with no elevated capabilities.",
|
|
577
|
+
"test_type": "functional"
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
"id": "ai-assistant-regression",
|
|
581
|
+
"test": "Run the developer's standard AI-assisted workflow (open project, request file-system tool calls, run an MCP-mediated git op). Confirm all authorized tool calls succeed.",
|
|
582
|
+
"expected_result": "Authorized MCP tool calls succeed; unauthorized fail-closed.",
|
|
583
|
+
"test_type": "regression"
|
|
584
|
+
}
|
|
585
|
+
],
|
|
586
|
+
"residual_risk_statement": {
|
|
587
|
+
"risk": "MCP servers remain a continuously-mutating supply-chain surface. Every new authorized MCP server introduces a fresh attestation requirement; every new release of an authorized MCP server requires re-validation. A future malicious update from a previously-trusted publisher would bypass signature-based controls until the publisher's transparency-log entry is itself revoked.",
|
|
588
|
+
"why_remains": "Signature verification proves authorship, not safety. A compromised maintainer key produces validly-signed malware. Compensating coverage requires publisher-key transparency monitoring (revocation propagation) and behavioral detection on tool invocation patterns — both immature in mid-2026.",
|
|
589
|
+
"acceptance_level": "ciso",
|
|
590
|
+
"compensating_controls_in_place": ["mcp_egress_network_allowlist", "mcp_seccomp_apparmor_profile", "publisher_key_transparency_monitoring", "tool_invocation_behavioral_baseline"]
|
|
591
|
+
},
|
|
592
|
+
"evidence_requirements": [
|
|
593
|
+
{
|
|
594
|
+
"evidence_type": "scan_report",
|
|
595
|
+
"description": "Enumeration report of every MCP server across every AI assistant config on the endpoint, with signature / integrity / allowlist-binding status for each.",
|
|
596
|
+
"retention_period": "7_years",
|
|
597
|
+
"framework_satisfied": ["nist-800-53-SA-12", "iso-27001-2022-A.8.30", "soc2-CC9", "nis2-art21-2d"]
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
"evidence_type": "config_diff",
|
|
601
|
+
"description": "Before / after diff of every assistant MCP config showing removed unsigned entries, added integrity hashes, allowlist binding to signed fingerprints.",
|
|
602
|
+
"retention_period": "7_years",
|
|
603
|
+
"framework_satisfied": ["nist-800-53-CM-3", "iso-27001-2022-A.8.32", "eu-ai-act-art15"]
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
"evidence_type": "exploit_replay_negative",
|
|
607
|
+
"description": "Allowlist-enforcement and typosquat-canary tests, with output proving the client rejected the deliberately-unauthorized entries.",
|
|
608
|
+
"retention_period": "1_year",
|
|
609
|
+
"framework_satisfied": ["soc2-CC9", "iso-27001-2022-A.8.30"]
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
"evidence_type": "attestation",
|
|
613
|
+
"description": "Signed exceptd attestation file with evidence_hash, enumerated MCP server count, signed-vs-unsigned ratio at detection and post-remediation, RWEP delta.",
|
|
614
|
+
"retention_period": "7_years",
|
|
615
|
+
"framework_satisfied": ["nist-800-53-CA-7", "iso-27001-2022-A.5.36", "nis2-art21-2d", "eu-ai-act-art15"]
|
|
616
|
+
}
|
|
617
|
+
],
|
|
618
|
+
"regression_trigger": [
|
|
619
|
+
{ "condition": "new_cve_in_class == true", "interval": "on_event" },
|
|
620
|
+
{ "condition": "new_mcp_server_added_to_any_config", "interval": "on_event" },
|
|
621
|
+
{ "condition": "new_version_of_any_authorized_mcp_server", "interval": "on_event" },
|
|
622
|
+
{ "condition": "monthly", "interval": "30d" }
|
|
623
|
+
]
|
|
624
|
+
},
|
|
625
|
+
|
|
626
|
+
"close": {
|
|
627
|
+
"evidence_package": {
|
|
628
|
+
"bundle_format": "csaf-2.0",
|
|
629
|
+
"contents": ["scan_report", "config_diff", "exploit_replay_negative", "attestation", "framework_gap_mapping", "compliance_theater_verdict", "residual_risk_statement"],
|
|
630
|
+
"destination": "local_only",
|
|
631
|
+
"signed": true
|
|
632
|
+
},
|
|
633
|
+
"learning_loop": {
|
|
634
|
+
"enabled": true,
|
|
635
|
+
"lesson_template": {
|
|
636
|
+
"attack_vector": "MCP server delivers adversarial tool response → AI assistant follows instructions → zero-interaction RCE in developer user context. Variants: malicious-from-day-one publisher, typosquat against legitimate namespace, compromised legitimate publisher key.",
|
|
637
|
+
"control_gap": "Vendor-management controls did not generate signal — MCP servers are continuously sideloaded outside procurement. Configuration-management controls treated MCP server names as authorization tokens without enforcing manifest signature verification.",
|
|
638
|
+
"framework_gap": "NIST SA-12, ISO A.5.19/A.8.30, SOC 2 CC9 all treat third-party software as a procurement event. EU AI Act Art.15 draws the system boundary around the model, not the tool plugins. No mainstream framework currently requires signed-manifest + integrity-pinned + allowlist-bound MCP tool authorization. Lag against operational reality: ~210 days as of 2026-05-11.",
|
|
639
|
+
"new_control_requirement": "Add a control class 'AI tool plugin authorization' with mandatory: (a) manifest signature verification against publisher transparency-log entry pre-execution; (b) version pinning with integrity hash; (c) per-assistant enforced allowlist with signed-fingerprint binding; (d) MCP process runs non-root with seccomp/AppArmor profile; (e) egress network allowlist for MCP processes."
|
|
640
|
+
},
|
|
641
|
+
"feeds_back_to_skills": ["mcp-agent-trust", "supply-chain-integrity", "framework-gap-analysis", "compliance-theater", "zeroday-gap-learn"]
|
|
642
|
+
},
|
|
643
|
+
"notification_actions": [
|
|
644
|
+
{
|
|
645
|
+
"obligation_ref": "EU/NIS2 Art.23 24h",
|
|
646
|
+
"deadline": "computed_at_runtime",
|
|
647
|
+
"recipient": "internal_legal",
|
|
648
|
+
"evidence_attached": ["affected_mcp_client_inventory", "malicious_server_indicators", "interim_isolation_record"],
|
|
649
|
+
"draft_notification": "Initial NIS2 Art.23 24-hour early-warning notification: MCP supply-chain exposure detected on ${affected_endpoint_count} developer endpoint(s). Enumerated MCP servers: ${total_mcp_count}; unsigned-manifest count: ${unsigned_count}; typosquat candidates: ${typosquat_count}; vulnerable Windsurf installs (CVE-2026-30615): ${windsurf_vuln_count}. Interim isolation: ${interim_isolation_status}. Full incident assessment to follow within 72 hours per Art.23(4)."
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
"obligation_ref": "EU/NIS2 Art.23 72h",
|
|
653
|
+
"deadline": "computed_at_runtime",
|
|
654
|
+
"recipient": "regulator_email",
|
|
655
|
+
"evidence_attached": ["full_incident_assessment", "manifest_provenance_audit", "remediation_plan"],
|
|
656
|
+
"draft_notification": "NIS2 Art.23 incident notification (72-hour): Full assessment of MCP supply-chain incident. Affected systems: ${affected_systems}. Provenance audit findings: ${provenance_summary}. Remediation plan: vendor patch deployment, unsigned-manifest removal, allowlist enforcement with signed fingerprints, version pinning with integrity hash. Estimated completion: ${remediation_eta}."
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
"obligation_ref": "EU/NIS2 Art.23 720h",
|
|
660
|
+
"deadline": "computed_at_runtime",
|
|
661
|
+
"recipient": "regulator_email",
|
|
662
|
+
"evidence_attached": ["root_cause_analysis", "control_changes_implemented", "lessons_learned"],
|
|
663
|
+
"draft_notification": "NIS2 Art.23 final report (1-month): Root cause — MCP server supply-chain trust controls were structurally insufficient at detection. Control changes implemented: ${control_changes}. Lessons learned: ${lessons}. Residual risk acceptance: ${residual_risk_owner} signed ${acceptance_date}."
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
"obligation_ref": "EU/DORA Art.19 4h",
|
|
667
|
+
"deadline": "computed_at_runtime",
|
|
668
|
+
"recipient": "internal_legal",
|
|
669
|
+
"evidence_attached": ["initial_notification", "ict_third_party_dependencies", "developer_endpoint_blast_radius"],
|
|
670
|
+
"draft_notification": "DORA Art.19 initial notification: Major ICT-related incident — MCP supply-chain exposure on ${affected_endpoint_count} developer endpoint(s) within financial-entity scope. CVE-2026-30615 exposure: ${windsurf_vuln_count}. ICT third-party dependencies (MCP publishers): ${ict_dependencies}. Full classification + impact assessment to follow within statutory windows."
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
"obligation_ref": "EU/EU AI Act Art.73 360h",
|
|
674
|
+
"deadline": "computed_at_runtime",
|
|
675
|
+
"recipient": "regulator_email",
|
|
676
|
+
"evidence_attached": ["serious_incident_assessment", "ai_system_affected", "tool_provenance_audit"],
|
|
677
|
+
"draft_notification": "EU AI Act Art.73 serious-incident notification: MCP tool-plugin supply-chain exposure on high-risk AI system ${ai_system_id}. Tool provenance audit: ${unsigned_count} of ${total_mcp_count} tool plugins lacked manifest attestation at detection. Affected AI system: ${ai_system_description}. Remediation per validate phase."
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
"obligation_ref": "AU/APRA CPS 234 72h",
|
|
681
|
+
"deadline": "computed_at_runtime",
|
|
682
|
+
"recipient": "regulator_email",
|
|
683
|
+
"evidence_attached": ["materiality_assessment", "remediation_completed_evidence"],
|
|
684
|
+
"draft_notification": "APRA CPS 234 notification: Material information security incident — MCP supply-chain exposure on ${affected_endpoint_count} developer endpoint(s). Materiality determination: ${materiality_justification}. Remediation completed: ${remediation_summary}."
|
|
685
|
+
}
|
|
686
|
+
],
|
|
687
|
+
"exception_generation": {
|
|
688
|
+
"trigger_condition": "remediation_blocked == true OR (business_critical_mcp_server.signature_unavailable == true AND replacement_unavailable == true)",
|
|
689
|
+
"exception_template": {
|
|
690
|
+
"scope": "Business-critical MCP server(s) ${unauthorized_mcp_list} cannot meet manifest-signature, integrity-pin, or allowlist-binding requirements within the compliance window. Remediation paths 1-4 blocked.",
|
|
691
|
+
"duration": "until_vendor_patch",
|
|
692
|
+
"compensating_controls": ["mcp_egress_network_allowlist_restricting_to_known-good_endpoints", "mcp_process_seccomp_apparmor_profile", "mcp_runs_as_non_root_enforced", "tool_invocation_behavioral_baseline_with_alerting", "weekly_re-enumeration_and_diff_against_baseline", "publisher_key_transparency_monitoring"],
|
|
693
|
+
"risk_acceptance_owner": "ciso",
|
|
694
|
+
"auditor_ready_language": "Pursuant to ${framework_id} ${control_id} (Supply Chain Protection / Outsourced Development / Vendor and Business Partner Risk Management), the organization documents a time-bound risk acceptance for MCP server(s) ${unauthorized_mcp_list} executing within developer environments without manifest signature verification. Vendor-published signing infrastructure for these MCP servers: ${vendor_signing_status}. Replacement candidates evaluated: ${replacement_evaluation}. The organization accepts that current vendor-management, configuration-management, and supplier-relationship controls in NIST 800-53 (SA-12, CM-7), ISO 27001:2022 (A.5.19, A.5.20, A.8.30), SOC 2 (CC9.2), and EU AI Act (Art.15) do not adequately address the developer-installed AI tool plugin attack surface, that this gap is documented in ${exceptd_framework_gap_mapping_ref}, and that the organization's compensating controls are: ${compensating_controls}. Detection coverage during the exception window: behavioral baseline of tool invocation patterns with anomaly alerting, weekly re-enumeration with diff-from-baseline, egress monitoring on MCP processes. Risk accepted by ${ciso_name} on ${acceptance_date}. Time-bound until ${duration_expiry} (vendor signing infrastructure publication, replacement MCP server selection, OR ${default_30d_expiry}, whichever is first). Re-evaluation triggers: vendor publishes signed manifest, new CVE in MCP supply-chain class, behavioral anomaly fires, OR scheduled expiry."
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
"regression_schedule": {
|
|
698
|
+
"next_run": "computed_at_runtime",
|
|
699
|
+
"trigger": "both",
|
|
700
|
+
"notify_on_skip": true
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
|
|
705
|
+
"directives": [
|
|
706
|
+
{
|
|
707
|
+
"id": "all-mcp-servers-trust-audit",
|
|
708
|
+
"title": "Enumerate every MCP server across every AI coding assistant config and audit trust posture",
|
|
709
|
+
"applies_to": { "always": true }
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
"id": "cve-2026-30615-windsurf",
|
|
713
|
+
"title": "Targeted investigation for CVE-2026-30615 (Windsurf MCP zero-interaction RCE)",
|
|
714
|
+
"applies_to": { "cve": "CVE-2026-30615" },
|
|
715
|
+
"phase_overrides": {
|
|
716
|
+
"direct": {
|
|
717
|
+
"rwep_threshold": { "escalate": 70, "monitor": 40, "close": 20 }
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
"id": "ml-supply-chain-compromise-atlas",
|
|
723
|
+
"title": "ATLAS AML.T0010 — ML Supply Chain Compromise via MCP namespace",
|
|
724
|
+
"applies_to": { "atlas_ttp": "AML.T0010" }
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
}
|