@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.
- package/LICENSE +661 -0
- package/README.md +281 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +703 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner/batch.d.ts +12 -0
- package/dist/scanner/batch.d.ts.map +1 -0
- package/dist/scanner/batch.js +104 -0
- package/dist/scanner/batch.js.map +1 -0
- package/dist/scanner/bundler.d.ts +35 -0
- package/dist/scanner/bundler.d.ts.map +1 -0
- package/dist/scanner/bundler.js +120 -0
- package/dist/scanner/bundler.js.map +1 -0
- package/dist/scanner/cache.d.ts +45 -0
- package/dist/scanner/cache.d.ts.map +1 -0
- package/dist/scanner/cache.js +153 -0
- package/dist/scanner/cache.js.map +1 -0
- package/dist/scanner/cache.test.d.ts +2 -0
- package/dist/scanner/cache.test.d.ts.map +1 -0
- package/dist/scanner/cache.test.js +149 -0
- package/dist/scanner/cache.test.js.map +1 -0
- package/dist/scanner/capabilities.d.ts +29 -0
- package/dist/scanner/capabilities.d.ts.map +1 -0
- package/dist/scanner/capabilities.js +217 -0
- package/dist/scanner/capabilities.js.map +1 -0
- package/dist/scanner/checks/ast.d.ts +3 -0
- package/dist/scanner/checks/ast.d.ts.map +1 -0
- package/dist/scanner/checks/ast.js +469 -0
- package/dist/scanner/checks/ast.js.map +1 -0
- package/dist/scanner/checks/ast.test.d.ts +2 -0
- package/dist/scanner/checks/ast.test.d.ts.map +1 -0
- package/dist/scanner/checks/ast.test.js +389 -0
- package/dist/scanner/checks/ast.test.js.map +1 -0
- package/dist/scanner/checks/behavioral.d.ts +3 -0
- package/dist/scanner/checks/behavioral.d.ts.map +1 -0
- package/dist/scanner/checks/behavioral.js +367 -0
- package/dist/scanner/checks/behavioral.js.map +1 -0
- package/dist/scanner/checks/blocklist.d.ts +3 -0
- package/dist/scanner/checks/blocklist.d.ts.map +1 -0
- package/dist/scanner/checks/blocklist.js +32 -0
- package/dist/scanner/checks/blocklist.js.map +1 -0
- package/dist/scanner/checks/blocklist.test.d.ts +2 -0
- package/dist/scanner/checks/blocklist.test.d.ts.map +1 -0
- package/dist/scanner/checks/blocklist.test.js +74 -0
- package/dist/scanner/checks/blocklist.test.js.map +1 -0
- package/dist/scanner/checks/chains.d.ts +35 -0
- package/dist/scanner/checks/chains.d.ts.map +1 -0
- package/dist/scanner/checks/chains.js +505 -0
- package/dist/scanner/checks/chains.js.map +1 -0
- package/dist/scanner/checks/chains.test.d.ts +2 -0
- package/dist/scanner/checks/chains.test.d.ts.map +1 -0
- package/dist/scanner/checks/chains.test.js +250 -0
- package/dist/scanner/checks/chains.test.js.map +1 -0
- package/dist/scanner/checks/dataflow.d.ts +3 -0
- package/dist/scanner/checks/dataflow.d.ts.map +1 -0
- package/dist/scanner/checks/dataflow.js +316 -0
- package/dist/scanner/checks/dataflow.js.map +1 -0
- package/dist/scanner/checks/dependencies.d.ts +13 -0
- package/dist/scanner/checks/dependencies.d.ts.map +1 -0
- package/dist/scanner/checks/dependencies.js +225 -0
- package/dist/scanner/checks/dependencies.js.map +1 -0
- package/dist/scanner/checks/dependencies.test.d.ts +2 -0
- package/dist/scanner/checks/dependencies.test.d.ts.map +1 -0
- package/dist/scanner/checks/dependencies.test.js +248 -0
- package/dist/scanner/checks/dependencies.test.js.map +1 -0
- package/dist/scanner/checks/finding-quality.test.d.ts +8 -0
- package/dist/scanner/checks/finding-quality.test.d.ts.map +1 -0
- package/dist/scanner/checks/finding-quality.test.js +164 -0
- package/dist/scanner/checks/finding-quality.test.js.map +1 -0
- package/dist/scanner/checks/ioc.d.ts +20 -0
- package/dist/scanner/checks/ioc.d.ts.map +1 -0
- package/dist/scanner/checks/ioc.js +234 -0
- package/dist/scanner/checks/ioc.js.map +1 -0
- package/dist/scanner/checks/ioc.test.d.ts +2 -0
- package/dist/scanner/checks/ioc.test.d.ts.map +1 -0
- package/dist/scanner/checks/ioc.test.js +298 -0
- package/dist/scanner/checks/ioc.test.js.map +1 -0
- package/dist/scanner/checks/manifest.d.ts +6 -0
- package/dist/scanner/checks/manifest.d.ts.map +1 -0
- package/dist/scanner/checks/manifest.js +123 -0
- package/dist/scanner/checks/manifest.js.map +1 -0
- package/dist/scanner/checks/manifest.test.d.ts +2 -0
- package/dist/scanner/checks/manifest.test.d.ts.map +1 -0
- package/dist/scanner/checks/manifest.test.js +108 -0
- package/dist/scanner/checks/manifest.test.js.map +1 -0
- package/dist/scanner/checks/obfuscation.d.ts +3 -0
- package/dist/scanner/checks/obfuscation.d.ts.map +1 -0
- package/dist/scanner/checks/obfuscation.js +432 -0
- package/dist/scanner/checks/obfuscation.js.map +1 -0
- package/dist/scanner/checks/obfuscation.test.d.ts +2 -0
- package/dist/scanner/checks/obfuscation.test.d.ts.map +1 -0
- package/dist/scanner/checks/obfuscation.test.js +399 -0
- package/dist/scanner/checks/obfuscation.test.js.map +1 -0
- package/dist/scanner/checks/package.d.ts +17 -0
- package/dist/scanner/checks/package.d.ts.map +1 -0
- package/dist/scanner/checks/package.js +422 -0
- package/dist/scanner/checks/package.js.map +1 -0
- package/dist/scanner/checks/package.test.d.ts +2 -0
- package/dist/scanner/checks/package.test.d.ts.map +1 -0
- package/dist/scanner/checks/package.test.js +518 -0
- package/dist/scanner/checks/package.test.js.map +1 -0
- package/dist/scanner/checks/patterns.d.ts +5 -0
- package/dist/scanner/checks/patterns.d.ts.map +1 -0
- package/dist/scanner/checks/patterns.js +251 -0
- package/dist/scanner/checks/patterns.js.map +1 -0
- package/dist/scanner/checks/patterns.test.d.ts +2 -0
- package/dist/scanner/checks/patterns.test.d.ts.map +1 -0
- package/dist/scanner/checks/patterns.test.js +147 -0
- package/dist/scanner/checks/patterns.test.js.map +1 -0
- package/dist/scanner/checks/unicode.d.ts +3 -0
- package/dist/scanner/checks/unicode.d.ts.map +1 -0
- package/dist/scanner/checks/unicode.js +247 -0
- package/dist/scanner/checks/unicode.js.map +1 -0
- package/dist/scanner/checks/unicode.test.d.ts +2 -0
- package/dist/scanner/checks/unicode.test.d.ts.map +1 -0
- package/dist/scanner/checks/unicode.test.js +202 -0
- package/dist/scanner/checks/unicode.test.js.map +1 -0
- package/dist/scanner/checks/yara.d.ts +23 -0
- package/dist/scanner/checks/yara.d.ts.map +1 -0
- package/dist/scanner/checks/yara.js +349 -0
- package/dist/scanner/checks/yara.js.map +1 -0
- package/dist/scanner/checks/yara.test.d.ts +2 -0
- package/dist/scanner/checks/yara.test.d.ts.map +1 -0
- package/dist/scanner/checks/yara.test.js +126 -0
- package/dist/scanner/checks/yara.test.js.map +1 -0
- package/dist/scanner/constants.d.ts +18 -0
- package/dist/scanner/constants.d.ts.map +1 -0
- package/dist/scanner/constants.js +37 -0
- package/dist/scanner/constants.js.map +1 -0
- package/dist/scanner/detection-coverage.test.d.ts +2 -0
- package/dist/scanner/detection-coverage.test.d.ts.map +1 -0
- package/dist/scanner/detection-coverage.test.js +216 -0
- package/dist/scanner/detection-coverage.test.js.map +1 -0
- package/dist/scanner/download.d.ts +76 -0
- package/dist/scanner/download.d.ts.map +1 -0
- package/dist/scanner/download.js +339 -0
- package/dist/scanner/download.js.map +1 -0
- package/dist/scanner/download.test.d.ts +2 -0
- package/dist/scanner/download.test.d.ts.map +1 -0
- package/dist/scanner/download.test.js +149 -0
- package/dist/scanner/download.test.js.map +1 -0
- package/dist/scanner/index.d.ts +8 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +167 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/index.test.d.ts +2 -0
- package/dist/scanner/index.test.d.ts.map +1 -0
- package/dist/scanner/index.test.js +71 -0
- package/dist/scanner/index.test.js.map +1 -0
- package/dist/scanner/loaders/zoo.d.ts +3 -0
- package/dist/scanner/loaders/zoo.d.ts.map +1 -0
- package/dist/scanner/loaders/zoo.js +112 -0
- package/dist/scanner/loaders/zoo.js.map +1 -0
- package/dist/scanner/types.d.ts +118 -0
- package/dist/scanner/types.d.ts.map +1 -0
- package/dist/scanner/types.js +2 -0
- package/dist/scanner/types.js.map +1 -0
- package/dist/scanner/utils.d.ts +14 -0
- package/dist/scanner/utils.d.ts.map +1 -0
- package/dist/scanner/utils.js +25 -0
- package/dist/scanner/utils.js.map +1 -0
- package/dist/scanner/vsix.d.ts +6 -0
- package/dist/scanner/vsix.d.ts.map +1 -0
- package/dist/scanner/vsix.js +213 -0
- package/dist/scanner/vsix.js.map +1 -0
- package/dist/scanner/vsix.test.d.ts +2 -0
- package/dist/scanner/vsix.test.d.ts.map +1 -0
- package/dist/scanner/vsix.test.js +355 -0
- package/dist/scanner/vsix.test.js.map +1 -0
- package/package.json +60 -0
- package/zoo/blocklist/extensions.json +201 -0
- package/zoo/iocs/blockchain-extensions.txt +21 -0
- package/zoo/iocs/c2-domains.txt +50 -0
- package/zoo/iocs/c2-ips.txt +24 -0
- package/zoo/iocs/hashes.txt +47 -0
- package/zoo/iocs/malicious-npm.txt +85 -0
- package/zoo/iocs/wallets.txt +18 -0
- package/zoo/signatures/yara/README.md +46 -0
- package/zoo/signatures/yara/blockchain_c2.yar +48 -0
- package/zoo/signatures/yara/code_execution.yar +165 -0
- package/zoo/signatures/yara/credential_harvesting.yar +116 -0
- package/zoo/signatures/yara/crypto_wallet_targeting.yar +92 -0
- package/zoo/signatures/yara/data_exfiltration.yar +207 -0
- package/zoo/signatures/yara/google_calendar_c2.yar +187 -0
- package/zoo/signatures/yara/messaging_c2.yar +103 -0
- package/zoo/signatures/yara/multi_stage_attacks.yar +331 -0
- package/zoo/signatures/yara/obfuscation_patterns.yar +208 -0
- package/zoo/signatures/yara/powershell_attacks.yar +116 -0
- package/zoo/signatures/yara/rat_capabilities.yar +243 -0
- package/zoo/signatures/yara/self_propagation.yar +239 -0
- package/zoo/signatures/yara/unicode_stealth.yar +48 -0
- package/zoo/signatures/yara/websocket_c2.yar +83 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { detectBundler } from "../bundler.js";
|
|
2
|
+
import { isScannable, SCANNABLE_EXTENSIONS_UNICODE } from "../constants.js";
|
|
3
|
+
function findLineAndColumn(content, index) {
|
|
4
|
+
const beforeMatch = content.slice(0, index);
|
|
5
|
+
const lines = beforeMatch.split("\n");
|
|
6
|
+
const line = lines.length;
|
|
7
|
+
const column = (lines.at(-1)?.length ?? 0) + 1;
|
|
8
|
+
return { line, column };
|
|
9
|
+
}
|
|
10
|
+
function getContext(content, index, length) {
|
|
11
|
+
const start = Math.max(0, index - 20);
|
|
12
|
+
const end = Math.min(content.length, index + length + 20);
|
|
13
|
+
let ctx = content.slice(start, end);
|
|
14
|
+
if (start > 0)
|
|
15
|
+
ctx = "..." + ctx;
|
|
16
|
+
if (end < content.length)
|
|
17
|
+
ctx = ctx + "...";
|
|
18
|
+
return ctx.replace(/[\n\r]/g, "\\n").slice(0, 80);
|
|
19
|
+
}
|
|
20
|
+
// Zero-width characters: U+200B-200D, U+FEFF
|
|
21
|
+
const ZERO_WIDTH_REGEX = /[\u200B-\u200D\uFEFF]/g;
|
|
22
|
+
// Variation selectors: U+FE00-FE0F (GlassWorm technique)
|
|
23
|
+
const VARIATION_SELECTOR_REGEX = /[\uFE00-\uFE0F]/g;
|
|
24
|
+
// Bidirectional overrides: U+202A-202E (Trojan Source attack)
|
|
25
|
+
const BIDI_OVERRIDE_REGEX = /[\u202A-\u202E]/g;
|
|
26
|
+
// Unicode escapes for ASCII: \u00XX where XX is 20-7E (printable ASCII)
|
|
27
|
+
// This is obfuscation - using unicode escapes for normal characters
|
|
28
|
+
const UNICODE_ASCII_ESCAPE_REGEX = /\\u00[2-7][0-9a-fA-F]/g;
|
|
29
|
+
// Cyrillic homoglyphs that look like Latin letters
|
|
30
|
+
// а(U+0430)/a, с(U+0441)/c, е(U+0435)/e, о(U+043E)/o, р(U+0440)/p, х(U+0445)/x, у(U+0443)/y
|
|
31
|
+
// Also uppercase: А(U+0410)/A, В(U+0412)/B, С(U+0421)/C, Е(U+0415)/E, Н(U+041D)/H, etc.
|
|
32
|
+
const CYRILLIC_LOOKALIKE_REGEX = /[\u0430\u0441\u0435\u043E\u0440\u0445\u0443\u0410\u0412\u0421\u0415\u041D\u041A\u041C\u041E\u0420\u0422\u0425]/g;
|
|
33
|
+
// Additional invisible/confusable characters
|
|
34
|
+
// U+00AD Soft hyphen, U+034F Combining grapheme joiner, U+115F-1160 Hangul fillers
|
|
35
|
+
// U+17B4-17B5 Khmer vowels, U+180E Mongolian vowel separator
|
|
36
|
+
const OTHER_INVISIBLE_REGEX = /[\u00AD\u034F\u115F\u1160\u17B4\u17B5\u180E\u2060-\u2064\u206A-\u206F]/g;
|
|
37
|
+
function detectPattern(content, regex, minMatches = 1) {
|
|
38
|
+
const matches = [];
|
|
39
|
+
const r = new RegExp(regex.source, regex.flags);
|
|
40
|
+
let match;
|
|
41
|
+
while ((match = r.exec(content)) !== null) {
|
|
42
|
+
const { line, column } = findLineAndColumn(content, match.index);
|
|
43
|
+
matches.push({
|
|
44
|
+
line,
|
|
45
|
+
column,
|
|
46
|
+
matched: match[0],
|
|
47
|
+
context: getContext(content, match.index, match[0].length),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return matches.length >= minMatches ? matches : [];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if the file appears to be primarily i18n/localization content.
|
|
54
|
+
* These files naturally have high Unicode diversity and should be treated differently.
|
|
55
|
+
*/
|
|
56
|
+
function isI18nFile(filename, content) {
|
|
57
|
+
// Check filename patterns
|
|
58
|
+
const i18nPatterns = [
|
|
59
|
+
/locales?\//i,
|
|
60
|
+
/i18n\//i,
|
|
61
|
+
/translations?\//i,
|
|
62
|
+
/lang\//i,
|
|
63
|
+
/messages/i,
|
|
64
|
+
/\.l10n\./i,
|
|
65
|
+
/nls\./i,
|
|
66
|
+
];
|
|
67
|
+
if (i18nPatterns.some((p) => p.test(filename))) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
// Check if content looks like localization JSON
|
|
71
|
+
// (has many string keys with translated values)
|
|
72
|
+
if (filename.endsWith(".json")) {
|
|
73
|
+
const keyValuePairs = (content.match(/"[^"]+"\s*:\s*"[^"]+"/g) ?? []).length;
|
|
74
|
+
const lines = content.split("\n").length;
|
|
75
|
+
// If more than 50% of lines are key-value pairs, likely i18n
|
|
76
|
+
if (keyValuePairs / lines > 0.5 && keyValuePairs > 10) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if invisible characters are in proximity to execution patterns.
|
|
84
|
+
* This helps distinguish malicious steganography from benign Unicode use.
|
|
85
|
+
*/
|
|
86
|
+
function hasExecutionInProximity(content, invisibleIndex, proximityChars = 200) {
|
|
87
|
+
const start = Math.max(0, invisibleIndex - proximityChars);
|
|
88
|
+
const end = Math.min(content.length, invisibleIndex + proximityChars);
|
|
89
|
+
const region = content.slice(start, end);
|
|
90
|
+
const execPatterns = [
|
|
91
|
+
/eval\s*\(/i,
|
|
92
|
+
/Function\s*\(/i,
|
|
93
|
+
/exec\s*\(/i,
|
|
94
|
+
/spawn\s*\(/i,
|
|
95
|
+
/execSync\s*\(/i,
|
|
96
|
+
/child_process/i,
|
|
97
|
+
/\.\s*call\s*\(/,
|
|
98
|
+
/\.\s*apply\s*\(/,
|
|
99
|
+
/new\s+Function/i,
|
|
100
|
+
/atob\s*\(/i,
|
|
101
|
+
/Buffer\.from/i,
|
|
102
|
+
];
|
|
103
|
+
return execPatterns.some((p) => p.test(region));
|
|
104
|
+
}
|
|
105
|
+
// Rules that should be skipped for bundled code (they're noisy on minified i18n)
|
|
106
|
+
const SKIP_FOR_BUNDLED = new Set([
|
|
107
|
+
"ZERO_WIDTH_CHARS",
|
|
108
|
+
"CYRILLIC_HOMOGLYPH",
|
|
109
|
+
"OTHER_INVISIBLE_CHARS",
|
|
110
|
+
"UNICODE_ASCII_ESCAPE",
|
|
111
|
+
]);
|
|
112
|
+
const UNICODE_RULES = [
|
|
113
|
+
{
|
|
114
|
+
id: "ZERO_WIDTH_CHARS",
|
|
115
|
+
title: "Zero-width characters detected",
|
|
116
|
+
description: "File contains zero-width Unicode characters (U+200B-200D, U+FEFF). These invisible characters can hide malicious code or be used for steganography.",
|
|
117
|
+
severity: "high",
|
|
118
|
+
detect: (content) => detectPattern(content, ZERO_WIDTH_REGEX, 3),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: "VARIATION_SELECTOR",
|
|
122
|
+
title: "Unicode variation selectors detected (GlassWorm technique)",
|
|
123
|
+
description: "File contains many Unicode variation selectors (U+FE00-FE0F). GlassWorm malware uses these to hide executable code. A few variation selectors are normal (emoji formatting), but many indicates hidden data.",
|
|
124
|
+
severity: "critical",
|
|
125
|
+
// Require at least 10 variation selectors - a few are normal for emojis
|
|
126
|
+
// GlassWorm attack uses hundreds to encode hidden payloads
|
|
127
|
+
detect: (content) => detectPattern(content, VARIATION_SELECTOR_REGEX, 10),
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "BIDI_OVERRIDE",
|
|
131
|
+
title: "Bidirectional text override detected (Trojan Source)",
|
|
132
|
+
description: "File contains Unicode bidirectional override characters (U+202A-202E). This is the Trojan Source attack technique that can make malicious code appear benign by reordering how text is displayed.",
|
|
133
|
+
severity: "critical",
|
|
134
|
+
detect: (content) => detectPattern(content, BIDI_OVERRIDE_REGEX, 1),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "UNICODE_ASCII_ESCAPE",
|
|
138
|
+
title: "Unicode escape sequences for ASCII characters",
|
|
139
|
+
description: "File uses Unicode escape sequences (\\u00XX) for normal ASCII characters. This is a common obfuscation technique to hide string contents from static analysis.",
|
|
140
|
+
severity: "medium",
|
|
141
|
+
detect: (content) => detectPattern(content, UNICODE_ASCII_ESCAPE_REGEX, 5),
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "CYRILLIC_HOMOGLYPH",
|
|
145
|
+
title: "Cyrillic homoglyph characters detected",
|
|
146
|
+
description: "File contains Cyrillic characters that visually resemble Latin letters (e.g., Cyrillic 'а' vs Latin 'a'). This can be used for homoglyph attacks to disguise malicious identifiers.",
|
|
147
|
+
severity: "high",
|
|
148
|
+
detect: (content, filename) => {
|
|
149
|
+
// Only flag in code files, not documentation
|
|
150
|
+
if (filename.endsWith(".md") || filename.endsWith(".txt")) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
// Skip i18n files which legitimately contain Cyrillic
|
|
154
|
+
if (isI18nFile(filename, content)) {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
return detectPattern(content, CYRILLIC_LOOKALIKE_REGEX, 1);
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "OTHER_INVISIBLE_CHARS",
|
|
162
|
+
title: "Other invisible Unicode characters detected",
|
|
163
|
+
description: "File contains invisible Unicode characters (soft hyphens, combining marks, format controls). These can be used to hide malicious content.",
|
|
164
|
+
severity: "medium",
|
|
165
|
+
detect: (content) => detectPattern(content, OTHER_INVISIBLE_REGEX, 3),
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "INVISIBLE_CODE_EXECUTION",
|
|
169
|
+
title: "Invisible characters near code execution",
|
|
170
|
+
description: "File contains many invisible Unicode characters in proximity to code execution functions (eval, Function, exec). This is a strong indicator of hidden malicious code.",
|
|
171
|
+
severity: "critical",
|
|
172
|
+
detect: (content, filename) => {
|
|
173
|
+
// Skip i18n files - these legitimately contain RTL and combining chars
|
|
174
|
+
if (isI18nFile(filename, content)) {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
// Check for truly suspicious invisible chars (exclude ZWNJ which is common in RTL text)
|
|
178
|
+
// Only flag: variation selectors (GlassWorm), bidi overrides (Trojan Source)
|
|
179
|
+
// Note: Single ZWSPs are common in UI libraries as constants, so exclude them
|
|
180
|
+
const suspiciousInvisible = new RegExp(`${VARIATION_SELECTOR_REGEX.source}|${BIDI_OVERRIDE_REGEX.source}`, "g");
|
|
181
|
+
// Require at least 5 invisible chars - single chars are often legitimate
|
|
182
|
+
const matches = detectPattern(content, suspiciousInvisible, 5);
|
|
183
|
+
// Only return matches that are actually near execution patterns
|
|
184
|
+
// (within 200 chars of eval, Function, exec, etc.)
|
|
185
|
+
return matches.filter((m) => {
|
|
186
|
+
const matchIndex = content.slice(0, m.line).split("\n").length > 1
|
|
187
|
+
? content
|
|
188
|
+
.split("\n")
|
|
189
|
+
.slice(0, m.line - 1)
|
|
190
|
+
.join("\n").length + m.column
|
|
191
|
+
: m.column;
|
|
192
|
+
return hasExecutionInProximity(content, matchIndex);
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
export function checkUnicode(contents) {
|
|
198
|
+
const findings = [];
|
|
199
|
+
const seenFindings = new Set();
|
|
200
|
+
for (const [filename, buffer] of contents.files) {
|
|
201
|
+
if (!isScannable(filename, SCANNABLE_EXTENSIONS_UNICODE))
|
|
202
|
+
continue;
|
|
203
|
+
const content = buffer.toString("utf8");
|
|
204
|
+
// Detect bundled code
|
|
205
|
+
const bundlerInfo = detectBundler(content, filename);
|
|
206
|
+
for (const rule of UNICODE_RULES) {
|
|
207
|
+
// Skip noisy rules for bundled code
|
|
208
|
+
if (bundlerInfo.isBundled && SKIP_FOR_BUNDLED.has(rule.id)) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const matches = rule.detect(content, filename);
|
|
212
|
+
if (matches.length === 0)
|
|
213
|
+
continue;
|
|
214
|
+
// Create one finding per rule per file, with all matches in metadata
|
|
215
|
+
const key = `${rule.id}:${filename}`;
|
|
216
|
+
if (seenFindings.has(key))
|
|
217
|
+
continue;
|
|
218
|
+
seenFindings.add(key);
|
|
219
|
+
const firstMatch = matches[0];
|
|
220
|
+
if (!firstMatch)
|
|
221
|
+
continue;
|
|
222
|
+
findings.push({
|
|
223
|
+
id: rule.id,
|
|
224
|
+
title: rule.title,
|
|
225
|
+
description: rule.description,
|
|
226
|
+
severity: rule.severity,
|
|
227
|
+
category: "unicode",
|
|
228
|
+
location: {
|
|
229
|
+
file: filename,
|
|
230
|
+
line: firstMatch.line,
|
|
231
|
+
column: firstMatch.column,
|
|
232
|
+
},
|
|
233
|
+
metadata: {
|
|
234
|
+
matchCount: matches.length,
|
|
235
|
+
firstMatch: firstMatch.context,
|
|
236
|
+
codePoints: matches
|
|
237
|
+
.slice(0, 5)
|
|
238
|
+
.map((m) => [...m.matched]
|
|
239
|
+
.map((c) => `U+${c.codePointAt(0)?.toString(16).toUpperCase().padStart(4, "0")}`)
|
|
240
|
+
.join(", ")),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return findings;
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=unicode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unicode.js","sourceRoot":"","sources":["../../../src/scanner/checks/unicode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unicode.test.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/unicode.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { checkUnicode } from "./unicode.js";
|
|
3
|
+
function makeContents(files) {
|
|
4
|
+
const manifest = {
|
|
5
|
+
name: "test-extension",
|
|
6
|
+
publisher: "test",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
};
|
|
9
|
+
const fileMap = new Map();
|
|
10
|
+
for (const [name, content] of Object.entries(files)) {
|
|
11
|
+
fileMap.set(name, Buffer.from(content, "utf8"));
|
|
12
|
+
}
|
|
13
|
+
return { manifest, files: fileMap, basePath: "/test" };
|
|
14
|
+
}
|
|
15
|
+
describe("checkUnicode", () => {
|
|
16
|
+
describe("zero-width characters", () => {
|
|
17
|
+
it("detects zero-width space characters (U+200B)", () => {
|
|
18
|
+
// 3+ occurrences needed
|
|
19
|
+
const content = "const x\u200B = 'a\u200Bb\u200Bc';";
|
|
20
|
+
const contents = makeContents({ "extension.js": content });
|
|
21
|
+
const findings = checkUnicode(contents);
|
|
22
|
+
expect(findings).toHaveLength(1);
|
|
23
|
+
expect(findings.some((f) => f.id === "ZERO_WIDTH_CHARS")).toBe(true);
|
|
24
|
+
expect(findings.some((f) => f.severity === "high")).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it("detects zero-width joiner (U+200D)", () => {
|
|
27
|
+
const content = "const x\u200D = 'a\u200Db\u200Dc';";
|
|
28
|
+
const contents = makeContents({ "extension.js": content });
|
|
29
|
+
const findings = checkUnicode(contents);
|
|
30
|
+
expect(findings).toHaveLength(1);
|
|
31
|
+
expect(findings.some((f) => f.id === "ZERO_WIDTH_CHARS")).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it("ignores files with only 1-2 zero-width chars", () => {
|
|
34
|
+
const content = "const x\u200B = 'a\u200Bb';";
|
|
35
|
+
const contents = makeContents({ "extension.js": content });
|
|
36
|
+
const findings = checkUnicode(contents);
|
|
37
|
+
const zeroWidthFinding = findings.find((f) => f.id === "ZERO_WIDTH_CHARS");
|
|
38
|
+
expect(zeroWidthFinding).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("variation selectors (GlassWorm technique)", () => {
|
|
42
|
+
it("detects variation selectors (U+FE00-FE0F) when >= 10 present", () => {
|
|
43
|
+
// Implementation requires 10+ variation selectors (GlassWorm uses hundreds)
|
|
44
|
+
// A few are normal for emoji formatting
|
|
45
|
+
const content = "a\uFE00b\uFE01c\uFE02d\uFE03e\uFE04f\uFE05g\uFE06h\uFE07i\uFE08j\uFE09k\uFE0A";
|
|
46
|
+
const contents = makeContents({ "extension.js": content });
|
|
47
|
+
const findings = checkUnicode(contents);
|
|
48
|
+
expect(findings).toHaveLength(1);
|
|
49
|
+
expect(findings.some((f) => f.id === "VARIATION_SELECTOR")).toBe(true);
|
|
50
|
+
expect(findings.some((f) => f.severity === "critical")).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
it("ignores few variation selectors (normal for emoji)", () => {
|
|
53
|
+
// Less than 10 variation selectors should be ignored (normal emoji use)
|
|
54
|
+
const content = "a\uFE00b\uFE01c\uFE02d\uFE0F";
|
|
55
|
+
const contents = makeContents({ "extension.js": content });
|
|
56
|
+
const findings = checkUnicode(contents);
|
|
57
|
+
expect(findings.find((f) => f.id === "VARIATION_SELECTOR")).toBeUndefined();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("bidirectional overrides (Trojan Source)", () => {
|
|
61
|
+
it("detects left-to-right override (U+202D)", () => {
|
|
62
|
+
const content = "const admin\u202D = true;";
|
|
63
|
+
const contents = makeContents({ "extension.js": content });
|
|
64
|
+
const findings = checkUnicode(contents);
|
|
65
|
+
expect(findings).toHaveLength(1);
|
|
66
|
+
expect(findings.some((f) => f.id === "BIDI_OVERRIDE")).toBe(true);
|
|
67
|
+
expect(findings.some((f) => f.severity === "critical")).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
it("detects right-to-left override (U+202E)", () => {
|
|
70
|
+
const content = "const admin\u202E = true;";
|
|
71
|
+
const contents = makeContents({ "extension.js": content });
|
|
72
|
+
const findings = checkUnicode(contents);
|
|
73
|
+
expect(findings).toHaveLength(1);
|
|
74
|
+
expect(findings.some((f) => f.id === "BIDI_OVERRIDE")).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe("Unicode ASCII escapes", () => {
|
|
78
|
+
it("detects excessive Unicode escapes for ASCII", () => {
|
|
79
|
+
// Using \\u00XX for normal printable ASCII is suspicious
|
|
80
|
+
const content = "const x = '\\u0068\\u0065\\u006c\\u006c\\u006f\\u0077';"; // "hellow"
|
|
81
|
+
const contents = makeContents({ "extension.js": content });
|
|
82
|
+
const findings = checkUnicode(contents);
|
|
83
|
+
expect(findings).toHaveLength(1);
|
|
84
|
+
expect(findings.some((f) => f.id === "UNICODE_ASCII_ESCAPE")).toBe(true);
|
|
85
|
+
expect(findings.some((f) => f.severity === "medium")).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
it("ignores files with few Unicode escapes", () => {
|
|
88
|
+
const content = "const x = '\\u0068\\u0065';"; // Only 2 escapes
|
|
89
|
+
const contents = makeContents({ "extension.js": content });
|
|
90
|
+
const findings = checkUnicode(contents);
|
|
91
|
+
const escapeFinding = findings.find((f) => f.id === "UNICODE_ASCII_ESCAPE");
|
|
92
|
+
expect(escapeFinding).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe("Cyrillic homoglyphs", () => {
|
|
96
|
+
it("detects Cyrillic 'а' (U+0430) that looks like Latin 'a'", () => {
|
|
97
|
+
const content = "const \u0430dmin = true;"; // Cyrillic а
|
|
98
|
+
const contents = makeContents({ "extension.js": content });
|
|
99
|
+
const findings = checkUnicode(contents);
|
|
100
|
+
expect(findings).toHaveLength(1);
|
|
101
|
+
expect(findings.some((f) => f.id === "CYRILLIC_HOMOGLYPH")).toBe(true);
|
|
102
|
+
expect(findings.some((f) => f.severity === "high")).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
it("detects Cyrillic 'е' (U+0435) that looks like Latin 'e'", () => {
|
|
105
|
+
const content = "const s\u0435cret = 'password';"; // Cyrillic е
|
|
106
|
+
const contents = makeContents({ "extension.js": content });
|
|
107
|
+
const findings = checkUnicode(contents);
|
|
108
|
+
expect(findings).toHaveLength(1);
|
|
109
|
+
expect(findings.some((f) => f.id === "CYRILLIC_HOMOGLYPH")).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it("ignores Cyrillic in markdown files", () => {
|
|
112
|
+
const content = "# Hello \u0430nd welcome"; // Cyrillic а in markdown
|
|
113
|
+
const contents = makeContents({ "README.md": content });
|
|
114
|
+
const findings = checkUnicode(contents);
|
|
115
|
+
const cyrillicFinding = findings.find((f) => f.id === "CYRILLIC_HOMOGLYPH");
|
|
116
|
+
expect(cyrillicFinding).toBeUndefined();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe("invisible characters near code execution", () => {
|
|
120
|
+
it("flags many invisible chars in file with eval()", () => {
|
|
121
|
+
// Implementation requires 5+ invisible chars near execution patterns
|
|
122
|
+
const content = "const payload\uFE01\uFE02\uFE03\uFE04\uFE05 = 'data';\neval(payload);";
|
|
123
|
+
const contents = makeContents({ "extension.js": content });
|
|
124
|
+
const findings = checkUnicode(contents);
|
|
125
|
+
expect(findings.some((f) => f.id === "INVISIBLE_CODE_EXECUTION")).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
it("flags many invisible chars in file with Function()", () => {
|
|
128
|
+
const content = "const x\uFE01\uFE02\uFE03\uFE04\uFE05 = 1;\nnew Function('return x')();";
|
|
129
|
+
const contents = makeContents({ "extension.js": content });
|
|
130
|
+
const findings = checkUnicode(contents);
|
|
131
|
+
expect(findings.some((f) => f.id === "INVISIBLE_CODE_EXECUTION")).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
it("flags many invisible chars in file with child_process", () => {
|
|
134
|
+
const content = "require('child_process').exec('ls');\nconst hidden\uFE01\uFE02\uFE03\uFE04\uFE05 = 1;";
|
|
135
|
+
const contents = makeContents({ "extension.js": content });
|
|
136
|
+
const findings = checkUnicode(contents);
|
|
137
|
+
expect(findings.some((f) => f.id === "INVISIBLE_CODE_EXECUTION")).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
it("does NOT flag few invisible chars even with execution context", () => {
|
|
140
|
+
// Single invisible char isn't enough - needs 5+
|
|
141
|
+
const content = "const x\uFE01 = 1;\neval(x);";
|
|
142
|
+
const contents = makeContents({ "extension.js": content });
|
|
143
|
+
const findings = checkUnicode(contents);
|
|
144
|
+
expect(findings.some((f) => f.id === "INVISIBLE_CODE_EXECUTION")).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe("file filtering", () => {
|
|
148
|
+
it("scans JavaScript files", () => {
|
|
149
|
+
// Use bidi override which triggers with just 1 occurrence
|
|
150
|
+
const content = "const admin\u202E = true;";
|
|
151
|
+
const contents = makeContents({ "test.js": content });
|
|
152
|
+
const findings = checkUnicode(contents);
|
|
153
|
+
expect(findings.length).toBeGreaterThan(0);
|
|
154
|
+
});
|
|
155
|
+
it("scans TypeScript files", () => {
|
|
156
|
+
const content = "const admin\u202E: boolean = true;";
|
|
157
|
+
const contents = makeContents({ "test.ts": content });
|
|
158
|
+
const findings = checkUnicode(contents);
|
|
159
|
+
expect(findings.length).toBeGreaterThan(0);
|
|
160
|
+
});
|
|
161
|
+
it("scans JSON files", () => {
|
|
162
|
+
const content = '{"key\u202E": "value"}';
|
|
163
|
+
const contents = makeContents({ "test.json": content });
|
|
164
|
+
const findings = checkUnicode(contents);
|
|
165
|
+
expect(findings.length).toBeGreaterThan(0);
|
|
166
|
+
});
|
|
167
|
+
it("ignores binary files", () => {
|
|
168
|
+
// Even critical patterns should be ignored in binary files
|
|
169
|
+
const content = "\u202E\u202D\u202C";
|
|
170
|
+
const contents = makeContents({ "test.png": content });
|
|
171
|
+
const findings = checkUnicode(contents);
|
|
172
|
+
expect(findings).toHaveLength(0);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe("metadata", () => {
|
|
176
|
+
it("includes match count in metadata", () => {
|
|
177
|
+
// Use BIDI_OVERRIDE which triggers with 1+ occurrences
|
|
178
|
+
const content = "a\u202Db\u202Ec\u202D";
|
|
179
|
+
const contents = makeContents({ "test.js": content });
|
|
180
|
+
const findings = checkUnicode(contents);
|
|
181
|
+
const finding = findings.find((f) => f.id === "BIDI_OVERRIDE");
|
|
182
|
+
expect(finding?.metadata?.["matchCount"]).toBe(3);
|
|
183
|
+
});
|
|
184
|
+
it("includes code points in metadata", () => {
|
|
185
|
+
const content = "const admin\u202E = true;";
|
|
186
|
+
const contents = makeContents({ "test.js": content });
|
|
187
|
+
const findings = checkUnicode(contents);
|
|
188
|
+
const finding = findings.find((f) => f.id === "BIDI_OVERRIDE");
|
|
189
|
+
const codePoints = finding?.metadata?.["codePoints"];
|
|
190
|
+
expect(codePoints).toBeDefined();
|
|
191
|
+
expect(codePoints?.some((cp) => cp.includes("202E"))).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
it("includes line number in location", () => {
|
|
194
|
+
const content = "line1\nline2\nconst admin\u202E = true;";
|
|
195
|
+
const contents = makeContents({ "test.js": content });
|
|
196
|
+
const findings = checkUnicode(contents);
|
|
197
|
+
const finding = findings.find((f) => f.id === "BIDI_OVERRIDE");
|
|
198
|
+
expect(finding?.location?.line).toBe(3);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=unicode.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unicode.test.js","sourceRoot":"","sources":["../../../src/scanner/checks/unicode.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,SAAS,YAAY,CAAC,KAA6B;IACjD,MAAM,QAAQ,GAAiB;QAC7B,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,MAAM;QACjB,OAAO,EAAE,OAAO;KACjB,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AACzD,CAAC;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,wBAAwB;YACxB,MAAM,OAAO,GAAG,oCAAoC,CAAC;YACrD,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,OAAO,GAAG,oCAAoC,CAAC;YACrD,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,OAAO,GAAG,6BAA6B,CAAC;YAC9C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,kBAAkB,CAAC,CAAC;YAE3E,MAAM,CAAC,gBAAgB,CAAC,CAAC,aAAa,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACzD,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;YACtE,4EAA4E;YAC5E,wCAAwC;YACxC,MAAM,OAAO,GACX,+EAA+E,CAAC;YAClF,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;YAC5D,wEAAwE;YACxE,MAAM,OAAO,GAAG,8BAA8B,CAAC;YAC/C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,oBAAoB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC9E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACvD,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,OAAO,GAAG,2BAA2B,CAAC;YAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,OAAO,GAAG,2BAA2B,CAAC;YAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,yDAAyD;YACzD,MAAM,OAAO,GAAG,yDAAyD,CAAC,CAAC,WAAW;YACtF,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,OAAO,GAAG,6BAA6B,CAAC,CAAC,iBAAiB;YAChE,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,sBAAsB,CAAC,CAAC;YAE5E,MAAM,CAAC,aAAa,CAAC,CAAC,aAAa,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,OAAO,GAAG,0BAA0B,CAAC,CAAC,aAAa;YACzD,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,OAAO,GAAG,iCAAiC,CAAC,CAAC,aAAa;YAChE,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;YAC5C,MAAM,OAAO,GAAG,0BAA0B,CAAC,CAAC,yBAAyB;YACrE,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;YAExD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,oBAAoB,CAAC,CAAC;YAE5E,MAAM,CAAC,eAAe,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACxD,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACxD,qEAAqE;YACrE,MAAM,OAAO,GAAG,uEAAuE,CAAC;YACxF,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;YAC5D,MAAM,OAAO,GAAG,yEAAyE,CAAC;YAC1F,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;YAC/D,MAAM,OAAO,GACX,uFAAuF,CAAC;YAC1F,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;YACvE,gDAAgD;YAChD,MAAM,OAAO,GAAG,8BAA8B,CAAC;YAC/C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC;YAE3D,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YAExC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;YAChC,0DAA0D;YAC1D,MAAM,OAAO,GAAG,2BAA2B,CAAC;YAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAEtD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;YAChC,MAAM,OAAO,GAAG,oCAAoC,CAAC;YACrD,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAEtD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;YAC1B,MAAM,OAAO,GAAG,wBAAwB,CAAC;YACzC,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;YAExD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;YAC9B,2DAA2D;YAC3D,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACrC,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;YAEvD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,uDAAuD;YACvD,MAAM,OAAO,GAAG,uBAAuB,CAAC;YACxC,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAEtD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC;YAE/D,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,OAAO,GAAG,2BAA2B,CAAC;YAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAEtD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC;YAC/D,MAAM,UAAU,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,YAAY,CAAyB,CAAC;YAE7E,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YACjC,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,OAAO,GAAG,yCAAyC,CAAC;YAC1D,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;YAEtD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC;YAE/D,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Finding, VsixContents } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Get the default YARA rules directory (cached)
|
|
4
|
+
*/
|
|
5
|
+
export declare function getDefaultYaraRulesDir(): Promise<string>;
|
|
6
|
+
export declare const DEFAULT_YARA_RULES_DIR: string;
|
|
7
|
+
/**
|
|
8
|
+
* Check if YARA-X is installed and available
|
|
9
|
+
*/
|
|
10
|
+
export declare function isYaraAvailable(): Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Get YARA-X version string
|
|
13
|
+
*/
|
|
14
|
+
export declare function getYaraVersion(): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* List all YARA rule files in a directory
|
|
17
|
+
*/
|
|
18
|
+
export declare function listYaraRules(rulesDir: string): Promise<string[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Run YARA rules against extension contents
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkYara(contents: VsixContents, rulesDir?: string): Promise<Finding[]>;
|
|
23
|
+
//# sourceMappingURL=yara.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yara.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/yara.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA+GzD;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK9D;AAGD,eAAO,MAAM,sBAAsB,QAQlC,CAAC;AASF;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAOxD;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAO7D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOvE;AAqHD;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA+G7F"}
|