This commit is contained in:
Mick Grove 2026-03-15 13:59:07 -07:00
commit bc1093ca4a
12 changed files with 350 additions and 6 deletions

View file

@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
## [v1.90.0]
- Expanded built-in revocation support with new YAML revocation flows for Cloudflare, Confluent, Doppler, Mapbox, Particle.io, Twitch, and additional Vercel token formats.
- Added revocation coverage documentation: new `docs/REVOCATION_PROVIDERS.md` matrix and README links highlighting supported revocation providers/rule IDs.
## [v1.89.0]
- Access Map: added Microsoft Teams provider. Parses Incoming Webhook URLs (legacy and workflow-based) to extract tenant and webhook identity, probes for active status, and reports channel-level blast radius. Supports standalone `access-map microsoftteams` (alias `msteams`) and automatic mapping for validated `kingfisher.msteams.*` and `kingfisher.microsoftteamswebhook.*` findings.
- Added Microsoft Teams scan target: `kingfisher scan teams "QUERY"` searches Teams messages via Microsoft Graph Search API and scans them for secrets, mirroring the Slack integration.

View file

@ -48,7 +48,7 @@ http = "1.4"
[package]
name = "kingfisher"
version = "1.89.0"
version = "1.90.0"
description = "MongoDB's blazingly fast and accurate secret scanning and validation tool"
edition.workspace = true
rust-version.workspace = true

View file

@ -46,6 +46,7 @@ Kingfisher is a high-performance, open source secret detection tool for source c
- **Performance**: multithreaded, Hyperscanpowered scanning built for huge codebases
- **Extensible rules**: hundreds of built-in detectors plus YAML-defined custom rules ([docs/RULES.md](/docs/RULES.md))
- **Validate & Revoke**: live validation of discovered secrets, plus direct revocation for supported platforms (GitHub, GitLab, Slack, AWS, GCP, and more) ([docs/USAGE.md](/docs/USAGE.md))
- **Revocation support matrix**: current built-in revocation coverage across providers and rule IDs ([docs/REVOCATION_PROVIDERS.md](/docs/REVOCATION_PROVIDERS.md))
- **Blast Radius Mapping**: instantly map leaked keys to their effective cloud identities and exposed resources with `--access-map`. Supports AWS, GCP, Azure, GitHub, GitLab, Slack, Microsoft Teams, and more.
- **Broad AI SaaS coverage**: finds and validates tokens for OpenAI, Anthropic, Google Gemini, Cohere, AWS Bedrock, Voyage AI, Mistral, Stability AI, Replicate, xAI (Grok), Ollama, Langchain, Perplexity, Weights & Biases, Cerebras, Friendli, Fireworks.ai, NVIDIA NIM, Together.ai, Zhipu, and many more
- **Compressed Files**: Supports extracting and scanning compressed files for secrets
@ -645,6 +646,7 @@ kingfisher scan /tmp/repo --branch feature-1 \
| [ACCESS_MAP.md](docs/ACCESS_MAP.md) | Access map: supported tokens and credential formats (GitHub/GitLab/Slack/AWS/GCP/Azure Storage/Postgres/MongoDB/Microsoft Teams) |
| [ADVANCED.md](docs/ADVANCED.md) | Advanced features: baselines, confidence levels, validation tuning, CI scanning, and more |
| [RULES.md](docs/RULES.md) | Writing custom detection rules, pattern requirements, and checksum intelligence |
| [REVOCATION_PROVIDERS.md](docs/REVOCATION_PROVIDERS.md) | Built-in revocation coverage by provider and rule ID |
| [BASELINE.md](docs/BASELINE.md) | Baseline management for tracking known secrets and detecting new ones |
| [LIBRARY.md](docs/LIBRARY.md) | Using Kingfisher as a Rust library in your own applications |
| [FINGERPRINT.md](docs/FINGERPRINT.md) | Understanding finding fingerprints and deduplication |

View file

@ -100,3 +100,27 @@ rules:
depends_on_rule:
- rule_id: "kingfisher.artifactory.2"
variable: JFROGURL
- name: Artifactory NPM Auth (base64)
id: kingfisher.artifactory.4
pattern: |
(?x)
// [A-Za-z0-9._:-]+
/artifactory/api/npm/[A-Za-z0-9._\-/]+/:_auth
\s*=\s*
(
[A-Za-z0-9+/]{20,}={0,2}
)
\b
min_entropy: 3.0
confidence: medium
examples:
- |
registry=https://artifactory.corp.company.com/artifactory/api/npm/npm-cloud/
//artifactory.corp.company.com/artifactory/api/npm/npm-cloud/:_auth=bW9uZ29kYi1jbF81ZDpHMzhoZCE2aElSTjk=
- |
@cloud-company-js:registry=https://artifactory.corp.company.com/artifactory/api/npm/compass-data-explorer/
//artifactory.corp.company.com/artifactory/api/npm/compass-data-explorer/:_auth=bW9uZ29kYi1jbF81ZDpHMzhoZCE2aElSTjk=
references:
- https://docs.npmjs.com/cli/v10/configuring-npm/npmrc
- https://jfrog.com/help/r/jfrog-platform-administration-documentation/npm-registry

View file

@ -24,6 +24,7 @@ rules:
cloudflare_key="y3u7gjcxzpboe2hs50hvuewsx10koco3z327z_1i"
references:
- https://developers.cloudflare.com/api/resources/user/subresources/tokens/methods/verify/
- https://developers.cloudflare.com/api/resources/user/subresources/tokens/methods/delete/
validation:
type: Http
content:
@ -38,6 +39,40 @@ rules:
- 200
type: StatusMatch
url: https://api.cloudflare.com/client/v4/user/tokens/verify
revocation:
type: HttpMultiStep
content:
steps:
- name: lookup_token_id
request:
method: GET
url: https://api.cloudflare.com/client/v4/user/tokens/verify
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- type: StatusMatch
status: [200]
- type: JsonValid
extract:
TOKEN_ID:
type: JsonPath
path: "$.result.id"
- name: revoke_token
request:
method: DELETE
url: https://api.cloudflare.com/client/v4/user/tokens/{{ TOKEN_ID }}
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
words: ['"success":true']
match_all_words: true
- name: Cloudflare CA Key
id: kingfisher.cloudflare.2
@ -74,4 +109,4 @@ rules:
- status:
- 200
type: StatusMatch
url: https://api.cloudflare.com/client/v4/certificates?per_page=1
url: https://api.cloudflare.com/client/v4/certificates?per_page=1

View file

@ -39,6 +39,7 @@ rules:
- kafka_token=cbaDEFGHIJKLMNOPQRSTUVWXYZ3214567890cbadefghijklmnopqrstuvwxyzAB
references:
- https://docs.confluent.io/cloud/current/api.html#tag/API-Keys-(iamv2)/operation/getIamV2ApiKey
- https://docs.confluent.io/cloud/current/api.html#tag/API-Keys-(iamv2)/operation/deleteIamV2ApiKey
validation:
type: Http
content:
@ -52,6 +53,18 @@ rules:
- 200
type: StatusMatch
url: https://api.confluent.cloud/iam/v2/api-keys/{{ CLIENTID }}
revocation:
type: Http
content:
request:
headers:
Authorization: 'Basic {{ CLIENTID | append: ":" | append: TOKEN | b64enc }}'
method: DELETE
url: https://api.confluent.cloud/iam/v2/api-keys/{{ CLIENTID }}
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
depends_on_rule:
- rule_id: "kingfisher.confluent.1"
variable: CLIENTID
@ -76,6 +89,7 @@ rules:
- confluent secret=cfltcUBElySxR0ubmwjcLaVic7aOYceZ1HzCyW9BbhBhC+KbPgaTcGc9S4HfrjhA
references:
- https://docs.confluent.io/cloud/current/api.html#tag/API-Keys-(iamv2)/operation/getIamV2ApiKey
- https://docs.confluent.io/cloud/current/api.html#tag/API-Keys-(iamv2)/operation/deleteIamV2ApiKey
validation:
type: Http
content:
@ -89,6 +103,18 @@ rules:
- 200
type: StatusMatch
url: https://api.confluent.cloud/iam/v2/api-keys/{{ CLIENTID }}
revocation:
type: Http
content:
request:
headers:
Authorization: 'Basic {{ CLIENTID | append: ":" | append: TOKEN | b64enc }}'
method: DELETE
url: https://api.confluent.cloud/iam/v2/api-keys/{{ CLIENTID }}
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
depends_on_rule:
- rule_id: "kingfisher.confluent.1"
variable: CLIENTID
variable: CLIENTID

View file

@ -18,6 +18,7 @@ rules:
- https://docs.doppler.com/reference/api
- https://docs.doppler.com/reference/auth-token-formats
- https://docs.doppler.com/reference/auth-me
- https://docs.doppler.com/reference/auth-revoke
validation:
type: Http
content:
@ -32,6 +33,19 @@ rules:
- type: StatusMatch
status:
- 200
revocation:
type: Http
content:
request:
method: POST
url: https://api.doppler.com/v3/auth/revoke
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
- name: Doppler Personal Token
id: kingfisher.doppler.2
pattern: |
@ -47,6 +61,7 @@ rules:
- https://docs.doppler.com/reference/api
- https://docs.doppler.com/reference/auth-token-formats
- https://docs.doppler.com/reference/auth-me
- https://docs.doppler.com/reference/auth-revoke
validation:
type: Http
content:
@ -61,6 +76,19 @@ rules:
- type: StatusMatch
status:
- 200
revocation:
type: Http
content:
request:
method: POST
url: https://api.doppler.com/v3/auth/revoke
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
- name: Doppler Service Token
id: kingfisher.doppler.3
@ -77,6 +105,7 @@ rules:
- https://docs.doppler.com/reference/api
- https://docs.doppler.com/reference/auth-token-formats
- https://docs.doppler.com/reference/auth-me
- https://docs.doppler.com/reference/auth-revoke
validation:
type: Http
content:
@ -91,6 +120,19 @@ rules:
- type: StatusMatch
status:
- 200
revocation:
type: Http
content:
request:
method: POST
url: https://api.doppler.com/v3/auth/revoke
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
- name: Doppler Service Account Token
id: kingfisher.doppler.4
@ -107,6 +149,7 @@ rules:
- https://docs.doppler.com/reference/api
- https://docs.doppler.com/reference/auth-token-formats
- https://docs.doppler.com/reference/auth-me
- https://docs.doppler.com/reference/auth-revoke
validation:
type: Http
content:
@ -121,6 +164,19 @@ rules:
- type: StatusMatch
status:
- 200
revocation:
type: Http
content:
request:
method: POST
url: https://api.doppler.com/v3/auth/revoke
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
- name: Doppler SCIM Token
id: kingfisher.doppler.5
@ -137,6 +193,7 @@ rules:
- https://docs.doppler.com/reference/api
- https://docs.doppler.com/reference/auth-token-formats
- https://docs.doppler.com/reference/auth-me
- https://docs.doppler.com/reference/auth-revoke
validation:
type: Http
content:
@ -151,6 +208,19 @@ rules:
- type: StatusMatch
status:
- 200
revocation:
type: Http
content:
request:
method: POST
url: https://api.doppler.com/v3/auth/revoke
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
- name: Doppler Audit Token
id: kingfisher.doppler.6
@ -167,6 +237,7 @@ rules:
- https://docs.doppler.com/reference/api
- https://docs.doppler.com/reference/auth-token-formats
- https://docs.doppler.com/reference/auth-me
- https://docs.doppler.com/reference/auth-revoke
validation:
type: Http
content:
@ -180,4 +251,17 @@ rules:
- report_response: true
- type: StatusMatch
status:
- 200
- 200
revocation:
type: Http
content:
request:
method: POST
url: https://api.doppler.com/v3/auth/revoke
headers:
Authorization: Bearer {{ TOKEN }}
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]

View file

@ -44,6 +44,8 @@ rules:
- 'export MAPBOX_SECRET_TOKEN=sk.eyJ1IjoiY2FwcGVsYWVyZSIsImEicf99c1BaTkZnIn0.P4lD1eHeSEx7AsBq1zbJ4g'
references:
- https://docs.mapbox.com/api/accounts/tokens/#token-format
- https://docs.mapbox.com/api/accounts/tokens/#retrieve-a-token
- https://docs.mapbox.com/api/accounts/tokens/#delete-a-token
- https://docs.mapbox.com/help/getting-started/access-tokens/
- https://docs.mapbox.com/help/troubleshooting/how-to-use-mapbox-securely
validation:
@ -57,6 +59,34 @@ rules:
- type: StatusMatch
status: [200]
- type: JsonValid
revocation:
type: HttpMultiStep
content:
steps:
- name: lookup_token_metadata
request:
method: GET
url: https://api.mapbox.com/tokens/v2?access_token={{ TOKEN }}
response_matcher:
- type: StatusMatch
status: [200]
- type: JsonValid
extract:
TOKEN_USER:
type: JsonPath
path: "$.user"
TOKEN_AUTHORIZATION:
type: JsonPath
path: "$.authorization"
- name: revoke_token
request:
method: DELETE
url: https://api.mapbox.com/tokens/v2/{{ TOKEN_USER }}/{{ TOKEN_AUTHORIZATION }}?access_token={{ TOKEN }}
response_matcher:
- report_response: true
- type: StatusMatch
status: [204]
- name: Mapbox Temporary Access Token
id: kingfisher.mapbox.3

View file

@ -39,6 +39,19 @@ rules:
- type: WordMatch
match_all_words: true
words: ['"username":']
revocation:
type: Http
content:
request:
method: DELETE
url: https://api.particle.io/v1/access_tokens/current?access_token={{ TOKEN }}
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
match_all_words: true
words: ['"ok":']
- name: particle.io Access Token
id: kingfisher.particleio.2
@ -73,4 +86,17 @@ rules:
status: [200]
- type: WordMatch
match_all_words: true
words: ['"username":']
words: ['"username":']
revocation:
type: Http
content:
request:
method: DELETE
url: https://api.particle.io/v1/access_tokens/current?access_token={{ TOKEN }}
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
match_all_words: true
words: ['"ok":']

View file

@ -22,6 +22,7 @@ rules:
- "twitch_api_token: '0123456789ABcdefghijklmnopqrst'"
references:
- https://dev.twitch.tv/docs/authentication/validate-tokens/
- https://dev.twitch.tv/docs/authentication/revoke-tokens/
validation:
type: Http
content:
@ -35,4 +36,35 @@ rules:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid
- type: JsonValid
revocation:
type: HttpMultiStep
content:
steps:
- name: lookup_client_id
request:
method: GET
url: https://id.twitch.tv/oauth2/validate
headers:
Authorization: 'OAuth {{ TOKEN }}'
Accept: application/json
response_matcher:
- type: StatusMatch
status: [200]
- type: JsonValid
extract:
CLIENT_ID:
type: JsonPath
path: "$.client_id"
- name: revoke_token
request:
method: POST
url: https://id.twitch.tv/oauth2/revoke
headers:
Content-Type: application/x-www-form-urlencoded
body: "client_id={{ CLIENT_ID | url_encode }}&token={{ TOKEN | url_encode }}"
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]

View file

@ -34,9 +34,22 @@ rules:
- '"user":'
- '"email":'
match_all_words: true
revocation:
type: Http
content:
request:
method: DELETE
url: https://api.vercel.com/v3/user/tokens/current
headers:
Authorization: "Bearer {{TOKEN}}"
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
references:
- https://vercel.com/kb/guide/how-do-i-use-a-vercel-api-access-token
- https://docs.vercel.com/docs/rest-api/reference/welcome#authentication
- https://vercel.com/docs/rest-api/reference/endpoints/authentication/delete-an-authentication-token
examples:
- "vercel-key = DdZV6ZDZW6Vpl7n7JqtrCE5i"
- "vercel_token = zyMBA1qVEMAf4UNNZtCAbg6u"
@ -81,10 +94,23 @@ rules:
- '"user":'
- '"email":'
match_all_words: true
revocation:
type: Http
content:
request:
method: DELETE
url: https://api.vercel.com/v3/user/tokens/current
headers:
Authorization: "Bearer {{TOKEN}}"
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
references:
- https://vercel.com/kb/guide/how-do-i-use-a-vercel-api-access-token
- https://docs.vercel.com/docs/rest-api/reference/welcome#authentication
- https://vercel.com/changelog/new-token-formats-and-secret-scanning
- https://vercel.com/docs/rest-api/reference/endpoints/authentication/delete-an-authentication-token
examples:
- "vcp_35UYJwYZDigYATKhxJUAhPqRhit2Xe3dtiG60LsUTHeklEXDQ94Jafpu"
- "vercel_access_token=vcp_4mcjwVDwqtVCVGWCcxRjdzGpkGZ3NkwXZv8ktcoQ0EG0dnjpMP1Rzi71"
@ -124,9 +150,22 @@ rules:
words:
- '"user":'
match_all_words: false
revocation:
type: Http
content:
request:
method: DELETE
url: https://api.vercel.com/v3/user/tokens/current
headers:
Authorization: "Bearer {{TOKEN}}"
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 204]
references:
- https://vercel.com/docs/integrations/vercel-api-integrations#create-an-access-token
- https://vercel.com/changelog/new-token-formats-and-secret-scanning
- https://vercel.com/docs/rest-api/reference/endpoints/authentication/delete-an-authentication-token
examples:
- "Vercel Integration Token: vci_35UYJwYZDigYATKhxJUAhPqRhit2Xe3dtiG60LsUTHeklEXDQ94Jafpu"

View file

@ -0,0 +1,42 @@
# Revocation Support Matrix
Kingfisher supports direct secret revocation through rule-level `revocation:` blocks.
Current coverage in built-in rules:
- `23` provider families
- `39` revocation-enabled rules
Use `kingfisher revoke --rule <rule-id> <secret>` to invoke these flows. See [USAGE.md](USAGE.md#direct-secret-revocation-with-kingfisher-revoke) for command details.
## Supported Providers
| Provider | Revocation Rule Count | Rule IDs |
|---|---:|---|
| `aws` | 1 | `kingfisher.aws.2` |
| `browserstack` | 1 | `kingfisher.browserstack.1` |
| `buildkite` | 1 | `kingfisher.buildkite.1` |
| `cloudflare` | 1 | `kingfisher.cloudflare.1` |
| `confluent` | 2 | `kingfisher.confluent.2`, `kingfisher.confluent.3` |
| `deviantart` | 1 | `kingfisher.deviantart.1` |
| `doppler` | 6 | `kingfisher.doppler.1`, `kingfisher.doppler.2`, `kingfisher.doppler.3`, `kingfisher.doppler.4`, `kingfisher.doppler.5`, `kingfisher.doppler.6` |
| `gcp` | 1 | `kingfisher.gcp.1` |
| `github` | 3 | `kingfisher.github.1`, `kingfisher.github.2`, `kingfisher.github.5` |
| `gitlab` | 2 | `kingfisher.gitlab.1`, `kingfisher.gitlab.4` |
| `harness` | 1 | `kingfisher.harness.pat.1` |
| `mapbox` | 1 | `kingfisher.mapbox.2` |
| `mongodb` | 1 | `kingfisher.mongodb.1` |
| `npm` | 2 | `kingfisher.npm.1`, `kingfisher.npm.2` |
| `particle.io` | 2 | `kingfisher.particleio.1`, `kingfisher.particleio.2` |
| `sendgrid` | 1 | `kingfisher.sendgrid.1` |
| `slack` | 2 | `kingfisher.slack.1`, `kingfisher.slack.2` |
| `sumologic` | 1 | `kingfisher.sumologic.2` |
| `tailscale` | 1 | `kingfisher.tailscale.1` |
| `twilio` | 1 | `kingfisher.twilio.2` |
| `twitch` | 1 | `kingfisher.twitch.1` |
| `unkey` | 1 | `kingfisher.unkey.2` |
| `vercel` | 5 | `kingfisher.vercel.1`, `kingfisher.vercel.2`, `kingfisher.vercel.3`, `kingfisher.vercel.4`, `kingfisher.vercel.5` |
## Notes
- Coverage above is derived from built-in YAML rules under `crates/kingfisher-rules/data/rules/` that currently define a `revocation:` block.
- A provider may have additional detection/validation rules that do not yet support revocation.