@trailofbits/vsix-audit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +281 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +703 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +4 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/scanner/batch.d.ts +12 -0
  12. package/dist/scanner/batch.d.ts.map +1 -0
  13. package/dist/scanner/batch.js +104 -0
  14. package/dist/scanner/batch.js.map +1 -0
  15. package/dist/scanner/bundler.d.ts +35 -0
  16. package/dist/scanner/bundler.d.ts.map +1 -0
  17. package/dist/scanner/bundler.js +120 -0
  18. package/dist/scanner/bundler.js.map +1 -0
  19. package/dist/scanner/cache.d.ts +45 -0
  20. package/dist/scanner/cache.d.ts.map +1 -0
  21. package/dist/scanner/cache.js +153 -0
  22. package/dist/scanner/cache.js.map +1 -0
  23. package/dist/scanner/cache.test.d.ts +2 -0
  24. package/dist/scanner/cache.test.d.ts.map +1 -0
  25. package/dist/scanner/cache.test.js +149 -0
  26. package/dist/scanner/cache.test.js.map +1 -0
  27. package/dist/scanner/capabilities.d.ts +29 -0
  28. package/dist/scanner/capabilities.d.ts.map +1 -0
  29. package/dist/scanner/capabilities.js +217 -0
  30. package/dist/scanner/capabilities.js.map +1 -0
  31. package/dist/scanner/checks/ast.d.ts +3 -0
  32. package/dist/scanner/checks/ast.d.ts.map +1 -0
  33. package/dist/scanner/checks/ast.js +469 -0
  34. package/dist/scanner/checks/ast.js.map +1 -0
  35. package/dist/scanner/checks/ast.test.d.ts +2 -0
  36. package/dist/scanner/checks/ast.test.d.ts.map +1 -0
  37. package/dist/scanner/checks/ast.test.js +389 -0
  38. package/dist/scanner/checks/ast.test.js.map +1 -0
  39. package/dist/scanner/checks/behavioral.d.ts +3 -0
  40. package/dist/scanner/checks/behavioral.d.ts.map +1 -0
  41. package/dist/scanner/checks/behavioral.js +367 -0
  42. package/dist/scanner/checks/behavioral.js.map +1 -0
  43. package/dist/scanner/checks/blocklist.d.ts +3 -0
  44. package/dist/scanner/checks/blocklist.d.ts.map +1 -0
  45. package/dist/scanner/checks/blocklist.js +32 -0
  46. package/dist/scanner/checks/blocklist.js.map +1 -0
  47. package/dist/scanner/checks/blocklist.test.d.ts +2 -0
  48. package/dist/scanner/checks/blocklist.test.d.ts.map +1 -0
  49. package/dist/scanner/checks/blocklist.test.js +74 -0
  50. package/dist/scanner/checks/blocklist.test.js.map +1 -0
  51. package/dist/scanner/checks/chains.d.ts +35 -0
  52. package/dist/scanner/checks/chains.d.ts.map +1 -0
  53. package/dist/scanner/checks/chains.js +505 -0
  54. package/dist/scanner/checks/chains.js.map +1 -0
  55. package/dist/scanner/checks/chains.test.d.ts +2 -0
  56. package/dist/scanner/checks/chains.test.d.ts.map +1 -0
  57. package/dist/scanner/checks/chains.test.js +250 -0
  58. package/dist/scanner/checks/chains.test.js.map +1 -0
  59. package/dist/scanner/checks/dataflow.d.ts +3 -0
  60. package/dist/scanner/checks/dataflow.d.ts.map +1 -0
  61. package/dist/scanner/checks/dataflow.js +316 -0
  62. package/dist/scanner/checks/dataflow.js.map +1 -0
  63. package/dist/scanner/checks/dependencies.d.ts +13 -0
  64. package/dist/scanner/checks/dependencies.d.ts.map +1 -0
  65. package/dist/scanner/checks/dependencies.js +225 -0
  66. package/dist/scanner/checks/dependencies.js.map +1 -0
  67. package/dist/scanner/checks/dependencies.test.d.ts +2 -0
  68. package/dist/scanner/checks/dependencies.test.d.ts.map +1 -0
  69. package/dist/scanner/checks/dependencies.test.js +248 -0
  70. package/dist/scanner/checks/dependencies.test.js.map +1 -0
  71. package/dist/scanner/checks/finding-quality.test.d.ts +8 -0
  72. package/dist/scanner/checks/finding-quality.test.d.ts.map +1 -0
  73. package/dist/scanner/checks/finding-quality.test.js +164 -0
  74. package/dist/scanner/checks/finding-quality.test.js.map +1 -0
  75. package/dist/scanner/checks/ioc.d.ts +20 -0
  76. package/dist/scanner/checks/ioc.d.ts.map +1 -0
  77. package/dist/scanner/checks/ioc.js +234 -0
  78. package/dist/scanner/checks/ioc.js.map +1 -0
  79. package/dist/scanner/checks/ioc.test.d.ts +2 -0
  80. package/dist/scanner/checks/ioc.test.d.ts.map +1 -0
  81. package/dist/scanner/checks/ioc.test.js +298 -0
  82. package/dist/scanner/checks/ioc.test.js.map +1 -0
  83. package/dist/scanner/checks/manifest.d.ts +6 -0
  84. package/dist/scanner/checks/manifest.d.ts.map +1 -0
  85. package/dist/scanner/checks/manifest.js +123 -0
  86. package/dist/scanner/checks/manifest.js.map +1 -0
  87. package/dist/scanner/checks/manifest.test.d.ts +2 -0
  88. package/dist/scanner/checks/manifest.test.d.ts.map +1 -0
  89. package/dist/scanner/checks/manifest.test.js +108 -0
  90. package/dist/scanner/checks/manifest.test.js.map +1 -0
  91. package/dist/scanner/checks/obfuscation.d.ts +3 -0
  92. package/dist/scanner/checks/obfuscation.d.ts.map +1 -0
  93. package/dist/scanner/checks/obfuscation.js +432 -0
  94. package/dist/scanner/checks/obfuscation.js.map +1 -0
  95. package/dist/scanner/checks/obfuscation.test.d.ts +2 -0
  96. package/dist/scanner/checks/obfuscation.test.d.ts.map +1 -0
  97. package/dist/scanner/checks/obfuscation.test.js +399 -0
  98. package/dist/scanner/checks/obfuscation.test.js.map +1 -0
  99. package/dist/scanner/checks/package.d.ts +17 -0
  100. package/dist/scanner/checks/package.d.ts.map +1 -0
  101. package/dist/scanner/checks/package.js +422 -0
  102. package/dist/scanner/checks/package.js.map +1 -0
  103. package/dist/scanner/checks/package.test.d.ts +2 -0
  104. package/dist/scanner/checks/package.test.d.ts.map +1 -0
  105. package/dist/scanner/checks/package.test.js +518 -0
  106. package/dist/scanner/checks/package.test.js.map +1 -0
  107. package/dist/scanner/checks/patterns.d.ts +5 -0
  108. package/dist/scanner/checks/patterns.d.ts.map +1 -0
  109. package/dist/scanner/checks/patterns.js +251 -0
  110. package/dist/scanner/checks/patterns.js.map +1 -0
  111. package/dist/scanner/checks/patterns.test.d.ts +2 -0
  112. package/dist/scanner/checks/patterns.test.d.ts.map +1 -0
  113. package/dist/scanner/checks/patterns.test.js +147 -0
  114. package/dist/scanner/checks/patterns.test.js.map +1 -0
  115. package/dist/scanner/checks/unicode.d.ts +3 -0
  116. package/dist/scanner/checks/unicode.d.ts.map +1 -0
  117. package/dist/scanner/checks/unicode.js +247 -0
  118. package/dist/scanner/checks/unicode.js.map +1 -0
  119. package/dist/scanner/checks/unicode.test.d.ts +2 -0
  120. package/dist/scanner/checks/unicode.test.d.ts.map +1 -0
  121. package/dist/scanner/checks/unicode.test.js +202 -0
  122. package/dist/scanner/checks/unicode.test.js.map +1 -0
  123. package/dist/scanner/checks/yara.d.ts +23 -0
  124. package/dist/scanner/checks/yara.d.ts.map +1 -0
  125. package/dist/scanner/checks/yara.js +349 -0
  126. package/dist/scanner/checks/yara.js.map +1 -0
  127. package/dist/scanner/checks/yara.test.d.ts +2 -0
  128. package/dist/scanner/checks/yara.test.d.ts.map +1 -0
  129. package/dist/scanner/checks/yara.test.js +126 -0
  130. package/dist/scanner/checks/yara.test.js.map +1 -0
  131. package/dist/scanner/constants.d.ts +18 -0
  132. package/dist/scanner/constants.d.ts.map +1 -0
  133. package/dist/scanner/constants.js +37 -0
  134. package/dist/scanner/constants.js.map +1 -0
  135. package/dist/scanner/detection-coverage.test.d.ts +2 -0
  136. package/dist/scanner/detection-coverage.test.d.ts.map +1 -0
  137. package/dist/scanner/detection-coverage.test.js +216 -0
  138. package/dist/scanner/detection-coverage.test.js.map +1 -0
  139. package/dist/scanner/download.d.ts +76 -0
  140. package/dist/scanner/download.d.ts.map +1 -0
  141. package/dist/scanner/download.js +339 -0
  142. package/dist/scanner/download.js.map +1 -0
  143. package/dist/scanner/download.test.d.ts +2 -0
  144. package/dist/scanner/download.test.d.ts.map +1 -0
  145. package/dist/scanner/download.test.js +149 -0
  146. package/dist/scanner/download.test.js.map +1 -0
  147. package/dist/scanner/index.d.ts +8 -0
  148. package/dist/scanner/index.d.ts.map +1 -0
  149. package/dist/scanner/index.js +167 -0
  150. package/dist/scanner/index.js.map +1 -0
  151. package/dist/scanner/index.test.d.ts +2 -0
  152. package/dist/scanner/index.test.d.ts.map +1 -0
  153. package/dist/scanner/index.test.js +71 -0
  154. package/dist/scanner/index.test.js.map +1 -0
  155. package/dist/scanner/loaders/zoo.d.ts +3 -0
  156. package/dist/scanner/loaders/zoo.d.ts.map +1 -0
  157. package/dist/scanner/loaders/zoo.js +112 -0
  158. package/dist/scanner/loaders/zoo.js.map +1 -0
  159. package/dist/scanner/types.d.ts +118 -0
  160. package/dist/scanner/types.d.ts.map +1 -0
  161. package/dist/scanner/types.js +2 -0
  162. package/dist/scanner/types.js.map +1 -0
  163. package/dist/scanner/utils.d.ts +14 -0
  164. package/dist/scanner/utils.d.ts.map +1 -0
  165. package/dist/scanner/utils.js +25 -0
  166. package/dist/scanner/utils.js.map +1 -0
  167. package/dist/scanner/vsix.d.ts +6 -0
  168. package/dist/scanner/vsix.d.ts.map +1 -0
  169. package/dist/scanner/vsix.js +213 -0
  170. package/dist/scanner/vsix.js.map +1 -0
  171. package/dist/scanner/vsix.test.d.ts +2 -0
  172. package/dist/scanner/vsix.test.d.ts.map +1 -0
  173. package/dist/scanner/vsix.test.js +355 -0
  174. package/dist/scanner/vsix.test.js.map +1 -0
  175. package/package.json +60 -0
  176. package/zoo/blocklist/extensions.json +201 -0
  177. package/zoo/iocs/blockchain-extensions.txt +21 -0
  178. package/zoo/iocs/c2-domains.txt +50 -0
  179. package/zoo/iocs/c2-ips.txt +24 -0
  180. package/zoo/iocs/hashes.txt +47 -0
  181. package/zoo/iocs/malicious-npm.txt +85 -0
  182. package/zoo/iocs/wallets.txt +18 -0
  183. package/zoo/signatures/yara/README.md +46 -0
  184. package/zoo/signatures/yara/blockchain_c2.yar +48 -0
  185. package/zoo/signatures/yara/code_execution.yar +165 -0
  186. package/zoo/signatures/yara/credential_harvesting.yar +116 -0
  187. package/zoo/signatures/yara/crypto_wallet_targeting.yar +92 -0
  188. package/zoo/signatures/yara/data_exfiltration.yar +207 -0
  189. package/zoo/signatures/yara/google_calendar_c2.yar +187 -0
  190. package/zoo/signatures/yara/messaging_c2.yar +103 -0
  191. package/zoo/signatures/yara/multi_stage_attacks.yar +331 -0
  192. package/zoo/signatures/yara/obfuscation_patterns.yar +208 -0
  193. package/zoo/signatures/yara/powershell_attacks.yar +116 -0
  194. package/zoo/signatures/yara/rat_capabilities.yar +243 -0
  195. package/zoo/signatures/yara/self_propagation.yar +239 -0
  196. package/zoo/signatures/yara/unicode_stealth.yar +48 -0
  197. package/zoo/signatures/yara/websocket_c2.yar +83 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Tests for finding quality and metadata completeness
3
+ *
4
+ * These tests verify that findings have sufficient context for human/agent triage.
5
+ * Run with: VSIX_AUDIT_INTEGRATION_TESTS=1 npm test -- finding-quality
6
+ */
7
+ import { tmpdir } from "node:os";
8
+ import { mkdir, rm } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
11
+ import { downloadExtension } from "../download.js";
12
+ import { scanExtension } from "../index.js";
13
+ const INTEGRATION_TESTS_ENABLED = process.env["VSIX_AUDIT_INTEGRATION_TESTS"] === "1";
14
+ const TEST_EXTENSIONS = [
15
+ { id: "esbenp.prettier-vscode", category: "baseline" },
16
+ { id: "ms-vscode-remote.remote-ssh", category: "edge-case", expectedPatterns: ["SSH"] },
17
+ { id: "eamodio.gitlens", category: "edge-case", expectedPatterns: ["child_process"] },
18
+ ];
19
+ let workDir;
20
+ const scanResults = new Map();
21
+ describe.skipIf(!INTEGRATION_TESTS_ENABLED)("finding quality (integration)", () => {
22
+ beforeAll(async () => {
23
+ workDir = join(tmpdir(), `vsix-audit-test-${Date.now()}`);
24
+ await mkdir(workDir, { recursive: true });
25
+ // Download and scan all test extensions
26
+ for (const ext of TEST_EXTENSIONS) {
27
+ try {
28
+ const { path } = await downloadExtension(ext.id, { destDir: workDir });
29
+ const result = await scanExtension(path, {
30
+ output: "json",
31
+ severity: "low",
32
+ network: false,
33
+ });
34
+ scanResults.set(ext.id, result);
35
+ }
36
+ catch (error) {
37
+ console.error(`Failed to scan ${ext.id}:`, error);
38
+ }
39
+ }
40
+ }, 120000); // 2 minute timeout for downloads
41
+ afterAll(async () => {
42
+ if (workDir) {
43
+ await rm(workDir, { recursive: true, force: true });
44
+ }
45
+ });
46
+ describe("metadata completeness", () => {
47
+ it("all findings have required fields", () => {
48
+ for (const [extId, result] of scanResults) {
49
+ for (const finding of result.findings) {
50
+ expect(finding.id, `${extId}: finding missing id`).toBeDefined();
51
+ expect(finding.title, `${extId}: finding missing title`).toBeDefined();
52
+ expect(finding.description, `${extId}: finding missing description`).toBeDefined();
53
+ expect(finding.severity, `${extId}: finding missing severity`).toMatch(/^(low|medium|high|critical)$/);
54
+ expect(finding.category, `${extId}: finding missing category`).toBeDefined();
55
+ }
56
+ }
57
+ });
58
+ it("all findings have file location", () => {
59
+ for (const [extId, result] of scanResults) {
60
+ for (const finding of result.findings) {
61
+ expect(finding.location?.file, `${extId} finding ${finding.id}: missing file location`).toBeDefined();
62
+ }
63
+ }
64
+ });
65
+ it("descriptions are meaningful (>50 chars)", () => {
66
+ for (const [extId, result] of scanResults) {
67
+ for (const finding of result.findings) {
68
+ expect(finding.description.length, `${extId} finding ${finding.id}: description too short`).toBeGreaterThan(50);
69
+ }
70
+ }
71
+ });
72
+ });
73
+ describe("edge case documentation", () => {
74
+ it("remote-ssh findings explain legitimate SSH usage", () => {
75
+ const result = scanResults.get("ms-vscode-remote.remote-ssh");
76
+ if (!result)
77
+ return;
78
+ const sshFindings = result.findings.filter((f) => f.id.includes("SSH") || f.description.toLowerCase().includes("ssh"));
79
+ for (const finding of sshFindings) {
80
+ const hasContext = finding.description.toLowerCase().includes("legitimate") ||
81
+ finding.description.toLowerCase().includes("common") ||
82
+ finding.description.toLowerCase().includes("expected") ||
83
+ finding.description.toLowerCase().includes("remote") ||
84
+ finding.metadata?.["legitimateUses"]?.length;
85
+ expect(hasContext, `SSH finding ${finding.id} should mention legitimate uses for SSH extension`).toBe(true);
86
+ }
87
+ });
88
+ it("gitlens findings explain legitimate git CLI usage", () => {
89
+ const result = scanResults.get("eamodio.gitlens");
90
+ if (!result)
91
+ return;
92
+ const childProcessFindings = result.findings.filter((f) => f.id.includes("CHILD_PROCESS") ||
93
+ f.id.includes("EXEC") ||
94
+ f.description.toLowerCase().includes("child_process"));
95
+ for (const finding of childProcessFindings) {
96
+ const hasContext = finding.description.toLowerCase().includes("git") ||
97
+ finding.description.toLowerCase().includes("cli") ||
98
+ finding.description.toLowerCase().includes("legitimate") ||
99
+ finding.description.toLowerCase().includes("common") ||
100
+ finding.metadata?.["legitimateUses"]?.some((use) => use.toLowerCase().includes("git"));
101
+ expect(hasContext, `child_process finding ${finding.id} should mention git CLI as legitimate use`).toBe(true);
102
+ }
103
+ });
104
+ });
105
+ describe("baseline extensions", () => {
106
+ it("prettier-vscode has minimal findings", () => {
107
+ const result = scanResults.get("esbenp.prettier-vscode");
108
+ if (!result)
109
+ return;
110
+ // A simple formatter shouldn't have many security findings
111
+ // Allow some informational findings but no high/critical
112
+ const highSeverityFindings = result.findings.filter((f) => f.severity === "high" || f.severity === "critical");
113
+ expect(highSeverityFindings.length, `Simple formatter should not have high/critical findings: ${highSeverityFindings.map((f) => f.id).join(", ")}`).toBe(0);
114
+ });
115
+ });
116
+ });
117
+ describe("finding structure", () => {
118
+ function createMockFinding(overrides = {}) {
119
+ return {
120
+ id: "TEST_FINDING",
121
+ title: "Test finding title",
122
+ description: "This is a test finding with a description that is long enough to pass validation",
123
+ severity: "medium",
124
+ category: "pattern",
125
+ location: {
126
+ file: "test.js",
127
+ line: 42,
128
+ },
129
+ metadata: {
130
+ matched: "test pattern",
131
+ legitimateUses: ["Testing", "Documentation"],
132
+ redFlags: ["Combined with other suspicious patterns"],
133
+ },
134
+ ...overrides,
135
+ };
136
+ }
137
+ it("validates finding has all required fields", () => {
138
+ const finding = createMockFinding();
139
+ expect(finding.id).toBeDefined();
140
+ expect(finding.title).toBeDefined();
141
+ expect(finding.description).toBeDefined();
142
+ expect(finding.description.length).toBeGreaterThan(50);
143
+ expect(finding.severity).toMatch(/^(low|medium|high|critical)$/);
144
+ expect(finding.category).toBeDefined();
145
+ expect(finding.location?.file).toBeDefined();
146
+ });
147
+ it("validates metadata structure", () => {
148
+ const finding = createMockFinding();
149
+ expect(finding.metadata?.["matched"]).toBeDefined();
150
+ expect(finding.metadata?.["legitimateUses"]).toBeInstanceOf(Array);
151
+ expect(finding.metadata?.["redFlags"]).toBeInstanceOf(Array);
152
+ });
153
+ it("description mentions context for triage", () => {
154
+ const finding = createMockFinding({
155
+ description: "Code uses child_process module. Common in extensions that run CLI tools (git, compilers, linters). Review the commands being executed.",
156
+ });
157
+ const descLower = finding.description.toLowerCase();
158
+ const hasTriageContext = descLower.includes("common") ||
159
+ descLower.includes("legitimate") ||
160
+ descLower.includes("review");
161
+ expect(hasTriageContext).toBe(true);
162
+ });
163
+ });
164
+ //# sourceMappingURL=finding-quality.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding-quality.test.js","sourceRoot":"","sources":["../../../src/scanner/checks/finding-quality.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { Finding, VsixContents, ZooData } from "../types.js";
2
+ export declare function checkHashes(contents: VsixContents, knownHashes: Set<string>): Finding[];
3
+ export declare function checkDomains(contents: VsixContents, knownDomains: Set<string>): Finding[];
4
+ export declare function checkIps(contents: VsixContents, knownIps: Set<string>): Finding[];
5
+ /**
6
+ * Validates whether a Base58 string is likely a real Solana address.
7
+ *
8
+ * Filters out:
9
+ * - JS identifiers without digits or only trailing digits
10
+ * - Git/SHA hashes (lowercase hex strings)
11
+ * - Identifiers with clustered numbers (like "Sha256Thumbprint")
12
+ *
13
+ * Real SOL addresses have:
14
+ * - Digits (1-9) distributed throughout
15
+ * - Uppercase letters mixed in (not pure lowercase hex)
16
+ */
17
+ export declare function isLikelySolanaAddress(candidate: string): boolean;
18
+ export declare function checkWallets(contents: VsixContents, knownWallets: Set<string>, blockchainAllowlist?: Set<string>): Finding[];
19
+ export declare function checkIocs(contents: VsixContents, zooData: ZooData): Finding[];
20
+ //# sourceMappingURL=ioc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ioc.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/ioc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAiDlE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,CAwBvF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,CA4BzF;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,CA4BjF;AAsBD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAwBhE;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,YAAY,EACtB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EACzB,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAChC,OAAO,EAAE,CAkEX;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAO7E"}
@@ -0,0 +1,234 @@
1
+ import { isScannable, SCANNABLE_EXTENSIONS_IOC } from "../constants.js";
2
+ import { findLineNumberByString } from "../utils.js";
3
+ import { computeSha256 } from "../vsix.js";
4
+ function extractDomains(content) {
5
+ const domainPattern = /(?:https?:\/\/)?([a-zA-Z0-9][-a-zA-Z0-9]*(?:\.[a-zA-Z0-9][-a-zA-Z0-9]*)+)/g;
6
+ const matches = [];
7
+ for (const match of content.matchAll(domainPattern)) {
8
+ const domain = match[1];
9
+ if (domain) {
10
+ matches.push(domain.toLowerCase());
11
+ }
12
+ }
13
+ return matches;
14
+ }
15
+ function extractIps(content) {
16
+ const ipPattern = /\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b/g;
17
+ const matches = [];
18
+ for (const match of content.matchAll(ipPattern)) {
19
+ const ip = match[1];
20
+ if (ip && isValidIp(ip)) {
21
+ matches.push(ip);
22
+ }
23
+ }
24
+ return matches;
25
+ }
26
+ function isValidIp(ip) {
27
+ const parts = ip.split(".");
28
+ if (parts.length !== 4)
29
+ return false;
30
+ for (const part of parts) {
31
+ const num = parseInt(part, 10);
32
+ if (isNaN(num) || num < 0 || num > 255)
33
+ return false;
34
+ }
35
+ if (ip === "0.0.0.0" || ip === "127.0.0.1" || ip.startsWith("192.168.") || ip.startsWith("10.")) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+ export function checkHashes(contents, knownHashes) {
41
+ const findings = [];
42
+ for (const [filename, buffer] of contents.files) {
43
+ const hash = computeSha256(buffer);
44
+ if (knownHashes.has(hash)) {
45
+ findings.push({
46
+ id: "KNOWN_MALWARE_HASH",
47
+ title: "File matches known malware hash",
48
+ description: `File "${filename}" has SHA256 hash ${hash} which is in the malware database`,
49
+ severity: "critical",
50
+ category: "ioc",
51
+ location: {
52
+ file: filename,
53
+ },
54
+ metadata: {
55
+ sha256: hash,
56
+ },
57
+ });
58
+ }
59
+ }
60
+ return findings;
61
+ }
62
+ export function checkDomains(contents, knownDomains) {
63
+ const findings = [];
64
+ for (const [filename, buffer] of contents.files) {
65
+ if (!isScannable(filename, SCANNABLE_EXTENSIONS_IOC))
66
+ continue;
67
+ const content = buffer.toString("utf8");
68
+ const foundDomains = extractDomains(content);
69
+ for (const domain of foundDomains) {
70
+ if (knownDomains.has(domain)) {
71
+ const line = findLineNumberByString(content, domain);
72
+ findings.push({
73
+ id: "KNOWN_C2_DOMAIN",
74
+ title: "Known C2 domain detected",
75
+ description: `File "${filename}" contains known C2 domain: ${domain}`,
76
+ severity: "critical",
77
+ category: "ioc",
78
+ location: line !== undefined ? { file: filename, line } : { file: filename },
79
+ metadata: {
80
+ domain,
81
+ },
82
+ });
83
+ }
84
+ }
85
+ }
86
+ return findings;
87
+ }
88
+ export function checkIps(contents, knownIps) {
89
+ const findings = [];
90
+ for (const [filename, buffer] of contents.files) {
91
+ if (!isScannable(filename, SCANNABLE_EXTENSIONS_IOC))
92
+ continue;
93
+ const content = buffer.toString("utf8");
94
+ const foundIps = extractIps(content);
95
+ for (const ip of foundIps) {
96
+ if (knownIps.has(ip)) {
97
+ const line = findLineNumberByString(content, ip);
98
+ findings.push({
99
+ id: "KNOWN_C2_IP",
100
+ title: "Known C2 IP address detected",
101
+ description: `File "${filename}" contains known C2 IP: ${ip}`,
102
+ severity: "critical",
103
+ category: "ioc",
104
+ location: line !== undefined ? { file: filename, line } : { file: filename },
105
+ metadata: {
106
+ ip,
107
+ },
108
+ });
109
+ }
110
+ }
111
+ }
112
+ return findings;
113
+ }
114
+ // Wallet patterns ordered from most specific to least specific.
115
+ // More specific patterns (BTC, ETH, XMR) are checked first to avoid
116
+ // the broad Solana Base58 pattern from matching them.
117
+ const WALLET_PATTERNS = [
118
+ // Bitcoin Legacy (P2PKH) - starts with 1
119
+ { name: "BTC", pattern: /\b1[a-km-zA-HJ-NP-Z1-9]{25,34}\b/g },
120
+ // Bitcoin SegWit (P2SH) - starts with 3
121
+ { name: "BTC", pattern: /\b3[a-km-zA-HJ-NP-Z1-9]{25,34}\b/g },
122
+ // Bitcoin Bech32 (Native SegWit) - starts with bc1
123
+ { name: "BTC", pattern: /\bbc1[a-z0-9]{39,59}\b/g },
124
+ // Ethereum - 0x + 40 hex chars
125
+ { name: "ETH", pattern: /\b0x[a-fA-F0-9]{40}\b/g },
126
+ // Monero - starts with 4 or 8, 95 chars
127
+ { name: "XMR", pattern: /\b4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b/g },
128
+ // Solana - Base58, 32-44 chars (common range for pubkeys)
129
+ // This is a broad pattern - checked last to avoid matching BTC addresses
130
+ // Post-match validation is done via isLikelySolanaAddress()
131
+ { name: "SOL", pattern: /\b[1-9A-HJ-NP-Za-km-z]{32,44}\b/g },
132
+ ];
133
+ /**
134
+ * Validates whether a Base58 string is likely a real Solana address.
135
+ *
136
+ * Filters out:
137
+ * - JS identifiers without digits or only trailing digits
138
+ * - Git/SHA hashes (lowercase hex strings)
139
+ * - Identifiers with clustered numbers (like "Sha256Thumbprint")
140
+ *
141
+ * Real SOL addresses have:
142
+ * - Digits (1-9) distributed throughout
143
+ * - Uppercase letters mixed in (not pure lowercase hex)
144
+ */
145
+ export function isLikelySolanaAddress(candidate) {
146
+ // Reject pure lowercase hex strings (git hashes, checksums)
147
+ // These match Base58 charset but are clearly not wallets
148
+ if (/^[a-f0-9]+$/.test(candidate)) {
149
+ return false;
150
+ }
151
+ // Must have at least 2 digits
152
+ const digits = candidate.match(/[1-9]/g) ?? [];
153
+ if (digits.length < 2)
154
+ return false;
155
+ // At least one digit must be in the first 75% of the string
156
+ // This filters out JS identifiers with only trailing numbers like "Type2"
157
+ const firstDigitIndex = candidate.search(/[1-9]/);
158
+ if (firstDigitIndex >= candidate.length * 0.75)
159
+ return false;
160
+ // Require at least one uppercase letter
161
+ // This filters out pure lowercase identifiers and hex strings
162
+ // Real Base58 addresses use full alphanumeric range
163
+ if (!/[A-HJ-NP-Z]/.test(candidate)) {
164
+ return false;
165
+ }
166
+ return true;
167
+ }
168
+ export function checkWallets(contents, knownWallets, blockchainAllowlist) {
169
+ // Skip wallet detection for allowlisted blockchain development extensions
170
+ const extensionId = `${contents.manifest.publisher}.${contents.manifest.name}`;
171
+ if (blockchainAllowlist?.has(extensionId)) {
172
+ return [];
173
+ }
174
+ const findings = [];
175
+ for (const [filename, buffer] of contents.files) {
176
+ if (!isScannable(filename, SCANNABLE_EXTENSIONS_IOC))
177
+ continue;
178
+ const content = buffer.toString("utf8");
179
+ // Track wallets already found in this file to avoid duplicate findings
180
+ // (e.g., BTC addresses matching both BTC and SOL patterns)
181
+ const seenWallets = new Set();
182
+ for (const { name, pattern } of WALLET_PATTERNS) {
183
+ // Reset regex state
184
+ pattern.lastIndex = 0;
185
+ for (const match of content.matchAll(pattern)) {
186
+ const wallet = match[0];
187
+ // Skip if we've already reported this wallet address
188
+ if (seenWallets.has(wallet))
189
+ continue;
190
+ seenWallets.add(wallet);
191
+ // For SOL pattern, apply additional validation to filter out JS identifiers
192
+ if (name === "SOL" && !isLikelySolanaAddress(wallet)) {
193
+ continue;
194
+ }
195
+ const line = findLineNumberByString(content, wallet);
196
+ const isKnownMalicious = knownWallets.has(wallet);
197
+ if (isKnownMalicious) {
198
+ findings.push({
199
+ id: "KNOWN_MALWARE_WALLET",
200
+ title: "Known malware wallet address detected",
201
+ description: `File "${filename}" contains known malicious ${name} wallet: ${wallet}. ` +
202
+ "This wallet is associated with malware campaigns.",
203
+ severity: "critical",
204
+ category: "ioc",
205
+ location: line !== undefined ? { file: filename, line } : { file: filename },
206
+ metadata: { wallet, currency: name, knownMalicious: true },
207
+ });
208
+ }
209
+ else {
210
+ findings.push({
211
+ id: "CRYPTO_WALLET_DETECTED",
212
+ title: "Cryptocurrency wallet address detected",
213
+ description: `File "${filename}" contains ${name} wallet address: ${wallet}. ` +
214
+ "VS Code extensions should not contain wallet addresses.",
215
+ severity: "high",
216
+ category: "ioc",
217
+ location: line !== undefined ? { file: filename, line } : { file: filename },
218
+ metadata: { wallet, currency: name, knownMalicious: false },
219
+ });
220
+ }
221
+ }
222
+ }
223
+ }
224
+ return findings;
225
+ }
226
+ export function checkIocs(contents, zooData) {
227
+ return [
228
+ ...checkHashes(contents, zooData.hashes),
229
+ ...checkDomains(contents, zooData.domains),
230
+ ...checkIps(contents, zooData.ips),
231
+ ...checkWallets(contents, zooData.wallets, zooData.blockchainAllowlist),
232
+ ];
233
+ }
234
+ //# sourceMappingURL=ioc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ioc.js","sourceRoot":"","sources":["../../../src/scanner/checks/ioc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,SAAS,cAAc,CAAC,OAAe;IACrC,MAAM,aAAa,GACjB,4EAA4E,CAAC;IAC/E,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IACjC,MAAM,SAAS,GAAG,2CAA2C,CAAC;IAC9D,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,EAAE,IAAI,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS,CAAC,EAAU;IAC3B,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,GAAG;YAAE,OAAO,KAAK,CAAC;IACvD,CAAC;IAED,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,WAAW,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAChG,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAsB,EAAE,WAAwB;IAC1E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QAEnC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,oBAAoB;gBACxB,KAAK,EAAE,iCAAiC;gBACxC,WAAW,EAAE,SAAS,QAAQ,qBAAqB,IAAI,mCAAmC;gBAC1F,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,KAAK;gBACf,QAAQ,EAAE;oBACR,IAAI,EAAE,QAAQ;iBACf;gBACD,QAAQ,EAAE;oBACR,MAAM,EAAE,IAAI;iBACb;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAsB,EAAE,YAAyB;IAC5E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAChD,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,wBAAwB,CAAC;YAAE,SAAS;QAE/D,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,YAAY,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;QAE7C,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAClC,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACrD,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,iBAAiB;oBACrB,KAAK,EAAE,0BAA0B;oBACjC,WAAW,EAAE,SAAS,QAAQ,+BAA+B,MAAM,EAAE;oBACrE,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,KAAK;oBACf,QAAQ,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC5E,QAAQ,EAAE;wBACR,MAAM;qBACP;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,QAAsB,EAAE,QAAqB;IACpE,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAChD,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,wBAAwB,CAAC;YAAE,SAAS;QAE/D,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAErC,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,aAAa;oBACjB,KAAK,EAAE,8BAA8B;oBACrC,WAAW,EAAE,SAAS,QAAQ,2BAA2B,EAAE,EAAE;oBAC7D,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,KAAK;oBACf,QAAQ,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC5E,QAAQ,EAAE;wBACR,EAAE;qBACH;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,gEAAgE;AAChE,oEAAoE;AACpE,sDAAsD;AACtD,MAAM,eAAe,GAA6C;IAChE,yCAAyC;IACzC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,mCAAmC,EAAE;IAC7D,wCAAwC;IACxC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,mCAAmC,EAAE;IAC7D,mDAAmD;IACnD,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,yBAAyB,EAAE;IACnD,+BAA+B;IAC/B,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,wBAAwB,EAAE;IAClD,wCAAwC;IACxC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,uCAAuC,EAAE;IACjE,0DAA0D;IAC1D,yEAAyE;IACzE,4DAA4D;IAC5D,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,kCAAkC,EAAE;CAC7D,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACrD,4DAA4D;IAC5D,yDAAyD;IACzD,IAAI,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8BAA8B;IAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAEpC,4DAA4D;IAC5D,0EAA0E;IAC1E,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,eAAe,IAAI,SAAS,CAAC,MAAM,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC;IAE7D,wCAAwC;IACxC,8DAA8D;IAC9D,oDAAoD;IACpD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAsB,EACtB,YAAyB,EACzB,mBAAiC;IAEjC,0EAA0E;IAC1E,MAAM,WAAW,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/E,IAAI,mBAAmB,EAAE,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAChD,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,wBAAwB,CAAC;YAAE,SAAS;QAE/D,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxC,uEAAuE;QACvE,2DAA2D;QAC3D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QAEtC,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,eAAe,EAAE,CAAC;YAChD,oBAAoB;YACpB,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;YAEtB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC9C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBAExB,qDAAqD;gBACrD,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;oBAAE,SAAS;gBACtC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAExB,4EAA4E;gBAC5E,IAAI,IAAI,KAAK,KAAK,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;oBACrD,SAAS;gBACX,CAAC;gBAED,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACrD,MAAM,gBAAgB,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAElD,IAAI,gBAAgB,EAAE,CAAC;oBACrB,QAAQ,CAAC,IAAI,CAAC;wBACZ,EAAE,EAAE,sBAAsB;wBAC1B,KAAK,EAAE,uCAAuC;wBAC9C,WAAW,EACT,SAAS,QAAQ,8BAA8B,IAAI,YAAY,MAAM,IAAI;4BACzE,mDAAmD;wBACrD,QAAQ,EAAE,UAAU;wBACpB,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;wBAC5E,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;qBAC3D,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,IAAI,CAAC;wBACZ,EAAE,EAAE,wBAAwB;wBAC5B,KAAK,EAAE,wCAAwC;wBAC/C,WAAW,EACT,SAAS,QAAQ,cAAc,IAAI,oBAAoB,MAAM,IAAI;4BACjE,yDAAyD;wBAC3D,QAAQ,EAAE,MAAM;wBAChB,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;wBAC5E,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE;qBAC5D,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAsB,EAAE,OAAgB;IAChE,OAAO;QACL,GAAG,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC;QACxC,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC;QAC1C,GAAG,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC;QAClC,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,mBAAmB,CAAC;KACxE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ioc.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ioc.test.d.ts","sourceRoot":"","sources":["../../../src/scanner/checks/ioc.test.ts"],"names":[],"mappings":""}