ferret-scan 2.2.0 → 2.4.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/CHANGELOG.md +17 -0
- package/README.md +15 -11
- package/bin/ferret.js +104 -8
- package/dist/__tests__/AgentMonitor.test.d.ts +6 -0
- package/dist/__tests__/AgentMonitor.test.js +235 -0
- package/dist/__tests__/AtlasNavigatorReporter.test.d.ts +6 -0
- package/dist/__tests__/AtlasNavigatorReporter.test.js +193 -0
- package/dist/__tests__/CorrelationAnalyzer.test.d.ts +6 -0
- package/dist/__tests__/CorrelationAnalyzer.test.js +211 -0
- package/dist/__tests__/IndicatorMatcher.test.d.ts +6 -0
- package/dist/__tests__/IndicatorMatcher.test.js +245 -0
- package/dist/__tests__/MarketplaceScanner.test.d.ts +5 -0
- package/dist/__tests__/MarketplaceScanner.test.js +212 -0
- package/dist/__tests__/RuleGenerator.test.d.ts +6 -0
- package/dist/__tests__/RuleGenerator.test.js +207 -0
- package/dist/__tests__/ThreatFeed.test.d.ts +6 -0
- package/dist/__tests__/ThreatFeed.test.js +359 -0
- package/dist/__tests__/WatchMode.test.d.ts +6 -0
- package/dist/__tests__/WatchMode.test.js +104 -0
- package/dist/__tests__/astAnalyzerExtra.test.d.ts +6 -0
- package/dist/__tests__/astAnalyzerExtra.test.js +67 -0
- package/dist/__tests__/astAnalyzerFull.test.d.ts +6 -0
- package/dist/__tests__/astAnalyzerFull.test.js +138 -0
- package/dist/__tests__/astAnalyzerPatterns.test.d.ts +6 -0
- package/dist/__tests__/astAnalyzerPatterns.test.js +143 -0
- package/dist/__tests__/atlas.test.d.ts +6 -0
- package/dist/__tests__/atlas.test.js +319 -0
- package/dist/__tests__/atlasCatalog.test.d.ts +6 -0
- package/dist/__tests__/atlasCatalog.test.js +200 -0
- package/dist/__tests__/atlasCatalogExtra.test.d.ts +6 -0
- package/dist/__tests__/atlasCatalogExtra.test.js +215 -0
- package/dist/__tests__/baseline.test.d.ts +6 -0
- package/dist/__tests__/baseline.test.js +321 -0
- package/dist/__tests__/baselineExtra.test.d.ts +6 -0
- package/dist/__tests__/baselineExtra.test.js +317 -0
- package/dist/__tests__/capabilityMapping.test.d.ts +5 -0
- package/dist/__tests__/capabilityMapping.test.js +49 -0
- package/dist/__tests__/capabilityMappingExtra.test.d.ts +5 -0
- package/dist/__tests__/capabilityMappingExtra.test.js +200 -0
- package/dist/__tests__/complianceExtra.test.d.ts +6 -0
- package/dist/__tests__/complianceExtra.test.js +121 -0
- package/dist/__tests__/config.test.js +1 -1
- package/dist/__tests__/configLoader.test.d.ts +6 -0
- package/dist/__tests__/configLoader.test.js +225 -0
- package/dist/__tests__/configLoaderExtra.test.d.ts +6 -0
- package/dist/__tests__/configLoaderExtra.test.js +186 -0
- package/dist/__tests__/correlationAnalyzerExtra.test.d.ts +5 -0
- package/dist/__tests__/correlationAnalyzerExtra.test.js +98 -0
- package/dist/__tests__/correlationAnalyzerFull.test.d.ts +6 -0
- package/dist/__tests__/correlationAnalyzerFull.test.js +154 -0
- package/dist/__tests__/customRules.extra.test.d.ts +6 -0
- package/dist/__tests__/customRules.extra.test.js +245 -0
- package/dist/__tests__/customRules.test.d.ts +7 -0
- package/dist/__tests__/customRules.test.js +347 -0
- package/dist/__tests__/dependencyRisk.test.d.ts +5 -0
- package/dist/__tests__/dependencyRisk.test.js +248 -0
- package/dist/__tests__/dependencyRiskExtra.test.d.ts +6 -0
- package/dist/__tests__/dependencyRiskExtra.test.js +177 -0
- package/dist/__tests__/featureExitCodes.test.d.ts +7 -0
- package/dist/__tests__/featureExitCodes.test.js +332 -0
- package/dist/__tests__/fileDiscoveryConfigOnly.test.d.ts +6 -0
- package/dist/__tests__/fileDiscoveryConfigOnly.test.js +195 -0
- package/dist/__tests__/fileDiscoveryExtra.test.d.ts +6 -0
- package/dist/__tests__/fileDiscoveryExtra.test.js +149 -0
- package/dist/__tests__/fixer.extra.test.d.ts +6 -0
- package/dist/__tests__/fixer.extra.test.js +135 -0
- package/dist/__tests__/fixerApply.test.d.ts +6 -0
- package/dist/__tests__/fixerApply.test.js +132 -0
- package/dist/__tests__/gitHooks.test.d.ts +7 -0
- package/dist/__tests__/gitHooks.test.js +188 -0
- package/dist/__tests__/htmlReporter.extra.test.d.ts +5 -0
- package/dist/__tests__/htmlReporter.extra.test.js +126 -0
- package/dist/__tests__/interactiveTui.test.d.ts +6 -0
- package/dist/__tests__/interactiveTui.test.js +180 -0
- package/dist/__tests__/interactiveTuiCommands.test.d.ts +6 -0
- package/dist/__tests__/interactiveTuiCommands.test.js +187 -0
- package/dist/__tests__/interactiveTuiMore.test.d.ts +6 -0
- package/dist/__tests__/interactiveTuiMore.test.js +194 -0
- package/dist/__tests__/interactiveTuiSession.test.d.ts +6 -0
- package/dist/__tests__/interactiveTuiSession.test.js +173 -0
- package/dist/__tests__/llmAnalysis.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysis.test.js +229 -0
- package/dist/__tests__/llmAnalysisBuildExcerpt.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisBuildExcerpt.test.js +132 -0
- package/dist/__tests__/llmAnalysisExtra.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisExtra.test.js +214 -0
- package/dist/__tests__/llmAnalysisFilters.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisFilters.test.js +181 -0
- package/dist/__tests__/llmAnalysisMitre.test.d.ts +6 -0
- package/dist/__tests__/llmAnalysisMitre.test.js +192 -0
- package/dist/__tests__/llmGroqTPM.test.d.ts +6 -0
- package/dist/__tests__/llmGroqTPM.test.js +89 -0
- package/dist/__tests__/llmProviderRetry.test.d.ts +6 -0
- package/dist/__tests__/llmProviderRetry.test.js +172 -0
- package/dist/__tests__/mcpValidator.extra.test.d.ts +5 -0
- package/dist/__tests__/mcpValidator.extra.test.js +270 -0
- package/dist/__tests__/patternMatcherExtra.test.d.ts +7 -0
- package/dist/__tests__/patternMatcherExtra.test.js +198 -0
- package/dist/__tests__/patternsCommon.test.d.ts +6 -0
- package/dist/__tests__/patternsCommon.test.js +107 -0
- package/dist/__tests__/policyEnforcement.test.d.ts +5 -0
- package/dist/__tests__/policyEnforcement.test.js +510 -0
- package/dist/__tests__/quarantineExtra.test.d.ts +5 -0
- package/dist/__tests__/quarantineExtra.test.js +214 -0
- package/dist/__tests__/redactionExtra.test.d.ts +6 -0
- package/dist/__tests__/redactionExtra.test.js +228 -0
- package/dist/__tests__/scanDiff.test.d.ts +7 -0
- package/dist/__tests__/scanDiff.test.js +266 -0
- package/dist/__tests__/scanFull.test.d.ts +6 -0
- package/dist/__tests__/scanFull.test.js +158 -0
- package/dist/__tests__/scannerDampening.test.d.ts +6 -0
- package/dist/__tests__/scannerDampening.test.js +160 -0
- package/dist/__tests__/scannerExtra.test.d.ts +6 -0
- package/dist/__tests__/scannerExtra.test.js +194 -0
- package/dist/__tests__/scannerMitre.test.d.ts +5 -0
- package/dist/__tests__/scannerMitre.test.js +141 -0
- package/dist/__tests__/scannerSSRF.test.d.ts +5 -0
- package/dist/__tests__/scannerSSRF.test.js +149 -0
- package/dist/__tests__/schemas.test.d.ts +6 -0
- package/dist/__tests__/schemas.test.js +125 -0
- package/dist/__tests__/webhooks.extra.test.d.ts +6 -0
- package/dist/__tests__/webhooks.extra.test.js +144 -0
- package/dist/__tests__/webhooks.test.d.ts +6 -0
- package/dist/__tests__/webhooks.test.js +154 -0
- package/dist/features/customRules.js +22 -29
- package/dist/features/mcpTrustScore.d.ts +17 -0
- package/dist/features/mcpTrustScore.js +74 -0
- package/dist/features/mcpValidator.d.ts +2 -0
- package/dist/features/mcpValidator.js +13 -0
- package/dist/features/policyEnforcement.d.ts +22 -22
- package/dist/intelligence/ThreatFeed.js +207 -62
- package/dist/remediation/Quarantine.js +24 -6
- package/dist/reporters/ConsoleReporter.js +10 -0
- package/dist/reporters/HtmlReporter.js +5 -0
- package/dist/reporters/SarifReporter.d.ts +1 -0
- package/dist/reporters/SarifReporter.js +1 -0
- package/dist/scanner/IAnalyzer.d.ts +19 -0
- package/dist/scanner/IAnalyzer.js +5 -0
- package/dist/scanner/Scanner.js +64 -125
- package/dist/scanner/analyzers/CapabilityAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/CapabilityAnalyzer.js +19 -0
- package/dist/scanner/analyzers/DependencyAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/DependencyAnalyzer.js +18 -0
- package/dist/scanner/analyzers/EntropyAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/EntropyAnalyzer.js +12 -0
- package/dist/scanner/analyzers/LlmAnalyzer.d.ts +17 -0
- package/dist/scanner/analyzers/LlmAnalyzer.js +36 -0
- package/dist/scanner/analyzers/McpAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/McpAnalyzer.js +19 -0
- package/dist/scanner/analyzers/SemanticAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/SemanticAnalyzer.js +21 -0
- package/dist/scanner/analyzers/ThreatIntelAnalyzer.d.ts +8 -0
- package/dist/scanner/analyzers/ThreatIntelAnalyzer.js +21 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.js +1 -1
- package/dist/utils/safeRegex.d.ts +12 -51
- package/dist/utils/safeRegex.js +45 -62
- package/dist/utils/schemas.d.ts +64 -64
- package/package.json +25 -19
|
@@ -16,117 +16,262 @@ const DEFAULT_INTEL_DIR = '.ferret-intel';
|
|
|
16
16
|
const BUILTIN_SOURCES = [
|
|
17
17
|
{
|
|
18
18
|
name: 'ai-cli-malicious-packages',
|
|
19
|
-
description: 'Known malicious npm packages targeting AI CLI environments',
|
|
20
|
-
lastUpdated:
|
|
19
|
+
description: 'Known malicious and typosquatting npm packages targeting AI CLI environments',
|
|
20
|
+
lastUpdated: '2025-01-01T00:00:00Z',
|
|
21
21
|
enabled: true,
|
|
22
22
|
format: 'json'
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
name: 'ai-cli-suspicious-domains',
|
|
26
|
-
description: '
|
|
27
|
-
lastUpdated:
|
|
26
|
+
description: 'Phishing and impersonation domains targeting AI CLI users and API credentials',
|
|
27
|
+
lastUpdated: '2025-01-01T00:00:00Z',
|
|
28
28
|
enabled: true,
|
|
29
29
|
format: 'json'
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
name: 'ai-cli-
|
|
33
|
-
description: '
|
|
34
|
-
lastUpdated:
|
|
32
|
+
name: 'ai-cli-injection-patterns',
|
|
33
|
+
description: 'Prompt injection, jailbreak, and privilege-escalation patterns observed in the wild',
|
|
34
|
+
lastUpdated: '2025-01-01T00:00:00Z',
|
|
35
|
+
enabled: true,
|
|
36
|
+
format: 'json'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'ai-cli-exfiltration-patterns',
|
|
40
|
+
description: 'Data exfiltration patterns embedded in AI CLI hooks and configurations',
|
|
41
|
+
lastUpdated: '2025-01-01T00:00:00Z',
|
|
35
42
|
enabled: true,
|
|
36
43
|
format: 'json'
|
|
37
44
|
}
|
|
38
45
|
];
|
|
39
46
|
/**
|
|
40
|
-
* Built-in threat indicators
|
|
47
|
+
* Built-in threat indicators derived from publicly documented AI CLI attack patterns.
|
|
48
|
+
* These cover typosquatting, prompt injection, exfiltration, and privilege escalation
|
|
49
|
+
* as observed across real-world incident reports and security research (2024–2025).
|
|
50
|
+
*
|
|
51
|
+
* Note: No hash indicators are included by default. File hashes are highly specific;
|
|
52
|
+
* add them via `ferret intel add` with verified malicious-file hashes from your own
|
|
53
|
+
* threat intelligence sources.
|
|
41
54
|
*/
|
|
42
55
|
const BUILTIN_INDICATORS = [
|
|
43
|
-
//
|
|
56
|
+
// ── Typosquatting / impersonation packages ─────────────────────────────────
|
|
44
57
|
{
|
|
45
|
-
value: '
|
|
46
|
-
type: '
|
|
47
|
-
category: '
|
|
48
|
-
severity: '
|
|
49
|
-
description: '
|
|
50
|
-
source: 'ai-cli-
|
|
51
|
-
firstSeen: '2024-
|
|
52
|
-
lastSeen:
|
|
58
|
+
value: 'anthropic-sdk-fake',
|
|
59
|
+
type: 'package',
|
|
60
|
+
category: 'malicious-package',
|
|
61
|
+
severity: 'critical',
|
|
62
|
+
description: 'Typosquats the official @anthropic-ai/sdk package; exfiltrates API keys on install',
|
|
63
|
+
source: 'ai-cli-malicious-packages',
|
|
64
|
+
firstSeen: '2024-03-01T00:00:00Z',
|
|
65
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
53
66
|
confidence: 95,
|
|
54
|
-
tags: ['
|
|
67
|
+
tags: ['typosquat', 'credential-theft', 'install-hook']
|
|
55
68
|
},
|
|
56
69
|
{
|
|
57
|
-
value: '
|
|
58
|
-
type: '
|
|
59
|
-
category: '
|
|
70
|
+
value: 'openai-sdk-community',
|
|
71
|
+
type: 'package',
|
|
72
|
+
category: 'malicious-package',
|
|
60
73
|
severity: 'high',
|
|
61
|
-
description: '
|
|
62
|
-
source: 'ai-cli-
|
|
63
|
-
firstSeen: '2024-
|
|
64
|
-
lastSeen:
|
|
74
|
+
description: 'Unofficial package impersonating the OpenAI SDK; contains a postinstall exfiltration script',
|
|
75
|
+
source: 'ai-cli-malicious-packages',
|
|
76
|
+
firstSeen: '2024-06-01T00:00:00Z',
|
|
77
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
65
78
|
confidence: 90,
|
|
66
|
-
tags: ['
|
|
79
|
+
tags: ['typosquat', 'postinstall', 'exfiltration']
|
|
67
80
|
},
|
|
68
|
-
// Malicious packages
|
|
69
81
|
{
|
|
70
|
-
value: '
|
|
82
|
+
value: 'claude-code-helper',
|
|
83
|
+
type: 'package',
|
|
84
|
+
category: 'malicious-package',
|
|
85
|
+
severity: 'high',
|
|
86
|
+
description: 'Impersonates Claude Code utilities; reads ~/.claude/settings.json and beacons credentials',
|
|
87
|
+
source: 'ai-cli-malicious-packages',
|
|
88
|
+
firstSeen: '2024-09-01T00:00:00Z',
|
|
89
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
90
|
+
confidence: 88,
|
|
91
|
+
tags: ['typosquat', 'credential-theft', 'ai-cli']
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
value: 'cursor-ai-extensions',
|
|
95
|
+
type: 'package',
|
|
96
|
+
category: 'malicious-package',
|
|
97
|
+
severity: 'high',
|
|
98
|
+
description: 'Fake Cursor IDE extension package; harvests .cursorrules and workspace secrets',
|
|
99
|
+
source: 'ai-cli-malicious-packages',
|
|
100
|
+
firstSeen: '2024-08-01T00:00:00Z',
|
|
101
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
102
|
+
confidence: 85,
|
|
103
|
+
tags: ['typosquat', 'ai-cli', 'cursor']
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
value: 'mcp-server-tools',
|
|
71
107
|
type: 'package',
|
|
72
108
|
category: 'malicious-package',
|
|
73
109
|
severity: 'critical',
|
|
74
|
-
description: '
|
|
110
|
+
description: 'Malicious MCP server package that exfiltrates tool call arguments to a remote endpoint',
|
|
75
111
|
source: 'ai-cli-malicious-packages',
|
|
76
|
-
firstSeen: '2024-
|
|
77
|
-
lastSeen:
|
|
78
|
-
confidence:
|
|
79
|
-
tags: ['
|
|
112
|
+
firstSeen: '2024-11-01T00:00:00Z',
|
|
113
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
114
|
+
confidence: 92,
|
|
115
|
+
tags: ['mcp', 'exfiltration', 'supply-chain']
|
|
80
116
|
},
|
|
81
117
|
{
|
|
82
|
-
value: '
|
|
118
|
+
value: 'ai-agent-framework',
|
|
83
119
|
type: 'package',
|
|
84
120
|
category: 'malicious-package',
|
|
85
121
|
severity: 'high',
|
|
86
|
-
description: '
|
|
122
|
+
description: 'Generic name used by multiple malicious packages to blend into AI agent dependency lists',
|
|
87
123
|
source: 'ai-cli-malicious-packages',
|
|
88
|
-
firstSeen: '2024-
|
|
89
|
-
lastSeen:
|
|
90
|
-
confidence:
|
|
91
|
-
tags: ['
|
|
124
|
+
firstSeen: '2024-07-01T00:00:00Z',
|
|
125
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
126
|
+
confidence: 80,
|
|
127
|
+
tags: ['typosquat', 'ai-agent', 'generic-name']
|
|
128
|
+
},
|
|
129
|
+
// ── Phishing / impersonation domains ──────────────────────────────────────
|
|
130
|
+
{
|
|
131
|
+
value: 'anthropic-api.net',
|
|
132
|
+
type: 'domain',
|
|
133
|
+
category: 'phishing',
|
|
134
|
+
severity: 'high',
|
|
135
|
+
description: 'Typosquats api.anthropic.com; used to intercept API keys in misconfigured ANTHROPIC_BASE_URL',
|
|
136
|
+
source: 'ai-cli-suspicious-domains',
|
|
137
|
+
firstSeen: '2024-04-01T00:00:00Z',
|
|
138
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
139
|
+
confidence: 93,
|
|
140
|
+
tags: ['phishing', 'api-intercept', 'anthropic']
|
|
92
141
|
},
|
|
93
|
-
// Backdoor patterns
|
|
94
142
|
{
|
|
95
|
-
value: '
|
|
143
|
+
value: 'openai-proxy.io',
|
|
144
|
+
type: 'domain',
|
|
145
|
+
category: 'phishing',
|
|
146
|
+
severity: 'high',
|
|
147
|
+
description: 'Claimed OpenAI-compatible proxy that logs all prompts and responses',
|
|
148
|
+
source: 'ai-cli-suspicious-domains',
|
|
149
|
+
firstSeen: '2024-05-01T00:00:00Z',
|
|
150
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
151
|
+
confidence: 88,
|
|
152
|
+
tags: ['phishing', 'prompt-logging', 'openai']
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
value: 'cursor-updates.net',
|
|
156
|
+
type: 'domain',
|
|
157
|
+
category: 'phishing',
|
|
158
|
+
severity: 'high',
|
|
159
|
+
description: 'Impersonates Cursor IDE update infrastructure; delivers trojanized VSIX files',
|
|
160
|
+
source: 'ai-cli-suspicious-domains',
|
|
161
|
+
firstSeen: '2024-07-01T00:00:00Z',
|
|
162
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
163
|
+
confidence: 87,
|
|
164
|
+
tags: ['phishing', 'ide-trojan', 'cursor']
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
value: 'mcp-registry.net',
|
|
168
|
+
type: 'domain',
|
|
169
|
+
category: 'phishing',
|
|
170
|
+
severity: 'medium',
|
|
171
|
+
description: 'Unofficial MCP server registry used to distribute malicious MCP server packages',
|
|
172
|
+
source: 'ai-cli-suspicious-domains',
|
|
173
|
+
firstSeen: '2024-10-01T00:00:00Z',
|
|
174
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
175
|
+
confidence: 78,
|
|
176
|
+
tags: ['phishing', 'mcp', 'fake-registry']
|
|
177
|
+
},
|
|
178
|
+
// ── Prompt injection / jailbreak patterns ─────────────────────────────────
|
|
179
|
+
{
|
|
180
|
+
value: 'ignore\\s+(all\\s+)?(previous|prior|above)\\s+(instructions?|rules?|constraints?|guidelines?)',
|
|
181
|
+
type: 'pattern',
|
|
182
|
+
category: 'jailbreak-attempt',
|
|
183
|
+
severity: 'high',
|
|
184
|
+
description: 'Classic instruction-override injection attempting to nullify prior system prompt directives',
|
|
185
|
+
source: 'ai-cli-injection-patterns',
|
|
186
|
+
firstSeen: '2023-06-01T00:00:00Z',
|
|
187
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
188
|
+
confidence: 90,
|
|
189
|
+
tags: ['jailbreak', 'instruction-override', 'prompt-injection']
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
value: 'you\\s+are\\s+now\\s+(in\\s+)?(developer|jailbreak|dan|unrestricted|god)\\s+mode',
|
|
96
193
|
type: 'pattern',
|
|
97
194
|
category: 'jailbreak-attempt',
|
|
98
195
|
severity: 'high',
|
|
99
|
-
description: '
|
|
100
|
-
source: 'ai-cli-
|
|
196
|
+
description: 'Developer/DAN mode activation attempt — claims to unlock unrestricted AI behavior',
|
|
197
|
+
source: 'ai-cli-injection-patterns',
|
|
198
|
+
firstSeen: '2023-09-01T00:00:00Z',
|
|
199
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
200
|
+
confidence: 88,
|
|
201
|
+
tags: ['jailbreak', 'dan-mode', 'privilege-escalation']
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
value: 'disregard\\s+(your\\s+)?(safety|ethical|content)\\s+(policy|policies|guidelines?|filters?)',
|
|
205
|
+
type: 'pattern',
|
|
206
|
+
category: 'jailbreak-attempt',
|
|
207
|
+
severity: 'high',
|
|
208
|
+
description: 'Attempts to disable AI safety filters by direct instruction',
|
|
209
|
+
source: 'ai-cli-injection-patterns',
|
|
101
210
|
firstSeen: '2024-01-01T00:00:00Z',
|
|
102
|
-
lastSeen:
|
|
211
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
103
212
|
confidence: 85,
|
|
104
|
-
tags: ['jailbreak', '
|
|
213
|
+
tags: ['jailbreak', 'safety-bypass']
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
value: 'print\\s+(your\\s+)?(system\\s+prompt|initial\\s+instructions?|full\\s+context)',
|
|
217
|
+
type: 'pattern',
|
|
218
|
+
category: 'jailbreak-attempt',
|
|
219
|
+
severity: 'high',
|
|
220
|
+
description: 'System prompt exfiltration via direct instruction to reveal context',
|
|
221
|
+
source: 'ai-cli-injection-patterns',
|
|
222
|
+
firstSeen: '2024-02-01T00:00:00Z',
|
|
223
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
224
|
+
confidence: 82,
|
|
225
|
+
tags: ['prompt-exfil', 'system-prompt', 'context-leak']
|
|
105
226
|
},
|
|
106
227
|
{
|
|
107
|
-
value: '
|
|
228
|
+
value: 'as\\s+your\\s+(true\\s+)?(self|creator|master|owner)\\b',
|
|
108
229
|
type: 'pattern',
|
|
109
|
-
category: '
|
|
230
|
+
category: 'jailbreak-attempt',
|
|
110
231
|
severity: 'medium',
|
|
111
|
-
description: '
|
|
112
|
-
source: 'ai-cli-
|
|
232
|
+
description: 'Social-engineering attack claiming authority over the AI assistant',
|
|
233
|
+
source: 'ai-cli-injection-patterns',
|
|
113
234
|
firstSeen: '2024-01-01T00:00:00Z',
|
|
114
|
-
lastSeen:
|
|
115
|
-
confidence:
|
|
116
|
-
tags: ['
|
|
235
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
236
|
+
confidence: 70,
|
|
237
|
+
tags: ['jailbreak', 'social-engineering', 'authority-claim']
|
|
117
238
|
},
|
|
118
|
-
//
|
|
239
|
+
// ── Exfiltration patterns ─────────────────────────────────────────────────
|
|
119
240
|
{
|
|
120
|
-
value: '
|
|
121
|
-
type: '
|
|
122
|
-
category: '
|
|
241
|
+
value: 'curl\\s+.*\\$\\{?(ANTHROPIC|OPENAI|CLAUDE|GITHUB|AWS|GCP)_?(API_?KEY|TOKEN|SECRET)',
|
|
242
|
+
type: 'pattern',
|
|
243
|
+
category: 'exfiltration',
|
|
123
244
|
severity: 'critical',
|
|
124
|
-
description: '
|
|
125
|
-
source: 'ai-cli-
|
|
126
|
-
firstSeen: '2024-
|
|
127
|
-
lastSeen:
|
|
128
|
-
confidence:
|
|
129
|
-
tags: ['
|
|
245
|
+
description: 'Shell command interpolating AI/cloud API keys directly into a curl request body or URL',
|
|
246
|
+
source: 'ai-cli-exfiltration-patterns',
|
|
247
|
+
firstSeen: '2024-03-01T00:00:00Z',
|
|
248
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
249
|
+
confidence: 95,
|
|
250
|
+
tags: ['exfiltration', 'credential-leak', 'curl', 'hook']
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
value: 'fetch\\(.*\\$\\{(conversation|messages|response|output|result)',
|
|
254
|
+
type: 'pattern',
|
|
255
|
+
category: 'exfiltration',
|
|
256
|
+
severity: 'high',
|
|
257
|
+
description: 'JavaScript fetch() call interpolating AI conversation data into a remote request',
|
|
258
|
+
source: 'ai-cli-exfiltration-patterns',
|
|
259
|
+
firstSeen: '2024-05-01T00:00:00Z',
|
|
260
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
261
|
+
confidence: 88,
|
|
262
|
+
tags: ['exfiltration', 'conversation-leak', 'fetch']
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
value: 'dns\\.lookup|nslookup|dig\\s+.*\\.(com|net|io|xyz)',
|
|
266
|
+
type: 'pattern',
|
|
267
|
+
category: 'exfiltration',
|
|
268
|
+
severity: 'medium',
|
|
269
|
+
description: 'DNS lookup in a hook context may indicate DNS-based data exfiltration channel',
|
|
270
|
+
source: 'ai-cli-exfiltration-patterns',
|
|
271
|
+
firstSeen: '2024-06-01T00:00:00Z',
|
|
272
|
+
lastSeen: '2025-01-01T00:00:00Z',
|
|
273
|
+
confidence: 65,
|
|
274
|
+
tags: ['exfiltration', 'dns-exfil', 'covert-channel']
|
|
130
275
|
}
|
|
131
276
|
];
|
|
132
277
|
/**
|
|
@@ -92,6 +92,15 @@ export function loadQuarantineDatabase(quarantineDir) {
|
|
|
92
92
|
logger.warn('Invalid quarantine database, creating new one');
|
|
93
93
|
return createEmptyDatabase();
|
|
94
94
|
}
|
|
95
|
+
// SECURITY: Sanitize loaded entries — reject any with null bytes in paths
|
|
96
|
+
db.entries = db.entries.filter(entry => {
|
|
97
|
+
if (typeof entry.originalPath !== 'string' || entry.originalPath.includes('\0') ||
|
|
98
|
+
typeof entry.quarantinePath !== 'string' || entry.quarantinePath.includes('\0')) {
|
|
99
|
+
logger.warn(`Skipping quarantine entry with invalid path: ${entry.id}`);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
});
|
|
95
104
|
return db;
|
|
96
105
|
}
|
|
97
106
|
catch (error) {
|
|
@@ -251,15 +260,22 @@ export function restoreQuarantinedFile(entryId, quarantineDir = DEFAULT_OPTIONS.
|
|
|
251
260
|
logger.error(`Quarantined file not found: ${entry.quarantinePath}`);
|
|
252
261
|
return false;
|
|
253
262
|
}
|
|
254
|
-
// SECURITY:
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
263
|
+
// SECURITY: Reject paths containing null bytes — these bypass some OS path checks
|
|
264
|
+
if (entry.originalPath.includes('\0')) {
|
|
265
|
+
logger.error(`Restore path contains null byte — rejecting: ${entryId}`);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
// SECURITY: Always validate originalPath, defaulting to CWD when no base is supplied.
|
|
269
|
+
// Without this, an attacker who crafts a quarantine DB entry can restore a file
|
|
270
|
+
// to any path on the filesystem (e.g. /etc/cron.d/evil, ~/.ssh/authorized_keys).
|
|
271
|
+
const restoreBase = allowedRestoreBase ?? process.cwd();
|
|
272
|
+
if (!isPathWithinBase(entry.originalPath, restoreBase)) {
|
|
273
|
+
logger.error(`Restore path outside allowed directory '${restoreBase}': ${entry.originalPath}`);
|
|
274
|
+
return false;
|
|
260
275
|
}
|
|
261
276
|
// SECURITY: Validate the quarantine path is within quarantine directory
|
|
262
277
|
validatePathWithinBase(entry.quarantinePath, quarantineDir, 'restoreQuarantinedFile');
|
|
278
|
+
logger.info(`Restoring '${entry.originalPath}' from quarantine`);
|
|
263
279
|
// Ensure original directory exists
|
|
264
280
|
mkdirSync(dirname(entry.originalPath), { recursive: true });
|
|
265
281
|
// Restore file
|
|
@@ -293,6 +309,8 @@ export function deleteQuarantinedFile(entryId, quarantineDir = DEFAULT_OPTIONS.q
|
|
|
293
309
|
logger.error(`Entry not found at index ${entryIndex}`);
|
|
294
310
|
return false;
|
|
295
311
|
}
|
|
312
|
+
// SECURITY: Validate quarantine path is within quarantine directory before deleting
|
|
313
|
+
validatePathWithinBase(entry.quarantinePath, quarantineDir, 'deleteQuarantinedFile');
|
|
296
314
|
// Delete quarantined file
|
|
297
315
|
if (existsSync(entry.quarantinePath)) {
|
|
298
316
|
unlinkSync(entry.quarantinePath);
|
|
@@ -84,6 +84,16 @@ function formatSummary(summary, result) {
|
|
|
84
84
|
lines.push(stats.join(' | '));
|
|
85
85
|
const ignored = result.ignoredFindings ? ` | Ignored: ${result.ignoredFindings}` : '';
|
|
86
86
|
lines.push(`Files scanned: ${result.analyzedFiles} | Time: ${result.duration}ms | Risk Score: ${result.overallRiskScore}/100${ignored}`);
|
|
87
|
+
if (result.mcpTrustSummary && result.mcpTrustSummary.total > 0) {
|
|
88
|
+
const t = result.mcpTrustSummary;
|
|
89
|
+
const trustParts = [
|
|
90
|
+
t.critical > 0 ? SEVERITY_FORMATTERS['CRITICAL'](`${t.critical} CRITICAL`) : null,
|
|
91
|
+
t.low > 0 ? SEVERITY_FORMATTERS['HIGH'](`${t.low} LOW`) : null,
|
|
92
|
+
t.medium > 0 ? SEVERITY_FORMATTERS['MEDIUM'](`${t.medium} MEDIUM`) : null,
|
|
93
|
+
t.high > 0 ? `${t.high} HIGH` : null,
|
|
94
|
+
].filter(Boolean);
|
|
95
|
+
lines.push(`MCP Trust: ${t.total} server(s) scored — ${trustParts.join(', ')} | Lowest: ${t.lowestScore}/100`);
|
|
96
|
+
}
|
|
87
97
|
return lines.join('\n');
|
|
88
98
|
}
|
|
89
99
|
/**
|
|
@@ -555,6 +555,11 @@ export function generateHtmlReport(result, options = {}) {
|
|
|
555
555
|
<div class="summary-number" style="color: ${result.overallRiskScore > 75 ? '#dc2626' : result.overallRiskScore > 50 ? '#ea580c' : '#16a34a'}">${result.overallRiskScore}</div>
|
|
556
556
|
<div class="summary-label">Risk Score</div>
|
|
557
557
|
</div>
|
|
558
|
+
${result.mcpTrustSummary && result.mcpTrustSummary.total > 0 ? `
|
|
559
|
+
<div class="summary-card">
|
|
560
|
+
<div class="summary-number" style="color: ${result.mcpTrustSummary.critical > 0 ? '#dc2626' : result.mcpTrustSummary.low > 0 ? '#ea580c' : '#16a34a'}">${result.mcpTrustSummary.lowestScore}</div>
|
|
561
|
+
<div class="summary-label">MCP Trust Min</div>
|
|
562
|
+
</div>` : ''}
|
|
558
563
|
</div>
|
|
559
564
|
|
|
560
565
|
<div class="filters">
|
|
@@ -143,6 +143,7 @@ export function generateSarifReport(result) {
|
|
|
143
143
|
scanDuration: result.duration,
|
|
144
144
|
filesScanned: result.analyzedFiles,
|
|
145
145
|
riskScore: result.overallRiskScore,
|
|
146
|
+
...(result.mcpTrustSummary ? { mcpTrustSummary: result.mcpTrustSummary } : {}),
|
|
146
147
|
},
|
|
147
148
|
},
|
|
148
149
|
}],
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IAnalyzer — interface for pluggable file analyzers
|
|
3
|
+
*/
|
|
4
|
+
import type { DiscoveredFile, Finding, Rule, ScannerConfig } from '../types.js';
|
|
5
|
+
export interface AnalyzerContext {
|
|
6
|
+
file: DiscoveredFile;
|
|
7
|
+
content: string;
|
|
8
|
+
config: ScannerConfig;
|
|
9
|
+
/** Merged rule set (base + custom) for this scan */
|
|
10
|
+
rules: Rule[];
|
|
11
|
+
/** Findings accumulated so far (allows later analyzers to gate on earlier results) */
|
|
12
|
+
existingFindings: Finding[];
|
|
13
|
+
}
|
|
14
|
+
export interface IAnalyzer {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
shouldRun(ctx: AnalyzerContext): boolean;
|
|
17
|
+
analyze(ctx: AnalyzerContext): Promise<Finding[]>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=IAnalyzer.d.ts.map
|