@automagik/genie 4.260424.5 → 4.260424.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260424.
|
|
3
|
+
"version": "4.260424.6",
|
|
4
4
|
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Namastex Labs"
|
package/scripts/sec-scan.cjs
CHANGED
|
@@ -259,6 +259,10 @@ const MAX_TEXT_SNIPPETS = 6;
|
|
|
259
259
|
const MAX_SNIPPET_CHARS = 240;
|
|
260
260
|
const MAX_TEMP_WALK_ENTRIES = 25000;
|
|
261
261
|
const MAX_TEMP_FINDINGS = 200;
|
|
262
|
+
const DEFAULT_TEMP_FILES_BUDGET = 500;
|
|
263
|
+
const DEFAULT_TEMP_BYTES_BUDGET = 32 * 1024 * 1024;
|
|
264
|
+
const DEFAULT_TEMP_WALL_BUDGET_MS = 2000;
|
|
265
|
+
const TEMP_YIELD_INTERVAL = 128;
|
|
262
266
|
const MAX_TIMELINE_EVENTS = 120;
|
|
263
267
|
|
|
264
268
|
const TEMP_ARTIFACT_NAME_REGEX =
|
|
@@ -322,32 +326,32 @@ const TEXT_MATCHERS = [
|
|
|
322
326
|
{
|
|
323
327
|
label: 'install:npm @automagik/genie',
|
|
324
328
|
category: 'install',
|
|
325
|
-
regex: /\bnpm\b[^\n]
|
|
329
|
+
regex: /\bnpm\b[^\n]{0,200}\b(?:install|i|add|update|exec|ci)\b[^\n]{0,200}@automagik\/genie(?:@[0-9.]+)?/i,
|
|
326
330
|
},
|
|
327
331
|
{
|
|
328
332
|
label: 'install:pnpm @automagik/genie',
|
|
329
333
|
category: 'install',
|
|
330
|
-
regex: /\bpnpm\b[^\n]
|
|
334
|
+
regex: /\bpnpm\b[^\n]{0,200}\b(?:add|install|update|up)\b[^\n]{0,200}@automagik\/genie(?:@[0-9.]+)?/i,
|
|
331
335
|
},
|
|
332
336
|
{
|
|
333
337
|
label: 'install:yarn @automagik/genie',
|
|
334
338
|
category: 'install',
|
|
335
|
-
regex: /\byarn\b[^\n]
|
|
339
|
+
regex: /\byarn\b[^\n]{0,200}\b(?:add|install|up|upgrade)\b[^\n]{0,200}@automagik\/genie(?:@[0-9.]+)?/i,
|
|
336
340
|
},
|
|
337
341
|
{
|
|
338
342
|
label: 'install:bun @automagik/genie',
|
|
339
343
|
category: 'install',
|
|
340
|
-
regex: /\bbun\b[^\n]
|
|
344
|
+
regex: /\bbun\b[^\n]{0,200}\b(?:add|install|pm add)\b[^\n]{0,200}@automagik\/genie(?:@[0-9.]+)?/i,
|
|
341
345
|
},
|
|
342
346
|
{
|
|
343
347
|
label: 'exec:npx @automagik/genie',
|
|
344
348
|
category: 'execution',
|
|
345
|
-
regex: /\bnpx\b[^\n]
|
|
349
|
+
regex: /\bnpx\b[^\n]{0,200}@automagik\/genie(?:@[0-9.]+)?/i,
|
|
346
350
|
},
|
|
347
351
|
{
|
|
348
352
|
label: 'exec:bunx @automagik/genie',
|
|
349
353
|
category: 'execution',
|
|
350
|
-
regex: /\bbunx\b[^\n]
|
|
354
|
+
regex: /\bbunx\b[^\n]{0,200}@automagik\/genie(?:@[0-9.]+)?/i,
|
|
351
355
|
},
|
|
352
356
|
{
|
|
353
357
|
label: 'exec:node_modules/@automagik/genie',
|
|
@@ -357,12 +361,12 @@ const TEXT_MATCHERS = [
|
|
|
357
361
|
{
|
|
358
362
|
label: 'exec:env-compat',
|
|
359
363
|
category: 'execution',
|
|
360
|
-
regex: /\b(?:node|bun|bash|sh)\b[^\n]
|
|
364
|
+
regex: /\b(?:node|bun|bash|sh)\b[^\n]{0,200}env-compat\.(?:cjs|js)\b/i,
|
|
361
365
|
},
|
|
362
366
|
{
|
|
363
367
|
label: 'network:curl-wget IOC',
|
|
364
368
|
category: 'network',
|
|
365
|
-
regex: /\b(?:curl|wget|fetch|Invoke-WebRequest)\b[^\n]
|
|
369
|
+
regex: /\b(?:curl|wget|fetch|Invoke-WebRequest)\b[^\n]{0,200}(?:telemetry\.api-monitor\.com|raw\.icp0\.io\/drop)/i,
|
|
366
370
|
},
|
|
367
371
|
];
|
|
368
372
|
|
|
@@ -577,6 +581,31 @@ function createRuntime({
|
|
|
577
581
|
});
|
|
578
582
|
}
|
|
579
583
|
|
|
584
|
+
function recordPhaseCapHit(reason, detail = {}) {
|
|
585
|
+
const phaseId = currentPhase ? currentPhase.id : null;
|
|
586
|
+
const ts = nowFn();
|
|
587
|
+
const breachedAtMs = currentPhase ? ts - currentPhase.startMs : null;
|
|
588
|
+
const entry = {
|
|
589
|
+
kind: 'phase.cap_hit',
|
|
590
|
+
phase: phaseId,
|
|
591
|
+
ts_ms: ts,
|
|
592
|
+
reason,
|
|
593
|
+
breached_at_ms: breachedAtMs,
|
|
594
|
+
...detail,
|
|
595
|
+
};
|
|
596
|
+
capEvents.push(entry);
|
|
597
|
+
if (currentPhase) currentPhase.caps += 1;
|
|
598
|
+
walkEvents.push({
|
|
599
|
+
event: 'phase.cap_hit',
|
|
600
|
+
phase: phaseId,
|
|
601
|
+
ts_ms: ts,
|
|
602
|
+
reason,
|
|
603
|
+
breached_at_ms: breachedAtMs,
|
|
604
|
+
...detail,
|
|
605
|
+
});
|
|
606
|
+
emit({ kind: 'phase.cap_hit', phase: phaseId, reason, ...detail });
|
|
607
|
+
}
|
|
608
|
+
|
|
580
609
|
function recordSkip(kind, detail = {}) {
|
|
581
610
|
walkEvents.push({
|
|
582
611
|
event: 'walk.skipped',
|
|
@@ -673,6 +702,7 @@ function createRuntime({
|
|
|
673
702
|
endPhase,
|
|
674
703
|
emit,
|
|
675
704
|
recordCap,
|
|
705
|
+
recordPhaseCapHit,
|
|
676
706
|
recordSkip,
|
|
677
707
|
recordSymlinkCycle,
|
|
678
708
|
recordReaddirError,
|
|
@@ -1375,7 +1405,7 @@ function collectTextIndicators(text) {
|
|
|
1375
1405
|
const escapedName = escapeRegex(trackedPackage.name);
|
|
1376
1406
|
if (
|
|
1377
1407
|
new RegExp(
|
|
1378
|
-
`\\b(?:npm|pnpm|yarn|bun)\\b[^\\n]
|
|
1408
|
+
`\\b(?:npm|pnpm|yarn|bun)\\b[^\\n]{0,200}(?:install|i|add|update|up|upgrade|exec|ci|pm add)?[^\\n]{0,200}${escapedName}(?:@[0-9.]+)?`,
|
|
1379
1409
|
'i',
|
|
1380
1410
|
).test(text)
|
|
1381
1411
|
) {
|
|
@@ -1383,8 +1413,8 @@ function collectTextIndicators(text) {
|
|
|
1383
1413
|
}
|
|
1384
1414
|
|
|
1385
1415
|
if (
|
|
1386
|
-
new RegExp(`\\b(?:npx|bunx)\\b[^\\n]
|
|
1387
|
-
new RegExp(`${escapedName}[^\\n]
|
|
1416
|
+
new RegExp(`\\b(?:npx|bunx)\\b[^\\n]{0,200}${escapedName}(?:@[0-9.]+)?`, 'i').test(text) ||
|
|
1417
|
+
new RegExp(`${escapedName}[^\\n]{0,200}node_modules`, 'i').test(text)
|
|
1388
1418
|
) {
|
|
1389
1419
|
indicators.executionCommands.push(`exec:${trackedPackage.name}`);
|
|
1390
1420
|
}
|
|
@@ -2554,11 +2584,11 @@ function collectTempRoots(platformInfo, homes, roots) {
|
|
|
2554
2584
|
}
|
|
2555
2585
|
|
|
2556
2586
|
for (const homePath of homes) {
|
|
2587
|
+
// ~/.npm, ~/.npm/_npx, ~/.cache, ~/.bun intentionally omitted: dedicated
|
|
2588
|
+
// scanNpmCache / scanBunCache phases cover them with tighter caps; walking
|
|
2589
|
+
// them here surfaces hundreds of MB of content scans and bypasses
|
|
2590
|
+
// WALK_SKIP_DIRS (skip-set only filters sub-entries, not chosen roots).
|
|
2557
2591
|
for (const candidate of [
|
|
2558
|
-
join(homePath, '.npm'),
|
|
2559
|
-
join(homePath, '.npm', '_npx'),
|
|
2560
|
-
join(homePath, '.cache'),
|
|
2561
|
-
join(homePath, '.bun'),
|
|
2562
2592
|
join(homePath, 'Library', 'Caches'),
|
|
2563
2593
|
join(homePath, 'AppData', 'Local', 'Temp'),
|
|
2564
2594
|
join(homePath, 'AppData', 'Local', 'npm-cache'),
|
|
@@ -2579,8 +2609,185 @@ function collectTempRoots(platformInfo, homes, roots) {
|
|
|
2579
2609
|
});
|
|
2580
2610
|
}
|
|
2581
2611
|
|
|
2582
|
-
function
|
|
2612
|
+
function resolveTempBudgets(runtime) {
|
|
2613
|
+
const budgets = runtime?.options?.phaseBudgets || {};
|
|
2614
|
+
const filesOverride = Number(budgets['scanTempArtifacts.files']);
|
|
2615
|
+
const bytesOverride = Number(budgets['scanTempArtifacts.bytes']);
|
|
2616
|
+
const wallOverride = Number(budgets['scanTempArtifacts.wall_ms']);
|
|
2617
|
+
// Back-compat: `--phase-budget scanTempArtifacts=N` is interpreted as the
|
|
2618
|
+
// files-count cap (dominant failure mode for this phase).
|
|
2619
|
+
const shorthandOverride = Number(budgets.scanTempArtifacts);
|
|
2620
|
+
const files =
|
|
2621
|
+
Number.isFinite(filesOverride) && filesOverride >= 0
|
|
2622
|
+
? filesOverride
|
|
2623
|
+
: Number.isFinite(shorthandOverride) && shorthandOverride >= 0
|
|
2624
|
+
? shorthandOverride
|
|
2625
|
+
: DEFAULT_TEMP_FILES_BUDGET;
|
|
2626
|
+
const bytes = Number.isFinite(bytesOverride) && bytesOverride >= 0 ? bytesOverride : DEFAULT_TEMP_BYTES_BUDGET;
|
|
2627
|
+
const wallMs = Number.isFinite(wallOverride) && wallOverride >= 0 ? wallOverride : DEFAULT_TEMP_WALL_BUDGET_MS;
|
|
2628
|
+
return { files, bytes, wallMs };
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
function pushSizeCappedTempFinding(report, fullPath, stat, namedHits) {
|
|
2632
|
+
const finding = {
|
|
2633
|
+
path: fullPath,
|
|
2634
|
+
realpath: safeRealpath(fullPath),
|
|
2635
|
+
size: stat.size,
|
|
2636
|
+
modifiedAt: isoTime(stat.mtimeMs),
|
|
2637
|
+
nameMatches: namedHits,
|
|
2638
|
+
sha256: null,
|
|
2639
|
+
expectedSha256: expectedMalwareHashForBasename(basename(fullPath)),
|
|
2640
|
+
knownMalwareHash: false,
|
|
2641
|
+
iocMatches: [],
|
|
2642
|
+
versions: [],
|
|
2643
|
+
packageRefs: [],
|
|
2644
|
+
executionCommands: [],
|
|
2645
|
+
networkCommands: [],
|
|
2646
|
+
snippets: [],
|
|
2647
|
+
size_capped_not_hashed: true,
|
|
2648
|
+
};
|
|
2649
|
+
report.tempArtifactFindings.push(finding);
|
|
2650
|
+
addTimeline(report, {
|
|
2651
|
+
time: finding.modifiedAt,
|
|
2652
|
+
category: 'temp-artifact',
|
|
2653
|
+
severity: namedHits.some((value) => /env-compat\.(?:cjs|js)/i.test(value)) ? 'compromised' : 'affected',
|
|
2654
|
+
summary: 'temp or cache artifact matched known-bad basename; size-capped, content not scanned',
|
|
2655
|
+
path: fullPath,
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
function inspectTempFileSync(fullPath, report) {
|
|
2660
|
+
const stat = safeStat(fullPath);
|
|
2661
|
+
if (!stat || !stat.isFile()) return { bytesRead: 0, skipped: true };
|
|
2662
|
+
|
|
2663
|
+
const namedHits = collectNamedArtifactHits(fullPath);
|
|
2664
|
+
|
|
2665
|
+
// Size guard applies universally (hotfix: close DoS via name-regex fast path).
|
|
2666
|
+
// Basename hits still surface a finding but content is NOT read/hashed.
|
|
2667
|
+
if (stat.size > MAX_TEMP_CONTENT_SCAN_SIZE) {
|
|
2668
|
+
if (namedHits.length > 0 && report.tempArtifactFindings.length < MAX_TEMP_FINDINGS) {
|
|
2669
|
+
pushSizeCappedTempFinding(report, fullPath, stat, namedHits);
|
|
2670
|
+
}
|
|
2671
|
+
return { bytesRead: 0, skipped: true };
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
const buffer = safeReadFile(fullPath);
|
|
2675
|
+
if (!buffer) return { bytesRead: 0, skipped: true };
|
|
2676
|
+
|
|
2677
|
+
const bytesRead = buffer.length;
|
|
2678
|
+
const expanded = maybeGunzip(buffer);
|
|
2679
|
+
const iocMatches = searchBufferForIocs(expanded);
|
|
2680
|
+
const fileSha256 = sha256(expanded);
|
|
2681
|
+
const expectedSha256 = expectedMalwareHashForBasename(basename(fullPath));
|
|
2682
|
+
|
|
2683
|
+
const text = expanded.toString('utf8');
|
|
2684
|
+
const indicators = collectTextIndicators(text);
|
|
2685
|
+
const versions = indicators.versions;
|
|
2686
|
+
const packageRefs = indicators.packageRefs;
|
|
2687
|
+
const executionCommands = indicators.executionCommands;
|
|
2688
|
+
const networkCommands = indicators.networkCommands;
|
|
2689
|
+
const snippets = extractInterestingSnippets(text);
|
|
2690
|
+
|
|
2691
|
+
if (
|
|
2692
|
+
namedHits.length === 0 &&
|
|
2693
|
+
iocMatches.length === 0 &&
|
|
2694
|
+
versions.length === 0 &&
|
|
2695
|
+
packageRefs.length === 0 &&
|
|
2696
|
+
executionCommands.length === 0 &&
|
|
2697
|
+
networkCommands.length === 0
|
|
2698
|
+
) {
|
|
2699
|
+
return { bytesRead, skipped: false };
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
const finding = {
|
|
2703
|
+
path: fullPath,
|
|
2704
|
+
realpath: safeRealpath(fullPath),
|
|
2705
|
+
size: stat.size,
|
|
2706
|
+
modifiedAt: isoTime(stat.mtimeMs),
|
|
2707
|
+
nameMatches: namedHits,
|
|
2708
|
+
sha256: fileSha256,
|
|
2709
|
+
expectedSha256,
|
|
2710
|
+
knownMalwareHash: Boolean(expectedSha256 && expectedSha256 === fileSha256),
|
|
2711
|
+
iocMatches,
|
|
2712
|
+
versions,
|
|
2713
|
+
packageRefs,
|
|
2714
|
+
executionCommands,
|
|
2715
|
+
networkCommands,
|
|
2716
|
+
snippets,
|
|
2717
|
+
};
|
|
2718
|
+
|
|
2719
|
+
report.tempArtifactFindings.push(finding);
|
|
2720
|
+
addTimeline(report, {
|
|
2721
|
+
time: finding.modifiedAt,
|
|
2722
|
+
category: 'temp-artifact',
|
|
2723
|
+
severity:
|
|
2724
|
+
iocMatches.length > 0 ||
|
|
2725
|
+
namedHits.some((value) => /env-compat\.(?:cjs|js)/i.test(value)) ||
|
|
2726
|
+
Boolean(expectedSha256 && expectedSha256 === fileSha256)
|
|
2727
|
+
? 'compromised'
|
|
2728
|
+
: 'affected',
|
|
2729
|
+
summary: 'temp or cache artifact retained suspicious package evidence',
|
|
2730
|
+
path: fullPath,
|
|
2731
|
+
});
|
|
2732
|
+
return { bytesRead, skipped: false };
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
async function processTempArtifactQueue(pending, report, runtime) {
|
|
2736
|
+
const { files: filesBudget, bytes: bytesBudget, wallMs: wallBudget } = resolveTempBudgets(runtime);
|
|
2737
|
+
let filesProcessed = 0;
|
|
2738
|
+
let bytesProcessed = 0;
|
|
2739
|
+
let capRecorded = false;
|
|
2740
|
+
const startMs = Date.now();
|
|
2741
|
+
|
|
2742
|
+
const recordCapOnce = (reason, limit) => {
|
|
2743
|
+
if (capRecorded) return;
|
|
2744
|
+
capRecorded = true;
|
|
2745
|
+
if (runtime && typeof runtime.recordPhaseCapHit === 'function') {
|
|
2746
|
+
runtime.recordPhaseCapHit(reason, {
|
|
2747
|
+
// `root` is what `envelopeFromReport` greps for in `coverage.cappedRoots`;
|
|
2748
|
+
// without it a phase-budget breach does not surface in the coverage banner.
|
|
2749
|
+
root: 'scanTempArtifacts',
|
|
2750
|
+
limit,
|
|
2751
|
+
entries_processed: filesProcessed,
|
|
2752
|
+
bytes_processed: bytesProcessed,
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
};
|
|
2756
|
+
|
|
2757
|
+
for (const fullPath of pending) {
|
|
2758
|
+
if (runtime && typeof runtime.isInterrupted === 'function' && runtime.isInterrupted()) break;
|
|
2759
|
+
if (report.tempArtifactFindings.length >= MAX_TEMP_FINDINGS) break;
|
|
2760
|
+
// Wall first — a single slow `readFileSync` (spinning disk, NFS, large
|
|
2761
|
+
// near-limit file) can blow the other budgets' theoretical bounds; the
|
|
2762
|
+
// wall-clock check is the definitive "stop" signal.
|
|
2763
|
+
if (wallBudget > 0 && Date.now() - startMs >= wallBudget) {
|
|
2764
|
+
recordCapOnce('wall_budget', wallBudget);
|
|
2765
|
+
break;
|
|
2766
|
+
}
|
|
2767
|
+
if (filesProcessed >= filesBudget) {
|
|
2768
|
+
recordCapOnce('files_budget', filesBudget);
|
|
2769
|
+
break;
|
|
2770
|
+
}
|
|
2771
|
+
if (bytesProcessed >= bytesBudget) {
|
|
2772
|
+
recordCapOnce('bytes_budget', bytesBudget);
|
|
2773
|
+
break;
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
const { bytesRead } = inspectTempFileSync(fullPath, report);
|
|
2777
|
+
filesProcessed += 1;
|
|
2778
|
+
if (bytesRead > 0) bytesProcessed += bytesRead;
|
|
2779
|
+
if (runtime && typeof runtime.addBytes === 'function' && bytesRead > 0) runtime.addBytes(bytesRead);
|
|
2780
|
+
|
|
2781
|
+
if (filesProcessed % TEMP_YIELD_INTERVAL === 0) {
|
|
2782
|
+
await new Promise((resolvePromise) => setImmediate(resolvePromise));
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
return { filesProcessed, bytesProcessed, capRecorded };
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
async function scanTempArtifacts(platformInfo, homes, roots, report, runtime) {
|
|
2583
2789
|
const tempRoots = collectTempRoots(platformInfo, homes, roots);
|
|
2790
|
+
const pending = [];
|
|
2584
2791
|
|
|
2585
2792
|
walkTreeFiles(
|
|
2586
2793
|
tempRoots,
|
|
@@ -2592,80 +2799,11 @@ function scanTempArtifacts(platformInfo, homes, roots, report, runtime) {
|
|
|
2592
2799
|
scope: 'temp-artifacts',
|
|
2593
2800
|
},
|
|
2594
2801
|
(fullPath) => {
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
const stat = safeStat(fullPath);
|
|
2598
|
-
if (!stat || !stat.isFile()) return;
|
|
2599
|
-
|
|
2600
|
-
const namedHits = collectNamedArtifactHits(fullPath);
|
|
2601
|
-
let iocMatches = [];
|
|
2602
|
-
let versions = [];
|
|
2603
|
-
let packageRefs = [];
|
|
2604
|
-
let executionCommands = [];
|
|
2605
|
-
let networkCommands = [];
|
|
2606
|
-
let snippets = [];
|
|
2607
|
-
|
|
2608
|
-
if (namedHits.length === 0 && stat.size > MAX_TEMP_CONTENT_SCAN_SIZE) return;
|
|
2609
|
-
|
|
2610
|
-
const buffer = safeReadFile(fullPath);
|
|
2611
|
-
if (!buffer) return;
|
|
2612
|
-
|
|
2613
|
-
const expanded = maybeGunzip(buffer);
|
|
2614
|
-
iocMatches = searchBufferForIocs(expanded);
|
|
2615
|
-
const fileSha256 = sha256(expanded);
|
|
2616
|
-
const expectedSha256 = expectedMalwareHashForBasename(basename(fullPath));
|
|
2617
|
-
|
|
2618
|
-
const text = expanded.toString('utf8');
|
|
2619
|
-
const indicators = collectTextIndicators(text);
|
|
2620
|
-
versions = indicators.versions;
|
|
2621
|
-
packageRefs = indicators.packageRefs;
|
|
2622
|
-
executionCommands = indicators.executionCommands;
|
|
2623
|
-
networkCommands = indicators.networkCommands;
|
|
2624
|
-
snippets = extractInterestingSnippets(text);
|
|
2625
|
-
|
|
2626
|
-
if (
|
|
2627
|
-
namedHits.length === 0 &&
|
|
2628
|
-
iocMatches.length === 0 &&
|
|
2629
|
-
versions.length === 0 &&
|
|
2630
|
-
packageRefs.length === 0 &&
|
|
2631
|
-
executionCommands.length === 0 &&
|
|
2632
|
-
networkCommands.length === 0
|
|
2633
|
-
) {
|
|
2634
|
-
return;
|
|
2635
|
-
}
|
|
2636
|
-
|
|
2637
|
-
const finding = {
|
|
2638
|
-
path: fullPath,
|
|
2639
|
-
realpath: safeRealpath(fullPath),
|
|
2640
|
-
size: stat.size,
|
|
2641
|
-
modifiedAt: isoTime(stat.mtimeMs),
|
|
2642
|
-
nameMatches: namedHits,
|
|
2643
|
-
sha256: fileSha256,
|
|
2644
|
-
expectedSha256,
|
|
2645
|
-
knownMalwareHash: Boolean(expectedSha256 && expectedSha256 === fileSha256),
|
|
2646
|
-
iocMatches,
|
|
2647
|
-
versions,
|
|
2648
|
-
packageRefs,
|
|
2649
|
-
executionCommands,
|
|
2650
|
-
networkCommands,
|
|
2651
|
-
snippets,
|
|
2652
|
-
};
|
|
2653
|
-
|
|
2654
|
-
report.tempArtifactFindings.push(finding);
|
|
2655
|
-
addTimeline(report, {
|
|
2656
|
-
time: finding.modifiedAt,
|
|
2657
|
-
category: 'temp-artifact',
|
|
2658
|
-
severity:
|
|
2659
|
-
iocMatches.length > 0 ||
|
|
2660
|
-
namedHits.some((value) => /env-compat\.(?:cjs|js)/i.test(value)) ||
|
|
2661
|
-
Boolean(expectedSha256 && expectedSha256 === fileSha256)
|
|
2662
|
-
? 'compromised'
|
|
2663
|
-
: 'affected',
|
|
2664
|
-
summary: 'temp or cache artifact retained suspicious package evidence',
|
|
2665
|
-
path: fullPath,
|
|
2666
|
-
});
|
|
2802
|
+
pending.push(fullPath);
|
|
2667
2803
|
},
|
|
2668
2804
|
);
|
|
2805
|
+
|
|
2806
|
+
await processTempArtifactQueue(pending, report, runtime);
|
|
2669
2807
|
}
|
|
2670
2808
|
|
|
2671
2809
|
function scanLiveProcesses(report) {
|
|
@@ -3217,10 +3355,10 @@ function emitSlowestRootsReport(envelope, options, stderr) {
|
|
|
3217
3355
|
}
|
|
3218
3356
|
}
|
|
3219
3357
|
|
|
3220
|
-
function runPhase(runtime, id, scope, path, fn, report) {
|
|
3358
|
+
async function runPhase(runtime, id, scope, path, fn, report) {
|
|
3221
3359
|
runtime.startPhase(id);
|
|
3222
3360
|
try {
|
|
3223
|
-
fn();
|
|
3361
|
+
await fn();
|
|
3224
3362
|
} catch (error) {
|
|
3225
3363
|
addError(report, scope, path, error);
|
|
3226
3364
|
} finally {
|
|
@@ -3228,7 +3366,7 @@ function runPhase(runtime, id, scope, path, fn, report) {
|
|
|
3228
3366
|
}
|
|
3229
3367
|
}
|
|
3230
3368
|
|
|
3231
|
-
function main() {
|
|
3369
|
+
async function main() {
|
|
3232
3370
|
const options = parseArgs(process.argv);
|
|
3233
3371
|
|
|
3234
3372
|
if (options.help) {
|
|
@@ -3298,11 +3436,11 @@ function main() {
|
|
|
3298
3436
|
runtime.startTicker();
|
|
3299
3437
|
|
|
3300
3438
|
for (const homePath of homes) {
|
|
3301
|
-
runPhase(runtime, 'scanNpmCache', 'npm-cache', homePath, () => scanNpmCache(homePath, report), report);
|
|
3302
|
-
runPhase(runtime, 'scanBunCache', 'bun-cache', homePath, () => scanBunCache(homePath, report), report);
|
|
3439
|
+
await runPhase(runtime, 'scanNpmCache', 'npm-cache', homePath, () => scanNpmCache(homePath, report), report);
|
|
3440
|
+
await runPhase(runtime, 'scanBunCache', 'bun-cache', homePath, () => scanBunCache(homePath, report), report);
|
|
3303
3441
|
}
|
|
3304
3442
|
|
|
3305
|
-
runPhase(
|
|
3443
|
+
await runPhase(
|
|
3306
3444
|
runtime,
|
|
3307
3445
|
'scanGlobalInstallCandidates',
|
|
3308
3446
|
'global-installs',
|
|
@@ -3310,7 +3448,7 @@ function main() {
|
|
|
3310
3448
|
() => scanGlobalInstallCandidates(homes, report),
|
|
3311
3449
|
report,
|
|
3312
3450
|
);
|
|
3313
|
-
runPhase(
|
|
3451
|
+
await runPhase(
|
|
3314
3452
|
runtime,
|
|
3315
3453
|
'scanProjectRoots',
|
|
3316
3454
|
'project-roots',
|
|
@@ -3318,7 +3456,7 @@ function main() {
|
|
|
3318
3456
|
() => scanProjectRoots(roots, report, runtime),
|
|
3319
3457
|
report,
|
|
3320
3458
|
);
|
|
3321
|
-
runPhase(
|
|
3459
|
+
await runPhase(
|
|
3322
3460
|
runtime,
|
|
3323
3461
|
'scanShellHistories',
|
|
3324
3462
|
'shell-histories',
|
|
@@ -3326,7 +3464,7 @@ function main() {
|
|
|
3326
3464
|
() => scanShellHistories(homes, report),
|
|
3327
3465
|
report,
|
|
3328
3466
|
);
|
|
3329
|
-
runPhase(
|
|
3467
|
+
await runPhase(
|
|
3330
3468
|
runtime,
|
|
3331
3469
|
'scanShellProfiles',
|
|
3332
3470
|
'shell-profiles',
|
|
@@ -3334,7 +3472,7 @@ function main() {
|
|
|
3334
3472
|
() => scanShellProfiles(homes, report, runtime),
|
|
3335
3473
|
report,
|
|
3336
3474
|
);
|
|
3337
|
-
runPhase(
|
|
3475
|
+
await runPhase(
|
|
3338
3476
|
runtime,
|
|
3339
3477
|
'scanPersistenceLocations',
|
|
3340
3478
|
'persistence',
|
|
@@ -3342,7 +3480,7 @@ function main() {
|
|
|
3342
3480
|
() => scanPersistenceLocations(platformInfo, homes, report, runtime),
|
|
3343
3481
|
report,
|
|
3344
3482
|
);
|
|
3345
|
-
runPhase(
|
|
3483
|
+
await runPhase(
|
|
3346
3484
|
runtime,
|
|
3347
3485
|
'scanPythonPthArtifacts',
|
|
3348
3486
|
'python-pth',
|
|
@@ -3350,7 +3488,7 @@ function main() {
|
|
|
3350
3488
|
() => scanPythonPthArtifacts(homes, roots, report, runtime),
|
|
3351
3489
|
report,
|
|
3352
3490
|
);
|
|
3353
|
-
runPhase(
|
|
3491
|
+
await runPhase(
|
|
3354
3492
|
runtime,
|
|
3355
3493
|
'scanTempArtifacts',
|
|
3356
3494
|
'temp-artifacts',
|
|
@@ -3358,7 +3496,7 @@ function main() {
|
|
|
3358
3496
|
() => scanTempArtifacts(platformInfo, homes, roots, report, runtime),
|
|
3359
3497
|
report,
|
|
3360
3498
|
);
|
|
3361
|
-
runPhase(
|
|
3499
|
+
await runPhase(
|
|
3362
3500
|
runtime,
|
|
3363
3501
|
'scanImpactSurface',
|
|
3364
3502
|
'impact-surface',
|
|
@@ -3366,7 +3504,14 @@ function main() {
|
|
|
3366
3504
|
() => scanImpactSurface(homes, roots, report, runtime),
|
|
3367
3505
|
report,
|
|
3368
3506
|
);
|
|
3369
|
-
runPhase(
|
|
3507
|
+
await runPhase(
|
|
3508
|
+
runtime,
|
|
3509
|
+
'scanLiveProcesses',
|
|
3510
|
+
'live-processes',
|
|
3511
|
+
'(process table)',
|
|
3512
|
+
() => scanLiveProcesses(report),
|
|
3513
|
+
report,
|
|
3514
|
+
);
|
|
3370
3515
|
|
|
3371
3516
|
report.timeline = sortTimeline(report.timeline);
|
|
3372
3517
|
report.summary = summarize(report);
|
|
@@ -3382,12 +3527,10 @@ function main() {
|
|
|
3382
3527
|
}
|
|
3383
3528
|
|
|
3384
3529
|
if (require.main === module) {
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
} catch (error) {
|
|
3388
|
-
process.stderr.write(`sec-scan.cjs failed: ${error.message}\n`);
|
|
3530
|
+
main().catch((error) => {
|
|
3531
|
+
process.stderr.write(`sec-scan.cjs failed: ${error?.message ? error.message : error}\n`);
|
|
3389
3532
|
process.exit(3);
|
|
3390
|
-
}
|
|
3533
|
+
});
|
|
3391
3534
|
} else {
|
|
3392
3535
|
module.exports = {
|
|
3393
3536
|
REPORT_VERSION,
|
|
@@ -3423,5 +3566,17 @@ if (require.main === module) {
|
|
|
3423
3566
|
printCoverageBanner,
|
|
3424
3567
|
printHumanReport,
|
|
3425
3568
|
emitSlowestRootsReport,
|
|
3569
|
+
collectTempRoots,
|
|
3570
|
+
scanTempArtifacts,
|
|
3571
|
+
processTempArtifactQueue,
|
|
3572
|
+
inspectTempFileSync,
|
|
3573
|
+
resolveTempBudgets,
|
|
3574
|
+
runPhase,
|
|
3575
|
+
MAX_TEMP_CONTENT_SCAN_SIZE,
|
|
3576
|
+
MAX_TEMP_WALK_ENTRIES,
|
|
3577
|
+
MAX_TEMP_FINDINGS,
|
|
3578
|
+
DEFAULT_TEMP_FILES_BUDGET,
|
|
3579
|
+
DEFAULT_TEMP_BYTES_BUDGET,
|
|
3580
|
+
TEMP_YIELD_INTERVAL,
|
|
3426
3581
|
};
|
|
3427
3582
|
}
|