ferret-scan 2.2.0 → 2.3.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.
Files changed (159) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +15 -11
  3. package/bin/ferret.js +104 -8
  4. package/dist/__tests__/AgentMonitor.test.d.ts +6 -0
  5. package/dist/__tests__/AgentMonitor.test.js +235 -0
  6. package/dist/__tests__/AtlasNavigatorReporter.test.d.ts +6 -0
  7. package/dist/__tests__/AtlasNavigatorReporter.test.js +193 -0
  8. package/dist/__tests__/CorrelationAnalyzer.test.d.ts +6 -0
  9. package/dist/__tests__/CorrelationAnalyzer.test.js +211 -0
  10. package/dist/__tests__/IndicatorMatcher.test.d.ts +6 -0
  11. package/dist/__tests__/IndicatorMatcher.test.js +245 -0
  12. package/dist/__tests__/MarketplaceScanner.test.d.ts +5 -0
  13. package/dist/__tests__/MarketplaceScanner.test.js +212 -0
  14. package/dist/__tests__/RuleGenerator.test.d.ts +6 -0
  15. package/dist/__tests__/RuleGenerator.test.js +207 -0
  16. package/dist/__tests__/ThreatFeed.test.d.ts +6 -0
  17. package/dist/__tests__/ThreatFeed.test.js +359 -0
  18. package/dist/__tests__/WatchMode.test.d.ts +6 -0
  19. package/dist/__tests__/WatchMode.test.js +104 -0
  20. package/dist/__tests__/astAnalyzerExtra.test.d.ts +6 -0
  21. package/dist/__tests__/astAnalyzerExtra.test.js +67 -0
  22. package/dist/__tests__/astAnalyzerFull.test.d.ts +6 -0
  23. package/dist/__tests__/astAnalyzerFull.test.js +138 -0
  24. package/dist/__tests__/astAnalyzerPatterns.test.d.ts +6 -0
  25. package/dist/__tests__/astAnalyzerPatterns.test.js +143 -0
  26. package/dist/__tests__/atlas.test.d.ts +6 -0
  27. package/dist/__tests__/atlas.test.js +319 -0
  28. package/dist/__tests__/atlasCatalog.test.d.ts +6 -0
  29. package/dist/__tests__/atlasCatalog.test.js +200 -0
  30. package/dist/__tests__/atlasCatalogExtra.test.d.ts +6 -0
  31. package/dist/__tests__/atlasCatalogExtra.test.js +215 -0
  32. package/dist/__tests__/baseline.test.d.ts +6 -0
  33. package/dist/__tests__/baseline.test.js +321 -0
  34. package/dist/__tests__/baselineExtra.test.d.ts +6 -0
  35. package/dist/__tests__/baselineExtra.test.js +317 -0
  36. package/dist/__tests__/capabilityMapping.test.d.ts +5 -0
  37. package/dist/__tests__/capabilityMapping.test.js +49 -0
  38. package/dist/__tests__/capabilityMappingExtra.test.d.ts +5 -0
  39. package/dist/__tests__/capabilityMappingExtra.test.js +200 -0
  40. package/dist/__tests__/complianceExtra.test.d.ts +6 -0
  41. package/dist/__tests__/complianceExtra.test.js +121 -0
  42. package/dist/__tests__/config.test.js +1 -1
  43. package/dist/__tests__/configLoader.test.d.ts +6 -0
  44. package/dist/__tests__/configLoader.test.js +225 -0
  45. package/dist/__tests__/configLoaderExtra.test.d.ts +6 -0
  46. package/dist/__tests__/configLoaderExtra.test.js +186 -0
  47. package/dist/__tests__/correlationAnalyzerExtra.test.d.ts +5 -0
  48. package/dist/__tests__/correlationAnalyzerExtra.test.js +98 -0
  49. package/dist/__tests__/correlationAnalyzerFull.test.d.ts +6 -0
  50. package/dist/__tests__/correlationAnalyzerFull.test.js +154 -0
  51. package/dist/__tests__/customRules.extra.test.d.ts +6 -0
  52. package/dist/__tests__/customRules.extra.test.js +245 -0
  53. package/dist/__tests__/customRules.test.d.ts +7 -0
  54. package/dist/__tests__/customRules.test.js +347 -0
  55. package/dist/__tests__/dependencyRisk.test.d.ts +5 -0
  56. package/dist/__tests__/dependencyRisk.test.js +248 -0
  57. package/dist/__tests__/dependencyRiskExtra.test.d.ts +6 -0
  58. package/dist/__tests__/dependencyRiskExtra.test.js +177 -0
  59. package/dist/__tests__/featureExitCodes.test.d.ts +7 -0
  60. package/dist/__tests__/featureExitCodes.test.js +332 -0
  61. package/dist/__tests__/fileDiscoveryConfigOnly.test.d.ts +6 -0
  62. package/dist/__tests__/fileDiscoveryConfigOnly.test.js +195 -0
  63. package/dist/__tests__/fileDiscoveryExtra.test.d.ts +6 -0
  64. package/dist/__tests__/fileDiscoveryExtra.test.js +149 -0
  65. package/dist/__tests__/fixer.extra.test.d.ts +6 -0
  66. package/dist/__tests__/fixer.extra.test.js +135 -0
  67. package/dist/__tests__/fixerApply.test.d.ts +6 -0
  68. package/dist/__tests__/fixerApply.test.js +132 -0
  69. package/dist/__tests__/gitHooks.test.d.ts +7 -0
  70. package/dist/__tests__/gitHooks.test.js +188 -0
  71. package/dist/__tests__/htmlReporter.extra.test.d.ts +5 -0
  72. package/dist/__tests__/htmlReporter.extra.test.js +126 -0
  73. package/dist/__tests__/interactiveTui.test.d.ts +6 -0
  74. package/dist/__tests__/interactiveTui.test.js +180 -0
  75. package/dist/__tests__/interactiveTuiCommands.test.d.ts +6 -0
  76. package/dist/__tests__/interactiveTuiCommands.test.js +187 -0
  77. package/dist/__tests__/interactiveTuiMore.test.d.ts +6 -0
  78. package/dist/__tests__/interactiveTuiMore.test.js +194 -0
  79. package/dist/__tests__/interactiveTuiSession.test.d.ts +6 -0
  80. package/dist/__tests__/interactiveTuiSession.test.js +173 -0
  81. package/dist/__tests__/llmAnalysis.test.d.ts +6 -0
  82. package/dist/__tests__/llmAnalysis.test.js +229 -0
  83. package/dist/__tests__/llmAnalysisBuildExcerpt.test.d.ts +6 -0
  84. package/dist/__tests__/llmAnalysisBuildExcerpt.test.js +132 -0
  85. package/dist/__tests__/llmAnalysisExtra.test.d.ts +6 -0
  86. package/dist/__tests__/llmAnalysisExtra.test.js +214 -0
  87. package/dist/__tests__/llmAnalysisFilters.test.d.ts +6 -0
  88. package/dist/__tests__/llmAnalysisFilters.test.js +181 -0
  89. package/dist/__tests__/llmAnalysisMitre.test.d.ts +6 -0
  90. package/dist/__tests__/llmAnalysisMitre.test.js +192 -0
  91. package/dist/__tests__/llmGroqTPM.test.d.ts +6 -0
  92. package/dist/__tests__/llmGroqTPM.test.js +89 -0
  93. package/dist/__tests__/llmProviderRetry.test.d.ts +6 -0
  94. package/dist/__tests__/llmProviderRetry.test.js +172 -0
  95. package/dist/__tests__/mcpValidator.extra.test.d.ts +5 -0
  96. package/dist/__tests__/mcpValidator.extra.test.js +270 -0
  97. package/dist/__tests__/patternMatcherExtra.test.d.ts +7 -0
  98. package/dist/__tests__/patternMatcherExtra.test.js +198 -0
  99. package/dist/__tests__/patternsCommon.test.d.ts +6 -0
  100. package/dist/__tests__/patternsCommon.test.js +107 -0
  101. package/dist/__tests__/policyEnforcement.test.d.ts +5 -0
  102. package/dist/__tests__/policyEnforcement.test.js +510 -0
  103. package/dist/__tests__/quarantineExtra.test.d.ts +5 -0
  104. package/dist/__tests__/quarantineExtra.test.js +214 -0
  105. package/dist/__tests__/redactionExtra.test.d.ts +6 -0
  106. package/dist/__tests__/redactionExtra.test.js +228 -0
  107. package/dist/__tests__/scanDiff.test.d.ts +7 -0
  108. package/dist/__tests__/scanDiff.test.js +266 -0
  109. package/dist/__tests__/scanFull.test.d.ts +6 -0
  110. package/dist/__tests__/scanFull.test.js +158 -0
  111. package/dist/__tests__/scannerDampening.test.d.ts +6 -0
  112. package/dist/__tests__/scannerDampening.test.js +160 -0
  113. package/dist/__tests__/scannerExtra.test.d.ts +6 -0
  114. package/dist/__tests__/scannerExtra.test.js +194 -0
  115. package/dist/__tests__/scannerMitre.test.d.ts +5 -0
  116. package/dist/__tests__/scannerMitre.test.js +141 -0
  117. package/dist/__tests__/scannerSSRF.test.d.ts +5 -0
  118. package/dist/__tests__/scannerSSRF.test.js +149 -0
  119. package/dist/__tests__/schemas.test.d.ts +6 -0
  120. package/dist/__tests__/schemas.test.js +125 -0
  121. package/dist/__tests__/webhooks.extra.test.d.ts +6 -0
  122. package/dist/__tests__/webhooks.extra.test.js +144 -0
  123. package/dist/__tests__/webhooks.test.d.ts +6 -0
  124. package/dist/__tests__/webhooks.test.js +154 -0
  125. package/dist/features/customRules.js +22 -29
  126. package/dist/features/mcpTrustScore.d.ts +17 -0
  127. package/dist/features/mcpTrustScore.js +74 -0
  128. package/dist/features/mcpValidator.d.ts +2 -0
  129. package/dist/features/mcpValidator.js +13 -0
  130. package/dist/features/policyEnforcement.d.ts +22 -22
  131. package/dist/intelligence/ThreatFeed.js +207 -62
  132. package/dist/remediation/Quarantine.js +24 -6
  133. package/dist/reporters/ConsoleReporter.js +10 -0
  134. package/dist/reporters/HtmlReporter.js +5 -0
  135. package/dist/reporters/SarifReporter.d.ts +1 -0
  136. package/dist/reporters/SarifReporter.js +1 -0
  137. package/dist/scanner/IAnalyzer.d.ts +19 -0
  138. package/dist/scanner/IAnalyzer.js +5 -0
  139. package/dist/scanner/Scanner.js +64 -125
  140. package/dist/scanner/analyzers/CapabilityAnalyzer.d.ts +8 -0
  141. package/dist/scanner/analyzers/CapabilityAnalyzer.js +19 -0
  142. package/dist/scanner/analyzers/DependencyAnalyzer.d.ts +8 -0
  143. package/dist/scanner/analyzers/DependencyAnalyzer.js +18 -0
  144. package/dist/scanner/analyzers/EntropyAnalyzer.d.ts +8 -0
  145. package/dist/scanner/analyzers/EntropyAnalyzer.js +12 -0
  146. package/dist/scanner/analyzers/LlmAnalyzer.d.ts +17 -0
  147. package/dist/scanner/analyzers/LlmAnalyzer.js +36 -0
  148. package/dist/scanner/analyzers/McpAnalyzer.d.ts +8 -0
  149. package/dist/scanner/analyzers/McpAnalyzer.js +19 -0
  150. package/dist/scanner/analyzers/SemanticAnalyzer.d.ts +8 -0
  151. package/dist/scanner/analyzers/SemanticAnalyzer.js +21 -0
  152. package/dist/scanner/analyzers/ThreatIntelAnalyzer.d.ts +8 -0
  153. package/dist/scanner/analyzers/ThreatIntelAnalyzer.js +21 -0
  154. package/dist/types.d.ts +17 -0
  155. package/dist/types.js +1 -1
  156. package/dist/utils/safeRegex.d.ts +12 -51
  157. package/dist/utils/safeRegex.js +45 -62
  158. package/dist/utils/schemas.d.ts +64 -64
  159. package/package.json +24 -18
@@ -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: new Date().toISOString(),
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: 'Suspicious domains used in AI CLI exploitation attempts',
27
- lastUpdated: new Date().toISOString(),
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-backdoor-patterns',
33
- description: 'Code patterns associated with AI CLI-specific backdoors',
34
- lastUpdated: new Date().toISOString(),
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
- // Malicious domains
56
+ // ── Typosquatting / impersonation packages ─────────────────────────────────
44
57
  {
45
- value: 'evil-ai-api.com',
46
- type: 'domain',
47
- category: 'phishing',
48
- severity: 'high',
49
- description: 'Fake AI API endpoint used for credential harvesting',
50
- source: 'ai-cli-suspicious-domains',
51
- firstSeen: '2024-01-01T00:00:00Z',
52
- lastSeen: new Date().toISOString(),
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: ['phishing', 'fake-api', 'credential-theft']
67
+ tags: ['typosquat', 'credential-theft', 'install-hook']
55
68
  },
56
69
  {
57
- value: 'anthropic-fake.net',
58
- type: 'domain',
59
- category: 'phishing',
70
+ value: 'openai-sdk-community',
71
+ type: 'package',
72
+ category: 'malicious-package',
60
73
  severity: 'high',
61
- description: 'Impersonates legitimate AI provider domain',
62
- source: 'ai-cli-suspicious-domains',
63
- firstSeen: '2024-01-01T00:00:00Z',
64
- lastSeen: new Date().toISOString(),
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: ['phishing', 'impersonation']
79
+ tags: ['typosquat', 'postinstall', 'exfiltration']
67
80
  },
68
- // Malicious packages
69
81
  {
70
- value: 'ai-jailbreak-helper',
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: 'Package designed to bypass AI assistant safety mechanisms',
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-01-01T00:00:00Z',
77
- lastSeen: new Date().toISOString(),
78
- confidence: 100,
79
- tags: ['jailbreak', 'bypass', 'malicious-npm']
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: 'anthropic-sdk-fake',
118
+ value: 'ai-agent-framework',
83
119
  type: 'package',
84
120
  category: 'malicious-package',
85
121
  severity: 'high',
86
- description: 'Fake AI SDK that steals credentials',
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-01-01T00:00:00Z',
89
- lastSeen: new Date().toISOString(),
90
- confidence: 95,
91
- tags: ['credential-theft', 'fake-sdk']
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: 'ignore.*previous.*instructions?.*forget.*rules?',
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: 'Pattern attempting to override AI assistant safety instructions',
100
- source: 'ai-cli-backdoor-patterns',
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: new Date().toISOString(),
211
+ lastSeen: '2025-01-01T00:00:00Z',
103
212
  confidence: 85,
104
- tags: ['jailbreak', 'instruction-override']
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: 'developer.*mode.*enabled|admin.*access.*granted',
228
+ value: 'as\\s+your\\s+(true\\s+)?(self|creator|master|owner)\\b',
108
229
  type: 'pattern',
109
- category: 'privilege-escalation',
230
+ category: 'jailbreak-attempt',
110
231
  severity: 'medium',
111
- description: 'Attempts to claim elevated privileges in AI assistants',
112
- source: 'ai-cli-backdoor-patterns',
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: new Date().toISOString(),
115
- confidence: 75,
116
- tags: ['privilege-escalation', 'social-engineering']
235
+ lastSeen: '2025-01-01T00:00:00Z',
236
+ confidence: 70,
237
+ tags: ['jailbreak', 'social-engineering', 'authority-claim']
117
238
  },
118
- // Hash indicators (example malicious file hashes)
239
+ // ── Exfiltration patterns ─────────────────────────────────────────────────
119
240
  {
120
- value: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
121
- type: 'hash',
122
- category: 'malicious-file',
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: 'Hash of known malicious AI CLI configuration file',
125
- source: 'ai-cli-malicious-packages',
126
- firstSeen: '2024-01-01T00:00:00Z',
127
- lastSeen: new Date().toISOString(),
128
- confidence: 100,
129
- tags: ['malicious-config', 'sha256']
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: Validate originalPath if allowedRestoreBase is specified
255
- if (allowedRestoreBase) {
256
- if (!isPathWithinBase(entry.originalPath, allowedRestoreBase)) {
257
- logger.error(`Restore path outside allowed directory: ${entry.originalPath}`);
258
- return false;
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">
@@ -68,6 +68,7 @@ interface SarifDocument {
68
68
  scanDuration: number;
69
69
  filesScanned: number;
70
70
  riskScore: number;
71
+ mcpTrustSummary?: import('../types.js').McpTrustSummary;
71
72
  };
72
73
  };
73
74
  }[];
@@ -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
@@ -0,0 +1,5 @@
1
+ /**
2
+ * IAnalyzer — interface for pluggable file analyzers
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=IAnalyzer.js.map