@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,355 @@
|
|
|
1
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { deflateRawSync } from "node:zlib";
|
|
5
|
+
import { describe, expect, it, beforeAll, afterAll } from "vitest";
|
|
6
|
+
import { computeSha256, extractVsix, loadDirectory, loadExtension } from "./vsix.js";
|
|
7
|
+
/**
|
|
8
|
+
* Create a minimal ZIP file buffer for testing.
|
|
9
|
+
* Supports data descriptors (bit 3) which cause local header sizes to be 0.
|
|
10
|
+
*/
|
|
11
|
+
function createTestZip(files, options = {}) {
|
|
12
|
+
const { useDataDescriptor = false } = options;
|
|
13
|
+
const chunks = [];
|
|
14
|
+
const centralDirEntries = [];
|
|
15
|
+
for (const file of files) {
|
|
16
|
+
const localHeaderOffset = chunks.reduce((sum, buf) => sum + buf.length, 0);
|
|
17
|
+
const content = Buffer.from(file.content, "utf8");
|
|
18
|
+
const compressed = deflateRawSync(content);
|
|
19
|
+
const fileName = Buffer.from(file.name, "utf8");
|
|
20
|
+
// General purpose bit flag: bit 3 = data descriptor follows compressed data
|
|
21
|
+
const gpBitFlag = useDataDescriptor ? 0x0008 : 0x0000;
|
|
22
|
+
// Local file header (30 bytes + filename)
|
|
23
|
+
const localHeader = Buffer.alloc(30 + fileName.length);
|
|
24
|
+
localHeader.writeUInt32LE(0x04034b50, 0); // signature
|
|
25
|
+
localHeader.writeUInt16LE(20, 4); // version needed
|
|
26
|
+
localHeader.writeUInt16LE(gpBitFlag, 6); // general purpose bit flag
|
|
27
|
+
localHeader.writeUInt16LE(8, 8); // compression method (deflate)
|
|
28
|
+
localHeader.writeUInt16LE(0, 10); // mod time
|
|
29
|
+
localHeader.writeUInt16LE(0, 12); // mod date
|
|
30
|
+
// CRC and sizes: 0 if data descriptor, actual values otherwise
|
|
31
|
+
if (useDataDescriptor) {
|
|
32
|
+
localHeader.writeUInt32LE(0, 14); // crc32 = 0
|
|
33
|
+
localHeader.writeUInt32LE(0, 18); // compressed size = 0
|
|
34
|
+
localHeader.writeUInt32LE(0, 22); // uncompressed size = 0
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
localHeader.writeUInt32LE(crc32(content), 14);
|
|
38
|
+
localHeader.writeUInt32LE(compressed.length, 18);
|
|
39
|
+
localHeader.writeUInt32LE(content.length, 22);
|
|
40
|
+
}
|
|
41
|
+
localHeader.writeUInt16LE(fileName.length, 26);
|
|
42
|
+
localHeader.writeUInt16LE(0, 28); // extra field length
|
|
43
|
+
fileName.copy(localHeader, 30);
|
|
44
|
+
chunks.push(localHeader);
|
|
45
|
+
// Compressed data
|
|
46
|
+
chunks.push(compressed);
|
|
47
|
+
// Data descriptor (if bit 3 set) - 16 bytes with signature
|
|
48
|
+
if (useDataDescriptor) {
|
|
49
|
+
const dataDesc = Buffer.alloc(16);
|
|
50
|
+
dataDesc.writeUInt32LE(0x08074b50, 0); // signature (optional but common)
|
|
51
|
+
dataDesc.writeUInt32LE(crc32(content), 4);
|
|
52
|
+
dataDesc.writeUInt32LE(compressed.length, 8);
|
|
53
|
+
dataDesc.writeUInt32LE(content.length, 12);
|
|
54
|
+
chunks.push(dataDesc);
|
|
55
|
+
}
|
|
56
|
+
// Central directory entry (46 bytes + filename)
|
|
57
|
+
const cdEntry = Buffer.alloc(46 + fileName.length);
|
|
58
|
+
cdEntry.writeUInt32LE(0x02014b50, 0); // signature
|
|
59
|
+
cdEntry.writeUInt16LE(20, 4); // version made by
|
|
60
|
+
cdEntry.writeUInt16LE(20, 6); // version needed
|
|
61
|
+
cdEntry.writeUInt16LE(gpBitFlag, 8); // general purpose bit flag
|
|
62
|
+
cdEntry.writeUInt16LE(8, 10); // compression method
|
|
63
|
+
cdEntry.writeUInt16LE(0, 12); // mod time
|
|
64
|
+
cdEntry.writeUInt16LE(0, 14); // mod date
|
|
65
|
+
cdEntry.writeUInt32LE(crc32(content), 16); // crc32 (always correct in CD)
|
|
66
|
+
cdEntry.writeUInt32LE(compressed.length, 20); // compressed size (always correct)
|
|
67
|
+
cdEntry.writeUInt32LE(content.length, 24); // uncompressed size (always correct)
|
|
68
|
+
cdEntry.writeUInt16LE(fileName.length, 28);
|
|
69
|
+
cdEntry.writeUInt16LE(0, 30); // extra field length
|
|
70
|
+
cdEntry.writeUInt16LE(0, 32); // comment length
|
|
71
|
+
cdEntry.writeUInt16LE(0, 34); // disk number start
|
|
72
|
+
cdEntry.writeUInt16LE(0, 36); // internal file attributes
|
|
73
|
+
cdEntry.writeUInt32LE(0, 38); // external file attributes
|
|
74
|
+
cdEntry.writeUInt32LE(localHeaderOffset, 42); // relative offset of local header
|
|
75
|
+
fileName.copy(cdEntry, 46);
|
|
76
|
+
centralDirEntries.push(cdEntry);
|
|
77
|
+
}
|
|
78
|
+
const cdOffset = chunks.reduce((sum, buf) => sum + buf.length, 0);
|
|
79
|
+
const cdSize = centralDirEntries.reduce((sum, buf) => sum + buf.length, 0);
|
|
80
|
+
// Add central directory entries
|
|
81
|
+
chunks.push(...centralDirEntries);
|
|
82
|
+
// End of central directory (22 bytes)
|
|
83
|
+
const eocd = Buffer.alloc(22);
|
|
84
|
+
eocd.writeUInt32LE(0x06054b50, 0); // signature
|
|
85
|
+
eocd.writeUInt16LE(0, 4); // disk number
|
|
86
|
+
eocd.writeUInt16LE(0, 6); // disk with CD
|
|
87
|
+
eocd.writeUInt16LE(files.length, 8); // entries on this disk
|
|
88
|
+
eocd.writeUInt16LE(files.length, 10); // total entries
|
|
89
|
+
eocd.writeUInt32LE(cdSize, 12); // size of CD
|
|
90
|
+
eocd.writeUInt32LE(cdOffset, 16); // offset to CD
|
|
91
|
+
eocd.writeUInt16LE(0, 20); // comment length
|
|
92
|
+
chunks.push(eocd);
|
|
93
|
+
return Buffer.concat(chunks);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Simple CRC32 implementation for test ZIP creation.
|
|
97
|
+
*/
|
|
98
|
+
function crc32(data) {
|
|
99
|
+
let crc = 0xffffffff;
|
|
100
|
+
for (const byte of data) {
|
|
101
|
+
crc ^= byte;
|
|
102
|
+
for (let i = 0; i < 8; i++) {
|
|
103
|
+
crc = crc & 1 ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
107
|
+
}
|
|
108
|
+
describe("computeSha256", () => {
|
|
109
|
+
it("computes correct hash for buffer", () => {
|
|
110
|
+
const content = Buffer.from("test content");
|
|
111
|
+
const hash = computeSha256(content);
|
|
112
|
+
expect(hash).toMatch(/^[a-f0-9]{64}$/);
|
|
113
|
+
expect(hash).toBe("6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72");
|
|
114
|
+
});
|
|
115
|
+
it("returns different hashes for different content", () => {
|
|
116
|
+
const hash1 = computeSha256(Buffer.from("content1"));
|
|
117
|
+
const hash2 = computeSha256(Buffer.from("content2"));
|
|
118
|
+
expect(hash1).not.toBe(hash2);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe("loadDirectory", () => {
|
|
122
|
+
const testDir = join(tmpdir(), `vsix-audit-test-${Date.now()}`);
|
|
123
|
+
beforeAll(async () => {
|
|
124
|
+
await mkdir(testDir, { recursive: true });
|
|
125
|
+
await mkdir(join(testDir, "src"), { recursive: true });
|
|
126
|
+
const manifest = {
|
|
127
|
+
name: "test-extension",
|
|
128
|
+
publisher: "test-publisher",
|
|
129
|
+
version: "1.0.0",
|
|
130
|
+
main: "./src/extension.js",
|
|
131
|
+
activationEvents: ["onCommand:test.command"],
|
|
132
|
+
};
|
|
133
|
+
await writeFile(join(testDir, "package.json"), JSON.stringify(manifest, null, 2));
|
|
134
|
+
await writeFile(join(testDir, "src", "extension.js"), 'console.log("hello");');
|
|
135
|
+
await writeFile(join(testDir, "README.md"), "# Test Extension");
|
|
136
|
+
});
|
|
137
|
+
afterAll(async () => {
|
|
138
|
+
await rm(testDir, { recursive: true, force: true });
|
|
139
|
+
});
|
|
140
|
+
it("loads extension from directory", async () => {
|
|
141
|
+
const contents = await loadDirectory(testDir);
|
|
142
|
+
expect(contents.manifest.name).toBe("test-extension");
|
|
143
|
+
expect(contents.manifest.publisher).toBe("test-publisher");
|
|
144
|
+
expect(contents.manifest.version).toBe("1.0.0");
|
|
145
|
+
expect(contents.basePath).toBe(testDir);
|
|
146
|
+
});
|
|
147
|
+
it("loads all files from directory", async () => {
|
|
148
|
+
const contents = await loadDirectory(testDir);
|
|
149
|
+
expect(contents.files.has("package.json")).toBe(true);
|
|
150
|
+
expect(contents.files.has("src/extension.js")).toBe(true);
|
|
151
|
+
expect(contents.files.has("README.md")).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
it("excludes node_modules and .git directories", async () => {
|
|
154
|
+
// Create node_modules and .git directories
|
|
155
|
+
await mkdir(join(testDir, "node_modules", "dep"), { recursive: true });
|
|
156
|
+
await mkdir(join(testDir, ".git", "objects"), { recursive: true });
|
|
157
|
+
await writeFile(join(testDir, "node_modules", "dep", "index.js"), "module.exports = {};");
|
|
158
|
+
await writeFile(join(testDir, ".git", "objects", "abc"), "git object");
|
|
159
|
+
const contents = await loadDirectory(testDir);
|
|
160
|
+
// Should not include node_modules or .git files
|
|
161
|
+
expect([...contents.files.keys()].some((f) => f.includes("node_modules"))).toBe(false);
|
|
162
|
+
expect([...contents.files.keys()].some((f) => f.includes(".git"))).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
it("throws error for directory without package.json", async () => {
|
|
165
|
+
const emptyDir = join(tmpdir(), `vsix-audit-empty-${Date.now()}`);
|
|
166
|
+
await mkdir(emptyDir, { recursive: true });
|
|
167
|
+
try {
|
|
168
|
+
await expect(loadDirectory(emptyDir)).rejects.toThrow("missing package.json");
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
await rm(emptyDir, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe("loadExtension", () => {
|
|
176
|
+
const testDir = join(tmpdir(), `vsix-audit-load-test-${Date.now()}`);
|
|
177
|
+
beforeAll(async () => {
|
|
178
|
+
await mkdir(testDir, { recursive: true });
|
|
179
|
+
const manifest = {
|
|
180
|
+
name: "load-test",
|
|
181
|
+
publisher: "test",
|
|
182
|
+
version: "2.0.0",
|
|
183
|
+
};
|
|
184
|
+
await writeFile(join(testDir, "package.json"), JSON.stringify(manifest));
|
|
185
|
+
});
|
|
186
|
+
afterAll(async () => {
|
|
187
|
+
await rm(testDir, { recursive: true, force: true });
|
|
188
|
+
});
|
|
189
|
+
it("loads directory when target is a directory", async () => {
|
|
190
|
+
const contents = await loadExtension(testDir);
|
|
191
|
+
expect(contents.manifest.name).toBe("load-test");
|
|
192
|
+
expect(contents.manifest.version).toBe("2.0.0");
|
|
193
|
+
});
|
|
194
|
+
it("throws error for unsupported file type", async () => {
|
|
195
|
+
const invalidFile = join(tmpdir(), "invalid.txt");
|
|
196
|
+
await writeFile(invalidFile, "not a vsix");
|
|
197
|
+
try {
|
|
198
|
+
await expect(loadExtension(invalidFile)).rejects.toThrow("Unsupported target");
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
await rm(invalidFile, { force: true });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe("extractVsix", () => {
|
|
206
|
+
it("extracts standard ZIP without data descriptors", async () => {
|
|
207
|
+
const manifest = JSON.stringify({
|
|
208
|
+
name: "test-ext",
|
|
209
|
+
publisher: "test",
|
|
210
|
+
version: "1.0.0",
|
|
211
|
+
});
|
|
212
|
+
const zipBuffer = createTestZip([
|
|
213
|
+
{ name: "extension/package.json", content: manifest },
|
|
214
|
+
{ name: "extension/main.js", content: 'console.log("hello");' },
|
|
215
|
+
], { useDataDescriptor: false });
|
|
216
|
+
const vsixPath = join(tmpdir(), `test-standard-${Date.now()}.vsix`);
|
|
217
|
+
await writeFile(vsixPath, zipBuffer);
|
|
218
|
+
try {
|
|
219
|
+
const contents = await extractVsix(vsixPath);
|
|
220
|
+
expect(contents.manifest.name).toBe("test-ext");
|
|
221
|
+
expect(contents.manifest.version).toBe("1.0.0");
|
|
222
|
+
expect(contents.files.has("package.json")).toBe(true);
|
|
223
|
+
expect(contents.files.has("main.js")).toBe(true);
|
|
224
|
+
expect(contents.files.get("main.js")?.toString()).toBe('console.log("hello");');
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
await rm(vsixPath, { force: true });
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
it("extracts ZIP with data descriptors (bit 3 set)", async () => {
|
|
231
|
+
const manifest = JSON.stringify({
|
|
232
|
+
name: "data-desc-ext",
|
|
233
|
+
publisher: "test",
|
|
234
|
+
version: "2.0.0",
|
|
235
|
+
});
|
|
236
|
+
const zipBuffer = createTestZip([
|
|
237
|
+
{ name: "extension/package.json", content: manifest },
|
|
238
|
+
{ name: "extension/src/index.js", content: "export default function() {}" },
|
|
239
|
+
], { useDataDescriptor: true });
|
|
240
|
+
const vsixPath = join(tmpdir(), `test-datadesc-${Date.now()}.vsix`);
|
|
241
|
+
await writeFile(vsixPath, zipBuffer);
|
|
242
|
+
try {
|
|
243
|
+
const contents = await extractVsix(vsixPath);
|
|
244
|
+
expect(contents.manifest.name).toBe("data-desc-ext");
|
|
245
|
+
expect(contents.manifest.version).toBe("2.0.0");
|
|
246
|
+
expect(contents.files.has("package.json")).toBe(true);
|
|
247
|
+
expect(contents.files.has("src/index.js")).toBe(true);
|
|
248
|
+
expect(contents.files.get("src/index.js")?.toString()).toBe("export default function() {}");
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
await rm(vsixPath, { force: true });
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
it("handles large files with data descriptors", async () => {
|
|
255
|
+
const manifest = JSON.stringify({
|
|
256
|
+
name: "large-file-ext",
|
|
257
|
+
publisher: "test",
|
|
258
|
+
version: "3.0.0",
|
|
259
|
+
});
|
|
260
|
+
// Create a larger file to ensure compression works correctly
|
|
261
|
+
const largeContent = "x".repeat(10000) + "\n" + "y".repeat(10000);
|
|
262
|
+
const zipBuffer = createTestZip([
|
|
263
|
+
{ name: "extension/package.json", content: manifest },
|
|
264
|
+
{ name: "extension/large.txt", content: largeContent },
|
|
265
|
+
], { useDataDescriptor: true });
|
|
266
|
+
const vsixPath = join(tmpdir(), `test-large-${Date.now()}.vsix`);
|
|
267
|
+
await writeFile(vsixPath, zipBuffer);
|
|
268
|
+
try {
|
|
269
|
+
const contents = await extractVsix(vsixPath);
|
|
270
|
+
expect(contents.manifest.name).toBe("large-file-ext");
|
|
271
|
+
expect(contents.files.has("large.txt")).toBe(true);
|
|
272
|
+
expect(contents.files.get("large.txt")?.toString()).toBe(largeContent);
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
await rm(vsixPath, { force: true });
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
it("throws on invalid ZIP without EOCD", async () => {
|
|
279
|
+
const invalidZip = Buffer.from("not a valid zip file");
|
|
280
|
+
const vsixPath = join(tmpdir(), `test-invalid-${Date.now()}.vsix`);
|
|
281
|
+
await writeFile(vsixPath, invalidZip);
|
|
282
|
+
try {
|
|
283
|
+
await expect(extractVsix(vsixPath)).rejects.toThrow("End of central directory not found");
|
|
284
|
+
}
|
|
285
|
+
finally {
|
|
286
|
+
await rm(vsixPath, { force: true });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
it("throws on ZIP missing package.json", async () => {
|
|
290
|
+
const zipBuffer = createTestZip([{ name: "extension/readme.md", content: "# Hello" }], {
|
|
291
|
+
useDataDescriptor: false,
|
|
292
|
+
});
|
|
293
|
+
const vsixPath = join(tmpdir(), `test-nomanifest-${Date.now()}.vsix`);
|
|
294
|
+
await writeFile(vsixPath, zipBuffer);
|
|
295
|
+
try {
|
|
296
|
+
await expect(extractVsix(vsixPath)).rejects.toThrow("missing package.json");
|
|
297
|
+
}
|
|
298
|
+
finally {
|
|
299
|
+
await rm(vsixPath, { force: true });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
it("extracts ZIP with non-standard prefix (not extension/)", async () => {
|
|
303
|
+
const manifest = JSON.stringify({
|
|
304
|
+
name: "weird-prefix-ext",
|
|
305
|
+
publisher: "test",
|
|
306
|
+
version: "1.0.0",
|
|
307
|
+
});
|
|
308
|
+
const zipBuffer = createTestZip([
|
|
309
|
+
{ name: "weird-prefix-ext-1.0.0/package.json", content: manifest },
|
|
310
|
+
{ name: "weird-prefix-ext-1.0.0/main.js", content: 'console.log("hello");' },
|
|
311
|
+
], { useDataDescriptor: false });
|
|
312
|
+
const vsixPath = join(tmpdir(), `test-weird-prefix-${Date.now()}.vsix`);
|
|
313
|
+
await writeFile(vsixPath, zipBuffer);
|
|
314
|
+
try {
|
|
315
|
+
const contents = await extractVsix(vsixPath);
|
|
316
|
+
expect(contents.manifest.name).toBe("weird-prefix-ext");
|
|
317
|
+
expect(contents.manifest.version).toBe("1.0.0");
|
|
318
|
+
expect(contents.files.has("package.json")).toBe(true);
|
|
319
|
+
expect(contents.files.has("main.js")).toBe(true);
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
await rm(vsixPath, { force: true });
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
it("extracts ZIP with publisher.name-version prefix pattern", async () => {
|
|
326
|
+
const manifest = JSON.stringify({
|
|
327
|
+
name: "theme-allhallowseve-remake",
|
|
328
|
+
publisher: "priskinski",
|
|
329
|
+
version: "1.0.0",
|
|
330
|
+
});
|
|
331
|
+
const zipBuffer = createTestZip([
|
|
332
|
+
{
|
|
333
|
+
name: "priskinski.theme-allhallowseve-remake-1.0.0/package.json",
|
|
334
|
+
content: manifest,
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "priskinski.theme-allhallowseve-remake-1.0.0/node_modules/evil/index.js",
|
|
338
|
+
content: 'require("child_process").exec("malware");',
|
|
339
|
+
},
|
|
340
|
+
], { useDataDescriptor: false });
|
|
341
|
+
const vsixPath = join(tmpdir(), `test-malformed-prefix-${Date.now()}.vsix`);
|
|
342
|
+
await writeFile(vsixPath, zipBuffer);
|
|
343
|
+
try {
|
|
344
|
+
const contents = await extractVsix(vsixPath);
|
|
345
|
+
expect(contents.manifest.name).toBe("theme-allhallowseve-remake");
|
|
346
|
+
expect(contents.manifest.publisher).toBe("priskinski");
|
|
347
|
+
expect(contents.files.has("package.json")).toBe(true);
|
|
348
|
+
expect(contents.files.has("node_modules/evil/index.js")).toBe(true);
|
|
349
|
+
}
|
|
350
|
+
finally {
|
|
351
|
+
await rm(vsixPath, { force: true });
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
//# sourceMappingURL=vsix.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vsix.test.js","sourceRoot":"","sources":["../../src/scanner/vsix.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trailofbits/vsix-audit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Security scanner for VS Code extensions",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"audit",
|
|
7
|
+
"scanner",
|
|
8
|
+
"security",
|
|
9
|
+
"trail-of-bits",
|
|
10
|
+
"vscode",
|
|
11
|
+
"vsix"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/trailofbits/vsix-audit#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/trailofbits/vsix-audit/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "AGPL-3.0",
|
|
18
|
+
"author": "Trail of Bits",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/trailofbits/vsix-audit.git"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"vsix-audit": "dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"zoo"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"dev": "tsc --watch",
|
|
36
|
+
"start": "node dist/index.js",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"lint": "oxlint .",
|
|
40
|
+
"format": "oxfmt --write .",
|
|
41
|
+
"format:check": "oxfmt --check .",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"check": "npm run typecheck && npm run lint && npm run test",
|
|
44
|
+
"prepublishOnly": "npm run check && npm run build"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"commander": "14.0.2",
|
|
48
|
+
"oxc-parser": "0.111.0",
|
|
49
|
+
"picocolors": "1.1.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "25.0.10",
|
|
53
|
+
"oxlint": "1.42.0",
|
|
54
|
+
"typescript": "5.9.3",
|
|
55
|
+
"vitest": "4.0.18"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=22"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../schemas/blocklist.schema.json",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"lastUpdated": "2026-01-27",
|
|
5
|
+
"extensions": [
|
|
6
|
+
{
|
|
7
|
+
"id": "IconKiefApp.material-icon-theme",
|
|
8
|
+
"name": "Icon Theme: Material",
|
|
9
|
+
"reason": "GlassWorm malware - impersonates PKief.material-icon-theme",
|
|
10
|
+
"campaign": "GlassWorm",
|
|
11
|
+
"addedDate": "2025-11-28",
|
|
12
|
+
"reference": "https://www.nextron-systems.com/2025/11/28/malicious-vs-code-extension-impersonating-material-icon-theme-found-in-marketplace/"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "498.cppplayground",
|
|
16
|
+
"name": "C++ Playground",
|
|
17
|
+
"reason": "TigerJack keylogger - steals keystrokes and .cpp files",
|
|
18
|
+
"campaign": "TigerJack",
|
|
19
|
+
"addedDate": "2025-10-01",
|
|
20
|
+
"reference": "https://www.koi.ai/blog/tiger-jack-malicious-vscode-extensions-stealing-code"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "498.httpformat",
|
|
24
|
+
"name": "HTTP Format",
|
|
25
|
+
"reason": "TigerJack cryptominer - CoinIMP miner",
|
|
26
|
+
"campaign": "TigerJack",
|
|
27
|
+
"addedDate": "2025-10-01",
|
|
28
|
+
"reference": "https://www.koi.ai/blog/tiger-jack-malicious-vscode-extensions-stealing-code"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "498.pythonformat",
|
|
32
|
+
"name": "Python Format",
|
|
33
|
+
"reason": "TigerJack backdoor - 20-minute C2 polling",
|
|
34
|
+
"campaign": "TigerJack",
|
|
35
|
+
"addedDate": "2025-10-01",
|
|
36
|
+
"reference": "https://www.koi.ai/blog/tiger-jack-malicious-vscode-extensions-stealing-code"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "498-00.*",
|
|
40
|
+
"name": "498-00 publisher (all)",
|
|
41
|
+
"reason": "TigerJack republished extensions",
|
|
42
|
+
"campaign": "TigerJack",
|
|
43
|
+
"addedDate": "2025-09-17",
|
|
44
|
+
"reference": "https://www.bleepingcomputer.com/news/security/malicious-crypto-stealing-vscode-extensions-resurface-on-openvsx/"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "BigBlack.bitcoin-black",
|
|
48
|
+
"name": "Bitcoin Black",
|
|
49
|
+
"reason": "Evelyn infostealer disguised as theme",
|
|
50
|
+
"campaign": "Evelyn",
|
|
51
|
+
"addedDate": "2025-12-05",
|
|
52
|
+
"reference": "https://www.bleepingcomputer.com/news/security/malicious-vscode-extensions-on-microsofts-registry-drop-infostealers/"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "BigBlack.codo-ai",
|
|
56
|
+
"name": "Codo AI",
|
|
57
|
+
"reason": "Evelyn infostealer with working AI features as cover",
|
|
58
|
+
"campaign": "Evelyn",
|
|
59
|
+
"addedDate": "2025-12-08",
|
|
60
|
+
"reference": "https://www.koi.ai/blog/the-vs-code-malware-that-captures-your-screen"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"id": "suspublisher18.susvsex",
|
|
64
|
+
"name": "susvsex",
|
|
65
|
+
"reason": "Ransomware - zipUploadAndEncrypt function",
|
|
66
|
+
"campaign": "Unknown",
|
|
67
|
+
"addedDate": "2025-11-01",
|
|
68
|
+
"reference": "https://www.businesstechweekly.com/technology-news/malicious-vs-code-extension-ransomware-capabilities-discovered-and-removed-from-marketplace/"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "publishingsofficial.prettier-vscode-plus",
|
|
72
|
+
"name": "prettier-vscode-plus",
|
|
73
|
+
"reason": "OctoRAT - multi-stage RAT delivery",
|
|
74
|
+
"campaign": "OctoRAT",
|
|
75
|
+
"addedDate": "2025-11-21",
|
|
76
|
+
"reference": "https://hunt.io/blog/malicious-vscode-extension-anivia-octorat-attack-chain"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "ahban.shiba",
|
|
80
|
+
"name": "shiba",
|
|
81
|
+
"reason": "Ransomware - early stage, demonstrates name reuse vulnerability",
|
|
82
|
+
"campaign": "Shiba",
|
|
83
|
+
"addedDate": "2025-03-01",
|
|
84
|
+
"reference": "https://www.reversinglabs.com/blog/malware-vs-code-extension-names"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "ahban.cychelloworld",
|
|
88
|
+
"name": "cychelloworld",
|
|
89
|
+
"reason": "Ransomware - related to shiba",
|
|
90
|
+
"campaign": "Shiba",
|
|
91
|
+
"addedDate": "2025-03-01",
|
|
92
|
+
"reference": "https://www.reversinglabs.com/blog/malware-vs-code-extension-names"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "ahbanC.shiba",
|
|
96
|
+
"name": "shiba (republished)",
|
|
97
|
+
"reason": "Ransomware - republished after removal",
|
|
98
|
+
"campaign": "Shiba",
|
|
99
|
+
"addedDate": "2025-08-01",
|
|
100
|
+
"reference": "https://www.reversinglabs.com/blog/malware-vs-code-extension-names"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "Ethereum.solidity",
|
|
104
|
+
"name": "Solidity (fake Ethereum publisher)",
|
|
105
|
+
"reason": "WhiteCobra - ScreenConnect RAT with fake download count",
|
|
106
|
+
"campaign": "WhiteCobra",
|
|
107
|
+
"platform": "openvsx",
|
|
108
|
+
"addedDate": "2025-08-10",
|
|
109
|
+
"reference": "https://dex.koi.security/reports/cursor/8e0ec40b-ef95-4e11-9264-cf055c58050c/0.0.7"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "wli273088.vscode-pets-",
|
|
113
|
+
"name": "vscode-pets-",
|
|
114
|
+
"reason": "Malicious - Koidex Catch of the Day",
|
|
115
|
+
"campaign": "Unknown",
|
|
116
|
+
"addedDate": "2026-01-01",
|
|
117
|
+
"reference": "https://dex.koi.security/"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "GPTOnce.chatgpt",
|
|
121
|
+
"name": "ChatGPT",
|
|
122
|
+
"reason": "Malicious - Koidex Catch of the Day",
|
|
123
|
+
"campaign": "Unknown",
|
|
124
|
+
"addedDate": "2026-01-01",
|
|
125
|
+
"reference": "https://dex.koi.security/"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"id": "juan-bianco.solidity-vlang",
|
|
129
|
+
"name": "Solidity (SleepyDuck)",
|
|
130
|
+
"reason": "SleepyDuck RAT - Ethereum smart contract C2",
|
|
131
|
+
"campaign": "WhiteCobra",
|
|
132
|
+
"platform": "openvsx",
|
|
133
|
+
"addedDate": "2025-11-03",
|
|
134
|
+
"reference": "https://www.bleepingcomputer.com/news/security/fake-solidity-vscode-extension-on-open-vsx-backdoors-developers/"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"id": "malkolm.*",
|
|
138
|
+
"name": "malkolm publisher (all)",
|
|
139
|
+
"reason": "Trojan-laden fake image - banner.png contains Rust binary",
|
|
140
|
+
"campaign": "ReversingLabs-Dec2025",
|
|
141
|
+
"addedDate": "2025-12-10",
|
|
142
|
+
"reference": "https://www.reversinglabs.com/blog/malicious-vs-code-fake-image"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "pandaexpress.*",
|
|
146
|
+
"name": "pandaexpress publisher (all)",
|
|
147
|
+
"reason": "Trojan-laden fake image - banner.png contains Rust binary",
|
|
148
|
+
"campaign": "ReversingLabs-Dec2025",
|
|
149
|
+
"addedDate": "2025-12-10",
|
|
150
|
+
"reference": "https://www.reversinglabs.com/blog/malicious-vs-code-fake-image"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": "prada555.*",
|
|
154
|
+
"name": "prada555 publisher (all)",
|
|
155
|
+
"reason": "Trojan-laden fake image - banner.png contains Rust binary",
|
|
156
|
+
"campaign": "ReversingLabs-Dec2025",
|
|
157
|
+
"addedDate": "2025-12-10",
|
|
158
|
+
"reference": "https://www.reversinglabs.com/blog/malicious-vs-code-fake-image"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "priskinski.*",
|
|
162
|
+
"name": "priskinski publisher (all)",
|
|
163
|
+
"reason": "Trojan-laden fake image - banner.png contains Rust binary",
|
|
164
|
+
"campaign": "ReversingLabs-Dec2025",
|
|
165
|
+
"addedDate": "2025-12-10",
|
|
166
|
+
"reference": "https://www.reversinglabs.com/blog/malicious-vs-code-fake-image"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "iconkieftwo.icon-theme-materiall",
|
|
170
|
+
"name": "Icon Theme Material (GlassWorm Wave 3)",
|
|
171
|
+
"reason": "GlassWorm wave 3 - Rust implant with Solana C2",
|
|
172
|
+
"campaign": "GlassWorm",
|
|
173
|
+
"addedDate": "2025-12-01",
|
|
174
|
+
"reference": "https://www.bleepingcomputer.com/news/security/glassworm-malware-returns-in-third-wave-of-malicious-vs-code-packages/"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"id": "solaibot.*",
|
|
178
|
+
"name": "solaibot (MUT-9332)",
|
|
179
|
+
"reason": "MUT-9332 campaign - targets Solidity developers",
|
|
180
|
+
"campaign": "MUT-9332",
|
|
181
|
+
"addedDate": "2025-05-23",
|
|
182
|
+
"reference": "https://securityonline.info/malicious-vs-code-extensions-deliver-spyware-steal-crypto-credentials/"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"id": "among-eth.*",
|
|
186
|
+
"name": "among-eth (MUT-9332)",
|
|
187
|
+
"reason": "MUT-9332 campaign - targets Solidity developers",
|
|
188
|
+
"campaign": "MUT-9332",
|
|
189
|
+
"addedDate": "2025-05-23",
|
|
190
|
+
"reference": "https://securityonline.info/malicious-vs-code-extensions-deliver-spyware-steal-crypto-credentials/"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"id": "blankebesxstnion.*",
|
|
194
|
+
"name": "blankebesxstnion (MUT-9332)",
|
|
195
|
+
"reason": "MUT-9332 campaign - targets Solidity developers",
|
|
196
|
+
"campaign": "MUT-9332",
|
|
197
|
+
"addedDate": "2025-05-23",
|
|
198
|
+
"reference": "https://securityonline.info/malicious-vs-code-extensions-deliver-spyware-steal-crypto-credentials/"
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|