residoo 0.4.7 → 0.4.9

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 (3) hide show
  1. package/README.md +4 -4
  2. package/package.json +1 -1
  3. package/src/scan.js +53 -21
package/README.md CHANGED
@@ -195,10 +195,10 @@ won't be built into the tool that writes it.
195
195
  preview, never the real value, including in `--json` mode. A decoded or
196
196
  rejoined secret is redacted exactly like a plain one.
197
197
  - On an interactive terminal, prints who it is and where it lives before
198
- scanning starts (`residoo v0.4.7 · find secrets your AI coding agent left
198
+ scanning starts (`residoo v0.4.9 · find secrets your AI coding agent left
199
199
  on disk` plus the repo URL), then a live spinner naming the current file
200
200
  as it scans. Every report also opens with the exact version and timestamp
201
- it was run with (`residoo v0.4.7 · scanned 2026-01-01 12:00`; `--json`
201
+ it was run with (`residoo v0.4.9 · scanned 2026-01-01 12:00`; `--json`
202
202
  carries the same as `residooVersion`/`scannedAt`), so a report pasted or
203
203
  screenshotted later never leaves you guessing which build produced it.
204
204
  When there are findings, the report closes with a "Next steps" pointer to
@@ -429,7 +429,7 @@ As a GitHub Action (this repository doubles as a composite action):
429
429
  ```yaml
430
430
  steps:
431
431
  - uses: actions/checkout@v4
432
- - uses: dandovdub/residoo@v0.4.7
432
+ - uses: dandovdub/residoo@v0.4.9
433
433
  ```
434
434
 
435
435
  As a pre-commit hook:
@@ -437,7 +437,7 @@ As a pre-commit hook:
437
437
  ```yaml
438
438
  repos:
439
439
  - repo: https://github.com/dandovdub/residoo
440
- rev: v0.4.7
440
+ rev: v0.4.9
441
441
  hooks:
442
442
  - id: residoo
443
443
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "residoo",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Find secrets leaking through your AI coding agent's session history. Zero network calls in the scan path, zero dependencies.",
5
5
  "license": "MIT",
6
6
  "author": "CloudRoam (https://cloudroam.io)",
package/src/scan.js CHANGED
@@ -641,26 +641,68 @@ async function scan({ sources, includeNoisy = false, includeSuppressed = false,
641
641
  ref.verifiedDetail = result.detail;
642
642
  }
643
643
  };
644
+ const awsAvailable = pendingAwsVerifications.size === 0 || isAwsCliAvailable();
645
+ if (verify && anyPending) {
646
+ // One disclosure, not one per vendor: this used to print a full
647
+ // "this is a real network request..." paragraph for EACH vendor in
648
+ // turn, so a scan touching nine vendors put nine near-identical
649
+ // paragraphs on screen before any results — a wall of repeated
650
+ // boilerplate, caught live as bad UX. The safety-relevant fact (real
651
+ // outbound calls, using the exact matched value, one at a time) only
652
+ // needs saying once; per-vendor detail becomes a compact count table.
653
+ // Deliberately not gated on isTTY like the scan spinner: a script
654
+ // piping through a pager or into a log still needs this disclosure.
655
+ //
656
+ // Three columns, not two: an earlier version of this table showed only
657
+ // the vendor name ("AWS 2"), which answers "how many" but drops the
658
+ // one thing a security-conscious user actually wants to check before
659
+ // agreeing to real outbound calls, namely WHICH endpoint each vendor's
660
+ // credential gets sent to. Every vendor's own SIMPLE_VERIFY_VENDOR_LABEL
661
+ // is already written "<Vendor>'s <endpoint>", so split it into the two
662
+ // columns rather than maintaining a second, parallel description.
663
+ const rows = [];
664
+ if (pendingAwsVerifications.size > 0 && awsAvailable) {
665
+ rows.push(["AWS", "sts:get-caller-identity", pendingAwsVerifications.size]);
666
+ }
667
+ if (pendingPlanetScaleVerifications.size > 0) {
668
+ rows.push(["PlanetScale", "organizations endpoint", pendingPlanetScaleVerifications.size]);
669
+ }
670
+ for (const [ruleId, byValue] of pendingSimpleVerifications) {
671
+ if (byValue.size === 0) continue;
672
+ const [vendor, endpoint] = SIMPLE_VERIFY_VENDOR_LABEL[ruleId].split("'s ");
673
+ rows.push([vendor, endpoint, byValue.size]);
674
+ }
675
+ if (rows.length > 0) {
676
+ const vendorWidth = Math.max(...rows.map(([vendor]) => vendor.length));
677
+ const endpointWidth = Math.max(...rows.map(([, endpoint]) => endpoint.length));
678
+ const table = rows
679
+ .map(([vendor, endpoint, count]) => ` ${vendor.padEnd(vendorWidth)} ${endpoint.padEnd(endpointWidth)} ${count}`)
680
+ .join("\n");
681
+ process.stderr.write(
682
+ "residoo --verify: checking whether these credentials are still active. Real network " +
683
+ "calls, using the exact value found in your transcript, one at a time. Nothing is cached " +
684
+ "or sent anywhere but the endpoint listed below.\n\n" +
685
+ table + "\n\n"
686
+ );
687
+ }
688
+ if (pendingAwsVerifications.size > 0 && !awsAvailable) {
689
+ process.stderr.write(
690
+ "residoo --verify: the aws CLI was not found on PATH, so the " +
691
+ `${pendingAwsVerifications.size} AWS credential(s) found in this scan could not be checked. ` +
692
+ "Install it (https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) to use --verify.\n"
693
+ );
694
+ }
695
+ }
644
696
  if (verify && pendingAwsVerifications.size > 0) {
645
697
  const applyPair = (refs, result) => {
646
698
  for (const ref of refs) {
647
699
  applyVerifyResult([ref.akiaFinding, ref.secretFinding], result);
648
700
  }
649
701
  };
650
- if (!isAwsCliAvailable()) {
651
- process.stderr.write(
652
- "residoo --verify: the aws CLI was not found on PATH, so the " +
653
- `${pendingAwsVerifications.size} AWS credential(s) found in this scan could not be checked. ` +
654
- "Install it (https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) to use --verify.\n"
655
- );
702
+ if (!awsAvailable) {
656
703
  const result = { status: "error", detail: "aws CLI not found on PATH" };
657
704
  for (const { refs } of pendingAwsVerifications.values()) applyPair(refs, result);
658
705
  } else {
659
- process.stderr.write(
660
- `residoo --verify: calling AWS sts:get-caller-identity for ${pendingAwsVerifications.size} ` +
661
- "credential(s) found in this scan. This is a real network request to AWS, using the exact " +
662
- "credential found in your transcript, one at a time.\n"
663
- );
664
706
  for (const [accessKeyValue, { secretValue, refs }] of pendingAwsVerifications) {
665
707
  const result = verifyAwsCredential(accessKeyValue, secretValue);
666
708
  applyPair(refs, result);
@@ -668,11 +710,6 @@ async function scan({ sources, includeNoisy = false, includeSuppressed = false,
668
710
  }
669
711
  }
670
712
  if (verify && pendingPlanetScaleVerifications.size > 0) {
671
- process.stderr.write(
672
- `residoo --verify: calling PlanetScale's organizations endpoint for ${pendingPlanetScaleVerifications.size} ` +
673
- "credential(s) found in this scan. This is a real network request to PlanetScale, using the exact " +
674
- "credential found in your transcript, one at a time.\n"
675
- );
676
713
  for (const [secretValue, { idValue, refs }] of pendingPlanetScaleVerifications) {
677
714
  const result = await verifyPlanetScaleToken(idValue, secretValue);
678
715
  for (const ref of refs) applyVerifyResult([ref.secretFinding, ref.idFinding], result);
@@ -682,11 +719,6 @@ async function scan({ sources, includeNoisy = false, includeSuppressed = false,
682
719
  for (const [ruleId, byValue] of pendingSimpleVerifications) {
683
720
  if (byValue.size === 0) continue;
684
721
  const verifyFn = SIMPLE_VERIFY_FNS[ruleId];
685
- process.stderr.write(
686
- `residoo --verify: calling ${SIMPLE_VERIFY_VENDOR_LABEL[ruleId]} for ${byValue.size} ` +
687
- "token(s) found in this scan. This is a real network request, using the exact " +
688
- "token found in your transcript, one at a time.\n"
689
- );
690
722
  for (const [value, { refs }] of byValue) {
691
723
  const result = await verifyFn(value);
692
724
  applyVerifyResult(refs, result);