residoo 0.8.6 → 0.8.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/README.md +1 -1
- package/package.json +1 -1
- package/src/patterns.js +158 -0
- package/src/rotation.js +227 -0
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
|
|
100
|
+
- Scans your local AI-agent session transcripts for 79 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.
|
|
3
|
+
"version": "0.8.8",
|
|
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",
|
|
@@ -168,6 +228,50 @@ const PATTERNS = [
|
|
|
168
228
|
// collide with any of them.
|
|
169
229
|
{ id: "elevenlabs_key", label: "ElevenLabs API key", confidence: "high",
|
|
170
230
|
re: /\bsk_[a-f0-9]{48}\b/g },
|
|
231
|
+
// Third batch from the 2026-09-04 competitor research pass. NVIDIA's own
|
|
232
|
+
// docs (docs.nvidia.com) confirm the nvapi- prefix ("Keys typically start
|
|
233
|
+
// with nvapi-") without stating an exact length.
|
|
234
|
+
{ id: "nvidia_api_key", label: "NVIDIA API key", confidence: "high",
|
|
235
|
+
re: /\bnvapi-[A-Za-z0-9_-]{40,200}\b/g },
|
|
236
|
+
// Jina AI: prefix confirmed via Jina's own GitHub org/SDKs and broad
|
|
237
|
+
// ecosystem documentation; exact 60-char length taken from noseyparker's
|
|
238
|
+
// own shipped rule, not independently pinned by a Jina spec page.
|
|
239
|
+
{ id: "jina_key", label: "Jina AI API key", confidence: "high",
|
|
240
|
+
re: /\bjina_[A-Za-z0-9]{60}\b/g },
|
|
241
|
+
// Tavily: tvly- prefix confirmed via Tavily's own official SDK repos
|
|
242
|
+
// (tavily-python, tavily-js); exact 32-char length not independently
|
|
243
|
+
// pinned by a dedicated format spec page.
|
|
244
|
+
{ id: "tavily_key", label: "Tavily API key", confidence: "high",
|
|
245
|
+
re: /\btvly-[A-Za-z0-9]{32}\b/g },
|
|
246
|
+
// Firecrawl: fc- prefix appears in Firecrawl's own docs and SDKs, but
|
|
247
|
+
// only as illustrative placeholders -- no page independently confirms
|
|
248
|
+
// the exact 32-lowercase-hex body. Shipped anyway on the combination of
|
|
249
|
+
// the prefix plus a strict hex-only body requirement right after it.
|
|
250
|
+
{ id: "firecrawl_key", label: "Firecrawl API key", confidence: "high",
|
|
251
|
+
re: /\bfc-[a-f0-9]{32}\b/g },
|
|
252
|
+
// Databricks PAT: Databricks' own docs deliberately show only placeholder
|
|
253
|
+
// tokens, no real format. Corroborated by two independent secondary
|
|
254
|
+
// sources instead -- Microsoft Purview's own Sensitive-Information-Type
|
|
255
|
+
// definition for Azure Databricks PATs, and TruffleHog's shipped OSS
|
|
256
|
+
// detector -- both agreeing on dapi + 32 lowercase hex, optionally a
|
|
257
|
+
// trailing -<digits>. Not Databricks-primary-sourced, so flagged medium
|
|
258
|
+
// rather than high like the rest of this file. The strict lowercase-hex
|
|
259
|
+
// body avoids the one real collision risk found during research: Binance
|
|
260
|
+
// also uses a "dapi" naming convention for its COIN-margined futures API
|
|
261
|
+
// (e.g. dapiDataGetTopLongShortPositionRatio), but that never matches
|
|
262
|
+
// since it isn't hex.
|
|
263
|
+
{ id: "databricks_pat", label: "Databricks personal access token", confidence: "medium",
|
|
264
|
+
re: /\bdapi[a-f0-9]{32}(?:-[0-9]+)?\b/g },
|
|
265
|
+
// Sourcegraph: sgp_ prefix confirmed via Sourcegraph's own docs ("Access
|
|
266
|
+
// tokens now begin with the prefix sgp_"); exact body shape (an optional
|
|
267
|
+
// 16-hex instance-id infix, or a local_ marker, before the 40-hex body)
|
|
268
|
+
// taken from noseyparker's worked examples, not independently pinned.
|
|
269
|
+
// NOTE for future additions: Segment's own "Public API Token" also uses
|
|
270
|
+
// an sgp_ prefix (sgp_[a-zA-Z0-9]{64}) -- not added here, but if it ever
|
|
271
|
+
// is, the two rules are already mutually exclusive (hex-only 40 vs.
|
|
272
|
+
// mixed-case 64), just worth testing explicitly at that point.
|
|
273
|
+
{ id: "sourcegraph_token", label: "Sourcegraph access token", confidence: "high",
|
|
274
|
+
re: /\bsgp_(?:[a-fA-F0-9]{16}_|local_)?[a-fA-F0-9]{40}\b/g },
|
|
171
275
|
|
|
172
276
|
// ── Cloud / infra ──────────────────────────────────────────────────────
|
|
173
277
|
{ id: "digitalocean_token", label: "DigitalOcean access token", confidence: "high",
|
|
@@ -307,6 +411,17 @@ const PATTERNS = [
|
|
|
307
411
|
// ── Comms / SaaS ───────────────────────────────────────────────────────
|
|
308
412
|
{ id: "discord_webhook", label: "Discord webhook URL", confidence: "high",
|
|
309
413
|
re: /\bhttps:\/\/discord\.com\/api\/webhooks\/[0-9]{18,19}\/[0-9a-zA-Z_-]{68}\b/g },
|
|
414
|
+
// Discord BOT token -- a different, more sensitive credential than the
|
|
415
|
+
// webhook URL above (full bot API access vs. a single channel post).
|
|
416
|
+
// Three dot-separated base64url segments, first starting with M/N/O
|
|
417
|
+
// (the base64 encoding of a Discord user-id snowflake's leading byte
|
|
418
|
+
// range). Confirmed via Discord's own current developer docs
|
|
419
|
+
// (docs.discord.com/developers/reference, shows a live example token in
|
|
420
|
+
// this exact shape); same structure as detect-secrets' own shipped
|
|
421
|
+
// plugin. Never collides with the jwt rule below since a JWT's first two
|
|
422
|
+
// segments must start with the literal "eyJ", not M/N/O.
|
|
423
|
+
{ id: "discord_bot_token", label: "Discord bot token", confidence: "high",
|
|
424
|
+
re: /\b[MNO][A-Za-z0-9_-]{23,25}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27}\b/g },
|
|
310
425
|
{ id: "telegram_bot_token", label: "Telegram bot token", confidence: "high",
|
|
311
426
|
re: /\b[0-9]{8,10}:[a-zA-Z0-9_-]{35}\b/g },
|
|
312
427
|
{ id: "mailgun_key", label: "Mailgun API key", confidence: "high",
|
|
@@ -381,6 +496,49 @@ const PATTERNS = [
|
|
|
381
496
|
// random-looking segment.
|
|
382
497
|
{ id: "resend_key", label: "Resend API key", confidence: "high",
|
|
383
498
|
re: /\bre_[A-Za-z0-9]{6,12}_[A-Za-z0-9]{18,32}\b/g },
|
|
499
|
+
// Shopify Admin API access token: shpat_/shppa_ prefixes confirmed via
|
|
500
|
+
// Shopify's own current developer docs. Exact body length not stated on
|
|
501
|
+
// that page (a since-404'd Shopify changelog described it, quoted only
|
|
502
|
+
// by secondary sources), hence a generous floor/ceiling bound. Legacy
|
|
503
|
+
// shpca_/shpss_/shpua_ prefixes are real (Shopify community forum) but
|
|
504
|
+
// not on the current primary page, so left out here.
|
|
505
|
+
{ id: "shopify_admin_token", label: "Shopify Admin API access token", confidence: "high",
|
|
506
|
+
re: /\bshp(?:at|pa)_[A-Za-z0-9]{32,40}\b/g },
|
|
507
|
+
// HubSpot private app token: HubSpot's own docs show only a masked
|
|
508
|
+
// example (pat-**-***...), corroborated by real (redacted) examples on
|
|
509
|
+
// HubSpot's own community forum showing the pat-<region>-<UUID-shaped
|
|
510
|
+
// body> structure. Medium confidence: the shape is real but the exact
|
|
511
|
+
// per-segment lengths are inferred, not vendor-pinned.
|
|
512
|
+
{ id: "hubspot_token", label: "HubSpot private app access token", confidence: "medium",
|
|
513
|
+
re: /\bpat-[a-z]{2}\d-[a-f0-9-]{30,60}\b/gi },
|
|
514
|
+
// Grafana service account token: exact shape read directly off Grafana's
|
|
515
|
+
// own docs example token (grafana.com/docs/grafana/latest/administration/service-accounts/),
|
|
516
|
+
// not inferred -- two independently-random underscore-delimited segments
|
|
517
|
+
// behind a distinctive 5-char prefix.
|
|
518
|
+
{ id: "grafana_service_account_token", label: "Grafana service account token", confidence: "high",
|
|
519
|
+
re: /\bglsa_[A-Za-z0-9]{32}_[A-Fa-f0-9]{8}\b/g },
|
|
520
|
+
// New Relic: NRAK- prefix confirmed via New Relic's own migration-notice
|
|
521
|
+
// doc ("If your API key starts with NRAK, no update is required").
|
|
522
|
+
// Exact body length/charset not published; generously bounded.
|
|
523
|
+
{ id: "new_relic_api_key", label: "New Relic API key", confidence: "high",
|
|
524
|
+
re: /\bNRAK-[A-Z0-9]{27,64}\b/g },
|
|
525
|
+
// Mailchimp: 32 lowercase hex + a literal -us<datacenter digits> suffix,
|
|
526
|
+
// read directly off a live example in Mailchimp's own docs. The suffix
|
|
527
|
+
// is what makes this safe as a default rule -- a bare 32-hex string
|
|
528
|
+
// alone would be indistinguishable from countless unrelated hashes, but
|
|
529
|
+
// the specific "-usN" tail is Mailchimp-specific.
|
|
530
|
+
{ id: "mailchimp_key", label: "Mailchimp API key", confidence: "high",
|
|
531
|
+
re: /\b[0-9a-f]{32}-us[0-9]{1,2}\b/g },
|
|
532
|
+
// Akamai EdgeGrid access_token / client_token: akab- prefix and the
|
|
533
|
+
// hyphen-separated two-segment shape confirmed via Akamai's own current
|
|
534
|
+
// docs (literal examples shown), independently corroborated by a
|
|
535
|
+
// detect-secrets maintainer's own reproduction. Neither source states
|
|
536
|
+
// hard segment-length bounds, so the ranges here are a reasonable
|
|
537
|
+
// envelope, not vendor-pinned. Akamai's client_secret was investigated
|
|
538
|
+
// and NOT added: no distinctive prefix in Akamai's own examples, the
|
|
539
|
+
// same bare/opaque shape already excluded elsewhere in this file.
|
|
540
|
+
{ id: "akamai_edgegrid_token", label: "Akamai EdgeGrid token", confidence: "high",
|
|
541
|
+
re: /\bakab-[a-z0-9]{16,32}-[a-z0-9]{6,32}\b/g },
|
|
384
542
|
];
|
|
385
543
|
|
|
386
544
|
/**
|
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.
|
|
@@ -487,6 +572,71 @@ const ROTATION_GUIDANCE = {
|
|
|
487
572
|
],
|
|
488
573
|
revokeNote: "A key grants full account access (voices, generations, billing); deletion is immediate.",
|
|
489
574
|
},
|
|
575
|
+
// docs.nvidia.com's own key-management pages point at build.nvidia.com
|
|
576
|
+
// for personal keys; login-walled, hence the path in words.
|
|
577
|
+
nvidia_api_key: {
|
|
578
|
+
label: "NVIDIA API key",
|
|
579
|
+
consolePath: "build.nvidia.com > your profile > API Keys",
|
|
580
|
+
steps: [
|
|
581
|
+
"Open API Keys under your NVIDIA Build profile",
|
|
582
|
+
"Delete the leaked key",
|
|
583
|
+
"Generate a replacement and update whatever used the old one",
|
|
584
|
+
],
|
|
585
|
+
revokeNote: "Deletion is immediate; anything still using the old key starts failing authentication at once.",
|
|
586
|
+
},
|
|
587
|
+
jina_key: {
|
|
588
|
+
label: "Jina AI API key",
|
|
589
|
+
consolePath: "jina.ai > API keys (jina.ai/api-dashboard/key-manager)",
|
|
590
|
+
steps: [
|
|
591
|
+
"Open the API key manager",
|
|
592
|
+
"Delete the leaked key",
|
|
593
|
+
"Create a replacement and update whatever used the old one",
|
|
594
|
+
],
|
|
595
|
+
revokeNote: "Deletion is immediate; anything still using the old key starts failing authentication at once.",
|
|
596
|
+
},
|
|
597
|
+
tavily_key: {
|
|
598
|
+
label: "Tavily API key",
|
|
599
|
+
consolePath: "app.tavily.com > API Keys",
|
|
600
|
+
steps: [
|
|
601
|
+
"Open API Keys in the Tavily dashboard",
|
|
602
|
+
"Delete the leaked key",
|
|
603
|
+
"Create a replacement and update whatever used the old one",
|
|
604
|
+
],
|
|
605
|
+
revokeNote: "Deletion is immediate; anything still using the old key starts failing authentication at once.",
|
|
606
|
+
},
|
|
607
|
+
firecrawl_key: {
|
|
608
|
+
label: "Firecrawl API key",
|
|
609
|
+
consolePath: "firecrawl.dev dashboard > API Keys",
|
|
610
|
+
steps: [
|
|
611
|
+
"Open API Keys in the Firecrawl dashboard",
|
|
612
|
+
"Delete the leaked key",
|
|
613
|
+
"Create a replacement and update whatever used the old one",
|
|
614
|
+
],
|
|
615
|
+
revokeNote: "Deletion is immediate; anything still using the old key starts failing authentication at once.",
|
|
616
|
+
},
|
|
617
|
+
// Format is not Databricks-primary-sourced (see the comment in
|
|
618
|
+
// src/patterns.js), but the rotation location itself is confirmed
|
|
619
|
+
// directly from Databricks' own docs (docs.databricks.com/aws/en/dev-tools/auth/pat).
|
|
620
|
+
databricks_pat: {
|
|
621
|
+
label: "Databricks personal access token",
|
|
622
|
+
consolePath: "Databricks workspace > User Settings > Developer > Access tokens",
|
|
623
|
+
steps: [
|
|
624
|
+
"Open Access tokens under Developer settings in the workspace",
|
|
625
|
+
"Revoke the leaked token",
|
|
626
|
+
"Create a replacement with the shortest lifetime that works for your use case",
|
|
627
|
+
],
|
|
628
|
+
revokeNote: "Revocation is immediate; anything still using the old token starts failing authentication at once.",
|
|
629
|
+
},
|
|
630
|
+
sourcegraph_token: {
|
|
631
|
+
label: "Sourcegraph access token",
|
|
632
|
+
rotateUrl: "https://sourcegraph.com/docs/cli/how-tos/creating_an_access_token",
|
|
633
|
+
steps: [
|
|
634
|
+
"Sourcegraph instance > user menu > Settings > Access tokens",
|
|
635
|
+
"Delete the leaked token",
|
|
636
|
+
"Create a replacement with the narrowest scopes it needs",
|
|
637
|
+
],
|
|
638
|
+
revokeNote: "Deletion is immediate; anything still using the old token starts failing authentication at once.",
|
|
639
|
+
},
|
|
490
640
|
|
|
491
641
|
// ── Cloud / infra ─────────────────────────────────────────────────────
|
|
492
642
|
// docs.digitalocean.com/reference/api/create-personal-access-token/
|
|
@@ -746,6 +896,18 @@ const ROTATION_GUIDANCE = {
|
|
|
746
896
|
],
|
|
747
897
|
revokeNote: "The URL is the entire credential: anyone holding it can post to the channel until the webhook is deleted.",
|
|
748
898
|
},
|
|
899
|
+
// docs.discord.com/developers/reference (fetched 2026-09-04): bot tokens
|
|
900
|
+
// are regenerated from the same Developer Portal page as the bot itself.
|
|
901
|
+
discord_bot_token: {
|
|
902
|
+
label: "Discord bot token",
|
|
903
|
+
rotateUrl: "https://docs.discord.com/developers/reference",
|
|
904
|
+
steps: [
|
|
905
|
+
"Discord Developer Portal > Applications > your app > Bot",
|
|
906
|
+
"Click Reset Token to invalidate the old one and issue a new one",
|
|
907
|
+
"Update whatever hosts the bot with the new token",
|
|
908
|
+
],
|
|
909
|
+
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.",
|
|
910
|
+
},
|
|
749
911
|
// Fetched https://core.telegram.org/bots/features (2026-09-02): "If your
|
|
750
912
|
// existing token is compromised or you lost it for some reason, use the
|
|
751
913
|
// /token command to generate a new one."
|
|
@@ -866,6 +1028,71 @@ const ROTATION_GUIDANCE = {
|
|
|
866
1028
|
],
|
|
867
1029
|
revokeNote: "Deletion is immediate; the key stops authenticating on the next request.",
|
|
868
1030
|
},
|
|
1031
|
+
// shopify.dev/docs/apps/build/authentication-authorization/access-tokens
|
|
1032
|
+
shopify_admin_token: {
|
|
1033
|
+
label: "Shopify Admin API access token",
|
|
1034
|
+
rotateUrl: "https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens",
|
|
1035
|
+
steps: [
|
|
1036
|
+
"For a custom app: Shopify admin > Settings > Apps and sales channels > the app > uninstall/reinstall to reissue",
|
|
1037
|
+
"For a public/private app: revoke access from the Partner Dashboard and reinstall",
|
|
1038
|
+
"Update whatever used the old token",
|
|
1039
|
+
],
|
|
1040
|
+
revokeNote: "This token can read/write store data (orders, customers, products) depending on its scopes -- review recent admin activity, not just API logs.",
|
|
1041
|
+
},
|
|
1042
|
+
hubspot_token: {
|
|
1043
|
+
label: "HubSpot private app access token",
|
|
1044
|
+
consolePath: "HubSpot account > Settings > Integrations > Private Apps",
|
|
1045
|
+
steps: [
|
|
1046
|
+
"Open Private Apps under Integrations",
|
|
1047
|
+
"Open the relevant app and rotate or delete its access token",
|
|
1048
|
+
"Update whatever used the old token",
|
|
1049
|
+
],
|
|
1050
|
+
revokeNote: "Deletion/rotation is immediate; anything still using the old token starts failing authentication at once.",
|
|
1051
|
+
},
|
|
1052
|
+
// grafana.com/docs/grafana/latest/administration/service-accounts/
|
|
1053
|
+
grafana_service_account_token: {
|
|
1054
|
+
label: "Grafana service account token",
|
|
1055
|
+
rotateUrl: "https://grafana.com/docs/grafana/latest/administration/service-accounts/",
|
|
1056
|
+
steps: [
|
|
1057
|
+
"Administration > Users and access > Service accounts",
|
|
1058
|
+
"Open the relevant service account and delete the leaked token",
|
|
1059
|
+
"Add a replacement token and update whatever used the old one",
|
|
1060
|
+
],
|
|
1061
|
+
revokeNote: "Deletion is immediate; anything still using the old token starts failing authentication at once.",
|
|
1062
|
+
},
|
|
1063
|
+
// docs.newrelic.com's own migration notice confirms the NRAK prefix;
|
|
1064
|
+
// key management itself is login-walled.
|
|
1065
|
+
new_relic_api_key: {
|
|
1066
|
+
label: "New Relic API key",
|
|
1067
|
+
consolePath: "one.newrelic.com > API keys (under your user menu)",
|
|
1068
|
+
steps: [
|
|
1069
|
+
"Open API keys",
|
|
1070
|
+
"Delete the leaked key",
|
|
1071
|
+
"Create a replacement and update whatever used the old one",
|
|
1072
|
+
],
|
|
1073
|
+
revokeNote: "Deletion is immediate; anything still using the old key starts failing authentication at once.",
|
|
1074
|
+
},
|
|
1075
|
+
mailchimp_key: {
|
|
1076
|
+
label: "Mailchimp API key",
|
|
1077
|
+
consolePath: "Mailchimp account > Profile > Extras > API keys",
|
|
1078
|
+
steps: [
|
|
1079
|
+
"Open API keys under Extras in your account profile",
|
|
1080
|
+
"Delete the leaked key",
|
|
1081
|
+
"Create a replacement and update whatever used the old one",
|
|
1082
|
+
],
|
|
1083
|
+
revokeNote: "Deletion is immediate; anything still using the old key starts failing authentication at once.",
|
|
1084
|
+
},
|
|
1085
|
+
// techdocs.akamai.com/developer/docs/authenticate-with-edgegrid
|
|
1086
|
+
akamai_edgegrid_token: {
|
|
1087
|
+
label: "Akamai EdgeGrid token",
|
|
1088
|
+
rotateUrl: "https://techdocs.akamai.com/developer/docs/authenticate-with-edgegrid",
|
|
1089
|
+
steps: [
|
|
1090
|
+
"Akamai Control Center > Identity & access management > API clients",
|
|
1091
|
+
"Open the relevant API client and deactivate/delete the leaked credential",
|
|
1092
|
+
"Create a replacement client credential and update whatever used the old one",
|
|
1093
|
+
],
|
|
1094
|
+
revokeNote: "Deactivation is immediate; anything still using the old credential starts failing authentication at once.",
|
|
1095
|
+
},
|
|
869
1096
|
|
|
870
1097
|
// ── NOISY_PATTERNS (only reachable via --include-noisy) ───────────────
|
|
871
1098
|
generic_password_assignment: {
|