residoo 0.8.6 → 0.8.7

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 CHANGED
@@ -97,7 +97,7 @@ while losing rows, then fixed in public against the classes it was losing
97
97
 
98
98
  ## What it does
99
99
 
100
- - Scans your local AI-agent session transcripts for 59 high-confidence
100
+ - Scans your local AI-agent session transcripts for 67 high-confidence
101
101
  secret patterns: cloud provider keys, private key blocks, OAuth/API
102
102
  tokens, database connection strings, and more. See
103
103
  [`src/patterns.js`](src/patterns.js).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "residoo",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
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/patterns.js CHANGED
@@ -33,6 +33,17 @@ const PATTERNS = [
33
33
  re: /\b(?:gh[pousr]_|github_pat_)[A-Za-z0-9_.-]{20,600}\b/g },
34
34
  { id: "gitlab_pat", label: "GitLab personal access token", confidence: "high",
35
35
  re: /\bglpat-[A-Za-z0-9_-]{20,100}\b/g },
36
+ // GitLab's OTHER token kinds, beyond the personal access token above --
37
+ // deploy, runner, CI/CD job, trigger, feed, incoming-mail, agent,
38
+ // workspace, SCIM, feature-flags-client, and OAuth-app-secret tokens.
39
+ // All prefixes taken directly from GitLab's own published table
40
+ // (docs.gitlab.com/security/tokens/), which gives every prefix but no
41
+ // exact body length for any of them -- one bundled rule rather than
42
+ // eleven near-identical ones, since they share the same risk profile
43
+ // (repo/registry/CI access) and the same rotation path (Settings >
44
+ // Access Tokens for the relevant scope).
45
+ { id: "gitlab_other_token", label: "GitLab token (deploy/runner/CI/other)", confidence: "high",
46
+ re: /\bgl(?:oas|dt|rtr|rt|cbt|ptt|ft|imt|agent|wt|soat|ffct)-[A-Za-z0-9_-]{16,100}\b/g },
36
47
  { id: "slack_token", label: "Slack token", confidence: "high",
37
48
  re: /\bxox[baprs]-[0-9A-Za-z-]{10,500}\b/g },
38
49
  { id: "stripe_key", label: "Stripe API key (live mode)", confidence: "high",
@@ -84,6 +95,29 @@ const PATTERNS = [
84
95
  // already excluded for Weights & Biases' classic key format.
85
96
  { id: "azure_ad_client_secret", label: "Azure AD (Entra ID) client secret", confidence: "high",
86
97
  re: /(?<![A-Za-z0-9_.~-])[A-Za-z0-9_.~]{2,4}\dQ~[A-Za-z0-9_.~-]{28,36}(?![A-Za-z0-9_.~-])/g },
98
+ // Azure DevOps PAT. Microsoft's own current docs (learn.microsoft.com,
99
+ // .../use-personal-access-tokens-to-authenticate, updated 2026-07-08)
100
+ // state: "Tokens are 84 characters long, with 52 characters being
101
+ // randomized data... Tokens issued by Azure DevOps include a fixed AZDO
102
+ // signature at positions 76-80." That description is imprecise about
103
+ // the exact byte offset (a 4-char literal can't cleanly occupy a
104
+ // 5-position range), so rather than hard-code a single offset that
105
+ // might be off by one and never match a real token, this allows a
106
+ // window for where AZDO can sit while still requiring both the fixed
107
+ // literal anchor and a close-to-84 overall length -- the anchor is what
108
+ // actually carries the false-positive protection.
109
+ { id: "azure_devops_pat", label: "Azure DevOps personal access token", confidence: "high",
110
+ re: /\b[A-Za-z0-9]{65,75}AZDO[A-Za-z0-9]{5,15}\b/g },
111
+ // Atlassian Cloud API token (classic/scoped). Prefix and structure from
112
+ // noseyparker's own shipped rule; independently confirmed current via
113
+ // an Atlassian staff reply on Atlassian's own community forum naming
114
+ // the three current token families and their prefixes (API Token:
115
+ // ATAT, App Password: ATBB, Access Tokens: ATCT) -- this rule covers
116
+ // only the first, most common one. The newer Access Token family
117
+ // (ATCTT3xFfGN0...) exists per that same staff reply but with no
118
+ // length/charset spec found anywhere, so it isn't guessed at here.
119
+ { id: "atlassian_api_token", label: "Atlassian Cloud API token", confidence: "high",
120
+ re: /\bATATT3xFfGF0[A-Za-z0-9_-]{20,200}=[0-9A-F]{8}\b/g },
87
121
  // Tailscale auth key. Found missing via gitleaks/gitleaks#1778 (still
88
122
  // open there too). No fully authoritative current spec found: Tailscale's
89
123
  // own kb/1085/auth-keys page shows an older bare "tskey-<hex>" example,
@@ -108,6 +142,32 @@ const PATTERNS = [
108
142
  re: /\bAIza[0-9A-Za-z_-]{35}\b/g },
109
143
  { id: "npm_token", label: "npm access token", confidence: "high",
110
144
  re: /\bnpm_[A-Za-z0-9]{36}\b/g },
145
+ // Found via a competitor research pass mining noseyparker/ggshield/
146
+ // detect-secrets (2026-09-04). PyPI publishes its own regex directly
147
+ // (docs.pypi.org/api/secrets/): "pypi-[A-Za-z0-9-_]{85,}", no stated
148
+ // upper bound ("can be arbitrarily long") -- capped here at 700 as a
149
+ // practical ceiling, not a vendor limit.
150
+ { id: "pypi_token", label: "PyPI API token", confidence: "high",
151
+ re: /\bpypi-[A-Za-z0-9_-]{85,700}\b/g },
152
+ // Verified against crates.io's own current token-generation source
153
+ // (rust-lang/crates.io, crates/crates_io_database/src/utils/token.rs):
154
+ // TOKEN_PREFIX = "cio", TOKEN_LENGTH = 32, generated via
155
+ // rand::distr::Alphanumeric -- the strongest sourcing in this batch,
156
+ // read from the vendor's actual live code rather than its docs.
157
+ { id: "crates_io_key", label: "crates.io API token", confidence: "high",
158
+ re: /\bcio[A-Za-z0-9]{32}\b/g },
159
+ // Exact length independently counted against RubyGems' own guide
160
+ // (guides.rubygems.org/rubygems-org-api/), whose worked example shows
161
+ // the body as exactly 48 lowercase hex characters after the prefix.
162
+ { id: "rubygems_key", label: "RubyGems API key", confidence: "high",
163
+ re: /\brubygems_[a-f0-9]{48}\b/g },
164
+ // Docker's own OpenAPI spec (docker/docs, content/reference/api/
165
+ // ai-governance/api.yaml) names both prefixes explicitly -- Personal
166
+ // Access Token dckr_pat_*, Organization Access Token dckr_oat_* -- but
167
+ // gives no exact body length/charset for either, hence the generous
168
+ // bound already used elsewhere in this file for the same situation.
169
+ { id: "docker_hub_token", label: "Docker Hub access token", confidence: "high",
170
+ re: /\bdckr_(?:pat|oat)_[A-Za-z0-9_-]{20,200}\b/g },
111
171
  { id: "sendgrid_key", label: "SendGrid API key", confidence: "high",
112
172
  re: /\bSG\.[A-Za-z0-9_-]{16,100}\.[A-Za-z0-9_-]{16,100}\b/g },
113
173
  { id: "twilio_key", label: "Twilio API key", confidence: "high",
@@ -307,6 +367,17 @@ const PATTERNS = [
307
367
  // ── Comms / SaaS ───────────────────────────────────────────────────────
308
368
  { id: "discord_webhook", label: "Discord webhook URL", confidence: "high",
309
369
  re: /\bhttps:\/\/discord\.com\/api\/webhooks\/[0-9]{18,19}\/[0-9a-zA-Z_-]{68}\b/g },
370
+ // Discord BOT token -- a different, more sensitive credential than the
371
+ // webhook URL above (full bot API access vs. a single channel post).
372
+ // Three dot-separated base64url segments, first starting with M/N/O
373
+ // (the base64 encoding of a Discord user-id snowflake's leading byte
374
+ // range). Confirmed via Discord's own current developer docs
375
+ // (docs.discord.com/developers/reference, shows a live example token in
376
+ // this exact shape); same structure as detect-secrets' own shipped
377
+ // plugin. Never collides with the jwt rule below since a JWT's first two
378
+ // segments must start with the literal "eyJ", not M/N/O.
379
+ { id: "discord_bot_token", label: "Discord bot token", confidence: "high",
380
+ re: /\b[MNO][A-Za-z0-9_-]{23,25}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27}\b/g },
310
381
  { id: "telegram_bot_token", label: "Telegram bot token", confidence: "high",
311
382
  re: /\b[0-9]{8,10}:[a-zA-Z0-9_-]{35}\b/g },
312
383
  { id: "mailgun_key", label: "Mailgun API key", confidence: "high",
package/src/rotation.js CHANGED
@@ -164,6 +164,19 @@ const ROTATION_GUIDANCE = {
164
164
  ],
165
165
  revokeNote: "Rotate revokes the old token and issues its replacement in one step.",
166
166
  },
167
+ // docs.gitlab.com/security/tokens/ documents every token kind's own
168
+ // settings location; this rule bundles several, so the guidance points
169
+ // at the relevant scope's own settings rather than one fixed page.
170
+ gitlab_other_token: {
171
+ label: "GitLab token (deploy/runner/CI/other)",
172
+ consolePath: "GitLab > the relevant scope's Settings > Access Tokens (project Settings for deploy/CI-job/trigger tokens, group Settings for group-scoped ones, admin area for runner registration tokens)",
173
+ steps: [
174
+ "Identify which token kind leaked from its prefix (gldt- deploy, glrt-/glrtr- runner, glcbt- CI/CD job, glptt- trigger, and so on)",
175
+ "Revoke it from that scope's Access Tokens or Runners settings",
176
+ "Issue a replacement and update whatever used the old one (CI variable, deploy config, webhook)",
177
+ ],
178
+ revokeNote: "A CI/CD job token (glcbt-) is normally short-lived and scoped to a single pipeline run -- if this is one, confirm the run has already finished before treating it as urgent.",
179
+ },
167
180
  // Fetched https://docs.slack.dev/reference/methods/auth.revoke (2026-09-02):
168
181
  // "This method revokes an access token." (api.slack.com/methods/auth.revoke
169
182
  // now 302s here.) App-level management lives at api.slack.com/apps.
@@ -229,6 +242,32 @@ const ROTATION_GUIDANCE = {
229
242
  ],
230
243
  revokeNote: "Deletion is immediate. Prefer create-then-delete over delete-then-create if the app can tolerate two live secrets briefly -- it avoids an outage window for whatever depends on this secret.",
231
244
  },
245
+ // learn.microsoft.com/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate
246
+ // (fetched 2026-09-04): PAT management lives under User settings in the
247
+ // Azure DevOps web UI, login-walled, hence the path in words.
248
+ azure_devops_pat: {
249
+ label: "Azure DevOps personal access token",
250
+ consolePath: "Azure DevOps > User settings (top right) > Personal access tokens",
251
+ steps: [
252
+ "Open Personal access tokens under User settings",
253
+ "Revoke the leaked token",
254
+ "Create a replacement with the narrowest scopes and a short expiry",
255
+ ],
256
+ revokeNote: "Revocation is immediate; anything still using the old token starts failing authentication at once.",
257
+ },
258
+ // Atlassian's own community forum, staff reply (fetched 2026-09-04):
259
+ // confirms the token families and that revocation happens from the
260
+ // account's own API tokens page, login-walled.
261
+ atlassian_api_token: {
262
+ label: "Atlassian Cloud API token",
263
+ consolePath: "id.atlassian.com > Security > API tokens",
264
+ steps: [
265
+ "Open the API tokens page under account Security settings",
266
+ "Revoke the leaked token",
267
+ "Create a replacement and update whatever used the old one",
268
+ ],
269
+ revokeNote: "This is a personal API token tied to the account that created it -- check that account's recent activity across every Atlassian product it has access to (Jira, Confluence, Bitbucket), not just one.",
270
+ },
232
271
  // Fetched tailscale.com/kb/1085/auth-keys (2026-09-04): exact console
233
272
  // path and the important revoke-vs-deauthorize distinction are quoted
234
273
  // from that page, not inferred.
@@ -294,6 +333,52 @@ const ROTATION_GUIDANCE = {
294
333
  ],
295
334
  revokeNote: "Check your packages' recent publishes afterward: a leaked npm token is a supply-chain foothold, not just an account problem.",
296
335
  },
336
+ // docs.pypi.org/api/secrets/ (fetched 2026-09-04): PyPI documents its own
337
+ // token format on this page and links account management from there.
338
+ pypi_token: {
339
+ label: "PyPI API token",
340
+ rotateUrl: "https://docs.pypi.org/api/secrets/",
341
+ steps: [
342
+ "pypi.org > Account settings > API tokens",
343
+ "Remove the leaked token",
344
+ "Add a replacement scoped to a single project rather than the whole account, if possible",
345
+ ],
346
+ revokeNote: "Check the project's recent releases afterward: a leaked PyPI token is a supply-chain foothold, the same risk class as a leaked npm token.",
347
+ },
348
+ // crates.io's own token settings page; management is login-walled.
349
+ crates_io_key: {
350
+ label: "crates.io API token",
351
+ consolePath: "crates.io > Account Settings > API Tokens",
352
+ steps: [
353
+ "Open API Tokens under Account Settings",
354
+ "Revoke the leaked token",
355
+ "Create a replacement scoped as narrowly as crates.io's scope options allow",
356
+ ],
357
+ revokeNote: "Check the account's recent publishes afterward: a leaked crates.io token is a supply-chain foothold for every crate it can publish to.",
358
+ },
359
+ // RubyGems' own API dashboard; management is login-walled.
360
+ rubygems_key: {
361
+ label: "RubyGems API key",
362
+ consolePath: "rubygems.org > Edit Profile > API Keys",
363
+ steps: [
364
+ "Open API Keys under your profile",
365
+ "Revoke the leaked key",
366
+ "Create a replacement with the narrowest scopes (e.g. push-only, not yank/owner)",
367
+ ],
368
+ revokeNote: "Check the account's recent gem pushes afterward: a leaked RubyGems key is a supply-chain foothold for every gem it can publish to.",
369
+ },
370
+ // Docker's own OpenAPI docs name the two token kinds; management pages
371
+ // are login-walled.
372
+ docker_hub_token: {
373
+ label: "Docker Hub access token",
374
+ consolePath: "hub.docker.com > Account Settings > Security (or Organization Settings > Access Tokens for an OAT)",
375
+ steps: [
376
+ "Open the Security / Access Tokens page for the relevant account or organization",
377
+ "Delete the leaked token",
378
+ "Create a replacement with the narrowest read/write/delete permissions it needs",
379
+ ],
380
+ revokeNote: "An Organization Access Token (dckr_oat_) can reach every repo in that org -- treat a leak of one as broader than a leaked personal token.",
381
+ },
297
382
  // Fetched https://www.twilio.com/docs/sendgrid/ui/account-and-settings/api-keys
298
383
  // (2026-09-02): Settings > API Keys, action menu > Delete API Key,
299
384
  // delete-then-recreate as the regeneration flow.
@@ -746,6 +831,18 @@ const ROTATION_GUIDANCE = {
746
831
  ],
747
832
  revokeNote: "The URL is the entire credential: anyone holding it can post to the channel until the webhook is deleted.",
748
833
  },
834
+ // docs.discord.com/developers/reference (fetched 2026-09-04): bot tokens
835
+ // are regenerated from the same Developer Portal page as the bot itself.
836
+ discord_bot_token: {
837
+ label: "Discord bot token",
838
+ rotateUrl: "https://docs.discord.com/developers/reference",
839
+ steps: [
840
+ "Discord Developer Portal > Applications > your app > Bot",
841
+ "Click Reset Token to invalidate the old one and issue a new one",
842
+ "Update whatever hosts the bot with the new token",
843
+ ],
844
+ revokeNote: "This is full bot API access, not a single-channel webhook -- resetting immediately disconnects the bot everywhere it's running until redeployed with the new token.",
845
+ },
749
846
  // Fetched https://core.telegram.org/bots/features (2026-09-02): "If your
750
847
  // existing token is compromised or you lost it for some reason, use the
751
848
  // /token command to generate a new one."