@trailofbits/vsix-audit 0.1.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 (197) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +281 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +703 -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 +4 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/scanner/batch.d.ts +12 -0
  12. package/dist/scanner/batch.d.ts.map +1 -0
  13. package/dist/scanner/batch.js +104 -0
  14. package/dist/scanner/batch.js.map +1 -0
  15. package/dist/scanner/bundler.d.ts +35 -0
  16. package/dist/scanner/bundler.d.ts.map +1 -0
  17. package/dist/scanner/bundler.js +120 -0
  18. package/dist/scanner/bundler.js.map +1 -0
  19. package/dist/scanner/cache.d.ts +45 -0
  20. package/dist/scanner/cache.d.ts.map +1 -0
  21. package/dist/scanner/cache.js +153 -0
  22. package/dist/scanner/cache.js.map +1 -0
  23. package/dist/scanner/cache.test.d.ts +2 -0
  24. package/dist/scanner/cache.test.d.ts.map +1 -0
  25. package/dist/scanner/cache.test.js +149 -0
  26. package/dist/scanner/cache.test.js.map +1 -0
  27. package/dist/scanner/capabilities.d.ts +29 -0
  28. package/dist/scanner/capabilities.d.ts.map +1 -0
  29. package/dist/scanner/capabilities.js +217 -0
  30. package/dist/scanner/capabilities.js.map +1 -0
  31. package/dist/scanner/checks/ast.d.ts +3 -0
  32. package/dist/scanner/checks/ast.d.ts.map +1 -0
  33. package/dist/scanner/checks/ast.js +469 -0
  34. package/dist/scanner/checks/ast.js.map +1 -0
  35. package/dist/scanner/checks/ast.test.d.ts +2 -0
  36. package/dist/scanner/checks/ast.test.d.ts.map +1 -0
  37. package/dist/scanner/checks/ast.test.js +389 -0
  38. package/dist/scanner/checks/ast.test.js.map +1 -0
  39. package/dist/scanner/checks/behavioral.d.ts +3 -0
  40. package/dist/scanner/checks/behavioral.d.ts.map +1 -0
  41. package/dist/scanner/checks/behavioral.js +367 -0
  42. package/dist/scanner/checks/behavioral.js.map +1 -0
  43. package/dist/scanner/checks/blocklist.d.ts +3 -0
  44. package/dist/scanner/checks/blocklist.d.ts.map +1 -0
  45. package/dist/scanner/checks/blocklist.js +32 -0
  46. package/dist/scanner/checks/blocklist.js.map +1 -0
  47. package/dist/scanner/checks/blocklist.test.d.ts +2 -0
  48. package/dist/scanner/checks/blocklist.test.d.ts.map +1 -0
  49. package/dist/scanner/checks/blocklist.test.js +74 -0
  50. package/dist/scanner/checks/blocklist.test.js.map +1 -0
  51. package/dist/scanner/checks/chains.d.ts +35 -0
  52. package/dist/scanner/checks/chains.d.ts.map +1 -0
  53. package/dist/scanner/checks/chains.js +505 -0
  54. package/dist/scanner/checks/chains.js.map +1 -0
  55. package/dist/scanner/checks/chains.test.d.ts +2 -0
  56. package/dist/scanner/checks/chains.test.d.ts.map +1 -0
  57. package/dist/scanner/checks/chains.test.js +250 -0
  58. package/dist/scanner/checks/chains.test.js.map +1 -0
  59. package/dist/scanner/checks/dataflow.d.ts +3 -0
  60. package/dist/scanner/checks/dataflow.d.ts.map +1 -0
  61. package/dist/scanner/checks/dataflow.js +316 -0
  62. package/dist/scanner/checks/dataflow.js.map +1 -0
  63. package/dist/scanner/checks/dependencies.d.ts +13 -0
  64. package/dist/scanner/checks/dependencies.d.ts.map +1 -0
  65. package/dist/scanner/checks/dependencies.js +225 -0
  66. package/dist/scanner/checks/dependencies.js.map +1 -0
  67. package/dist/scanner/checks/dependencies.test.d.ts +2 -0
  68. package/dist/scanner/checks/dependencies.test.d.ts.map +1 -0
  69. package/dist/scanner/checks/dependencies.test.js +248 -0
  70. package/dist/scanner/checks/dependencies.test.js.map +1 -0
  71. package/dist/scanner/checks/finding-quality.test.d.ts +8 -0
  72. package/dist/scanner/checks/finding-quality.test.d.ts.map +1 -0
  73. package/dist/scanner/checks/finding-quality.test.js +164 -0
  74. package/dist/scanner/checks/finding-quality.test.js.map +1 -0
  75. package/dist/scanner/checks/ioc.d.ts +20 -0
  76. package/dist/scanner/checks/ioc.d.ts.map +1 -0
  77. package/dist/scanner/checks/ioc.js +234 -0
  78. package/dist/scanner/checks/ioc.js.map +1 -0
  79. package/dist/scanner/checks/ioc.test.d.ts +2 -0
  80. package/dist/scanner/checks/ioc.test.d.ts.map +1 -0
  81. package/dist/scanner/checks/ioc.test.js +298 -0
  82. package/dist/scanner/checks/ioc.test.js.map +1 -0
  83. package/dist/scanner/checks/manifest.d.ts +6 -0
  84. package/dist/scanner/checks/manifest.d.ts.map +1 -0
  85. package/dist/scanner/checks/manifest.js +123 -0
  86. package/dist/scanner/checks/manifest.js.map +1 -0
  87. package/dist/scanner/checks/manifest.test.d.ts +2 -0
  88. package/dist/scanner/checks/manifest.test.d.ts.map +1 -0
  89. package/dist/scanner/checks/manifest.test.js +108 -0
  90. package/dist/scanner/checks/manifest.test.js.map +1 -0
  91. package/dist/scanner/checks/obfuscation.d.ts +3 -0
  92. package/dist/scanner/checks/obfuscation.d.ts.map +1 -0
  93. package/dist/scanner/checks/obfuscation.js +432 -0
  94. package/dist/scanner/checks/obfuscation.js.map +1 -0
  95. package/dist/scanner/checks/obfuscation.test.d.ts +2 -0
  96. package/dist/scanner/checks/obfuscation.test.d.ts.map +1 -0
  97. package/dist/scanner/checks/obfuscation.test.js +399 -0
  98. package/dist/scanner/checks/obfuscation.test.js.map +1 -0
  99. package/dist/scanner/checks/package.d.ts +17 -0
  100. package/dist/scanner/checks/package.d.ts.map +1 -0
  101. package/dist/scanner/checks/package.js +422 -0
  102. package/dist/scanner/checks/package.js.map +1 -0
  103. package/dist/scanner/checks/package.test.d.ts +2 -0
  104. package/dist/scanner/checks/package.test.d.ts.map +1 -0
  105. package/dist/scanner/checks/package.test.js +518 -0
  106. package/dist/scanner/checks/package.test.js.map +1 -0
  107. package/dist/scanner/checks/patterns.d.ts +5 -0
  108. package/dist/scanner/checks/patterns.d.ts.map +1 -0
  109. package/dist/scanner/checks/patterns.js +251 -0
  110. package/dist/scanner/checks/patterns.js.map +1 -0
  111. package/dist/scanner/checks/patterns.test.d.ts +2 -0
  112. package/dist/scanner/checks/patterns.test.d.ts.map +1 -0
  113. package/dist/scanner/checks/patterns.test.js +147 -0
  114. package/dist/scanner/checks/patterns.test.js.map +1 -0
  115. package/dist/scanner/checks/unicode.d.ts +3 -0
  116. package/dist/scanner/checks/unicode.d.ts.map +1 -0
  117. package/dist/scanner/checks/unicode.js +247 -0
  118. package/dist/scanner/checks/unicode.js.map +1 -0
  119. package/dist/scanner/checks/unicode.test.d.ts +2 -0
  120. package/dist/scanner/checks/unicode.test.d.ts.map +1 -0
  121. package/dist/scanner/checks/unicode.test.js +202 -0
  122. package/dist/scanner/checks/unicode.test.js.map +1 -0
  123. package/dist/scanner/checks/yara.d.ts +23 -0
  124. package/dist/scanner/checks/yara.d.ts.map +1 -0
  125. package/dist/scanner/checks/yara.js +349 -0
  126. package/dist/scanner/checks/yara.js.map +1 -0
  127. package/dist/scanner/checks/yara.test.d.ts +2 -0
  128. package/dist/scanner/checks/yara.test.d.ts.map +1 -0
  129. package/dist/scanner/checks/yara.test.js +126 -0
  130. package/dist/scanner/checks/yara.test.js.map +1 -0
  131. package/dist/scanner/constants.d.ts +18 -0
  132. package/dist/scanner/constants.d.ts.map +1 -0
  133. package/dist/scanner/constants.js +37 -0
  134. package/dist/scanner/constants.js.map +1 -0
  135. package/dist/scanner/detection-coverage.test.d.ts +2 -0
  136. package/dist/scanner/detection-coverage.test.d.ts.map +1 -0
  137. package/dist/scanner/detection-coverage.test.js +216 -0
  138. package/dist/scanner/detection-coverage.test.js.map +1 -0
  139. package/dist/scanner/download.d.ts +76 -0
  140. package/dist/scanner/download.d.ts.map +1 -0
  141. package/dist/scanner/download.js +339 -0
  142. package/dist/scanner/download.js.map +1 -0
  143. package/dist/scanner/download.test.d.ts +2 -0
  144. package/dist/scanner/download.test.d.ts.map +1 -0
  145. package/dist/scanner/download.test.js +149 -0
  146. package/dist/scanner/download.test.js.map +1 -0
  147. package/dist/scanner/index.d.ts +8 -0
  148. package/dist/scanner/index.d.ts.map +1 -0
  149. package/dist/scanner/index.js +167 -0
  150. package/dist/scanner/index.js.map +1 -0
  151. package/dist/scanner/index.test.d.ts +2 -0
  152. package/dist/scanner/index.test.d.ts.map +1 -0
  153. package/dist/scanner/index.test.js +71 -0
  154. package/dist/scanner/index.test.js.map +1 -0
  155. package/dist/scanner/loaders/zoo.d.ts +3 -0
  156. package/dist/scanner/loaders/zoo.d.ts.map +1 -0
  157. package/dist/scanner/loaders/zoo.js +112 -0
  158. package/dist/scanner/loaders/zoo.js.map +1 -0
  159. package/dist/scanner/types.d.ts +118 -0
  160. package/dist/scanner/types.d.ts.map +1 -0
  161. package/dist/scanner/types.js +2 -0
  162. package/dist/scanner/types.js.map +1 -0
  163. package/dist/scanner/utils.d.ts +14 -0
  164. package/dist/scanner/utils.d.ts.map +1 -0
  165. package/dist/scanner/utils.js +25 -0
  166. package/dist/scanner/utils.js.map +1 -0
  167. package/dist/scanner/vsix.d.ts +6 -0
  168. package/dist/scanner/vsix.d.ts.map +1 -0
  169. package/dist/scanner/vsix.js +213 -0
  170. package/dist/scanner/vsix.js.map +1 -0
  171. package/dist/scanner/vsix.test.d.ts +2 -0
  172. package/dist/scanner/vsix.test.d.ts.map +1 -0
  173. package/dist/scanner/vsix.test.js +355 -0
  174. package/dist/scanner/vsix.test.js.map +1 -0
  175. package/package.json +60 -0
  176. package/zoo/blocklist/extensions.json +201 -0
  177. package/zoo/iocs/blockchain-extensions.txt +21 -0
  178. package/zoo/iocs/c2-domains.txt +50 -0
  179. package/zoo/iocs/c2-ips.txt +24 -0
  180. package/zoo/iocs/hashes.txt +47 -0
  181. package/zoo/iocs/malicious-npm.txt +85 -0
  182. package/zoo/iocs/wallets.txt +18 -0
  183. package/zoo/signatures/yara/README.md +46 -0
  184. package/zoo/signatures/yara/blockchain_c2.yar +48 -0
  185. package/zoo/signatures/yara/code_execution.yar +165 -0
  186. package/zoo/signatures/yara/credential_harvesting.yar +116 -0
  187. package/zoo/signatures/yara/crypto_wallet_targeting.yar +92 -0
  188. package/zoo/signatures/yara/data_exfiltration.yar +207 -0
  189. package/zoo/signatures/yara/google_calendar_c2.yar +187 -0
  190. package/zoo/signatures/yara/messaging_c2.yar +103 -0
  191. package/zoo/signatures/yara/multi_stage_attacks.yar +331 -0
  192. package/zoo/signatures/yara/obfuscation_patterns.yar +208 -0
  193. package/zoo/signatures/yara/powershell_attacks.yar +116 -0
  194. package/zoo/signatures/yara/rat_capabilities.yar +243 -0
  195. package/zoo/signatures/yara/self_propagation.yar +239 -0
  196. package/zoo/signatures/yara/unicode_stealth.yar +48 -0
  197. package/zoo/signatures/yara/websocket_c2.yar +83 -0
@@ -0,0 +1,349 @@
1
+ import { execFile } from "node:child_process";
2
+ import { access, readdir, writeFile, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { promisify } from "node:util";
7
+ /**
8
+ * Binary file extensions that should be skipped for YARA scanning.
9
+ * These cause false positives because YARA pattern matching on binary
10
+ * content often matches arbitrary byte sequences.
11
+ */
12
+ const BINARY_EXTENSIONS = new Set([
13
+ // Java
14
+ ".jar",
15
+ ".class",
16
+ ".war",
17
+ ".ear",
18
+ // Compiled
19
+ ".wasm",
20
+ ".pyc",
21
+ ".pyo",
22
+ // Images
23
+ ".png",
24
+ ".jpg",
25
+ ".jpeg",
26
+ ".gif",
27
+ ".bmp",
28
+ ".ico",
29
+ ".webp",
30
+ ".svg",
31
+ ".tiff",
32
+ ".tif",
33
+ // Fonts
34
+ ".ttf",
35
+ ".otf",
36
+ ".woff",
37
+ ".woff2",
38
+ ".eot",
39
+ // Audio/Video
40
+ ".mp3",
41
+ ".mp4",
42
+ ".wav",
43
+ ".ogg",
44
+ ".webm",
45
+ ".avi",
46
+ // Archives
47
+ ".zip",
48
+ ".tar",
49
+ ".gz",
50
+ ".bz2",
51
+ ".xz",
52
+ ".7z",
53
+ ".rar",
54
+ // Documents
55
+ ".pdf",
56
+ // Native binaries (scanned separately by checkNativeFiles)
57
+ ".node",
58
+ ".dll",
59
+ ".dylib",
60
+ ".so",
61
+ ".exe",
62
+ // Other binary
63
+ ".bin",
64
+ ".dat",
65
+ ]);
66
+ /**
67
+ * Check if a file should be skipped for YARA scanning
68
+ */
69
+ function shouldSkipForYara(filename) {
70
+ const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
71
+ return BINARY_EXTENSIONS.has(ext);
72
+ }
73
+ const execFileAsync = promisify(execFile);
74
+ const __dirname = dirname(fileURLToPath(import.meta.url));
75
+ /**
76
+ * Find the YARA rules directory, checking multiple locations.
77
+ * Priority:
78
+ * 1. VSIX_AUDIT_ZOO_PATH environment variable + /signatures/yara
79
+ * 2. Development: ../../../zoo/signatures/yara relative to module
80
+ * 3. Installed: ../../zoo/signatures/yara relative to dist
81
+ */
82
+ async function findYaraRulesDir() {
83
+ // Check environment variable first
84
+ const envPath = process.env["VSIX_AUDIT_ZOO_PATH"];
85
+ if (envPath) {
86
+ return join(envPath, "signatures", "yara");
87
+ }
88
+ // Development path: src/scanner/checks -> zoo/signatures/yara
89
+ const devPath = join(__dirname, "..", "..", "..", "zoo", "signatures", "yara");
90
+ try {
91
+ await access(devPath);
92
+ return devPath;
93
+ }
94
+ catch {
95
+ // Not found, try installed path
96
+ }
97
+ // Installed path: dist/scanner/checks -> zoo/signatures/yara
98
+ const installedPath = join(__dirname, "..", "..", "zoo", "signatures", "yara");
99
+ try {
100
+ await access(installedPath);
101
+ return installedPath;
102
+ }
103
+ catch {
104
+ // Fall back to dev path (will error with helpful message later)
105
+ return devPath;
106
+ }
107
+ }
108
+ // Cached result of findYaraRulesDir
109
+ let cachedYaraRulesDir;
110
+ /**
111
+ * Get the default YARA rules directory (cached)
112
+ */
113
+ export async function getDefaultYaraRulesDir() {
114
+ if (!cachedYaraRulesDir) {
115
+ cachedYaraRulesDir = await findYaraRulesDir();
116
+ }
117
+ return cachedYaraRulesDir;
118
+ }
119
+ // For backwards compatibility - returns a path that may not exist until findYaraRulesDir is called
120
+ export const DEFAULT_YARA_RULES_DIR = join(__dirname, "..", "..", "..", "zoo", "signatures", "yara");
121
+ /**
122
+ * Check if YARA-X is installed and available
123
+ */
124
+ export async function isYaraAvailable() {
125
+ try {
126
+ const { stdout } = await execFileAsync("yr", ["--version"]);
127
+ return stdout.trim().length > 0;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ }
133
+ /**
134
+ * Get YARA-X version string
135
+ */
136
+ export async function getYaraVersion() {
137
+ try {
138
+ const { stdout } = await execFileAsync("yr", ["--version"]);
139
+ return stdout.trim();
140
+ }
141
+ catch {
142
+ return null;
143
+ }
144
+ }
145
+ /**
146
+ * List all YARA rule files in a directory
147
+ */
148
+ export async function listYaraRules(rulesDir) {
149
+ try {
150
+ const entries = await readdir(rulesDir);
151
+ return entries.filter((f) => f.endsWith(".yar") || f.endsWith(".yara"));
152
+ }
153
+ catch {
154
+ return [];
155
+ }
156
+ }
157
+ /**
158
+ * Parse YARA output into structured matches
159
+ */
160
+ function parseYaraOutput(output) {
161
+ const matches = [];
162
+ const lines = output.trim().split("\n").filter(Boolean);
163
+ for (const line of lines) {
164
+ // YARA output format: rule_name file_path
165
+ // With -s flag: rule_name file_path\n0xoffset:$string_name: string_content
166
+ const match = line.match(/^(\S+)\s+(.+)$/);
167
+ if (match) {
168
+ const [, rule, file] = match;
169
+ if (rule && file) {
170
+ matches.push({ rule, file });
171
+ }
172
+ }
173
+ }
174
+ return matches;
175
+ }
176
+ /**
177
+ * Cache for parsed YARA rule metadata to avoid re-reading files
178
+ */
179
+ const ruleMetaCache = new Map();
180
+ /**
181
+ * Parse YARA rule file and extract metadata for all rules
182
+ */
183
+ async function parseRuleFile(ruleFile) {
184
+ if (ruleMetaCache.has(ruleFile)) {
185
+ return ruleMetaCache.get(ruleFile);
186
+ }
187
+ const { readFile } = await import("node:fs/promises");
188
+ const ruleMap = new Map();
189
+ try {
190
+ const content = await readFile(ruleFile, "utf8");
191
+ // Match rule blocks: rule NAME { meta: ... }
192
+ const rulePattern = /rule\s+(\w+)\s*\{[^}]*meta\s*:\s*([^}]+?)(?:strings|condition)\s*:/gs;
193
+ for (const match of content.matchAll(rulePattern)) {
194
+ const ruleName = match[1];
195
+ const metaBlock = match[2];
196
+ if (!ruleName || !metaBlock)
197
+ continue;
198
+ const meta = {};
199
+ // Extract severity from meta block
200
+ const severityMatch = metaBlock.match(/severity\s*=\s*["'](\w+)["']/);
201
+ if (severityMatch?.[1]) {
202
+ meta.severity = severityMatch[1];
203
+ }
204
+ // Extract description from meta block
205
+ const descMatch = metaBlock.match(/description\s*=\s*["']([^"']+)["']/);
206
+ if (descMatch?.[1]) {
207
+ meta.description = descMatch[1];
208
+ }
209
+ ruleMap.set(ruleName, meta);
210
+ }
211
+ }
212
+ catch {
213
+ // If we can't read the file, return empty map
214
+ }
215
+ ruleMetaCache.set(ruleFile, ruleMap);
216
+ return ruleMap;
217
+ }
218
+ /**
219
+ * Extract metadata from YARA rule file for a specific rule
220
+ */
221
+ async function getRuleMeta(ruleFile, ruleName) {
222
+ const ruleMap = await parseRuleFile(ruleFile);
223
+ const meta = ruleMap.get(ruleName);
224
+ if (meta?.severity) {
225
+ return meta;
226
+ }
227
+ // Fallback: derive severity from rule name patterns
228
+ const result = {};
229
+ if (meta?.description) {
230
+ result.description = meta.description;
231
+ }
232
+ const lowerName = ruleName.toLowerCase();
233
+ if (lowerName.includes("critical") || lowerName.startsWith("mal_")) {
234
+ result.severity = "critical";
235
+ }
236
+ else if (lowerName.includes("stealth") ||
237
+ lowerName.startsWith("stealer_") ||
238
+ lowerName.startsWith("rat_") ||
239
+ lowerName.startsWith("c2_")) {
240
+ result.severity = "high";
241
+ }
242
+ else if (lowerName.startsWith("susp_") || lowerName.startsWith("loader_")) {
243
+ result.severity = "medium";
244
+ }
245
+ else {
246
+ result.severity = "medium"; // Default
247
+ }
248
+ return result;
249
+ }
250
+ /**
251
+ * Run YARA rules against extension contents
252
+ */
253
+ export async function checkYara(contents, rulesDir) {
254
+ const findings = [];
255
+ const targetRulesDir = rulesDir ?? (await getDefaultYaraRulesDir());
256
+ // Check if YARA is available
257
+ const available = await isYaraAvailable();
258
+ if (!available) {
259
+ findings.push({
260
+ id: "YARA_NOT_INSTALLED",
261
+ title: "YARA-X scanner not available",
262
+ description: "YARA-X is not installed. Install with 'brew install yara-x' to enable advanced malware detection using signature rules.",
263
+ severity: "low",
264
+ category: "yara",
265
+ metadata: {
266
+ suggestion: "brew install yara-x",
267
+ },
268
+ });
269
+ return findings;
270
+ }
271
+ // Check if rules directory exists and has rules
272
+ const rules = await listYaraRules(targetRulesDir);
273
+ if (rules.length === 0) {
274
+ return findings;
275
+ }
276
+ // Create a temporary directory for scanning
277
+ const tempDir = join(tmpdir(), `vsix-audit-${Date.now()}`);
278
+ try {
279
+ // Write extension files to temp directory for YARA to scan
280
+ const { mkdir } = await import("node:fs/promises");
281
+ await mkdir(tempDir, { recursive: true });
282
+ for (const [filename, buffer] of contents.files) {
283
+ // Skip binary files that are too large
284
+ if (buffer.length > 10 * 1024 * 1024)
285
+ continue; // Skip files > 10MB
286
+ // Skip binary files that cause false positives
287
+ if (shouldSkipForYara(filename))
288
+ continue;
289
+ const filePath = join(tempDir, filename);
290
+ await mkdir(dirname(filePath), { recursive: true });
291
+ await writeFile(filePath, buffer);
292
+ }
293
+ // Run YARA against the temp directory with all rules
294
+ for (const ruleFile of rules) {
295
+ const rulePath = join(targetRulesDir, ruleFile);
296
+ try {
297
+ // Run YARA-X with recursive scanning
298
+ // Using execFile instead of exec to prevent command injection
299
+ const { stdout, stderr } = await execFileAsync("yr", ["scan", "-r", rulePath, tempDir], { maxBuffer: 10 * 1024 * 1024 });
300
+ if (stderr && !stderr.includes("warning")) {
301
+ // YARA-X errors (not warnings) indicate rule issues
302
+ continue;
303
+ }
304
+ const matches = parseYaraOutput(stdout);
305
+ for (const match of matches) {
306
+ // Get rule metadata
307
+ const meta = await getRuleMeta(rulePath, match.rule);
308
+ // Convert temp path back to relative path
309
+ const relativePath = match.file.replace(tempDir + "/", "");
310
+ // Get file extension for metadata
311
+ const fileExt = relativePath.slice(relativePath.lastIndexOf(".")).toLowerCase();
312
+ findings.push({
313
+ id: `YARA_${match.rule}`,
314
+ title: `YARA rule match: ${match.rule}`,
315
+ description: `YARA rule "${match.rule}" from ${ruleFile} matched this file. This indicates the file contains patterns associated with known malware or suspicious behavior.`,
316
+ severity: meta.severity ?? "medium",
317
+ category: "yara",
318
+ location: {
319
+ file: relativePath,
320
+ },
321
+ metadata: {
322
+ rule: match.rule,
323
+ ruleFile,
324
+ fileType: fileExt,
325
+ },
326
+ });
327
+ }
328
+ }
329
+ catch (error) {
330
+ // YARA returns exit code 1 when no matches, which throws in exec
331
+ // Only log actual errors
332
+ if (error instanceof Error && !error.message.includes("exit code 1")) {
333
+ // Silently ignore scan errors for individual rules
334
+ }
335
+ }
336
+ }
337
+ }
338
+ finally {
339
+ // Clean up temp directory
340
+ try {
341
+ await rm(tempDir, { recursive: true, force: true });
342
+ }
343
+ catch {
344
+ // Ignore cleanup errors
345
+ }
346
+ }
347
+ return findings;
348
+ }
349
+ //# sourceMappingURL=yara.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yara.js","sourceRoot":"","sources":["../../../src/scanner/checks/yara.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=yara.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yara.test.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/yara.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { checkYara, getYaraVersion, isYaraAvailable, listYaraRules } from "./yara.js";
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const ZOO_YARA_DIR = join(__dirname, "..", "..", "..", "zoo", "signatures", "yara");
7
+ function makeContents(files) {
8
+ const manifest = {
9
+ name: "test-extension",
10
+ publisher: "test",
11
+ version: "1.0.0",
12
+ };
13
+ const fileMap = new Map();
14
+ for (const [name, content] of Object.entries(files)) {
15
+ fileMap.set(name, Buffer.from(content, "utf8"));
16
+ }
17
+ return { manifest, files: fileMap, basePath: "/test" };
18
+ }
19
+ describe("isYaraAvailable", () => {
20
+ it("returns boolean indicating YARA installation status", async () => {
21
+ const result = await isYaraAvailable();
22
+ // This will be true if yara is installed, false otherwise
23
+ expect(typeof result).toBe("boolean");
24
+ });
25
+ });
26
+ describe("getYaraVersion", () => {
27
+ it("returns version string or null", async () => {
28
+ const result = await getYaraVersion();
29
+ if (result !== null) {
30
+ // If YARA is installed, version should be a non-empty string
31
+ expect(result.length).toBeGreaterThan(0);
32
+ }
33
+ });
34
+ });
35
+ describe("listYaraRules", () => {
36
+ it("lists YARA rule files in zoo directory", async () => {
37
+ const rules = await listYaraRules(ZOO_YARA_DIR);
38
+ // We should have some YARA rules in the zoo
39
+ expect(Array.isArray(rules)).toBe(true);
40
+ // All files should have .yar or .yara extension
41
+ expect(rules.every((r) => r.endsWith(".yar") || r.endsWith(".yara"))).toBe(true);
42
+ });
43
+ it("returns empty array for non-existent directory", async () => {
44
+ const rules = await listYaraRules("/nonexistent/path");
45
+ expect(rules).toEqual([]);
46
+ });
47
+ });
48
+ describe("checkYara", () => {
49
+ it("returns appropriate result based on YARA availability", async () => {
50
+ const available = await isYaraAvailable();
51
+ const contents = makeContents({ "test.js": "console.log('test');" });
52
+ const findings = await checkYara(contents);
53
+ if (!available) {
54
+ // When YARA is not installed, should return informational finding
55
+ expect(findings).toHaveLength(1);
56
+ expect(findings.some((f) => f.id === "YARA_NOT_INSTALLED")).toBe(true);
57
+ expect(findings.some((f) => f.severity === "low")).toBe(true);
58
+ const finding = findings.find((f) => f.id === "YARA_NOT_INSTALLED");
59
+ expect(finding?.metadata?.["suggestion"]).toBe("brew install yara-x");
60
+ }
61
+ else {
62
+ // When YARA is installed, should return scan results (possibly empty)
63
+ expect(Array.isArray(findings)).toBe(true);
64
+ }
65
+ });
66
+ it("returns empty findings for clean extension when YARA is available", async () => {
67
+ const available = await isYaraAvailable();
68
+ if (!available)
69
+ return; // Skip if YARA not installed
70
+ const contents = makeContents({
71
+ "extension.js": "console.log('Hello, World!');",
72
+ "package.json": JSON.stringify({ name: "test", version: "1.0.0" }),
73
+ });
74
+ const findings = await checkYara(contents);
75
+ // Should not match any YARA rules for clean code
76
+ const yaraMatches = findings.filter((f) => f.id.startsWith("YARA_") && f.id !== "YARA_NOT_INSTALLED");
77
+ expect(yaraMatches).toHaveLength(0);
78
+ });
79
+ it("handles extension with potentially suspicious content", async () => {
80
+ const available = await isYaraAvailable();
81
+ if (!available)
82
+ return; // Skip if YARA not installed
83
+ // Create content that might match YARA rules
84
+ // This includes variation selectors (U+FE00-FE0F) and eval
85
+ const suspiciousContent = `
86
+ const payload = "test\uFE01\uFE02\uFE03";
87
+ eval(atob(payload));
88
+ `;
89
+ const contents = makeContents({
90
+ "extension.js": suspiciousContent,
91
+ });
92
+ const findings = await checkYara(contents);
93
+ // Should return array of findings (might match rules depending on rule content)
94
+ expect(Array.isArray(findings)).toBe(true);
95
+ });
96
+ it("includes metadata when rules match", async () => {
97
+ const available = await isYaraAvailable();
98
+ if (!available)
99
+ return; // Skip if YARA not installed
100
+ const contents = makeContents({
101
+ "extension.js": `
102
+ const wallet = "metamask";
103
+ const key = ".ssh/id_rsa";
104
+ eval(Buffer.from("Y29kZQ==", "base64").toString());
105
+ `,
106
+ });
107
+ const findings = await checkYara(contents);
108
+ // If any YARA rules matched, finding ID should include rule name
109
+ for (const finding of findings) {
110
+ if (finding.id !== "YARA_NOT_INSTALLED") {
111
+ expect(finding.id).toMatch(/^YARA_/);
112
+ expect(finding.metadata?.["rule"]).toBeDefined();
113
+ }
114
+ }
115
+ });
116
+ it("handles empty extension gracefully", async () => {
117
+ const available = await isYaraAvailable();
118
+ if (!available)
119
+ return; // Skip if YARA not installed
120
+ const contents = makeContents({});
121
+ const findings = await checkYara(contents);
122
+ // Should not throw and should return array
123
+ expect(Array.isArray(findings)).toBe(true);
124
+ });
125
+ });
126
+ //# sourceMappingURL=yara.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yara.test.js","sourceRoot":"","sources":["../../../src/scanner/checks/yara.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * File extensions that can be scanned for code patterns.
3
+ * Used by pattern, IOC, and unicode checks.
4
+ */
5
+ /** Code files - JavaScript, TypeScript, and script files */
6
+ export declare const CODE_EXTENSIONS: Set<string>;
7
+ /** Data files that may contain IOCs */
8
+ export declare const DATA_EXTENSIONS: Set<string>;
9
+ /** Text files for unicode analysis */
10
+ export declare const TEXT_EXTENSIONS: Set<string>;
11
+ /** All scannable extensions for IOC checks (code + data) */
12
+ export declare const SCANNABLE_EXTENSIONS_IOC: Set<string>;
13
+ /** Scannable extensions for pattern checks (code only) */
14
+ export declare const SCANNABLE_EXTENSIONS_PATTERN: Set<string>;
15
+ /** Scannable extensions for unicode checks (code + data + text) */
16
+ export declare const SCANNABLE_EXTENSIONS_UNICODE: Set<string>;
17
+ export declare function isScannable(filename: string, extensions: Set<string>): boolean;
18
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/scanner/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,eAAe,aAY1B,CAAC;AAEH,uCAAuC;AACvC,eAAO,MAAM,eAAe,aAAqB,CAAC;AAElD,sCAAsC;AACtC,eAAO,MAAM,eAAe,aAA2B,CAAC;AAExD,4DAA4D;AAC5D,eAAO,MAAM,wBAAwB,aAAoD,CAAC;AAE1F,0DAA0D;AAC1D,eAAO,MAAM,4BAA4B,aAAkB,CAAC;AAE5D,mEAAmE;AACnE,eAAO,MAAM,4BAA4B,aAIvC,CAAC;AAEH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAG9E"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * File extensions that can be scanned for code patterns.
3
+ * Used by pattern, IOC, and unicode checks.
4
+ */
5
+ /** Code files - JavaScript, TypeScript, and script files */
6
+ export const CODE_EXTENSIONS = new Set([
7
+ ".js",
8
+ ".ts",
9
+ ".mjs",
10
+ ".cjs",
11
+ ".jsx",
12
+ ".tsx",
13
+ ".ps1",
14
+ ".sh",
15
+ ".bat",
16
+ ".cmd",
17
+ ".py",
18
+ ]);
19
+ /** Data files that may contain IOCs */
20
+ export const DATA_EXTENSIONS = new Set([".json"]);
21
+ /** Text files for unicode analysis */
22
+ export const TEXT_EXTENSIONS = new Set([".md", ".txt"]);
23
+ /** All scannable extensions for IOC checks (code + data) */
24
+ export const SCANNABLE_EXTENSIONS_IOC = new Set([...CODE_EXTENSIONS, ...DATA_EXTENSIONS]);
25
+ /** Scannable extensions for pattern checks (code only) */
26
+ export const SCANNABLE_EXTENSIONS_PATTERN = CODE_EXTENSIONS;
27
+ /** Scannable extensions for unicode checks (code + data + text) */
28
+ export const SCANNABLE_EXTENSIONS_UNICODE = new Set([
29
+ ...CODE_EXTENSIONS,
30
+ ...DATA_EXTENSIONS,
31
+ ...TEXT_EXTENSIONS,
32
+ ]);
33
+ export function isScannable(filename, extensions) {
34
+ const ext = filename.slice(filename.lastIndexOf(".")).toLowerCase();
35
+ return extensions.has(ext);
36
+ }
37
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/scanner/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IACrC,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;CACN,CAAC,CAAC;AAEH,uCAAuC;AACvC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAElD,sCAAsC;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAExD,4DAA4D;AAC5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,eAAe,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC;AAE1F,0DAA0D;AAC1D,MAAM,CAAC,MAAM,4BAA4B,GAAG,eAAe,CAAC;AAE5D,mEAAmE;AACnE,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,GAAG,CAAC;IAClD,GAAG,eAAe;IAClB,GAAG,eAAe;IAClB,GAAG,eAAe;CACnB,CAAC,CAAC;AAEH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,UAAuB;IACnE,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,OAAO,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=detection-coverage.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detection-coverage.test.d.ts","sourceRoot":"","sources":["../../src/scanner/detection-coverage.test.ts"],"names":[],"mappings":""}