supply-chain-guard 1.0.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.
@@ -0,0 +1,423 @@
1
+ "use strict";
2
+ /**
3
+ * Core file scanner
4
+ *
5
+ * Scans local directories and GitHub repos for supply-chain malware indicators.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.scan = scan;
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const node_child_process_1 = require("node:child_process");
45
+ const types_js_1 = require("./types.js");
46
+ const patterns_js_1 = require("./patterns.js");
47
+ const TOOL_VERSION = "1.0.0";
48
+ /**
49
+ * Scan a local directory or GitHub repo for malware indicators.
50
+ */
51
+ async function scan(options) {
52
+ const startTime = Date.now();
53
+ const target = options.target;
54
+ let scanDir = target;
55
+ let scanType = "directory";
56
+ let tempDir = null;
57
+ // If target is a GitHub URL, clone it
58
+ if (target.startsWith("https://github.com/")) {
59
+ scanType = "github";
60
+ tempDir = fs.mkdtempSync(path.join("/tmp", "scg-"));
61
+ try {
62
+ (0, node_child_process_1.execSync)(`git clone --depth 1 "${target}" "${tempDir}/repo"`, {
63
+ stdio: "pipe",
64
+ });
65
+ }
66
+ catch {
67
+ throw new Error(`Failed to clone repository: ${target}`);
68
+ }
69
+ scanDir = path.join(tempDir, "repo");
70
+ }
71
+ // Validate directory exists
72
+ if (!fs.existsSync(scanDir)) {
73
+ throw new Error(`Target directory does not exist: ${scanDir}`);
74
+ }
75
+ const stat = fs.statSync(scanDir);
76
+ if (!stat.isDirectory()) {
77
+ throw new Error(`Target is not a directory: ${scanDir}`);
78
+ }
79
+ // Collect files
80
+ const allFiles = collectFiles(scanDir, options.maxDepth ?? 20);
81
+ const findings = [];
82
+ // Scan each file
83
+ let filesScanned = 0;
84
+ for (const filePath of allFiles) {
85
+ const ext = path.extname(filePath).toLowerCase();
86
+ const basename = path.basename(filePath);
87
+ const relativePath = path.relative(scanDir, filePath);
88
+ // Check suspicious file names
89
+ checkSuspiciousFileName(basename, relativePath, findings);
90
+ // Only scan content of known file types
91
+ if (!patterns_js_1.SCANNABLE_EXTENSIONS.has(ext))
92
+ continue;
93
+ // Skip large files
94
+ const fileStat = fs.statSync(filePath);
95
+ if (fileStat.size > patterns_js_1.MAX_FILE_SIZE)
96
+ continue;
97
+ filesScanned++;
98
+ try {
99
+ const content = fs.readFileSync(filePath, "utf-8");
100
+ // Check file content patterns
101
+ checkFilePatterns(content, relativePath, findings);
102
+ // Check package.json specifically
103
+ if (basename === "package.json") {
104
+ checkPackageJson(content, relativePath, findings);
105
+ }
106
+ }
107
+ catch {
108
+ // Skip files that can't be read (binary, permissions, etc.)
109
+ }
110
+ }
111
+ // Check git commit dates if it's a git repo
112
+ if (fs.existsSync(path.join(scanDir, ".git"))) {
113
+ checkGitDateAnomalies(scanDir, findings);
114
+ }
115
+ // Filter by severity and excluded rules
116
+ const filteredFindings = filterFindings(findings, options);
117
+ // Calculate summary and score
118
+ const summary = calculateSummary(allFiles.length, filesScanned, filteredFindings);
119
+ const score = calculateScore(filteredFindings);
120
+ const riskLevel = getRiskLevel(score);
121
+ const recommendations = generateRecommendations(filteredFindings);
122
+ // Cleanup temp directory
123
+ if (tempDir) {
124
+ fs.rmSync(tempDir, { recursive: true, force: true });
125
+ }
126
+ return {
127
+ tool: `supply-chain-guard v${TOOL_VERSION}`,
128
+ timestamp: new Date().toISOString(),
129
+ target,
130
+ scanType,
131
+ durationMs: Date.now() - startTime,
132
+ findings: filteredFindings,
133
+ summary,
134
+ score,
135
+ riskLevel,
136
+ recommendations,
137
+ };
138
+ }
139
+ /**
140
+ * Recursively collect all files in a directory.
141
+ */
142
+ function collectFiles(dir, maxDepth, depth = 0) {
143
+ if (depth > maxDepth)
144
+ return [];
145
+ const files = [];
146
+ let entries;
147
+ try {
148
+ entries = fs.readdirSync(dir, { withFileTypes: true });
149
+ }
150
+ catch {
151
+ return files;
152
+ }
153
+ for (const entry of entries) {
154
+ const fullPath = path.join(dir, entry.name);
155
+ // Skip common non-relevant directories
156
+ if (entry.isDirectory()) {
157
+ if (entry.name === "node_modules" ||
158
+ entry.name === ".git" ||
159
+ entry.name === "dist" ||
160
+ entry.name === "build" ||
161
+ entry.name === ".next" ||
162
+ entry.name === "__pycache__" ||
163
+ entry.name === ".venv" ||
164
+ entry.name === "venv") {
165
+ continue;
166
+ }
167
+ files.push(...collectFiles(fullPath, maxDepth, depth + 1));
168
+ }
169
+ else if (entry.isFile()) {
170
+ files.push(fullPath);
171
+ }
172
+ }
173
+ return files;
174
+ }
175
+ /**
176
+ * Check if a filename matches known suspicious patterns.
177
+ */
178
+ function checkSuspiciousFileName(basename, relativePath, findings) {
179
+ for (const suspicious of patterns_js_1.SUSPICIOUS_FILES) {
180
+ const regex = new RegExp(suspicious.pattern);
181
+ if (regex.test(basename)) {
182
+ findings.push({
183
+ rule: suspicious.rule,
184
+ description: suspicious.description,
185
+ severity: suspicious.severity,
186
+ file: relativePath,
187
+ recommendation: `Inspect ${relativePath} manually. This filename is commonly associated with malware campaigns.`,
188
+ });
189
+ }
190
+ }
191
+ }
192
+ /**
193
+ * Scan file content against known malicious patterns.
194
+ */
195
+ function checkFilePatterns(content, relativePath, findings) {
196
+ const lines = content.split("\n");
197
+ for (const pattern of patterns_js_1.FILE_PATTERNS) {
198
+ const regex = new RegExp(pattern.pattern, "g");
199
+ for (let i = 0; i < lines.length; i++) {
200
+ const line = lines[i];
201
+ const match = regex.exec(line);
202
+ if (match) {
203
+ findings.push({
204
+ rule: pattern.rule,
205
+ description: pattern.description,
206
+ severity: pattern.severity,
207
+ file: relativePath,
208
+ line: i + 1,
209
+ match: truncateMatch(match[0]),
210
+ recommendation: getRecommendation(pattern.rule),
211
+ });
212
+ // Reset regex lastIndex for next line
213
+ regex.lastIndex = 0;
214
+ }
215
+ }
216
+ }
217
+ }
218
+ /**
219
+ * Check package.json for suspicious install scripts.
220
+ */
221
+ function checkPackageJson(content, relativePath, findings) {
222
+ let pkg;
223
+ try {
224
+ pkg = JSON.parse(content);
225
+ }
226
+ catch {
227
+ return;
228
+ }
229
+ const scripts = pkg.scripts;
230
+ if (!scripts)
231
+ return;
232
+ const dangerousHooks = ["preinstall", "postinstall", "preuninstall", "postuninstall"];
233
+ for (const hook of dangerousHooks) {
234
+ const script = scripts[hook];
235
+ if (!script)
236
+ continue;
237
+ // Check against suspicious script patterns
238
+ for (const pattern of patterns_js_1.SUSPICIOUS_SCRIPTS) {
239
+ const regex = new RegExp(pattern.pattern, "i");
240
+ if (regex.test(script)) {
241
+ findings.push({
242
+ rule: pattern.rule,
243
+ description: `${hook}: ${pattern.description}`,
244
+ severity: pattern.severity,
245
+ file: relativePath,
246
+ match: truncateMatch(`${hook}: ${script}`),
247
+ recommendation: `Review the ${hook} script in ${relativePath}. Suspicious install scripts are a primary vector for supply-chain attacks.`,
248
+ });
249
+ }
250
+ }
251
+ // Flag any non-trivial postinstall/preinstall
252
+ if ((hook === "postinstall" || hook === "preinstall") &&
253
+ script.length > 50 &&
254
+ !findings.some((f) => f.file === relativePath &&
255
+ f.rule.startsWith("SCRIPT_"))) {
256
+ findings.push({
257
+ rule: "COMPLEX_INSTALL_SCRIPT",
258
+ description: `Complex ${hook} script detected (${script.length} chars). Long install scripts warrant manual review.`,
259
+ severity: "low",
260
+ file: relativePath,
261
+ match: truncateMatch(`${hook}: ${script}`),
262
+ recommendation: `Review the ${hook} script to ensure it only performs expected build/setup operations.`,
263
+ });
264
+ }
265
+ }
266
+ }
267
+ /**
268
+ * Check for git commit date anomalies (committer date much newer than author date).
269
+ */
270
+ function checkGitDateAnomalies(dir, findings) {
271
+ try {
272
+ const log = (0, node_child_process_1.execSync)(`git -C "${dir}" log --format="%H|%aI|%cI" -20 2>/dev/null`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
273
+ const lines = log.trim().split("\n").filter(Boolean);
274
+ for (const line of lines) {
275
+ const [hash, authorDate, committerDate] = line.split("|");
276
+ if (!hash || !authorDate || !committerDate)
277
+ continue;
278
+ const authorTime = new Date(authorDate).getTime();
279
+ const committerTime = new Date(committerDate).getTime();
280
+ const diffHours = (committerTime - authorTime) / (1000 * 60 * 60);
281
+ // Flag if committer date is more than 30 days newer than author date
282
+ if (diffHours > 30 * 24) {
283
+ findings.push({
284
+ rule: "GIT_DATE_ANOMALY",
285
+ description: `Git commit date anomaly: committer date is ${Math.round(diffHours / 24)} days newer than author date. This can indicate repository history manipulation.`,
286
+ severity: "medium",
287
+ match: `commit ${hash?.substring(0, 8)}: authored ${authorDate}, committed ${committerDate}`,
288
+ recommendation: "Investigate the commit history. Large gaps between author and committer dates may indicate a hijacked or manipulated repository.",
289
+ });
290
+ }
291
+ }
292
+ }
293
+ catch {
294
+ // Not a git repo or git not available
295
+ }
296
+ }
297
+ /**
298
+ * Filter findings based on scan options.
299
+ */
300
+ function filterFindings(findings, options) {
301
+ const severityOrder = {
302
+ critical: 4,
303
+ high: 3,
304
+ medium: 2,
305
+ low: 1,
306
+ info: 0,
307
+ };
308
+ let filtered = findings;
309
+ if (options.minSeverity) {
310
+ const minLevel = severityOrder[options.minSeverity] ?? 0;
311
+ filtered = filtered.filter((f) => (severityOrder[f.severity] ?? 0) >= minLevel);
312
+ }
313
+ if (options.excludeRules?.length) {
314
+ const excluded = new Set(options.excludeRules);
315
+ filtered = filtered.filter((f) => !excluded.has(f.rule));
316
+ }
317
+ return filtered;
318
+ }
319
+ /**
320
+ * Calculate summary statistics.
321
+ */
322
+ function calculateSummary(totalFiles, filesScanned, findings) {
323
+ return {
324
+ totalFiles,
325
+ filesScanned,
326
+ critical: findings.filter((f) => f.severity === "critical").length,
327
+ high: findings.filter((f) => f.severity === "high").length,
328
+ medium: findings.filter((f) => f.severity === "medium").length,
329
+ low: findings.filter((f) => f.severity === "low").length,
330
+ info: findings.filter((f) => f.severity === "info").length,
331
+ };
332
+ }
333
+ /**
334
+ * Calculate overall risk score (0-100).
335
+ */
336
+ function calculateScore(findings) {
337
+ let score = 0;
338
+ for (const finding of findings) {
339
+ score += types_js_1.SEVERITY_SCORES[finding.severity];
340
+ }
341
+ return Math.min(100, score);
342
+ }
343
+ /**
344
+ * Derive risk level from score.
345
+ */
346
+ function getRiskLevel(score) {
347
+ if (score === 0)
348
+ return "clean";
349
+ if (score <= 10)
350
+ return "low";
351
+ if (score <= 30)
352
+ return "medium";
353
+ if (score <= 60)
354
+ return "high";
355
+ return "critical";
356
+ }
357
+ /**
358
+ * Generate human-readable recommendations.
359
+ */
360
+ function generateRecommendations(findings) {
361
+ const recommendations = [];
362
+ const rules = new Set(findings.map((f) => f.rule));
363
+ if (rules.has("GLASSWORM_MARKER")) {
364
+ recommendations.push("CRITICAL: GlassWorm malware marker detected. Quarantine this code immediately and audit all downstream dependencies.");
365
+ }
366
+ if (rules.has("EVAL_ATOB") || rules.has("EVAL_BUFFER") || rules.has("FUNCTION_ATOB")) {
367
+ recommendations.push("CRITICAL: Encoded code execution detected. This is a strong indicator of malicious obfuscation. Do not run this code.");
368
+ }
369
+ if (rules.has("INVISIBLE_UNICODE")) {
370
+ recommendations.push("Review files with invisible Unicode characters. These can hide malicious code in otherwise normal-looking files.");
371
+ }
372
+ if (rules.has("SOLANA_MAINNET") || rules.has("HELIUS_RPC")) {
373
+ recommendations.push("Solana blockchain references detected. If this project has no legitimate blockchain functionality, this may indicate C2 communication via the Solana blockchain.");
374
+ }
375
+ if (rules.has("SCRIPT_CURL_EXEC") ||
376
+ rules.has("SCRIPT_WGET_EXEC") ||
377
+ rules.has("SCRIPT_NODE_INLINE")) {
378
+ recommendations.push("Dangerous install scripts detected. These scripts download and execute remote code, which is a common supply-chain attack vector.");
379
+ }
380
+ if (rules.has("GIT_DATE_ANOMALY")) {
381
+ recommendations.push("Git commit date anomalies detected. Verify the repository history has not been manipulated.");
382
+ }
383
+ if (rules.has("ENV_EXFILTRATION") || rules.has("DNS_EXFILTRATION")) {
384
+ recommendations.push("Potential data exfiltration patterns detected. Environment variables may be sent to external servers.");
385
+ }
386
+ if (recommendations.length === 0 && findings.length > 0) {
387
+ recommendations.push("Review the listed findings and assess whether they represent legitimate functionality or potential threats.");
388
+ }
389
+ if (findings.length === 0) {
390
+ recommendations.push("No malicious indicators detected. The scanned code appears clean.");
391
+ }
392
+ return recommendations;
393
+ }
394
+ /**
395
+ * Get a recommendation string for a specific rule.
396
+ */
397
+ function getRecommendation(rule) {
398
+ const map = {
399
+ GLASSWORM_MARKER: "Quarantine this code immediately. This is a known GlassWorm campaign indicator.",
400
+ INVISIBLE_UNICODE: "Inspect this file in a hex editor. Invisible characters may hide malicious code.",
401
+ EVAL_ATOB: "Do not execute this code. Decode the base64 content to inspect what would be evaluated.",
402
+ EVAL_BUFFER: "Do not execute this code. Inspect the Buffer contents to see the hidden payload.",
403
+ FUNCTION_ATOB: "Do not execute this code. The Function constructor with encoded content is a strong malware indicator.",
404
+ EVAL_HEX: "Do not execute this code. Decode the hex string to inspect the hidden payload.",
405
+ EXEC_ENCODED: "Review what this exec call is decoding and running.",
406
+ SOLANA_MAINNET: "If this project has no blockchain functionality, this reference may indicate C2 communication.",
407
+ HELIUS_RPC: "Helius RPC references in non-blockchain projects are suspicious. Investigate.",
408
+ HEX_ARRAY: "Large hex arrays may contain obfuscated payloads. Decode and inspect.",
409
+ CHARCODE_OBFUSCATION: "String construction from character codes is a common obfuscation technique.",
410
+ ENV_EXFILTRATION: "This pattern combines environment variable access with network requests, which is a data exfiltration indicator.",
411
+ DNS_EXFILTRATION: "DNS-based exfiltration encodes data in DNS queries. This is a covert data theft technique.",
412
+ };
413
+ return map[rule] ?? "Review this finding manually and assess the risk.";
414
+ }
415
+ /**
416
+ * Truncate a match string for display.
417
+ */
418
+ function truncateMatch(match, maxLen = 120) {
419
+ if (match.length <= maxLen)
420
+ return match;
421
+ return match.substring(0, maxLen) + "...";
422
+ }
423
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBH,oBAoGC;AAtHD,4CAA8B;AAC9B,gDAAkC;AAClC,2DAA8C;AAE9C,yCAA6C;AAC7C,+CAMuB;AAEvB,MAAM,YAAY,GAAG,OAAO,CAAC;AAE7B;;GAEG;AACI,KAAK,UAAU,IAAI,CAAC,OAAoB;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,QAAQ,GAA2B,WAAW,CAAC;IACnD,IAAI,OAAO,GAAkB,IAAI,CAAC;IAElC,sCAAsC;IACtC,IAAI,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,CAAC;QAC7C,QAAQ,GAAG,QAAQ,CAAC;QACpB,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC;YACH,IAAA,6BAAQ,EAAC,wBAAwB,MAAM,MAAM,OAAO,QAAQ,EAAE;gBAC5D,KAAK,EAAE,MAAM;aACd,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,4BAA4B;IAC5B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,oCAAoC,OAAO,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,gBAAgB;IAChB,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,iBAAiB;IACjB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEtD,8BAA8B;QAC9B,uBAAuB,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;QAE1D,wCAAwC;QACxC,IAAI,CAAC,kCAAoB,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAE7C,mBAAmB;QACnB,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,QAAQ,CAAC,IAAI,GAAG,2BAAa;YAAE,SAAS;QAE5C,YAAY,EAAE,CAAC;QAEf,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAEnD,8BAA8B;YAC9B,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;YAEnD,kCAAkC;YAClC,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;gBAChC,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;IACH,CAAC;IAED,4CAA4C;IAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QAC9C,qBAAqB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED,wCAAwC;IACxC,MAAM,gBAAgB,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE3D,8BAA8B;IAC9B,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,cAAc,CAAC,gBAAgB,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,eAAe,GAAG,uBAAuB,CAAC,gBAAgB,CAAC,CAAC;IAElE,yBAAyB;IACzB,IAAI,OAAO,EAAE,CAAC;QACZ,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,OAAO;QACL,IAAI,EAAE,uBAAuB,YAAY,EAAE;QAC3C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM;QACN,QAAQ;QACR,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;QAClC,QAAQ,EAAE,gBAAgB;QAC1B,OAAO;QACP,KAAK;QACL,SAAS;QACT,eAAe;KAChB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,GAAW,EAAE,QAAgB,EAAE,KAAK,GAAG,CAAC;IAC5D,IAAI,KAAK,GAAG,QAAQ;QAAE,OAAO,EAAE,CAAC;IAEhC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAoB,CAAC;IAEzB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE5C,uCAAuC;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,IACE,KAAK,CAAC,IAAI,KAAK,cAAc;gBAC7B,KAAK,CAAC,IAAI,KAAK,MAAM;gBACrB,KAAK,CAAC,IAAI,KAAK,MAAM;gBACrB,KAAK,CAAC,IAAI,KAAK,OAAO;gBACtB,KAAK,CAAC,IAAI,KAAK,OAAO;gBACtB,KAAK,CAAC,IAAI,KAAK,aAAa;gBAC5B,KAAK,CAAC,IAAI,KAAK,OAAO;gBACtB,KAAK,CAAC,IAAI,KAAK,MAAM,EACrB,CAAC;gBACD,SAAS;YACX,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAC9B,QAAgB,EAChB,YAAoB,EACpB,QAAmB;IAEnB,KAAK,MAAM,UAAU,IAAI,8BAAgB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,UAAU,CAAC,IAAI;gBACrB,WAAW,EAAE,UAAU,CAAC,WAAW;gBACnC,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,IAAI,EAAE,YAAY;gBAClB,cAAc,EAAE,WAAW,YAAY,yEAAyE;aACjH,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACxB,OAAe,EACf,YAAoB,EACpB,QAAmB;IAEnB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,KAAK,MAAM,OAAO,IAAI,2BAAa,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAE/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,IAAI,EAAE,YAAY;oBAClB,IAAI,EAAE,CAAC,GAAG,CAAC;oBACX,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC9B,cAAc,EAAE,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC;iBAChD,CAAC,CAAC;gBACH,sCAAsC;gBACtC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CACvB,OAAe,EACf,YAAoB,EACpB,QAAmB;IAEnB,IAAI,GAA4B,CAAC;IACjC,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,OAA6C,CAAC;IAClE,IAAI,CAAC,OAAO;QAAE,OAAO;IAErB,MAAM,cAAc,GAAG,CAAC,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC;IAEtF,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM;YAAE,SAAS;QAEtB,2CAA2C;QAC3C,KAAK,MAAM,OAAO,IAAI,gCAAkB,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC/C,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,WAAW,EAAE,GAAG,IAAI,KAAK,OAAO,CAAC,WAAW,EAAE;oBAC9C,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,IAAI,EAAE,YAAY;oBAClB,KAAK,EAAE,aAAa,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC1C,cAAc,EAAE,cAAc,IAAI,cAAc,YAAY,6EAA6E;iBAC1I,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,8CAA8C;QAC9C,IACE,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,YAAY,CAAC;YACjD,MAAM,CAAC,MAAM,GAAG,EAAE;YAClB,CAAC,QAAQ,CAAC,IAAI,CACZ,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,KAAK,YAAY;gBACvB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAC/B,EACD,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,wBAAwB;gBAC9B,WAAW,EAAE,WAAW,IAAI,qBAAqB,MAAM,CAAC,MAAM,sDAAsD;gBACpH,QAAQ,EAAE,KAAK;gBACf,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE,aAAa,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1C,cAAc,EAAE,cAAc,IAAI,qEAAqE;aACxG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,GAAW,EAAE,QAAmB;IAC7D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,6BAAQ,EAClB,WAAW,GAAG,6CAA6C,EAC3D,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CACvD,CAAC;QAEF,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAErD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,aAAa,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC1D,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,aAAa;gBAAE,SAAS;YAErD,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;YAClD,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,CAAC;YACxD,MAAM,SAAS,GAAG,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YAElE,qEAAqE;YACrE,IAAI,SAAS,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;gBACxB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,kBAAkB;oBACxB,WAAW,EAAE,8CAA8C,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,kFAAkF;oBACvK,QAAQ,EAAE,QAAQ;oBAClB,KAAK,EAAE,UAAU,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,cAAc,UAAU,eAAe,aAAa,EAAE;oBAC5F,cAAc,EACZ,kIAAkI;iBACrI,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,QAAmB,EAAE,OAAoB;IAC/D,MAAM,aAAa,GAA2B;QAC5C,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;KACR,CAAC;IAEF,IAAI,QAAQ,GAAG,QAAQ,CAAC;IAExB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACzD,QAAQ,GAAG,QAAQ,CAAC,MAAM,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,QAAQ,CACpD,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC/C,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CACvB,UAAkB,EAClB,YAAoB,EACpB,QAAmB;IAEnB,OAAO;QACL,UAAU;QACV,YAAY;QACZ,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;QAClE,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM;QAC1D,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM;QAC9D,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM;QACxD,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM;KAC3D,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,QAAmB;IACzC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,KAAK,IAAI,0BAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAChC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,KAAK,CAAC;IAC9B,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,QAAQ,CAAC;IACjC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAC/B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,QAAmB;IAClD,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEnD,IAAI,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAClC,eAAe,CAAC,IAAI,CAClB,sHAAsH,CACvH,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;QACrF,eAAe,CAAC,IAAI,CAClB,uHAAuH,CACxH,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACnC,eAAe,CAAC,IAAI,CAClB,kHAAkH,CACnH,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3D,eAAe,CAAC,IAAI,CAClB,kKAAkK,CACnK,CAAC;IACJ,CAAC;IACD,IACE,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAC7B,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAC7B,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAC/B,CAAC;QACD,eAAe,CAAC,IAAI,CAClB,mIAAmI,CACpI,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAClC,eAAe,CAAC,IAAI,CAClB,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACnE,eAAe,CAAC,IAAI,CAClB,uGAAuG,CACxG,CAAC;IACJ,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,eAAe,CAAC,IAAI,CAClB,6GAA6G,CAC9G,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,eAAe,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,GAAG,GAA2B;QAClC,gBAAgB,EACd,iFAAiF;QACnF,iBAAiB,EACf,kFAAkF;QACpF,SAAS,EACP,yFAAyF;QAC3F,WAAW,EACT,kFAAkF;QACpF,aAAa,EACX,wGAAwG;QAC1G,QAAQ,EACN,gFAAgF;QAClF,YAAY,EACV,qDAAqD;QACvD,cAAc,EACZ,gGAAgG;QAClG,UAAU,EACR,+EAA+E;QACjF,SAAS,EACP,uEAAuE;QACzE,oBAAoB,EAClB,6EAA6E;QAC/E,gBAAgB,EACd,kHAAkH;QACpH,gBAAgB,EACd,4FAA4F;KAC/F,CAAC;IAEF,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,mDAAmD,CAAC;AAC1E,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,KAAa,EAAE,MAAM,GAAG,GAAG;IAChD,IAAI,KAAK,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,KAAK,CAAC;IACzC,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Solana C2 wallet monitor
3
+ *
4
+ * Monitors Solana wallet addresses for transactions containing memo instructions.
5
+ * GlassWorm and similar campaigns encode C2 URLs as Solana transaction memos,
6
+ * making the blockchain an uncensorable command-and-control channel.
7
+ */
8
+ import type { SolanaMonitorOptions } from "./types.js";
9
+ interface TransactionDetail {
10
+ signature: string;
11
+ blockTime: number | null;
12
+ memos: string[];
13
+ }
14
+ /**
15
+ * Monitor a Solana wallet for C2 memo transactions.
16
+ * Runs continuously until stopped.
17
+ */
18
+ export declare function monitorWallet(options: SolanaMonitorOptions, onAlert: (alert: C2Alert) => void): Promise<void>;
19
+ /**
20
+ * One-shot check: get recent transactions and check for memos.
21
+ */
22
+ export declare function checkWallet(address: string, limit?: number): Promise<TransactionDetail[]>;
23
+ /**
24
+ * Alert structure for detected C2 communications.
25
+ */
26
+ export interface C2Alert {
27
+ timestamp: string;
28
+ wallet: string;
29
+ signature: string;
30
+ memo: string;
31
+ decodedUrls: string[];
32
+ blockTime: number | null;
33
+ }
34
+ /**
35
+ * Format a C2 alert for display.
36
+ */
37
+ export declare function formatAlert(alert: C2Alert): string;
38
+ export {};
39
+ //# sourceMappingURL=solana-monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solana-monitor.d.ts","sourceRoot":"","sources":["../src/solana-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAqB,MAAM,YAAY,CAAC;AAK1E,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,GACT,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAY9B;AA8ID;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAwBlD"}