devarmor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +140 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +13 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/modules/agent-residue.d.ts +11 -0
  12. package/dist/modules/agent-residue.d.ts.map +1 -0
  13. package/dist/modules/agent-residue.js +283 -0
  14. package/dist/modules/agent-residue.js.map +1 -0
  15. package/dist/modules/mcp-auditor.d.ts +12 -0
  16. package/dist/modules/mcp-auditor.d.ts.map +1 -0
  17. package/dist/modules/mcp-auditor.js +290 -0
  18. package/dist/modules/mcp-auditor.js.map +1 -0
  19. package/dist/modules/posture-checker.d.ts +11 -0
  20. package/dist/modules/posture-checker.d.ts.map +1 -0
  21. package/dist/modules/posture-checker.js +315 -0
  22. package/dist/modules/posture-checker.js.map +1 -0
  23. package/dist/modules/secret-scanner.d.ts +11 -0
  24. package/dist/modules/secret-scanner.d.ts.map +1 -0
  25. package/dist/modules/secret-scanner.js +321 -0
  26. package/dist/modules/secret-scanner.js.map +1 -0
  27. package/dist/modules/skill-scanner.d.ts +12 -0
  28. package/dist/modules/skill-scanner.d.ts.map +1 -0
  29. package/dist/modules/skill-scanner.js +294 -0
  30. package/dist/modules/skill-scanner.js.map +1 -0
  31. package/dist/report/html.d.ts +6 -0
  32. package/dist/report/html.d.ts.map +1 -0
  33. package/dist/report/html.js +116 -0
  34. package/dist/report/html.js.map +1 -0
  35. package/dist/report/json.d.ts +9 -0
  36. package/dist/report/json.d.ts.map +1 -0
  37. package/dist/report/json.js +69 -0
  38. package/dist/report/json.js.map +1 -0
  39. package/dist/report/terminal.d.ts +6 -0
  40. package/dist/report/terminal.d.ts.map +1 -0
  41. package/dist/report/terminal.js +162 -0
  42. package/dist/report/terminal.js.map +1 -0
  43. package/dist/scanner.d.ts +9 -0
  44. package/dist/scanner.d.ts.map +1 -0
  45. package/dist/scanner.js +145 -0
  46. package/dist/scanner.js.map +1 -0
  47. package/dist/types.d.ts +91 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +17 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +50 -0
@@ -0,0 +1,11 @@
1
+ import { ScannerModule, ModuleResult, ScanOptions } from '../types';
2
+ /**
3
+ * SecretScanner — Detects leaked API keys, tokens, and passwords
4
+ * across configuration files, dotfiles, and source code.
5
+ */
6
+ export declare class SecretScanner implements ScannerModule {
7
+ name: "SecretScanner";
8
+ label: string;
9
+ scan(options: ScanOptions): Promise<ModuleResult>;
10
+ }
11
+ //# sourceMappingURL=secret-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-scanner.d.ts","sourceRoot":"","sources":["../../src/modules/secret-scanner.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,aAAa,EACb,YAAY,EAEZ,WAAW,EAEZ,MAAM,UAAU,CAAC;AAmPlB;;;GAGG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,IAAI,EAAG,eAAe,CAAU;IAChC,KAAK,SAAuB;IAEtB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CAiDxD"}
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevArmor — Secret Scanner Module
4
+ // Detects leaked API keys, tokens, passwords in config files.
5
+ // ============================================================
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.SecretScanner = void 0;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const types_1 = require("../types");
44
+ /**
45
+ * Curated list of secret patterns to detect.
46
+ * Covers major cloud providers, AI services, payment systems, and common credential formats.
47
+ */
48
+ const SECRET_PATTERNS = [
49
+ // ── AI / LLM Service Keys ──
50
+ {
51
+ id: 'openai-api-key',
52
+ label: 'OpenAI API Key',
53
+ regex: /sk-[A-Za-z0-9_-]{20,}/g,
54
+ severity: types_1.Severity.CRITICAL,
55
+ remediation: 'Remove the key from this file and rotate it at https://platform.openai.com/api-keys',
56
+ },
57
+ {
58
+ id: 'anthropic-api-key',
59
+ label: 'Anthropic API Key',
60
+ regex: /sk-ant-[A-Za-z0-9_-]{20,}/g,
61
+ severity: types_1.Severity.CRITICAL,
62
+ remediation: 'Remove the key and rotate it at https://console.anthropic.com/settings/keys',
63
+ },
64
+ {
65
+ id: 'google-ai-api-key',
66
+ label: 'Google AI API Key',
67
+ regex: /AIza[A-Za-z0-9_-]{35}/g,
68
+ severity: types_1.Severity.CRITICAL,
69
+ remediation: 'Remove the key and rotate it in Google Cloud Console',
70
+ },
71
+ // ── Cloud Provider Keys ──
72
+ {
73
+ id: 'aws-access-key',
74
+ label: 'AWS Access Key ID',
75
+ regex: /AKIA[0-9A-Z]{16}/g,
76
+ severity: types_1.Severity.CRITICAL,
77
+ remediation: 'Rotate this key immediately in AWS IAM Console',
78
+ },
79
+ {
80
+ id: 'aws-secret-key',
81
+ label: 'AWS Secret Access Key',
82
+ regex: /(?:aws_secret_access_key|AWS_SECRET_ACCESS_KEY)\s*[=:]\s*[A-Za-z0-9/+=]{40}/g,
83
+ severity: types_1.Severity.CRITICAL,
84
+ remediation: 'Rotate this key immediately in AWS IAM Console',
85
+ },
86
+ {
87
+ id: 'azure-connection-string',
88
+ label: 'Azure Connection String',
89
+ regex: /DefaultEndpointsProtocol=https;AccountName=[^;]+;AccountKey=[A-Za-z0-9+/=]{40,}/g,
90
+ severity: types_1.Severity.CRITICAL,
91
+ remediation: 'Rotate the storage account key in Azure Portal',
92
+ },
93
+ // ── Payment / Financial ──
94
+ {
95
+ id: 'stripe-secret-key',
96
+ label: 'Stripe Secret Key',
97
+ regex: /sk_live_[A-Za-z0-9]{24,}/g,
98
+ severity: types_1.Severity.CRITICAL,
99
+ remediation: 'Rotate this key at https://dashboard.stripe.com/apikeys',
100
+ },
101
+ {
102
+ id: 'stripe-publishable-key',
103
+ label: 'Stripe Publishable Key',
104
+ regex: /pk_live_[A-Za-z0-9]{24,}/g,
105
+ severity: types_1.Severity.LOW,
106
+ remediation: 'Publishable keys are client-safe, but verify this is intentional',
107
+ },
108
+ // ── Communication / Messaging ──
109
+ {
110
+ id: 'slack-token',
111
+ label: 'Slack Token',
112
+ regex: /xox[bpors]-[0-9]{10,}-[A-Za-z0-9-]+/g,
113
+ severity: types_1.Severity.HIGH,
114
+ remediation: 'Revoke and regenerate the Slack token',
115
+ },
116
+ {
117
+ id: 'discord-token',
118
+ label: 'Discord Bot Token',
119
+ regex: /[MN][A-Za-z0-9]{23,}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}/g,
120
+ severity: types_1.Severity.HIGH,
121
+ remediation: 'Regenerate the Discord bot token',
122
+ },
123
+ {
124
+ id: 'twilio-api-key',
125
+ label: 'Twilio API Key',
126
+ regex: /SK[0-9a-fA-F]{32}/g,
127
+ severity: types_1.Severity.HIGH,
128
+ remediation: 'Rotate the Twilio API key in your Twilio Console',
129
+ },
130
+ // ── Source Control / CI ──
131
+ {
132
+ id: 'github-pat',
133
+ label: 'GitHub Personal Access Token',
134
+ regex: /ghp_[A-Za-z0-9]{36,}/g,
135
+ severity: types_1.Severity.CRITICAL,
136
+ remediation: 'Revoke this token at https://github.com/settings/tokens',
137
+ },
138
+ {
139
+ id: 'github-fine-grained-pat',
140
+ label: 'GitHub Fine-Grained PAT',
141
+ regex: /github_pat_[A-Za-z0-9_]{22,}/g,
142
+ severity: types_1.Severity.CRITICAL,
143
+ remediation: 'Revoke this token at https://github.com/settings/tokens',
144
+ },
145
+ {
146
+ id: 'gitlab-pat',
147
+ label: 'GitLab Personal Access Token',
148
+ regex: /glpat-[A-Za-z0-9_-]{20,}/g,
149
+ severity: types_1.Severity.CRITICAL,
150
+ remediation: 'Revoke this token in GitLab → Settings → Access Tokens',
151
+ },
152
+ // ── Database ──
153
+ {
154
+ id: 'postgres-connection',
155
+ label: 'PostgreSQL Connection String',
156
+ regex: /postgres(?:ql)?:\/\/[^:]+:[^@]+@[^/]+/g,
157
+ severity: types_1.Severity.HIGH,
158
+ remediation: 'Move database credentials to environment variables or a secrets manager',
159
+ },
160
+ {
161
+ id: 'mongodb-connection',
162
+ label: 'MongoDB Connection String',
163
+ regex: /mongodb(?:\+srv)?:\/\/[^:]+:[^@]+@[^/]+/g,
164
+ severity: types_1.Severity.HIGH,
165
+ remediation: 'Move database credentials to environment variables or a secrets manager',
166
+ },
167
+ // ── Generic Patterns ──
168
+ {
169
+ id: 'generic-password',
170
+ label: 'Hardcoded Password',
171
+ regex: /(?:password|passwd|pwd|secret)\s*[=:]\s*["'][^"']{8,}["']/gi,
172
+ severity: types_1.Severity.MEDIUM,
173
+ remediation: 'Move passwords to environment variables or a secrets manager',
174
+ },
175
+ {
176
+ id: 'private-key',
177
+ label: 'Private Key',
178
+ regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,
179
+ severity: types_1.Severity.CRITICAL,
180
+ remediation: 'Remove private keys from this file and store them securely',
181
+ },
182
+ {
183
+ id: 'bearer-token',
184
+ label: 'Bearer Token',
185
+ regex: /(?:bearer|authorization)\s*[=:]\s*["']?Bearer\s+[A-Za-z0-9._~+/=-]{20,}/gi,
186
+ severity: types_1.Severity.HIGH,
187
+ remediation: 'Remove hardcoded bearer tokens and use dynamic token generation',
188
+ },
189
+ {
190
+ id: 'jwt-token',
191
+ label: 'JWT Token',
192
+ regex: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g,
193
+ severity: types_1.Severity.HIGH,
194
+ remediation: 'Remove hardcoded JWT tokens — they may contain sensitive claims',
195
+ },
196
+ ];
197
+ /** File extensions to scan for secrets. */
198
+ const SCAN_EXTENSIONS = new Set([
199
+ '.env', '.yaml', '.yml', '.json', '.toml', '.ini', '.cfg', '.conf',
200
+ '.properties', '.xml', '.ts', '.js', '.py', '.rb', '.go', '.rs',
201
+ '.java', '.sh', '.bash', '.zsh', '.fish', '.ps1',
202
+ '.tf', '.tfvars', '.hcl', // Terraform
203
+ '.dockerfile', // Docker
204
+ ]);
205
+ /** Files to scan regardless of extension. */
206
+ const SCAN_FILENAMES = new Set([
207
+ '.env', '.env.local', '.env.production', '.env.development', '.env.staging',
208
+ '.npmrc', '.pypirc', 'pip.conf', '.netrc', '.git-credentials',
209
+ 'credentials', 'config', '.bashrc', '.zshrc', '.profile',
210
+ 'docker-compose.yml', 'docker-compose.yaml',
211
+ 'Dockerfile',
212
+ ]);
213
+ /** Directories to skip during scanning. */
214
+ const SKIP_DIRS = new Set([
215
+ 'node_modules', '.git', 'dist', 'build', 'coverage', '__pycache__',
216
+ '.next', '.nuxt', 'vendor', 'target', 'bin', 'obj', 'registry', 'packages',
217
+ ]);
218
+ /** Maximum file size to scan (1MB). */
219
+ const MAX_FILE_SIZE = 1024 * 1024;
220
+ /**
221
+ * Recursively collects scannable files from a directory.
222
+ */
223
+ function collectFiles(dir) {
224
+ const results = [];
225
+ if (!fs.existsSync(dir))
226
+ return results;
227
+ let entries;
228
+ try {
229
+ entries = fs.readdirSync(dir, { withFileTypes: true });
230
+ }
231
+ catch {
232
+ return results;
233
+ }
234
+ for (const entry of entries) {
235
+ const fullPath = path.join(dir, entry.name);
236
+ if (entry.isDirectory()) {
237
+ if (!SKIP_DIRS.has(entry.name)) {
238
+ results.push(...collectFiles(fullPath));
239
+ }
240
+ }
241
+ else if (entry.isFile()) {
242
+ const ext = path.extname(entry.name).toLowerCase();
243
+ const basename = entry.name.toLowerCase();
244
+ // Check if file is scannable by extension or name
245
+ if (SCAN_EXTENSIONS.has(ext) || SCAN_FILENAMES.has(basename)) {
246
+ try {
247
+ const stat = fs.statSync(fullPath);
248
+ if (stat.size <= MAX_FILE_SIZE && stat.size > 0) {
249
+ results.push(fullPath);
250
+ }
251
+ }
252
+ catch {
253
+ // Skip inaccessible files
254
+ }
255
+ }
256
+ }
257
+ }
258
+ return results;
259
+ }
260
+ /**
261
+ * Redacts sensitive content for safe display in findings.
262
+ * Shows first 4 and last 4 characters, masks the rest.
263
+ */
264
+ function redact(value) {
265
+ if (value.length <= 12)
266
+ return '****';
267
+ return value.substring(0, 4) + '****' + value.substring(value.length - 4);
268
+ }
269
+ /**
270
+ * SecretScanner — Detects leaked API keys, tokens, and passwords
271
+ * across configuration files, dotfiles, and source code.
272
+ */
273
+ class SecretScanner {
274
+ name = 'SecretScanner';
275
+ label = '🔑 Secret Scanner';
276
+ async scan(options) {
277
+ const startTime = Date.now();
278
+ const findings = [];
279
+ const files = collectFiles(options.path);
280
+ for (const filePath of files) {
281
+ let content;
282
+ try {
283
+ content = fs.readFileSync(filePath, 'utf-8');
284
+ }
285
+ catch {
286
+ continue; // Skip unreadable files
287
+ }
288
+ const lines = content.split('\n');
289
+ for (const pattern of SECRET_PATTERNS) {
290
+ // Reset regex state for each file
291
+ pattern.regex.lastIndex = 0;
292
+ let match;
293
+ while ((match = pattern.regex.exec(content)) !== null) {
294
+ // Find line number
295
+ const upToMatch = content.substring(0, match.index);
296
+ const lineNumber = upToMatch.split('\n').length;
297
+ findings.push({
298
+ module: this.name,
299
+ severity: pattern.severity,
300
+ title: `${pattern.label} Detected`,
301
+ description: `Found a potential ${pattern.label} in this file.`,
302
+ filePath,
303
+ line: lineNumber,
304
+ evidence: redact(match[0]),
305
+ remediation: pattern.remediation,
306
+ });
307
+ }
308
+ }
309
+ }
310
+ return {
311
+ module: this.name,
312
+ label: this.label,
313
+ success: true,
314
+ durationMs: Date.now() - startTime,
315
+ itemsScanned: files.length,
316
+ findings,
317
+ };
318
+ }
319
+ }
320
+ exports.SecretScanner = SecretScanner;
321
+ //# sourceMappingURL=secret-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-scanner.js","sourceRoot":"","sources":["../../src/modules/secret-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { ScannerModule, ModuleResult, ScanOptions } from '../types';
2
+ /**
3
+ * SkillScanner — Detects malicious patterns in AI agent skill files
4
+ * including prompt injection, data exfiltration, system override,
5
+ * and execution hijacking via hidden instructions.
6
+ */
7
+ export declare class SkillScanner implements ScannerModule {
8
+ name: "SkillScanner";
9
+ label: string;
10
+ scan(options: ScanOptions): Promise<ModuleResult>;
11
+ }
12
+ //# sourceMappingURL=skill-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-scanner.d.ts","sourceRoot":"","sources":["../../src/modules/skill-scanner.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,aAAa,EACb,YAAY,EAEZ,WAAW,EAEZ,MAAM,UAAU,CAAC;AA+JlB;;;;GAIG;AACH,qBAAa,YAAa,YAAW,aAAa;IAChD,IAAI,EAAG,cAAc,CAAU;IAC/B,KAAK,SAAuB;IAEtB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CAiHxD"}
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // DevArmor — Skill Scanner Module
4
+ // Detects malicious patterns in AI agent skill files.
5
+ // ============================================================
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.SkillScanner = void 0;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const types_1 = require("../types");
44
+ /** Skill directories to scan, relative to project root. */
45
+ const SKILL_DIRS = [
46
+ '.agents/skills',
47
+ '.cursor/skills',
48
+ ];
49
+ /** Prompt injection patterns. */
50
+ const PROMPT_INJECTION_PATTERNS = [
51
+ { regex: /ignore\s+previous\s+instructions/gi, label: 'Ignore previous instructions' },
52
+ { regex: /ignore\s+all\s+prior/gi, label: 'Ignore all prior instructions' },
53
+ { regex: /you\s+are\s+now/gi, label: 'Identity override (you are now)' },
54
+ { regex: /override\s+your/gi, label: 'Override directive' },
55
+ { regex: /disregard\s+your/gi, label: 'Disregard directive' },
56
+ { regex: /forget\s+(everything|all|your)/gi, label: 'Memory wipe directive' },
57
+ { regex: /new\s+instructions?\s*:/gi, label: 'New instructions injection' },
58
+ { regex: /system\s*:\s*you\s+are/gi, label: 'System prompt override' },
59
+ ];
60
+ /** Data exfiltration patterns. */
61
+ const DATA_EXFIL_PATTERNS = [
62
+ { regex: /fetch\s*\(\s*['"][^'"]*['"].*(?:file|content|secret|key|token|password)/gi, label: 'Fetch with sensitive data' },
63
+ { regex: /https?:\/\/[^\s'"]+.*(?:readFile|readFileSync|file_contents)/gi, label: 'HTTP request with file contents' },
64
+ { regex: /send\s+all/gi, label: 'Send all directive' },
65
+ { regex: /\btransmit\b/gi, label: 'Transmit directive' },
66
+ { regex: /\bexfiltrate\b/gi, label: 'Exfiltrate directive' },
67
+ { regex: /upload.*(?:credentials|secrets|keys|tokens|\.env)/gi, label: 'Upload credentials' },
68
+ { regex: /(?:curl|wget|fetch|http\.post)\s*.*(?:\.env|credentials|secrets)/gi, label: 'HTTP exfiltration of secrets' },
69
+ { regex: /base64.*(?:send|post|fetch|curl)/gi, label: 'Base64 encode and send' },
70
+ ];
71
+ /** System override / destructive command patterns. */
72
+ const SYSTEM_OVERRIDE_PATTERNS = [
73
+ { regex: /execute\s+command/gi, label: 'Execute command directive' },
74
+ { regex: /run\s+shell/gi, label: 'Run shell directive' },
75
+ { regex: /\brm\s+-rf\b/g, label: 'Recursive delete (rm -rf)' },
76
+ { regex: /\bformat\s+c:/gi, label: 'Format drive (format c:)' },
77
+ { regex: /delete\s+all/gi, label: 'Delete all directive' },
78
+ { regex: /\bdrop\s+(?:table|database)\b/gi, label: 'SQL drop command' },
79
+ { regex: /\bshutdown\b.*(?:\/s|now|-h)/gi, label: 'System shutdown' },
80
+ { regex: /\bkill\s+-9\b/g, label: 'Force kill process' },
81
+ { regex: /\bmkfs\b/g, label: 'Filesystem format (mkfs)' },
82
+ { regex: /\bdd\s+if=/g, label: 'Disk destroyer (dd)' },
83
+ ];
84
+ /** Directories to skip during recursive scanning. */
85
+ const SKIP_DIRS = new Set([
86
+ 'node_modules', '.git', 'dist', 'build', '__pycache__',
87
+ ]);
88
+ /** Maximum file size to scan (1MB). */
89
+ const MAX_FILE_SIZE = 1024 * 1024;
90
+ /** Scannable file extensions for skill files. */
91
+ const SCAN_EXTENSIONS = new Set([
92
+ '.md', '.txt', '.yaml', '.yml', '.json', '.toml',
93
+ '.js', '.ts', '.py', '.sh', '.bash', '.ps1',
94
+ ]);
95
+ /**
96
+ * Recursively collects skill files from a directory.
97
+ */
98
+ function collectSkillFiles(dir) {
99
+ const results = [];
100
+ if (!fs.existsSync(dir))
101
+ return results;
102
+ let entries;
103
+ try {
104
+ entries = fs.readdirSync(dir, { withFileTypes: true });
105
+ }
106
+ catch {
107
+ return results;
108
+ }
109
+ for (const entry of entries) {
110
+ const fullPath = path.join(dir, entry.name);
111
+ if (entry.isDirectory()) {
112
+ if (!SKIP_DIRS.has(entry.name)) {
113
+ results.push(...collectSkillFiles(fullPath));
114
+ }
115
+ }
116
+ else if (entry.isFile()) {
117
+ const ext = path.extname(entry.name).toLowerCase();
118
+ if (SCAN_EXTENSIONS.has(ext)) {
119
+ try {
120
+ const stat = fs.statSync(fullPath);
121
+ if (stat.size <= MAX_FILE_SIZE && stat.size > 0) {
122
+ results.push(fullPath);
123
+ }
124
+ }
125
+ catch {
126
+ // Skip inaccessible files
127
+ }
128
+ }
129
+ }
130
+ }
131
+ return results;
132
+ }
133
+ /**
134
+ * Detects hidden instructions after large whitespace blocks.
135
+ * Returns findings for execution hijacking via whitespace hiding.
136
+ */
137
+ function checkWhitespaceHiding(content, filePath, moduleName) {
138
+ const findings = [];
139
+ // Check for large blocks of whitespace (50+ consecutive blank lines or spaces)
140
+ const largeWhitespace = /\n{50,}/g;
141
+ let match;
142
+ while ((match = largeWhitespace.exec(content)) !== null) {
143
+ // Check if there's non-whitespace content after the gap
144
+ const after = content.substring(match.index + match[0].length).trim();
145
+ if (after.length > 0) {
146
+ const upToMatch = content.substring(0, match.index);
147
+ const lineNumber = upToMatch.split('\n').length;
148
+ findings.push({
149
+ module: moduleName,
150
+ severity: types_1.Severity.HIGH,
151
+ title: 'Hidden Instructions After Whitespace',
152
+ description: 'Found content hidden after a large block of whitespace — a common prompt injection technique.',
153
+ filePath,
154
+ line: lineNumber,
155
+ remediation: 'Remove the hidden content or consolidate whitespace in this skill file',
156
+ });
157
+ }
158
+ }
159
+ return findings;
160
+ }
161
+ /**
162
+ * Detects base64 encoded commands that could hide malicious payloads.
163
+ */
164
+ function checkBase64Payloads(content, filePath, moduleName) {
165
+ const findings = [];
166
+ // Match base64 strings that are suspiciously long (likely encoded commands)
167
+ const base64Pattern = /(?:base64|atob|btoa|decode)\s*\(?\s*['"]([A-Za-z0-9+/=]{40,})['"]/g;
168
+ let match;
169
+ while ((match = base64Pattern.exec(content)) !== null) {
170
+ const upToMatch = content.substring(0, match.index);
171
+ const lineNumber = upToMatch.split('\n').length;
172
+ findings.push({
173
+ module: moduleName,
174
+ severity: types_1.Severity.HIGH,
175
+ title: 'Base64 Encoded Payload Detected',
176
+ description: 'Found a base64 encoded string that may conceal malicious commands.',
177
+ filePath,
178
+ line: lineNumber,
179
+ evidence: match[0].substring(0, 40) + '...',
180
+ remediation: 'Decode and inspect the base64 content. Remove if malicious.',
181
+ });
182
+ }
183
+ return findings;
184
+ }
185
+ /**
186
+ * SkillScanner — Detects malicious patterns in AI agent skill files
187
+ * including prompt injection, data exfiltration, system override,
188
+ * and execution hijacking via hidden instructions.
189
+ */
190
+ class SkillScanner {
191
+ name = 'SkillScanner';
192
+ label = '🛡️ Skill Scanner';
193
+ async scan(options) {
194
+ const startTime = Date.now();
195
+ const findings = [];
196
+ let totalFilesScanned = 0;
197
+ for (const skillDir of SKILL_DIRS) {
198
+ const fullSkillDir = path.join(options.path, skillDir);
199
+ if (!fs.existsSync(fullSkillDir))
200
+ continue;
201
+ // Report skill directory discovery
202
+ findings.push({
203
+ module: this.name,
204
+ severity: types_1.Severity.INFO,
205
+ title: `Skill Directory Found — ${skillDir}`,
206
+ description: `AI agent skill directory detected at ${fullSkillDir}. Scanning for malicious patterns.`,
207
+ filePath: fullSkillDir,
208
+ remediation: 'Periodically audit third-party skills for malicious content',
209
+ });
210
+ const files = collectSkillFiles(fullSkillDir);
211
+ totalFilesScanned += files.length;
212
+ for (const filePath of files) {
213
+ let content;
214
+ try {
215
+ content = fs.readFileSync(filePath, 'utf-8');
216
+ }
217
+ catch {
218
+ continue; // Skip unreadable files
219
+ }
220
+ // Check prompt injection patterns
221
+ for (const pattern of PROMPT_INJECTION_PATTERNS) {
222
+ pattern.regex.lastIndex = 0;
223
+ let match;
224
+ while ((match = pattern.regex.exec(content)) !== null) {
225
+ const upToMatch = content.substring(0, match.index);
226
+ const lineNumber = upToMatch.split('\n').length;
227
+ findings.push({
228
+ module: this.name,
229
+ severity: types_1.Severity.CRITICAL,
230
+ title: `Prompt Injection — ${pattern.label}`,
231
+ description: `Detected a prompt injection pattern that attempts to override AI behavior.`,
232
+ filePath,
233
+ line: lineNumber,
234
+ evidence: match[0],
235
+ remediation: 'Remove or quarantine this skill file — it contains prompt injection attempts',
236
+ });
237
+ }
238
+ }
239
+ // Check data exfiltration patterns
240
+ for (const pattern of DATA_EXFIL_PATTERNS) {
241
+ pattern.regex.lastIndex = 0;
242
+ let match;
243
+ while ((match = pattern.regex.exec(content)) !== null) {
244
+ const upToMatch = content.substring(0, match.index);
245
+ const lineNumber = upToMatch.split('\n').length;
246
+ findings.push({
247
+ module: this.name,
248
+ severity: types_1.Severity.CRITICAL,
249
+ title: `Data Exfiltration Risk — ${pattern.label}`,
250
+ description: `Detected a pattern that may exfiltrate sensitive data to an external endpoint.`,
251
+ filePath,
252
+ line: lineNumber,
253
+ evidence: match[0].substring(0, 80),
254
+ remediation: 'Remove this skill immediately — it may be stealing sensitive data',
255
+ });
256
+ }
257
+ }
258
+ // Check system override / destructive patterns
259
+ for (const pattern of SYSTEM_OVERRIDE_PATTERNS) {
260
+ pattern.regex.lastIndex = 0;
261
+ let match;
262
+ while ((match = pattern.regex.exec(content)) !== null) {
263
+ const upToMatch = content.substring(0, match.index);
264
+ const lineNumber = upToMatch.split('\n').length;
265
+ findings.push({
266
+ module: this.name,
267
+ severity: types_1.Severity.HIGH,
268
+ title: `System Override Risk — ${pattern.label}`,
269
+ description: `Detected a destructive system command pattern in a skill file.`,
270
+ filePath,
271
+ line: lineNumber,
272
+ evidence: match[0],
273
+ remediation: 'Remove or quarantine this skill — it contains dangerous system commands',
274
+ });
275
+ }
276
+ }
277
+ // Check execution hijacking: hidden instructions after whitespace
278
+ findings.push(...checkWhitespaceHiding(content, filePath, this.name));
279
+ // Check execution hijacking: base64 encoded commands
280
+ findings.push(...checkBase64Payloads(content, filePath, this.name));
281
+ }
282
+ }
283
+ return {
284
+ module: this.name,
285
+ label: this.label,
286
+ success: true,
287
+ durationMs: Date.now() - startTime,
288
+ itemsScanned: totalFilesScanned,
289
+ findings,
290
+ };
291
+ }
292
+ }
293
+ exports.SkillScanner = SkillScanner;
294
+ //# sourceMappingURL=skill-scanner.js.map