residoo 0.3.8 → 0.3.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.
- package/README.md +14 -9
- package/package.json +1 -1
- package/src/cli.js +3 -2
- package/src/report.js +39 -12
package/README.md
CHANGED
|
@@ -121,13 +121,18 @@ won't be built into the tool that writes it.
|
|
|
121
121
|
- Redacts everything in its own output. You get a shape and a first/last-4
|
|
122
122
|
preview, never the real value, including in `--json` mode. A decoded or
|
|
123
123
|
rejoined secret is redacted exactly like a plain one.
|
|
124
|
-
-
|
|
125
|
-
(`residoo v0.3.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
- On an interactive terminal, prints who it is and where it lives before
|
|
125
|
+
scanning starts (`residoo v0.3.9 · find secrets your AI coding agent left
|
|
126
|
+
on disk` plus the repo URL), then a live spinner naming the current file
|
|
127
|
+
as it scans. Every report also opens with the exact version and timestamp
|
|
128
|
+
it was run with (`residoo v0.3.9 · scanned 2026-01-01 12:00`; `--json`
|
|
129
|
+
carries the same as `residooVersion`/`scannedAt`), so a report pasted or
|
|
130
|
+
screenshotted later never leaves you guessing which build produced it.
|
|
131
|
+
When there are findings, the report closes with a "Next steps" pointer to
|
|
132
|
+
`--json` and `--seal`. All of the interactive chatter (the intro, the
|
|
133
|
+
spinner) goes to stderr only and is a complete no-op when stdout/stderr
|
|
134
|
+
are piped, redirected, or run in CI, so none of it can ever interleave
|
|
135
|
+
with `--json`/`--sarif` output.
|
|
131
136
|
- `--sarif` emits SARIF 2.1.0 for GitHub code scanning's Security tab and
|
|
132
137
|
inline pull-request annotations, the same format gitleaks/trufflehog/
|
|
133
138
|
agentsweep already speak, so residoo's own Action and pre-commit hook plug
|
|
@@ -324,7 +329,7 @@ As a GitHub Action (this repository doubles as a composite action):
|
|
|
324
329
|
```yaml
|
|
325
330
|
steps:
|
|
326
331
|
- uses: actions/checkout@v4
|
|
327
|
-
- uses: dandovdub/residoo@v0.3.
|
|
332
|
+
- uses: dandovdub/residoo@v0.3.9
|
|
328
333
|
```
|
|
329
334
|
|
|
330
335
|
As a pre-commit hook:
|
|
@@ -332,7 +337,7 @@ As a pre-commit hook:
|
|
|
332
337
|
```yaml
|
|
333
338
|
repos:
|
|
334
339
|
- repo: https://github.com/dandovdub/residoo
|
|
335
|
-
rev: v0.3.
|
|
340
|
+
rev: v0.3.9
|
|
336
341
|
hooks:
|
|
337
342
|
- id: residoo
|
|
338
343
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "residoo",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.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/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require("fs");
|
|
|
5
5
|
const crypto = require("crypto");
|
|
6
6
|
const { availableSources, ALL_SOURCES } = require("./sources");
|
|
7
7
|
const { scan, emptyResult } = require("./scan");
|
|
8
|
-
const { render, renderIntegrity, renderJson, renderSarif, makeProgressReporter } = require("./report");
|
|
8
|
+
const { render, renderIntegrity, renderJson, renderSarif, makeProgressReporter, printIntro } = require("./report");
|
|
9
9
|
const { checkIntegrity } = require("./integrity");
|
|
10
10
|
const {
|
|
11
11
|
ROTATION_GUIDANCE, guidanceFor, loadAcks, ackFinding, renderRotation,
|
|
@@ -404,6 +404,7 @@ async function main(argv) {
|
|
|
404
404
|
// mutated env var would leak past this one invocation and silently kill
|
|
405
405
|
// color for a later call that never asked for that.
|
|
406
406
|
const noColor = args.includes("--no-color");
|
|
407
|
+
printIntro(noColor);
|
|
407
408
|
|
|
408
409
|
// Integrity runs by default: a scan that reports "no secrets leaked" while
|
|
409
410
|
// a planted SessionStart hook sits ready to re-leak them next session is
|
|
@@ -494,7 +495,7 @@ async function main(argv) {
|
|
|
494
495
|
return failOnFind && integrityWarnCount(integrity) > 0 ? 1 : 0;
|
|
495
496
|
}
|
|
496
497
|
|
|
497
|
-
const progress = makeProgressReporter();
|
|
498
|
+
const progress = makeProgressReporter(noColor);
|
|
498
499
|
const result = await scan({ sources, includeNoisy, includeSuppressed, onProgress: progress.onProgress });
|
|
499
500
|
progress.stop();
|
|
500
501
|
const integrity = wantsIntegrity ? runIntegrity() : null;
|
package/src/report.js
CHANGED
|
@@ -32,35 +32,58 @@ function ageDays(mtimeMs) {
|
|
|
32
32
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
35
|
+
* Printed once, before scanning starts: what this is, what version, where
|
|
36
|
+
* it lives. Answers exactly the question a first-time (or every-time)
|
|
37
|
+
* reader has before results ever appear, without waiting for the report.
|
|
38
|
+
* Same TTY gate and same stream as the progress spinner below and for the
|
|
39
|
+
* same reason: stderr only, so a --json/--sarif consumer's stdout is
|
|
40
|
+
* untouched, and a complete no-op under redirection, piping, or CI.
|
|
41
|
+
*/
|
|
42
|
+
function printIntro(noColor) {
|
|
43
|
+
if (!process.stderr.isTTY) return;
|
|
44
|
+
const paint = makePaint(noColor);
|
|
45
|
+
const { version } = require("../package.json");
|
|
46
|
+
process.stderr.write(
|
|
47
|
+
paint(c.bold + c.cyan, `residoo v${version}`) +
|
|
48
|
+
paint(c.dim, " · find secrets your AI coding agent left on disk\n") +
|
|
49
|
+
paint(c.dim, "https://github.com/dandovdub/residoo\n\n")
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A progress indicator for the scan phase, wired to scan()'s own
|
|
36
55
|
* onProgress callback. Writes to STDERR only, never stdout: --json/--sarif
|
|
37
56
|
* consumers pipe stdout into a parser, and a spinner corrupting that would
|
|
38
57
|
* be a much worse bug than not having one. Gated on stderr actually being a
|
|
39
58
|
* TTY, so it is a complete no-op under redirection, piping, or CI, exactly
|
|
40
59
|
* the contexts where carriage-return spam in a captured log would be
|
|
41
|
-
* useless or actively annoying, not merely invisible.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
60
|
+
* useless or actively annoying, not merely invisible. Shows the actual
|
|
61
|
+
* current file (basename only, through the same safeBasename() every other
|
|
62
|
+
* displayed path in this report goes through — control bytes stripped,
|
|
63
|
+
* invisible code points made visible), not just a running count: real
|
|
64
|
+
* signal, not just motion, and genuinely useful if a scan stalls on one
|
|
65
|
+
* huge file. `stop()` clears the line so whatever prints next starts
|
|
45
66
|
* clean.
|
|
46
67
|
*/
|
|
47
|
-
function makeProgressReporter() {
|
|
68
|
+
function makeProgressReporter(noColor) {
|
|
48
69
|
if (!process.stderr.isTTY) return { onProgress: null, stop() {} };
|
|
70
|
+
const paint = makePaint(noColor);
|
|
49
71
|
let count = 0;
|
|
50
72
|
let lastWriteMs = 0;
|
|
51
73
|
let lastLineLen = 0;
|
|
52
74
|
let frame = 0;
|
|
53
|
-
const write = (s) => {
|
|
75
|
+
const write = (s, visibleLen) => {
|
|
54
76
|
process.stderr.write("\r" + " ".repeat(lastLineLen) + "\r" + s);
|
|
55
|
-
lastLineLen =
|
|
77
|
+
lastLineLen = visibleLen;
|
|
56
78
|
};
|
|
57
|
-
const onProgress = ({ source }) => {
|
|
79
|
+
const onProgress = ({ source, file }) => {
|
|
58
80
|
count++;
|
|
59
81
|
const now = Date.now();
|
|
60
82
|
if (now - lastWriteMs < 80) return; // throttled: avoid flicker on a fast scan
|
|
61
83
|
lastWriteMs = now;
|
|
62
84
|
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
63
|
-
|
|
85
|
+
const label = `scanning ${source}… ${count} file${count === 1 ? "" : "s"} ${safeBasename(file)}`;
|
|
86
|
+
write(paint(c.bold + c.cyan, SPINNER_FRAMES[frame]) + " " + paint(c.dim, label), 2 + label.length);
|
|
64
87
|
};
|
|
65
88
|
const stop = () => { if (lastLineLen > 0) process.stderr.write("\r" + " ".repeat(lastLineLen) + "\r"); };
|
|
66
89
|
return { onProgress, stop };
|
|
@@ -304,9 +327,13 @@ function render({ findings, filesScanned, sourcesScanned, bytesScanned, suppress
|
|
|
304
327
|
push(renderIntegrity(integrity, { noColor }));
|
|
305
328
|
}
|
|
306
329
|
|
|
330
|
+
push();
|
|
331
|
+
push(paint(c.bold, "Next steps:"));
|
|
332
|
+
push(` residoo scan --json ${paint(c.dim, "machine-readable output, full detail")}`);
|
|
333
|
+
push(` residoo scan --seal ${paint(c.dim, "encrypt the affected files into a local vault (originals untouched)")}`);
|
|
307
334
|
push();
|
|
308
335
|
push(paint(c.dim, "Values are redacted in this report (first/last 4 characters only). Nothing scanned"));
|
|
309
|
-
push(paint(c.dim, "here left your machine; residoo makes no network calls.
|
|
336
|
+
push(paint(c.dim, "here left your machine; residoo makes no network calls."));
|
|
310
337
|
|
|
311
338
|
return lines.join("\n");
|
|
312
339
|
}
|
|
@@ -450,4 +477,4 @@ function renderSarif(result) {
|
|
|
450
477
|
}, null, 2);
|
|
451
478
|
}
|
|
452
479
|
|
|
453
|
-
module.exports = { render, renderIntegrity, renderRotationSection, renderJson, renderSarif, makeProgressReporter };
|
|
480
|
+
module.exports = { render, renderIntegrity, renderRotationSection, renderJson, renderSarif, makeProgressReporter, printIntro };
|