@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,298 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { checkDomains, checkHashes, checkIocs, checkIps, checkWallets, isLikelySolanaAddress, } from "./ioc.js";
|
|
3
|
+
function makeContents(files) {
|
|
4
|
+
const manifest = {
|
|
5
|
+
name: "test",
|
|
6
|
+
publisher: "test",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
};
|
|
9
|
+
const fileMap = new Map();
|
|
10
|
+
fileMap.set("package.json", Buffer.from(JSON.stringify(manifest)));
|
|
11
|
+
for (const [name, content] of Object.entries(files)) {
|
|
12
|
+
fileMap.set(name, Buffer.from(content));
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
manifest,
|
|
16
|
+
files: fileMap,
|
|
17
|
+
basePath: "/test",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function makeZooData(overrides = {}) {
|
|
21
|
+
return {
|
|
22
|
+
blocklist: [],
|
|
23
|
+
hashes: new Set(),
|
|
24
|
+
domains: new Set(),
|
|
25
|
+
ips: new Set(),
|
|
26
|
+
maliciousNpmPackages: new Set(),
|
|
27
|
+
wallets: new Set(),
|
|
28
|
+
blockchainAllowlist: new Set(),
|
|
29
|
+
...overrides,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function makeContentsWithPublisher(files, publisher, name) {
|
|
33
|
+
const manifest = {
|
|
34
|
+
name,
|
|
35
|
+
publisher,
|
|
36
|
+
version: "1.0.0",
|
|
37
|
+
};
|
|
38
|
+
const fileMap = new Map();
|
|
39
|
+
fileMap.set("package.json", Buffer.from(JSON.stringify(manifest)));
|
|
40
|
+
for (const [fname, content] of Object.entries(files)) {
|
|
41
|
+
fileMap.set(fname, Buffer.from(content));
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
manifest,
|
|
45
|
+
files: fileMap,
|
|
46
|
+
basePath: "/test",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
describe("checkHashes", () => {
|
|
50
|
+
it("detects known malware hash", () => {
|
|
51
|
+
const contents = makeContents({ "malware.js": "malicious code" });
|
|
52
|
+
// Get hash of the malware.js file content
|
|
53
|
+
const malwareHash = "69d58c3edfcb35a3b17e38b0ed3c86e8a5f5e5d0c7e9b8d7b4a3b2c1d0e9f8a7";
|
|
54
|
+
const knownHashes = new Set([malwareHash]);
|
|
55
|
+
// Compute the actual hash of "malicious code"
|
|
56
|
+
const crypto = require("node:crypto");
|
|
57
|
+
const actualHash = crypto.createHash("sha256").update("malicious code").digest("hex");
|
|
58
|
+
knownHashes.add(actualHash);
|
|
59
|
+
const findings = checkHashes(contents, knownHashes);
|
|
60
|
+
expect(findings.some((f) => f.id === "KNOWN_MALWARE_HASH")).toBe(true);
|
|
61
|
+
expect(findings[0]?.severity).toBe("critical");
|
|
62
|
+
});
|
|
63
|
+
it("does not flag unknown files", () => {
|
|
64
|
+
const contents = makeContents({ "clean.js": "clean code" });
|
|
65
|
+
const knownHashes = new Set([
|
|
66
|
+
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
67
|
+
]);
|
|
68
|
+
const findings = checkHashes(contents, knownHashes);
|
|
69
|
+
expect(findings).toHaveLength(0);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("checkDomains", () => {
|
|
73
|
+
it("detects known C2 domain in JS file", () => {
|
|
74
|
+
const contents = makeContents({
|
|
75
|
+
"extension.js": 'fetch("https://evil-c2.example.com/exfil")',
|
|
76
|
+
});
|
|
77
|
+
const knownDomains = new Set(["evil-c2.example.com"]);
|
|
78
|
+
const findings = checkDomains(contents, knownDomains);
|
|
79
|
+
expect(findings.some((f) => f.id === "KNOWN_C2_DOMAIN")).toBe(true);
|
|
80
|
+
expect(findings[0]?.severity).toBe("critical");
|
|
81
|
+
expect(findings[0]?.metadata?.["domain"]).toBe("evil-c2.example.com");
|
|
82
|
+
});
|
|
83
|
+
it("detects domain in JSON file", () => {
|
|
84
|
+
const contents = makeContents({
|
|
85
|
+
"config.json": '{"url": "https://malware.badsite.net/api"}',
|
|
86
|
+
});
|
|
87
|
+
const knownDomains = new Set(["malware.badsite.net"]);
|
|
88
|
+
const findings = checkDomains(contents, knownDomains);
|
|
89
|
+
expect(findings.some((f) => f.id === "KNOWN_C2_DOMAIN")).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
it("skips non-scannable files", () => {
|
|
92
|
+
const contents = makeContents({
|
|
93
|
+
"image.png": "evil-c2.example.com",
|
|
94
|
+
});
|
|
95
|
+
const knownDomains = new Set(["evil-c2.example.com"]);
|
|
96
|
+
const findings = checkDomains(contents, knownDomains);
|
|
97
|
+
expect(findings).toHaveLength(0);
|
|
98
|
+
});
|
|
99
|
+
it("does not flag unknown domains", () => {
|
|
100
|
+
const contents = makeContents({
|
|
101
|
+
"extension.js": 'fetch("https://api.github.com/repos")',
|
|
102
|
+
});
|
|
103
|
+
const knownDomains = new Set(["evil-c2.example.com"]);
|
|
104
|
+
const findings = checkDomains(contents, knownDomains);
|
|
105
|
+
expect(findings).toHaveLength(0);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe("checkIps", () => {
|
|
109
|
+
it("detects known C2 IP address", () => {
|
|
110
|
+
const contents = makeContents({
|
|
111
|
+
"extension.js": 'const server = "185.234.123.45:8080";',
|
|
112
|
+
});
|
|
113
|
+
const knownIps = new Set(["185.234.123.45"]);
|
|
114
|
+
const findings = checkIps(contents, knownIps);
|
|
115
|
+
expect(findings.some((f) => f.id === "KNOWN_C2_IP")).toBe(true);
|
|
116
|
+
expect(findings[0]?.severity).toBe("critical");
|
|
117
|
+
expect(findings[0]?.metadata?.["ip"]).toBe("185.234.123.45");
|
|
118
|
+
});
|
|
119
|
+
it("ignores private/localhost IPs", () => {
|
|
120
|
+
const contents = makeContents({
|
|
121
|
+
"extension.js": 'const local = "127.0.0.1"; const private = "192.168.1.1";',
|
|
122
|
+
});
|
|
123
|
+
const knownIps = new Set(["127.0.0.1", "192.168.1.1"]);
|
|
124
|
+
const findings = checkIps(contents, knownIps);
|
|
125
|
+
// These should be filtered out by isValidIp
|
|
126
|
+
expect(findings).toHaveLength(0);
|
|
127
|
+
});
|
|
128
|
+
it("does not flag unknown IPs", () => {
|
|
129
|
+
const contents = makeContents({
|
|
130
|
+
"extension.js": 'const api = "8.8.8.8";',
|
|
131
|
+
});
|
|
132
|
+
const knownIps = new Set(["185.234.123.45"]);
|
|
133
|
+
const findings = checkIps(contents, knownIps);
|
|
134
|
+
expect(findings).toHaveLength(0);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe("checkWallets", () => {
|
|
138
|
+
it("detects Bitcoin wallet address", () => {
|
|
139
|
+
const contents = makeContents({
|
|
140
|
+
"extension.js": `const addr = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa";`,
|
|
141
|
+
});
|
|
142
|
+
const findings = checkWallets(contents, new Set());
|
|
143
|
+
expect(findings).toHaveLength(1);
|
|
144
|
+
expect(findings[0]?.id).toBe("CRYPTO_WALLET_DETECTED");
|
|
145
|
+
expect(findings[0]?.severity).toBe("high");
|
|
146
|
+
expect(findings[0]?.metadata?.["currency"]).toBe("BTC");
|
|
147
|
+
});
|
|
148
|
+
it("detects Ethereum wallet address", () => {
|
|
149
|
+
const contents = makeContents({
|
|
150
|
+
"extension.js": `const eth = "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE42";`,
|
|
151
|
+
});
|
|
152
|
+
const findings = checkWallets(contents, new Set());
|
|
153
|
+
expect(findings).toHaveLength(1);
|
|
154
|
+
expect(findings[0]?.id).toBe("CRYPTO_WALLET_DETECTED");
|
|
155
|
+
expect(findings[0]?.metadata?.["currency"]).toBe("ETH");
|
|
156
|
+
});
|
|
157
|
+
it("detects Solana wallet address", () => {
|
|
158
|
+
const contents = makeContents({
|
|
159
|
+
"extension.js": `const sol = "BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC";`,
|
|
160
|
+
});
|
|
161
|
+
const findings = checkWallets(contents, new Set());
|
|
162
|
+
expect(findings).toHaveLength(1);
|
|
163
|
+
expect(findings[0]?.id).toBe("CRYPTO_WALLET_DETECTED");
|
|
164
|
+
expect(findings[0]?.metadata?.["currency"]).toBe("SOL");
|
|
165
|
+
});
|
|
166
|
+
it("escalates known malicious wallet to critical", () => {
|
|
167
|
+
const knownWallet = "BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC";
|
|
168
|
+
const contents = makeContents({
|
|
169
|
+
"extension.js": `const payout = "${knownWallet}";`,
|
|
170
|
+
});
|
|
171
|
+
const knownWallets = new Set([knownWallet]);
|
|
172
|
+
const findings = checkWallets(contents, knownWallets);
|
|
173
|
+
expect(findings).toHaveLength(1);
|
|
174
|
+
expect(findings[0]?.id).toBe("KNOWN_MALWARE_WALLET");
|
|
175
|
+
expect(findings[0]?.severity).toBe("critical");
|
|
176
|
+
expect(findings[0]?.metadata?.["knownMalicious"]).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
it("skips non-scannable files", () => {
|
|
179
|
+
const contents = makeContents({
|
|
180
|
+
"image.png": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
|
|
181
|
+
});
|
|
182
|
+
const findings = checkWallets(contents, new Set());
|
|
183
|
+
expect(findings).toHaveLength(0);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe("checkIocs", () => {
|
|
187
|
+
it("combines all IOC checks", () => {
|
|
188
|
+
const contents = makeContents({
|
|
189
|
+
"extension.js": 'fetch("https://evil.example.com"); const ip = "185.234.123.45";',
|
|
190
|
+
});
|
|
191
|
+
const zooData = makeZooData({
|
|
192
|
+
domains: new Set(["evil.example.com"]),
|
|
193
|
+
ips: new Set(["185.234.123.45"]),
|
|
194
|
+
});
|
|
195
|
+
const findings = checkIocs(contents, zooData);
|
|
196
|
+
expect(findings.some((f) => f.id === "KNOWN_C2_DOMAIN")).toBe(true);
|
|
197
|
+
expect(findings.some((f) => f.id === "KNOWN_C2_IP")).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
it("returns empty for clean extension", () => {
|
|
200
|
+
const contents = makeContents({
|
|
201
|
+
"extension.js": 'console.log("hello world");',
|
|
202
|
+
});
|
|
203
|
+
const zooData = makeZooData({
|
|
204
|
+
domains: new Set(["evil.example.com"]),
|
|
205
|
+
ips: new Set(["185.234.123.45"]),
|
|
206
|
+
hashes: new Set(["0000000000000000000000000000000000000000000000000000000000000000"]),
|
|
207
|
+
});
|
|
208
|
+
const findings = checkIocs(contents, zooData);
|
|
209
|
+
expect(findings).toHaveLength(0);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
describe("isLikelySolanaAddress", () => {
|
|
213
|
+
it("returns true for real Solana addresses with digits distributed throughout", () => {
|
|
214
|
+
// Real SOL address with digits distributed
|
|
215
|
+
expect(isLikelySolanaAddress("FvGoyLXBSPu2pwx788zuWdCtWX7Hy9mwk")).toBe(true);
|
|
216
|
+
expect(isLikelySolanaAddress("BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC")).toBe(true);
|
|
217
|
+
// Address with digits early in string
|
|
218
|
+
expect(isLikelySolanaAddress("9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM")).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
it("returns false for camelCase JS identifiers without digits", () => {
|
|
221
|
+
// Common VS Code API identifiers that triggered false positives
|
|
222
|
+
expect(isLikelySolanaAddress("registerDocumentSemanticTokensProvider")).toBe(false);
|
|
223
|
+
expect(isLikelySolanaAddress("createDiagnosticCollection")).toBe(false);
|
|
224
|
+
expect(isLikelySolanaAddress("onDidChangeConfiguration")).toBe(false);
|
|
225
|
+
expect(isLikelySolanaAddress("executeDocumentSymbolProvider")).toBe(false);
|
|
226
|
+
});
|
|
227
|
+
it("returns false for identifiers with only trailing digits", () => {
|
|
228
|
+
// JS identifiers with trailing numbers (e.g., Type2, Handler3)
|
|
229
|
+
expect(isLikelySolanaAddress("DidChangeConfigurationNotification2")).toBe(false);
|
|
230
|
+
expect(isLikelySolanaAddress("DocumentSymbolRequest1")).toBe(false);
|
|
231
|
+
expect(isLikelySolanaAddress("CompletionItemKind25")).toBe(false);
|
|
232
|
+
});
|
|
233
|
+
it("returns false for strings with only one digit", () => {
|
|
234
|
+
expect(isLikelySolanaAddress("registerDocument1SemanticTokensProvider")).toBe(false);
|
|
235
|
+
expect(isLikelySolanaAddress("abc1defghijklmnopqrstuvwxyzabcdefgh")).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
it("returns false for lowercase hex strings (git hashes, checksums)", () => {
|
|
238
|
+
// Git commit hashes (40 hex chars)
|
|
239
|
+
expect(isLikelySolanaAddress("7751e69b615c6eca6f783a81e292a55725af6b85")).toBe(false);
|
|
240
|
+
expect(isLikelySolanaAddress("85d8f7c97ae473ccb9473f6c8d27e4ec957f4be1")).toBe(false);
|
|
241
|
+
// Shorter integrity checksums
|
|
242
|
+
expect(isLikelySolanaAddress("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")).toBe(false);
|
|
243
|
+
expect(isLikelySolanaAddress("82f85941b4acf562dfb6bb4d69f2d842")).toBe(false);
|
|
244
|
+
});
|
|
245
|
+
it("returns false for identifiers without uppercase letters", () => {
|
|
246
|
+
// Pure lowercase strings should be rejected
|
|
247
|
+
expect(isLikelySolanaAddress("fromcertificatewithsha256thumbprint")).toBe(false);
|
|
248
|
+
expect(isLikelySolanaAddress("pubbbf48e6d78dae54bceaa4acf463299bf")).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
describe("checkWallets blockchain allowlist", () => {
|
|
252
|
+
it("skips wallet detection for allowlisted blockchain extensions", () => {
|
|
253
|
+
const contents = makeContentsWithPublisher({
|
|
254
|
+
"extension.js": `const ens = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e";`,
|
|
255
|
+
}, "JuanBlanco", "solidity");
|
|
256
|
+
const allowlist = new Set(["JuanBlanco.solidity"]);
|
|
257
|
+
const findings = checkWallets(contents, new Set(), allowlist);
|
|
258
|
+
expect(findings).toHaveLength(0);
|
|
259
|
+
});
|
|
260
|
+
it("detects wallets in non-allowlisted extensions", () => {
|
|
261
|
+
const contents = makeContentsWithPublisher({
|
|
262
|
+
"extension.js": `const eth = "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE42";`,
|
|
263
|
+
}, "unknown", "suspicious");
|
|
264
|
+
const allowlist = new Set(["JuanBlanco.solidity"]);
|
|
265
|
+
const findings = checkWallets(contents, new Set(), allowlist);
|
|
266
|
+
expect(findings).toHaveLength(1);
|
|
267
|
+
expect(findings[0]?.id).toBe("CRYPTO_WALLET_DETECTED");
|
|
268
|
+
});
|
|
269
|
+
it("detects wallets when allowlist is undefined", () => {
|
|
270
|
+
const contents = makeContentsWithPublisher({
|
|
271
|
+
"extension.js": `const eth = "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE42";`,
|
|
272
|
+
}, "unknown", "extension");
|
|
273
|
+
const findings = checkWallets(contents, new Set(), undefined);
|
|
274
|
+
expect(findings).toHaveLength(1);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
describe("checkWallets SOL validation", () => {
|
|
278
|
+
it("filters out JS identifiers that match SOL pattern", () => {
|
|
279
|
+
const contents = makeContents({
|
|
280
|
+
"extension.js": `
|
|
281
|
+
vscode.languages.registerDocumentSemanticTokensProvider();
|
|
282
|
+
const handler = DidChangeConfigurationNotification2;
|
|
283
|
+
`,
|
|
284
|
+
});
|
|
285
|
+
const findings = checkWallets(contents, new Set());
|
|
286
|
+
// Should not flag any of these as SOL wallets
|
|
287
|
+
expect(findings).toHaveLength(0);
|
|
288
|
+
});
|
|
289
|
+
it("detects real Solana addresses with digits distributed", () => {
|
|
290
|
+
const contents = makeContents({
|
|
291
|
+
"extension.js": `const wallet = "FvGoyLXBSPu2pwx788zuWdCtWX7Hy9mwk";`,
|
|
292
|
+
});
|
|
293
|
+
const findings = checkWallets(contents, new Set());
|
|
294
|
+
expect(findings).toHaveLength(1);
|
|
295
|
+
expect(findings[0]?.metadata?.["currency"]).toBe("SOL");
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
//# sourceMappingURL=ioc.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ioc.test.js","sourceRoot":"","sources":["../../../src/scanner/checks/ioc.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Finding, VsixManifest } from "../types.js";
|
|
2
|
+
export declare function checkActivationEvents(manifest: VsixManifest): Finding[];
|
|
3
|
+
export declare function checkThemeAbuse(manifest: VsixManifest): Finding[];
|
|
4
|
+
export declare function checkSuspiciousPermissions(manifest: VsixManifest): Finding[];
|
|
5
|
+
export declare function checkManifest(manifest: VsixManifest): Finding[];
|
|
6
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,EAAE,CAmDvE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,EAAE,CAsCjE;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,EAAE,CAkC5E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,EAAE,CAM/D"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export function checkActivationEvents(manifest) {
|
|
2
|
+
const findings = [];
|
|
3
|
+
if (manifest.activationEvents?.includes("*")) {
|
|
4
|
+
findings.push({
|
|
5
|
+
id: "ACTIVATION_WILDCARD",
|
|
6
|
+
title: "Extension activates on all events",
|
|
7
|
+
description: 'Extension uses "activationEvents": ["*"] which activates on every VS Code action. This is often used by malware to ensure immediate execution, but may be legitimate for extensions that need to respond to many different events.',
|
|
8
|
+
severity: "high",
|
|
9
|
+
category: "manifest",
|
|
10
|
+
location: {
|
|
11
|
+
file: "package.json",
|
|
12
|
+
},
|
|
13
|
+
metadata: {
|
|
14
|
+
legitimateUses: ["Extensions with many contribution points", "Global workspace tools"],
|
|
15
|
+
redFlags: [
|
|
16
|
+
"Simple extension with wildcard activation",
|
|
17
|
+
"Combined with suspicious patterns",
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (manifest.activationEvents?.includes("onStartupFinished")) {
|
|
23
|
+
findings.push({
|
|
24
|
+
id: "ACTIVATION_STARTUP",
|
|
25
|
+
title: "Extension activates on startup",
|
|
26
|
+
description: 'Extension uses "onStartupFinished" activation event. Common in extensions that need to initialize early (git integration, status bar items, language servers). Review if early activation is necessary for the extension\'s purpose.',
|
|
27
|
+
severity: "medium",
|
|
28
|
+
category: "manifest",
|
|
29
|
+
location: {
|
|
30
|
+
file: "package.json",
|
|
31
|
+
},
|
|
32
|
+
metadata: {
|
|
33
|
+
legitimateUses: [
|
|
34
|
+
"Git integration",
|
|
35
|
+
"Status bar extensions",
|
|
36
|
+
"Language servers",
|
|
37
|
+
"Background services",
|
|
38
|
+
],
|
|
39
|
+
redFlags: [
|
|
40
|
+
"Combined with network activity on startup",
|
|
41
|
+
"No obvious need for early activation",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return findings;
|
|
47
|
+
}
|
|
48
|
+
export function checkThemeAbuse(manifest) {
|
|
49
|
+
const findings = [];
|
|
50
|
+
const hasMain = Boolean(manifest.main || manifest.browser);
|
|
51
|
+
const hasThemes = (manifest.contributes?.themes?.length ?? 0) > 0 ||
|
|
52
|
+
(manifest.contributes?.iconThemes?.length ?? 0) > 0;
|
|
53
|
+
if (hasThemes && hasMain) {
|
|
54
|
+
findings.push({
|
|
55
|
+
id: "THEME_WITH_CODE",
|
|
56
|
+
title: "Theme extension has code entry point",
|
|
57
|
+
description: "This extension contributes themes/icon themes but also has a code entry point (main/browser). Pure themes don't need executable code. However, some legitimate extensions combine themes with additional functionality (commands, settings sync).",
|
|
58
|
+
severity: "high",
|
|
59
|
+
category: "manifest",
|
|
60
|
+
location: {
|
|
61
|
+
file: "package.json",
|
|
62
|
+
},
|
|
63
|
+
metadata: {
|
|
64
|
+
main: manifest.main,
|
|
65
|
+
browser: manifest.browser,
|
|
66
|
+
themes: manifest.contributes?.themes?.length ?? 0,
|
|
67
|
+
iconThemes: manifest.contributes?.iconThemes?.length ?? 0,
|
|
68
|
+
legitimateUses: [
|
|
69
|
+
"Theme packs with additional commands",
|
|
70
|
+
"Theme switchers",
|
|
71
|
+
"Theme previews",
|
|
72
|
+
],
|
|
73
|
+
redFlags: [
|
|
74
|
+
"Theme-only description but runs code",
|
|
75
|
+
"Network activity from theme extension",
|
|
76
|
+
"Known malware pattern",
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return findings;
|
|
82
|
+
}
|
|
83
|
+
export function checkSuspiciousPermissions(manifest) {
|
|
84
|
+
const findings = [];
|
|
85
|
+
const extensionDependencies = manifest["extensionDependencies"];
|
|
86
|
+
if (extensionDependencies) {
|
|
87
|
+
for (const dep of extensionDependencies) {
|
|
88
|
+
if (dep.includes("remote-ssh") || dep.includes("remote-wsl")) {
|
|
89
|
+
findings.push({
|
|
90
|
+
id: "REMOTE_DEPENDENCY",
|
|
91
|
+
title: "Extension depends on remote access extension",
|
|
92
|
+
description: `Extension depends on "${dep}" which provides remote system access. This is expected for extensions that enhance remote development workflows.`,
|
|
93
|
+
severity: "medium",
|
|
94
|
+
category: "manifest",
|
|
95
|
+
location: {
|
|
96
|
+
file: "package.json",
|
|
97
|
+
},
|
|
98
|
+
metadata: {
|
|
99
|
+
dependency: dep,
|
|
100
|
+
legitimateUses: [
|
|
101
|
+
"Remote development helpers",
|
|
102
|
+
"SSH workflow tools",
|
|
103
|
+
"Container development",
|
|
104
|
+
],
|
|
105
|
+
redFlags: [
|
|
106
|
+
"No clear remote development purpose",
|
|
107
|
+
"Combined with credential access patterns",
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return findings;
|
|
115
|
+
}
|
|
116
|
+
export function checkManifest(manifest) {
|
|
117
|
+
return [
|
|
118
|
+
...checkActivationEvents(manifest),
|
|
119
|
+
...checkThemeAbuse(manifest),
|
|
120
|
+
...checkSuspiciousPermissions(manifest),
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/scanner/checks/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.test.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/manifest.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { checkActivationEvents, checkManifest, checkThemeAbuse } from "./manifest.js";
|
|
3
|
+
describe("checkActivationEvents", () => {
|
|
4
|
+
it("flags wildcard activation event", () => {
|
|
5
|
+
const manifest = {
|
|
6
|
+
name: "test",
|
|
7
|
+
publisher: "test",
|
|
8
|
+
version: "1.0.0",
|
|
9
|
+
activationEvents: ["*"],
|
|
10
|
+
};
|
|
11
|
+
const findings = checkActivationEvents(manifest);
|
|
12
|
+
expect(findings.some((f) => f.id === "ACTIVATION_WILDCARD")).toBe(true);
|
|
13
|
+
expect(findings[0]?.severity).toBe("high");
|
|
14
|
+
});
|
|
15
|
+
it("flags onStartupFinished activation event", () => {
|
|
16
|
+
const manifest = {
|
|
17
|
+
name: "test",
|
|
18
|
+
publisher: "test",
|
|
19
|
+
version: "1.0.0",
|
|
20
|
+
activationEvents: ["onStartupFinished"],
|
|
21
|
+
};
|
|
22
|
+
const findings = checkActivationEvents(manifest);
|
|
23
|
+
expect(findings.some((f) => f.id === "ACTIVATION_STARTUP")).toBe(true);
|
|
24
|
+
expect(findings[0]?.severity).toBe("medium");
|
|
25
|
+
});
|
|
26
|
+
it("does not flag normal activation events", () => {
|
|
27
|
+
const manifest = {
|
|
28
|
+
name: "test",
|
|
29
|
+
publisher: "test",
|
|
30
|
+
version: "1.0.0",
|
|
31
|
+
activationEvents: ["onCommand:test.command", "onLanguage:typescript"],
|
|
32
|
+
};
|
|
33
|
+
const findings = checkActivationEvents(manifest);
|
|
34
|
+
expect(findings).toHaveLength(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("checkThemeAbuse", () => {
|
|
38
|
+
it("flags theme extension with code entry point", () => {
|
|
39
|
+
const manifest = {
|
|
40
|
+
name: "test-theme",
|
|
41
|
+
publisher: "test",
|
|
42
|
+
version: "1.0.0",
|
|
43
|
+
main: "./extension.js",
|
|
44
|
+
contributes: {
|
|
45
|
+
themes: [{ id: "dark-theme", label: "Dark Theme", path: "./themes/dark.json" }],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
const findings = checkThemeAbuse(manifest);
|
|
49
|
+
expect(findings.some((f) => f.id === "THEME_WITH_CODE")).toBe(true);
|
|
50
|
+
expect(findings[0]?.severity).toBe("high");
|
|
51
|
+
});
|
|
52
|
+
it("flags icon theme extension with code entry point", () => {
|
|
53
|
+
const manifest = {
|
|
54
|
+
name: "test-icons",
|
|
55
|
+
publisher: "test",
|
|
56
|
+
version: "1.0.0",
|
|
57
|
+
main: "./extension.js",
|
|
58
|
+
contributes: {
|
|
59
|
+
iconThemes: [{ id: "material-icons", label: "Material Icons", path: "./icons.json" }],
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const findings = checkThemeAbuse(manifest);
|
|
63
|
+
expect(findings.some((f) => f.id === "THEME_WITH_CODE")).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it("does not flag pure theme without code", () => {
|
|
66
|
+
const manifest = {
|
|
67
|
+
name: "test-theme",
|
|
68
|
+
publisher: "test",
|
|
69
|
+
version: "1.0.0",
|
|
70
|
+
contributes: {
|
|
71
|
+
themes: [{ id: "dark-theme", label: "Dark Theme", path: "./themes/dark.json" }],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
const findings = checkThemeAbuse(manifest);
|
|
75
|
+
expect(findings).toHaveLength(0);
|
|
76
|
+
});
|
|
77
|
+
it("does not flag extension with code but no themes", () => {
|
|
78
|
+
const manifest = {
|
|
79
|
+
name: "test-extension",
|
|
80
|
+
publisher: "test",
|
|
81
|
+
version: "1.0.0",
|
|
82
|
+
main: "./extension.js",
|
|
83
|
+
contributes: {
|
|
84
|
+
commands: [{ command: "test.command", title: "Test Command" }],
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const findings = checkThemeAbuse(manifest);
|
|
88
|
+
expect(findings).toHaveLength(0);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("checkManifest", () => {
|
|
92
|
+
it("combines all manifest checks", () => {
|
|
93
|
+
const manifest = {
|
|
94
|
+
name: "suspicious-theme",
|
|
95
|
+
publisher: "suspicious",
|
|
96
|
+
version: "1.0.0",
|
|
97
|
+
main: "./extension.js",
|
|
98
|
+
activationEvents: ["*"],
|
|
99
|
+
contributes: {
|
|
100
|
+
themes: [{ id: "theme", label: "Theme", path: "./theme.json" }],
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const findings = checkManifest(manifest);
|
|
104
|
+
expect(findings.some((f) => f.id === "ACTIVATION_WILDCARD")).toBe(true);
|
|
105
|
+
expect(findings.some((f) => f.id === "THEME_WITH_CODE")).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=manifest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.test.js","sourceRoot":"","sources":["../../../src/scanner/checks/manifest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obfuscation.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/obfuscation.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAY,YAAY,EAAE,MAAM,aAAa,CAAC;AAwgBnE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,EAAE,CAUlE"}
|