added new rules

This commit is contained in:
Mick Grove 2026-03-07 21:28:37 -08:00
commit 6d44e2c1b6
38 changed files with 1604 additions and 0 deletions

View file

@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file.
## [v1.87.0]
- Hardened Perplexity API key validation to reject auth failures (`401`/`403`) and avoid false "Active Credential" results from error payloads.
- Fixed Yelp API key validation false positives by switching to an auth-enforcing endpoint (`/v3/businesses/search`) and adding explicit auth error guards.
- Added 37 new provider detection + HTTP validation rules: Ably, AbstractAPI, AbuseIPDB, AviationStack, Better Stack, Brevo, Clearout, Clerk, Cloudinary, Coinlayer, Contentstack, Currencylayer, Daily, Fixer, Geoapify, Hunter.io, Mux, NewsAPI, Numverify, OneSignal, Pinecone, Pingdom, Positionstack, Railway, Render, Rollbar, Salesloft, Sanity, StatusCake, Storyblok, UptimeRobot, urlscan.io, VirusTotal, WeatherAPI, Webflow, and ZeroBounce.
- Tightened regex specificity for newly added rules by replacing broad variable-length token captures with explicit fixed formats/lengths and aligned examples to pass `rules check`.
## [v1.86.0]
- GitLab scanning: honor OS-trusted internal CAs without requiring `SSL_CERT_FILE`, and preserve custom GitLab API ports in repository enumeration and artifact fetching.

View file

@ -0,0 +1,78 @@
# AGENTS.md
Rule-authoring instructions for this directory.
## Scope
- Applies to `crates/kingfisher-rules/data/rules/` and all files under it.
- This file overrides broader AGENTS guidance for rule-writing tasks in this subtree.
## Goal
- Add or update YAML detection rules with high precision, low false positives, and safe remediation support.
## Rule File Conventions
- Keep provider rules in provider-named files (for example `github.yml`, `openai.yml`).
- Prefer lowercase filenames with `.yml`.
- Keep rule IDs stable and unique. Prefer `kingfisher.<provider>.<number>` unless a descriptive suffix is already established for that provider.
- Reuse nearby provider patterns/styles instead of inventing new structure.
## Required Rule Shape
Each rule entry should define:
- `name`
- `id`
- `pattern`
- `min_entropy` (default to 3.0)
- `confidence` (default to medium)
- `examples` (at least one realistic positive example)
Strongly recommended fields:
- `pattern_requirements` (for extra filtering)
- `references`
## Pattern Quality Rules
- Prefer specific anchors/prefixes and provider context over broad generic regex.
- Use `pattern_requirements` to enforce quality constraints (`min_digits`, `min_uppercase`, `min_lowercase`, `min_special_chars`, `ignore_if_contains`, `checksum`).
- Use checksum validation in `pattern_requirements.checksum` when token formats support it.
- Use `visible: false` for helper/non-secret captures used only by dependent rules.
- Use `depends_on_rule` for multi-part credential validation (for example ID + secret).
## Validation Policy (Important)
- Default: define validation logic in YAML under `validation:`.
- Do not move validation logic into Rust unless YAML cannot reliably express it.
- Code-backed validation types (for example AWS, GCP, Coinbase, MongoDB) are notable exceptions and should remain rare.
- For new rules, first attempt `Http`/`Grpc` YAML validation before considering exception paths.
## Revocation Policy
- If a rule has validation and the provider API safely supports revocation, add `revocation:` in the same YAML rule.
- Prefer explicit success criteria in `response_matcher`.
- Use `HttpMultiStep` revocation when API workflows require pre-fetch/extraction steps.
- If revocation is intentionally not supported, document why with an inline YAML comment.
## Authoring Workflow
1. Choose the target provider file (or add a new provider file if no suitable file exists).
2. Copy a structurally similar rule from this directory.
3. Implement/adjust `pattern`, `examples`, and filtering (`pattern_requirements`, `min_entropy`).
4. Add YAML `validation` (default path).
5. Add YAML `revocation` when supported.
6. Add `references` for token format/API behavior.
7. Verify locally (below).
## Local Verification Checklist
- Syntax/load checks:
- `cargo test -p kingfisher-rules`
- Broader regression check:
- `cargo test --workspace --all-targets`
- Behavioral check against sample content:
- `kingfisher scan ./testdata --rule <rule-family-or-id> --rule-stats`
- Validation check (when validation is present):
- `kingfisher validate --rule <rule-id> <token-or-secret>`
## Documentation
Read these before complex edits:
- `docs/RULES.md` (schema, pattern requirements, checksum, Liquid, validation/revocation)
- `docs/MULTI_STEP_REVOCATION.md`
- `docs/TOKEN_REVOCATION_SUPPORT.md`
## Change Discipline
- Keep changes scoped to the specific provider/rule request.
- Do not refactor unrelated rules in the same PR unless explicitly asked.
- Preserve existing YAML style and indentation conventions in this directory.

View file

@ -0,0 +1,39 @@
rules:
- name: Ably API Key
id: kingfisher.ably.1
pattern: |
(?xi)
\b
ably
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|API)
(?:.|[\n\r]){0,32}?
\b
(
[A-Za-z0-9_-]{8}\.[A-Za-z0-9_-]{8}:[A-Za-z0-9_-]{24}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- ABLY_API_KEY=abc12345.xyz78901:secretkeyvalue1234567890
- ably_key = "appid123.keyid987:AbCdEfGhIjKlMnOpQrStUvWx"
references:
- https://ably.com/docs/auth/basic
- https://ably.com/docs/api/rest-api
validation:
type: Http
content:
request:
method: GET
url: https://rest.ably.io/channels?limit=1
headers:
Authorization: "Basic {{ TOKEN | append: ':' | b64enc }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,41 @@
rules:
- name: AbstractAPI API Key
id: kingfisher.abstractapi.1
pattern: |
(?xi)
\b
abstractapi
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- abstractapi_api_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
- ABSTRACTAPI_KEY=abcdef1234567890abcdef1234567890
references:
- https://docs.abstractapi.com/api/ip-geolocation
- https://abstractapi.com/docs
validation:
type: Http
content:
request:
method: GET
url: https://ipgeolocation.abstractapi.com/v1/?api_key={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401, 403]
negative: true
- type: JsonValid

View file

@ -0,0 +1,39 @@
rules:
- name: AbuseIPDB API Key
id: kingfisher.abuseipdb.1
pattern: |
(?xi)
\b
abuseipdb
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{80}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- abuseipdb_api_key = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
- ABUSEIPDB_KEY=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
references:
- https://docs.abuseipdb.com/#introduction
- https://www.abuseipdb.com/api
validation:
type: Http
content:
request:
method: GET
url: https://api.abuseipdb.com/api/v2/check?ipAddress=127.0.0.1
headers:
Key: "{{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,40 @@
rules:
- name: AviationStack API Key
id: kingfisher.aviationstack.1
pattern: |
(?xi)
\b
aviationstack
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- AVIATIONSTACK_ACCESS_KEY=abc123def456ghi789jkl012mno345pq
- aviationstack_access_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
references:
- https://aviationstack.com/documentation
validation:
type: Http
content:
request:
method: GET
url: https://api.aviationstack.com/v1/flights?access_key={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,42 @@
rules:
- name: Better Stack API Token
id: kingfisher.betterstack.1
pattern: |
(?xi)
\b
betterstack
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9_-]{24}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- betterstack_api_token = "a1b2c3d4e5f6g7h8i9j0k1l2"
- BETTERSTACK_TOKEN=abcdef1234567890abcdef12
references:
- https://betterstack.com/docs/uptime/api/getting-started-with-uptime-api/
- https://betterstack.com/docs/uptime/api/list-all-existing-monitors
validation:
type: Http
content:
request:
method: GET
url: https://uptime.betterstack.com/api/v2/monitors
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401, 403]
negative: true
- type: JsonValid

View file

@ -0,0 +1,33 @@
rules:
- name: Brevo API Token
id: kingfisher.brevo.1
pattern: |
(?x)
\b
(
xkeysib-[a-fA-F0-9]{64}-[a-zA-Z0-9]{16}
)
\b
pattern_requirements:
min_digits: 2
min_entropy: 3.2
confidence: medium
examples:
- BREVO_API_KEY=xkeysib-abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd-1234567890abcd12
- '"brevo": "xkeysib-1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef-ab12cd34ef56gh78"'
references:
- https://developers.brevo.com/docs/api-key-authentication
- https://developers.brevo.com/docs/how-it-works
validation:
type: Http
content:
request:
method: GET
url: https://api.brevo.com/v3/account
headers:
api-key: "{{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]

View file

@ -0,0 +1,42 @@
rules:
- name: Clearout API Token
id: kingfisher.clearout.1
pattern: |
(?xi)
\b
clearout
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{28}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- clearout_api_token = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4"
- CLEAROUT_TOKEN=abcdef1234567890abcdef123456
references:
- https://docs.clearout.io/api-overview.html
- https://docs.clearout.io/email-verifier-api.html
validation:
type: Http
content:
request:
method: GET
url: https://api.clearout.io/v2/email_verify/getcredits
headers:
Authorization: "Bearer {{ TOKEN }}"
Content-Type: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,39 @@
rules:
- name: Clerk Secret Key
id: kingfisher.clerk.1
pattern: |
(?xi)
\b
clerk
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|API|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
sk_(?:test|live)_[A-Za-z0-9]{32}
)
\b
pattern_requirements:
min_digits: 2
min_entropy: 3.3
confidence: medium
examples:
- CLERK_SECRET_KEY=sk_live_abcdefghijklmnopqrstuvwxyz123456
- clerk_secret = sk_test_4pX9kL2mN8qR5sT7vY1zA3bC6dE0fG2h
references:
- https://clerk.com/docs/deployments/clerk-environment-variables
- https://clerk.com/docs/guides/development/machine-auth/api-keys
validation:
type: Http
content:
request:
method: GET
url: https://api.clerk.com/v1/users?limit=1
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,82 @@
rules:
- name: Cloudinary Cloud Name
id: kingfisher.cloudinary.3
pattern: |
(?xi)
\b
cloudinary
(?:.|[\n\r]){0,32}?
(?:CLOUD[_\s]?NAME|CLOUD)
(?:.|[\n\r]){0,16}?
\b
(
[a-z0-9_-]{3,32}
)
\b
min_entropy: 2.5
confidence: medium
visible: false
examples:
- CLOUDINARY_CLOUD_NAME=demo
- name: Cloudinary API Key
id: kingfisher.cloudinary.2
pattern: |
(?xi)
\b
cloudinary
(?:.|[\n\r]){0,32}?
(?:API[_\s]?KEY|KEY)
(?:.|[\n\r]){0,16}?
\b
(
[0-9]{15}
)
\b
min_entropy: 3.0
confidence: medium
visible: false
examples:
- CLOUDINARY_API_KEY=123456789012345
- name: Cloudinary API Secret
id: kingfisher.cloudinary.1
pattern: |
(?xi)
\b
cloudinary
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|API[_\s]?SECRET)
(?:.|[\n\r]){0,32}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- CLOUDINARY_API_SECRET=abcdefghijklmnopqrstuvwxyz123456
- cloudinary_secret = "aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
references:
- https://cloudinary.com/documentation/developer_onboarding_faq_find_credentials
- https://cloudinary.com/documentation/image_upload_api_reference
validation:
type: Http
content:
request:
method: GET
url: "https://api.cloudinary.com/v1_1/{{ CLOUDNAME }}/usage"
headers:
Authorization: "Basic {{ APIKEY | append: ':' | append: TOKEN | b64enc }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid
depends_on_rule:
- rule_id: kingfisher.cloudinary.2
variable: APIKEY
- rule_id: kingfisher.cloudinary.3
variable: CLOUDNAME

View file

@ -0,0 +1,41 @@
rules:
- name: Coinlayer API Key
id: kingfisher.coinlayer.1
pattern: |
(?xi)
\b
coinlayer
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- COINLAYER_ACCESS_KEY=abc123def456ghi789jkl012mno345pq
- coinlayer_access_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
references:
- https://coinlayer.com/documentation
- https://coinlayer.com/signup/free
validation:
type: Http
content:
request:
method: GET
url: https://api.coinlayer.com/live?access_key={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,62 @@
rules:
- name: Contentstack API Key
id: kingfisher.contentstack.2
pattern: |
(?xi)
\b
contentstack
(?:.|[\n\r]){0,32}?
(?:API[_\s]?KEY|STACK[_\s]?API[_\s]?KEY)
(?:.|[\n\r]){0,16}?
\b
(
blt[a-f0-9]{10}
)
\b
min_entropy: 3.0
confidence: medium
visible: false
examples:
- CONTENTSTACK_API_KEY=blt1234567890
- name: Contentstack Management Token
id: kingfisher.contentstack.1
pattern: |
(?xi)
\b
contentstack
(?:.|[\n\r]){0,32}?
(?:MANAGEMENT[_\s]?TOKEN|AUTH[_\s]?TOKEN|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
cs[a-f0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- CONTENTSTACK_MANAGEMENT_TOKEN=cs1234567890abcdef1234567890abcdef
- contentstack_token = "csa1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
references:
- https://www.contentstack.com/docs/developers/create-tokens/overview-of-tokens
- https://www.contentstack.com/docs/developers/apis/content-management-api
validation:
type: Http
content:
request:
method: GET
url: "https://api.contentstack.io/v3/stacks"
headers:
api_key: "{{ APIKEY }}"
authorization: "Bearer {{ TOKEN }}"
Content-Type: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid
depends_on_rule:
- rule_id: kingfisher.contentstack.2
variable: APIKEY

View file

@ -0,0 +1,41 @@
rules:
- name: Currencylayer API Key
id: kingfisher.currencylayer.1
pattern: |
(?xi)
\b
currencylayer
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- CURRENCYLAYER_ACCESS_KEY=abc123def456ghi789jkl012mno345pq
- currencylayer_access_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
references:
- https://currencylayer.com/documentation
- https://currencylayer.com/api-access
validation:
type: Http
content:
request:
method: GET
url: https://api.currencylayer.com/live?access_key={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,43 @@
rules:
- name: Daily API Key
id: kingfisher.daily.1
pattern: |
(?xi)
\b
daily
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|API)
(?:.|[\n\r]){0,32}?
\b
(
[A-Za-z0-9]{64}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- DAILY_API_KEY=abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
- daily_api_key = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2"
references:
- https://docs.daily.co/reference/rest-api
- https://docs.daily.co/reference/rest-api/rooms/list-rooms
validation:
type: Http
content:
request:
method: GET
url: https://api.daily.co/v1/rooms?limit=1
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
words:
- '"data"'
- '"total_count"'
match_all_words: true

View file

@ -0,0 +1,41 @@
rules:
- name: Fixer.io API Key
id: kingfisher.fixer.1
pattern: |
(?xi)
\b
fixer
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- FIXER_ACCESS_KEY=abc123def456ghi789jkl012mno345pq
- fixer_access_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
references:
- https://fixer.io/documentation
- https://fixer.io/api-key
validation:
type: Http
content:
request:
method: GET
url: https://data.fixer.io/api/latest?access_key={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,38 @@
rules:
- name: Geoapify API Key
id: kingfisher.geoapify.1
pattern: |
(?xi)
\b
geoapify
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- geoapify_api_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
- GEOAPIFY_KEY=abcdef1234567890abcdef1234567890
references:
- https://apidocs.geoapify.com/docs/geocoding/api/api
- https://apidocs.geoapify.com/docs/ip-geolocation
validation:
type: Http
content:
request:
method: GET
url: https://api.geoapify.com/v1/ipinfo?apiKey={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,40 @@
rules:
- name: Hunter.io API Key
id: kingfisher.hunterio.1
pattern: |
(?xi)
\b
hunter
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{40}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
ignore_if_contains:
- test-api-key
examples:
- hunter_api_key = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0"
- HUNTER_KEY=abcdef1234567890abcdef1234567890abcdef12
references:
- https://hunter.io/api/docs
- https://hunter.io/api-keys
validation:
type: Http
content:
request:
method: GET
url: https://api.hunter.io/v2/account?api_key={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,59 @@
rules:
- name: Mux Access Token ID
id: kingfisher.mux.2
pattern: |
(?xi)
\b
mux
(?:.|[\n\r]){0,32}?
(?:TOKEN[_\s]?ID|ACCESS[_\s]?TOKEN[_\s]?ID)
(?:.|[\n\r]){0,16}?
\b
(
[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}
)
\b
min_entropy: 3.0
confidence: medium
visible: false
examples:
- MUX_TOKEN_ID=44c819de-4add-4c9f-b2e9-384a0a71bede
- name: Mux Access Token Secret
id: kingfisher.mux.1
pattern: |
(?xi)
\b
mux
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
[A-Za-z0-9+/=]{75}
)
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- MUX_TOKEN_SECRET=INKxCoZ+cX6l1yrR6vqzYHVaeFEcqvZShznWM1U/No8KsV7h6Jxu1XXuTUQ91sdiGONK3H7NE7H
references:
- https://docs.mux.com/core/make-api-requests
- https://docs.mux.com/api-reference/video/video/v1/assets
validation:
type: Http
content:
request:
method: GET
url: https://api.mux.com/video/v1/assets?limit=1
headers:
Authorization: "Basic {{ MUXID | append: ':' | append: TOKEN | b64enc }}"
Content-Type: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid
depends_on_rule:
- rule_id: kingfisher.mux.2
variable: MUXID

View file

@ -0,0 +1,40 @@
rules:
- name: NewsAPI API Key
id: kingfisher.newsapi.1
pattern: |
(?xi)
\b
newsapi
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- NEWSAPI_API_KEY=abc123def456ghi789jkl012mno345pq
- newsapi_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
references:
- https://newsapi.org/docs/authentication
validation:
type: Http
content:
request:
method: GET
url: https://newsapi.org/v2/everything?q=test&apiKey={{ TOKEN }}
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,41 @@
rules:
- name: Numverify API Key
id: kingfisher.numverify.1
pattern: |
(?xi)
\b
numverify
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- NUMVERIFY_ACCESS_KEY=abc123def456ghi789jkl012mno345pq
- numverify_access_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
references:
- https://numverify.com/documentation
- https://numverify.com/faq
validation:
type: Http
content:
request:
method: GET
url: https://apilayer.net/api/validate?access_key={{ TOKEN }}&number=14155551234
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,42 @@
rules:
- name: OneSignal REST API Key
id: kingfisher.onesignal.1
pattern: |
(?xi)
\b
onesignal
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- onesignal_rest_api_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
- ONESIGNAL_REST_API_KEY=abcdef1234567890abcdef1234567890
references:
- https://documentation.onesignal.com/reference/rest-api-overview
- https://documentation.onesignal.com/docs/keys-and-ids
validation:
type: Http
content:
request:
method: GET
url: https://api.onesignal.com/apps
headers:
Authorization: "Key {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401, 403]
negative: true
- type: JsonValid

View file

@ -0,0 +1,39 @@
rules:
- name: Pinecone API Key
id: kingfisher.pinecone.1
pattern: |
(?xi)
\b
pinecone
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|API|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}
)
\b
pattern_requirements:
min_digits: 2
min_entropy: 3.0
confidence: medium
examples:
- PINECONE_API_KEY=62b0dbfe-3489-4b79-b850-34d911527c88
- pinecone_key = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
references:
- https://docs.pinecone.io/reference/api/authentication
- https://docs.pinecone.io/reference/api/2025-10/control-plane/list_indexes
validation:
type: Http
content:
request:
method: GET
url: https://api.pinecone.io/indexes
headers:
Api-Key: "{{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,42 @@
rules:
- name: Pingdom API Token
id: kingfisher.pingdom.1
pattern: |
(?xi)
\b
pingdom
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9_-]{64}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- pingdom_api_token = "ofOhK18Ca6w4S_XmInGv0QPkqly-rbRBBoHsp_2FEH5QnIbH0VZhRPO3tlvrjMIK"
- PINGDOM_TOKEN=abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
references:
- https://docs.pingdom.com/api/
- https://pingdom.com/resources/pingdom-api
validation:
type: Http
content:
request:
method: GET
url: https://api.pingdom.com/api/3.1/checks
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401, 403]
negative: true
- type: JsonValid

View file

@ -0,0 +1,40 @@
rules:
- name: Positionstack API Key
id: kingfisher.positionstack.1
pattern: |
(?xi)
\b
positionstack
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- positionstack_access_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
- POSITIONSTACK_KEY=1234567890abcdef1234567890abcdef
references:
- https://positionstack.com/documentation
validation:
type: Http
content:
request:
method: GET
url: https://api.positionstack.com/v1/forward?access_key={{ TOKEN }}&query=London
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401, 403]
negative: true
- type: JsonValid

View file

@ -0,0 +1,44 @@
rules:
- name: Railway API Token
id: kingfisher.railway.1
pattern: |
(?xi)
\b
railway
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|API)
(?:.|[\n\r]){0,32}?
\b
(
(?:[A-Za-z0-9]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- RAILWAY_API_TOKEN=abcdef1234567890abcdef1234567890
- railway_token = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
references:
- https://docs.railway.com/guides/public-api
- https://docs.railway.com/reference/oauth/login-and-tokens
validation:
type: Http
content:
request:
method: POST
url: https://backboard.railway.com/graphql/v2
headers:
Authorization: "Bearer {{ TOKEN }}"
Content-Type: application/json
body: '{"query":"query { me { name email } }"}'
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
words:
- '"data"'
- '"me"'
match_all_words: true

View file

@ -0,0 +1,39 @@
rules:
- name: Render API Key
id: kingfisher.render.1
pattern: |
(?xi)
\b
render
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|API)
(?:.|[\n\r]){0,32}?
\b
(
(?:[A-Za-z0-9]{32}|rnd_[A-Za-z0-9]{33})
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- RENDER_API_KEY=abcdef1234567890abcdef1234567890
- render_api_key = "rnd_abc123def456ghi789jkl012mno345pqr"
references:
- https://docs.render.com/api
- https://api-docs.render.com/docs
validation:
type: Http
content:
request:
method: GET
url: https://api.render.com/v1/services?limit=1
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,42 @@
rules:
- name: Rollbar Access Token
id: kingfisher.rollbar.1
pattern: |
(?xi)
\b
rollbar
(?:.|[\n\r]){0,32}?
(?:access[_-]?token|token|key)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- rollbar_access_token = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
- ROLLBAR_ACCESS_TOKEN=abcdef1234567890abcdef1234567890
references:
- https://docs.rollbar.com/docs/access-tokens
- https://docs.rollbar.com/reference/getting-started-1
validation:
type: Http
content:
request:
method: GET
url: https://api.rollbar.com/api/1/projects
headers:
X-Rollbar-Access-Token: "{{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [403]
negative: true
- type: JsonValid

View file

@ -0,0 +1,39 @@
rules:
- name: Salesloft API Key
id: kingfisher.salesloft.1
pattern: |
(?xi)
\b
salesloft
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|API|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
ak_[a-fA-F0-9]{64}
)
\b
pattern_requirements:
min_digits: 2
min_entropy: 3.2
confidence: medium
examples:
- SALESLOFT_API_KEY=ak_de656ec86bcab24878c24ff4d86758f8963d8ea6bcd4e90f8fae846ba8f9ac62
- salesloft_key = "ak_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
references:
- https://developer.salesloft.com/docs/platform/api-basics/api-key-authentication
- https://developers.salesloft.com/docs/api/me
validation:
type: Http
content:
request:
method: GET
url: https://api.salesloft.com/v2/me
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,39 @@
rules:
- name: Sanity API Token
id: kingfisher.sanity.1
pattern: |
(?xi)
\b
sanity
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|API)
(?:.|[\n\r]){0,32}?
\b
(
(?:sk[A-Za-z0-9]{52}|sk[A-Za-z0-9]{78})
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- SANITY_API_TOKEN=skE5UXUmBEy7U50jcG4In4v4xoHZTlduDxQYet8Y84tsTqAZxp2reIPJsA1JzqXJno2qcpauGwPfjHpU
- sanity_token = "sk1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdef"
references:
- https://www.sanity.io/docs/content-lake/http-auth
- https://www.sanity.io/docs/projects-api
validation:
type: Http
content:
request:
method: GET
url: https://api.sanity.io/v2021-06-07/projects
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,41 @@
rules:
- name: StatusCake API Token
id: kingfisher.statuscake.1
pattern: |
(?xi)
\b
statuscake
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9_-]{20}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- statuscake_api_token = "a1b2c3d4e5f6g7h8i9j0"
- STATUSCAKE_TOKEN=abcdef1234567890abcd
references:
- https://developers.statuscake.com/guides/api/authentication/
- https://developers.statuscake.com/api
validation:
type: Http
content:
request:
method: GET
url: https://api.statuscake.com/v1/uptime
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401, 403]
negative: true

View file

@ -0,0 +1,38 @@
rules:
- name: Storyblok API Token
id: kingfisher.storyblok.1
pattern: |
(?xi)
\b
storyblok
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|API)
(?:.|[\n\r]){0,32}?
\b
(
[A-Za-z0-9]{22}tt
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- STORYBLOK_ACCESS_TOKEN=wANpEQEsMYGOwLxwXQ76Ggtt
- storyblok_token = "13Kft3335iwbBOI333wawOtt"
references:
- https://www.storyblok.com/docs/api/content-delivery/v2/getting-started/authentication
- https://www.storyblok.com/docs/concepts/access-tokens
validation:
type: Http
content:
request:
method: GET
url: "https://api.storyblok.com/v2/cdn/stories?token={{ TOKEN }}&version=published&per_page=1"
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,38 @@
rules:
- name: UptimeRobot API Key
id: kingfisher.uptimerobot.1
pattern: |
(?xi)
\b
uptimerobot
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
(?:ur[A-Za-z0-9-]{29}|[A-Za-z0-9]{28})
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- uptimerobot_api_key = "ur123456-7890abcdef1234567890ab"
- UPTIMEROBOT_KEY=abcdef1234567890abcdef123456
references:
- https://uptimerobot.com/api/v3
- https://help.uptimerobot.com/en/articles/11620152-how-to-use-uptimerobot-s-api
validation:
type: Http
content:
request:
method: GET
url: https://api.uptimerobot.com/v2/getMonitors?api_key={{ TOKEN }}&format=json
headers:
Content-Type: application/x-www-form-urlencoded
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,39 @@
rules:
- name: urlscan.io API Key
id: kingfisher.urlscan.1
pattern: |
(?xi)
\b
urlscan
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
(?:[A-Za-z0-9]{32}|[A-Za-z0-9]{36})
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- urlscan_api_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8"
- URLSCAN_KEY=abcdef1234567890abcdef1234567890
references:
- https://urlscan.io/docs/api/
- https://docs.urlscan.io/
validation:
type: Http
content:
request:
method: GET
url: https://urlscan.io/user/quotas/
headers:
API-Key: "{{ TOKEN }}"
Content-Type: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,42 @@
rules:
- name: VirusTotal API Key
id: kingfisher.virustotal.1
pattern: |
(?xi)
\b
virustotal
(?:.|[\n\r]){0,32}?
(?:api[_-]?key|x[_-]?apikey|key)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{64}
)
\b
min_entropy: 3.6
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- VIRUSTOTAL_API_KEY=abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890
- virustotal_x_apikey = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2"
references:
- https://docs.virustotal.com/reference/authentication
- https://docs.virustotal.com/docs/please-give-me-an-api-key
validation:
type: Http
content:
request:
method: GET
url: https://www.virustotal.com/api/v3/domains/google.com
headers:
x-apikey: "{{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401, 403]
negative: true
- type: JsonValid

View file

@ -0,0 +1,40 @@
rules:
- name: WeatherAPI.com API Key
id: kingfisher.weatherapi.1
pattern: |
(?xi)
\b
weatherapi
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- WEATHERAPI_KEY=abc123def456ghi789jkl012mno345pq
- weatherapi_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
references:
- https://www.weatherapi.com/docs/
validation:
type: Http
content:
request:
method: GET
url: https://api.weatherapi.com/v1/current.json?key={{ TOKEN }}&q=London
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: StatusMatch
status: [401]
negative: true
- type: JsonValid

View file

@ -0,0 +1,39 @@
rules:
- name: Webflow API Token
id: kingfisher.webflow.1
pattern: |
(?xi)
\b
webflow
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|API)
(?:.|[\n\r]){0,32}?
\b
(
(?:[A-Za-z0-9]{32}|[A-Za-z0-9]{36})
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- WEBFLOW_API_TOKEN=abcdef1234567890abcdef1234567890
- webflow_token = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8"
references:
- https://developers.webflow.com/data/reference/authentication
- https://developers.webflow.com/data/reference/rest-introduction/quick-start
validation:
type: Http
content:
request:
method: GET
url: https://api.webflow.com/v2/sites
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid

View file

@ -0,0 +1,38 @@
rules:
- name: ZeroBounce API Key
id: kingfisher.zerobounce.1
pattern: |
(?xi)
\b
zerobounce
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Za-z0-9]{32}
)
\b
min_entropy: 3.5
confidence: medium
pattern_requirements:
min_digits: 2
examples:
- zerobounce_api_key = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
- ZEROBOUNCE_KEY=abcdef1234567890abcdef1234567890
references:
- https://www.zerobounce.net/docs/email-validation-api-quickstart/
- https://api.zerobounce.net/v2/validate
validation:
type: Http
content:
request:
method: GET
url: https://api.zerobounce.net/v2/validate?api_key={{ TOKEN }}&email=test@example.com&ip_address=
headers:
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: JsonValid