@automagik/genie 4.260424.7 → 4.260424.8

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": "@automagik/genie",
3
- "version": "4.260424.7",
3
+ "version": "4.260424.8",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260424.7",
3
+ "version": "4.260424.8",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260424.7",
3
+ "version": "4.260424.8",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",
@@ -1496,6 +1496,28 @@ function searchBufferForIocs(buffer) {
1496
1496
  return uniq(hits);
1497
1497
  }
1498
1498
 
1499
+ // An install finding is "hard evidence" only when the version matches the
1500
+ // compromised list OR a file hash matches a known malware hash. IOC-string
1501
+ // matches in file content are NOT hard evidence on their own — the scanner's
1502
+ // own source literally contains every IOC string as a detection pattern, so
1503
+ // scanning `@automagik/genie@<clean-version>` would always match itself
1504
+ // (the self-detection false positive fixed here).
1505
+ function hasHardInfectionEvidence(inspection) {
1506
+ if (inspection.compromisedVersion) return true;
1507
+ if (inspection.iocFileHashes.some((entry) => entry.knownMalwareHash === true)) return true;
1508
+ return false;
1509
+ }
1510
+
1511
+ // `@automagik/genie` is the scanner package itself. On CLEAN versions its
1512
+ // source files contain IOC strings as detection patterns — scanning its own
1513
+ // bytes therefore produces thousands of spurious `iocStrings` hits. Skip the
1514
+ // content walk entirely for clean versions of the scanner package.
1515
+ function shouldSkipContentWalk(packageName, version) {
1516
+ if (packageName !== '@automagik/genie') return false;
1517
+ if (!version) return false;
1518
+ return !isTrackedCompromisedVersion(packageName, version);
1519
+ }
1520
+
1499
1521
  function inspectPackageDirectory(packageDir) {
1500
1522
  const result = {
1501
1523
  path: packageDir,
@@ -1506,6 +1528,7 @@ function inspectPackageDirectory(packageDir) {
1506
1528
  iocFiles: [],
1507
1529
  iocFileHashes: [],
1508
1530
  iocStrings: [],
1531
+ contentWalkSkipped: false,
1509
1532
  };
1510
1533
 
1511
1534
  const packageJsonPath = join(packageDir, 'package.json');
@@ -1540,6 +1563,11 @@ function inspectPackageDirectory(packageDir) {
1540
1563
  });
1541
1564
  }
1542
1565
 
1566
+ if (shouldSkipContentWalk(result.packageName, result.version)) {
1567
+ result.contentWalkSkipped = true;
1568
+ return result;
1569
+ }
1570
+
1543
1571
  const stack = [packageDir];
1544
1572
  while (stack.length > 0) {
1545
1573
  const current = stack.pop();
@@ -1968,7 +1996,7 @@ function scanBunCache(homePath, report) {
1968
1996
  if (!safeExists(bunGlobal)) continue;
1969
1997
 
1970
1998
  const inspection = inspectPackageDirectory(bunGlobal);
1971
- if (inspection.compromisedVersion || inspection.iocFiles.length > 0 || inspection.iocStrings.length > 0) {
1999
+ if (hasHardInfectionEvidence(inspection)) {
1972
2000
  const finding = {
1973
2001
  kind: 'bun-global',
1974
2002
  home: homePath,
@@ -2017,9 +2045,7 @@ function scanGlobalInstallCandidates(homes, report) {
2017
2045
 
2018
2046
  for (const candidate of findTrackedPackageDirs(nodeModulesPath)) {
2019
2047
  const inspection = inspectPackageDirectory(candidate);
2020
- if (!inspection.compromisedVersion && inspection.iocFiles.length === 0 && inspection.iocStrings.length === 0) {
2021
- continue;
2022
- }
2048
+ if (!hasHardInfectionEvidence(inspection)) continue;
2023
2049
 
2024
2050
  const finding = {
2025
2051
  kind: 'global-install',
@@ -2178,9 +2204,7 @@ function scanProjectRoots(roots, report, runtime) {
2178
2204
  (nodeModulesPath) => {
2179
2205
  for (const packageDir of findTrackedPackageDirs(nodeModulesPath)) {
2180
2206
  const inspection = inspectPackageDirectory(packageDir);
2181
- if (!inspection.compromisedVersion && inspection.iocFiles.length === 0 && inspection.iocStrings.length === 0) {
2182
- continue;
2183
- }
2207
+ if (!hasHardInfectionEvidence(inspection)) continue;
2184
2208
 
2185
2209
  const finding = {
2186
2210
  kind: 'local-install',
@@ -2825,19 +2849,27 @@ function scanLiveProcesses(report) {
2825
2849
  if (!match) continue;
2826
2850
 
2827
2851
  const [, pid, ppid, user, elapsed, command] = match;
2852
+
2853
+ // Self-exclusion: the running scanner process itself always matches
2854
+ // `exec:@automagik/genie` in its own cmdline. Ignore it.
2855
+ if (command.includes('sec-scan.cjs') || command.includes('/sec-scan ')) continue;
2856
+
2828
2857
  const indicators = collectTextIndicators(command);
2829
2858
  const namedHits = collectNamedArtifactHits(command);
2830
2859
  const matchedInstallPaths = suspectPaths.filter((path) => command.includes(path));
2831
2860
 
2832
- const isStrongHit =
2861
+ // Hard evidence requires either an actual compromised version token in
2862
+ // the cmdline OR an IOC string hit OR a network-IOC command. Pure name
2863
+ // matches (e.g. `pgserve@1.1.10` where 1.1.10 is CLEAN) are NOT compromise
2864
+ // evidence — they only tell us the package is running, which is normal.
2865
+ const hasHardEvidence =
2833
2866
  indicators.iocMatches.length > 0 ||
2834
- indicators.executionCommands.length > 0 ||
2835
2867
  indicators.networkCommands.length > 0 ||
2836
2868
  indicators.versions.length > 0 ||
2837
- namedHits.length > 0 ||
2838
2869
  matchedInstallPaths.length > 0;
2839
2870
 
2840
- if (!isStrongHit) continue;
2871
+ const hasWeakHit = hasHardEvidence || indicators.executionCommands.length > 0 || namedHits.length > 0;
2872
+ if (!hasWeakHit) continue;
2841
2873
 
2842
2874
  report.liveProcessFindings.push({
2843
2875
  pid: Number(pid),
@@ -2851,13 +2883,16 @@ function scanLiveProcesses(report) {
2851
2883
  executionCommands: indicators.executionCommands,
2852
2884
  networkCommands: indicators.networkCommands,
2853
2885
  nameMatches: namedHits,
2886
+ hardEvidence: hasHardEvidence,
2854
2887
  });
2855
2888
 
2856
2889
  addTimeline(report, {
2857
2890
  time: null,
2858
2891
  category: 'live-process',
2859
- severity: 'compromised',
2860
- summary: `live process ${pid} matches suspicious package execution indicators`,
2892
+ severity: hasHardEvidence ? 'compromised' : 'observed',
2893
+ summary: hasHardEvidence
2894
+ ? `live process ${pid} matches suspicious package execution indicators`
2895
+ : `live process ${pid} running tracked package name (clean or unversioned) — informational`,
2861
2896
  path: command,
2862
2897
  });
2863
2898
  }
@@ -2943,7 +2978,8 @@ function summarize(report) {
2943
2978
  if (strongTempEvidence > 0) {
2944
2979
  compromiseReasons.push('temp or cache directories retain dropped env-compat artifacts or IOC strings');
2945
2980
  }
2946
- if (report.liveProcessFindings.length > 0) {
2981
+ const hardEvidenceProcesses = report.liveProcessFindings.filter((entry) => entry.hardEvidence);
2982
+ if (hardEvidenceProcesses.length > 0) {
2947
2983
  compromiseReasons.push('live processes match suspicious package execution indicators');
2948
2984
  }
2949
2985
  if (report.pythonPthFindings.length > 0) {
@@ -2983,7 +3019,7 @@ function summarize(report) {
2983
3019
  suspicionScore += Math.min(strongProfileEvidence * 20, 40);
2984
3020
  suspicionScore += Math.min(executionHistoryEvidence * 20, 40);
2985
3021
  suspicionScore += Math.min(strongTempEvidence * 20, 40);
2986
- suspicionScore += Math.min(report.liveProcessFindings.length * 25, 50);
3022
+ suspicionScore += Math.min(hardEvidenceProcesses.length * 25, 50);
2987
3023
  suspicionScore += Math.min(report.pythonPthFindings.length * 25, 50);
2988
3024
  suspicionScore += Math.min(report.installFindings.length * 12, 24);
2989
3025
  suspicionScore += Math.min(report.npmTarballFetches.length * 8, 24);