residoo 0.8.1 → 0.8.3
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 +29 -11
- package/package.json +7 -3
- package/src/patterns.js +34 -4
- package/src/rotation.js +26 -0
package/README.md
CHANGED
|
@@ -65,17 +65,35 @@ covered in [docs/features.md](docs/features.md).
|
|
|
65
65
|
|
|
66
66
|
Scored #1 of 8 real competing tools on a reproducible, synthetic-but-
|
|
67
67
|
pattern-true corpus, with live egress monitoring so "no network calls" is
|
|
68
|
-
observed, not just documented:
|
|
69
|
-
|
|
70
|
-
| |
|
|
71
|
-
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
observed, not just documented. All 8, not just the closest one:
|
|
69
|
+
|
|
70
|
+
| tool | distinct credentials found | precision | egress during the scan |
|
|
71
|
+
|---|---|---|---|
|
|
72
|
+
| **residoo** | **45/45 (100%)** | **100%** | **none-observed** |
|
|
73
|
+
| agentsweep | 33/42 (79%) | 100% | none-observed |
|
|
74
|
+
| gitleaks | 32/45 (71%) | 100% | none-observed |
|
|
75
|
+
| betterleaks | 32/45 (71%) | 100% | none-observed |
|
|
76
|
+
| whatileaked | 28/42 (67%) | 100% | none-observed |
|
|
77
|
+
| kingfisher | 29/45 (64%) | 100% | attempts calls in default mode |
|
|
78
|
+
| trufflehog | 29/45 (64%) | 97% | attempts calls in default mode |
|
|
79
|
+
| detect-secrets | 25/45 (56%) | 2% | attempts calls in default mode |
|
|
80
|
+
|
|
81
|
+
"none-observed" is a measured result, not a default assumption: every run
|
|
82
|
+
sits under a live proxy trap and process-tree polling, and a deliberate
|
|
83
|
+
canary connection is fired and confirmed caught *before* each real
|
|
84
|
+
benchmark run, specifically so a clean result is falsifiable evidence, not
|
|
85
|
+
silence. The 3 rows with real outbound calls prove the monitor was
|
|
86
|
+
watching them too — kingfisher, trufflehog, and detect-secrets each ship
|
|
87
|
+
an *optional* live-verification feature (checking a found secret against
|
|
88
|
+
the vendor's own API), scored here in their documented offline mode for a
|
|
89
|
+
fair recall comparison, with their default mode's real connection
|
|
90
|
+
attempts reported factually rather than hidden.
|
|
91
|
+
|
|
92
|
+
GitGuardian's `ggshield` is documented, not scored: it refuses to run
|
|
93
|
+
without a server account, so there's no local result to measure. Published
|
|
94
|
+
while losing rows, then fixed in public against the classes it was losing
|
|
95
|
+
— full methodology, every dated rerun, and how to reproduce it yourself:
|
|
96
|
+
[docs/benchmark.md](docs/benchmark.md).
|
|
79
97
|
|
|
80
98
|
## What it does
|
|
81
99
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "residoo",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
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)",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"node": ">=18"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"test": "node tests/smoke.js"
|
|
23
|
+
"test": "node tests/smoke.js",
|
|
24
|
+
"fuzz": "node tests/fuzz.js"
|
|
24
25
|
},
|
|
25
26
|
"files": [
|
|
26
27
|
"bin",
|
|
@@ -41,5 +42,8 @@
|
|
|
41
42
|
"privacy",
|
|
42
43
|
"cli",
|
|
43
44
|
"encryption"
|
|
44
|
-
]
|
|
45
|
+
],
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"fast-check": "4.9.0"
|
|
48
|
+
}
|
|
45
49
|
}
|
package/src/patterns.js
CHANGED
|
@@ -145,11 +145,16 @@ const PATTERNS = [
|
|
|
145
145
|
// Confirmed via airtable.com/developers/web/api: pat + 14 alnum + "." + 64 hex.
|
|
146
146
|
{ id: "airtable_token", label: "Airtable personal access token", confidence: "high",
|
|
147
147
|
re: /\bpat[A-Za-z0-9]{14}\.[a-f0-9]{64}\b/g },
|
|
148
|
-
// Current Cloudflare
|
|
149
|
-
// developers.cloudflare.com
|
|
150
|
-
//
|
|
148
|
+
// Current Cloudflare credential formats only (cfat_/cfut_/cfk_, all three
|
|
149
|
+
// confirmed via developers.cloudflare.com/fundamentals/api/get-started/token-formats,
|
|
150
|
+
// which describes all three with the identical "<prefix>_[40 characters]
|
|
151
|
+
// [checksum]" shape). cfk_ (Global API Key, full account access) found
|
|
152
|
+
// missing by cross-checking agentsweep's own open issue tracker -- it had
|
|
153
|
+
// cfat_/cfut_ before this project did, cfk_ after. The legacy formats are
|
|
154
|
+
// bare unprefixed strings, left out for the same reason as CircleCI's
|
|
155
|
+
// legacy form.
|
|
151
156
|
{ id: "cloudflare_api_token", label: "Cloudflare API token", confidence: "high",
|
|
152
|
-
re: /\bcf[au]
|
|
157
|
+
re: /\bcf(?:[au]t|k)_[a-zA-Z0-9]{40}[a-f0-9]{8}\b/g },
|
|
153
158
|
// Current Heroku API key format only (HRKU-AA + 58 chars, confirmed via
|
|
154
159
|
// Heroku's own help docs). The legacy format is a bare UUID, left out:
|
|
155
160
|
// "any UUID-shaped string" is exactly the noisy, unspecific shape this
|
|
@@ -267,6 +272,31 @@ const PATTERNS = [
|
|
|
267
272
|
// than a doc-confirmed exact count.
|
|
268
273
|
{ id: "posthog_key", label: "PostHog personal API key", confidence: "high",
|
|
269
274
|
re: /\bphx_[A-Za-z0-9]{40,}\b/g },
|
|
275
|
+
// LangSmith personal access token (lsv2_pt_) / service key (lsv2_sk_).
|
|
276
|
+
// Found missing by cross-checking agentsweep's own open-issue tracker.
|
|
277
|
+
// The first segment (32 hex, UUID-shaped) is consistent across every
|
|
278
|
+
// real example checked (docs.langchain.com and independent citations of
|
|
279
|
+
// it); the trailing segment's exact length is NOT confirmed by any
|
|
280
|
+
// primary source found (two secondary citations of the same example
|
|
281
|
+
// key disagreed by one character), so it's a generous bound, not a
|
|
282
|
+
// doc-confirmed exact count -- same honesty standard as posthog_key
|
|
283
|
+
// above. "lsv2_" itself is distinctive enough that this bound doesn't
|
|
284
|
+
// meaningfully raise false-positive risk either way.
|
|
285
|
+
{ id: "langsmith_key", label: "LangSmith API key", confidence: "high",
|
|
286
|
+
re: /\blsv2_(?:pt|sk)_[a-f0-9]{32}_[a-f0-9]{6,16}\b/g },
|
|
287
|
+
// Resend API key (re_). Found missing the same way. No primary source
|
|
288
|
+
// publishes an exact length spec; the one real example seen (Resend's
|
|
289
|
+
// own docs) is an 8-char id segment + a 24-char secret segment, both
|
|
290
|
+
// mixed-case alphanumeric, joined by one underscore. Bounded generously
|
|
291
|
+
// around that shape rather than pinned to it exactly. The two-segment,
|
|
292
|
+
// underscore-joined, both-high-entropy structure is deliberately what
|
|
293
|
+
// makes this safe as a DEFAULT (non-noisy) rule despite the short,
|
|
294
|
+
// otherwise-generic "re_" prefix: tested against realistic re_-prefixed
|
|
295
|
+
// code identifiers (re_try, re_send, re_connect, re_validate...) and
|
|
296
|
+
// none match, since none of them have a second underscore-delimited
|
|
297
|
+
// random-looking segment.
|
|
298
|
+
{ id: "resend_key", label: "Resend API key", confidence: "high",
|
|
299
|
+
re: /\bre_[A-Za-z0-9]{6,12}_[A-Za-z0-9]{18,32}\b/g },
|
|
270
300
|
];
|
|
271
301
|
|
|
272
302
|
/**
|
package/src/rotation.js
CHANGED
|
@@ -772,6 +772,32 @@ const ROTATION_GUIDANCE = {
|
|
|
772
772
|
],
|
|
773
773
|
revokeNote: "Deletion is immediate; the key stops authenticating on the next request.",
|
|
774
774
|
},
|
|
775
|
+
// Fetched docs.langchain.com/langsmith/create-account-api-key
|
|
776
|
+
// (2026-09-04): both personal access tokens and service keys are
|
|
777
|
+
// created and managed from the workspace's own API Keys settings page.
|
|
778
|
+
langsmith_key: {
|
|
779
|
+
label: "LangSmith API key",
|
|
780
|
+
consolePath: "smith.langchain.com > Settings > API Keys",
|
|
781
|
+
steps: [
|
|
782
|
+
"Open API Keys under the workspace's settings",
|
|
783
|
+
"Delete the leaked key (personal access token or service key)",
|
|
784
|
+
"Create a replacement and update whatever used the old one",
|
|
785
|
+
],
|
|
786
|
+
revokeNote: "Deletion is immediate; the key stops authenticating on the next request.",
|
|
787
|
+
},
|
|
788
|
+
// Fetched resend.com/docs/dashboard/api-keys/introduction (2026-09-04):
|
|
789
|
+
// keys are removed from the dashboard's API Keys list (More options ->
|
|
790
|
+
// Remove API key), or programmatically via the DELETE /api-keys endpoint.
|
|
791
|
+
resend_key: {
|
|
792
|
+
label: "Resend API key",
|
|
793
|
+
consolePath: "resend.com/api-keys",
|
|
794
|
+
steps: [
|
|
795
|
+
"Open the API Keys list in the Resend dashboard",
|
|
796
|
+
"Click More options on the leaked key, then Remove API key",
|
|
797
|
+
"Create a replacement and update whatever used the old one",
|
|
798
|
+
],
|
|
799
|
+
revokeNote: "Deletion is immediate; the key stops authenticating on the next request.",
|
|
800
|
+
},
|
|
775
801
|
|
|
776
802
|
// ── NOISY_PATTERNS (only reachable via --include-noisy) ───────────────
|
|
777
803
|
generic_password_assignment: {
|