Merge pull request #13 from mongodb/development

Preparing the v1.15.0 release by cleaning up temp files, enhancing update-check output styling, fixing a self-update binary lookup bug, and normalizing regex flags across secret-detection rules.

Add explicit temp-file cleanup (temp_dir.close()) before process exit.
Refactor src/update.rs to apply styled output and use semver comparisons.
Bulk regex “rule cleanup”: unify inline flags to (?xi) and enforce uppercase ranges.
This commit is contained in:
Mick Grove 2025-06-26 16:44:01 -07:00 committed by GitHub
commit 669110ccae
95 changed files with 732 additions and 373 deletions

View file

@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file.
## [1.15.0]
- Ensuring temp files are cleaned up
- Applying visual style to the update check output
- Fixed bug in --self-update where it was looking for the incorrect binary name on GitHub releases
- Rule cleanup
## [1.14.0]
- Fixed several malformed rules
- Now validating that response_matcher is present in validation section of all rules

View file

@ -10,7 +10,7 @@ publish = false
[package]
name = "kingfisher"
version = "1.14.0"
version = "1.15.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true

View file

@ -6,7 +6,7 @@ rules:
\b
(
aio_
[a-zA-Z0-9]{28}
[A-Z0-9]{28}
)
\b
min_entropy: 3.5

View file

@ -1,7 +1,13 @@
rules:
- name: Age Recipient (X25519 public key)
id: kingfisher.age.1
pattern: '\b(age1[0-9a-z]{58})\b'
pattern: |
(?xi)
\b
(
age1[0-9a-z]{58}
)
\b
min_entropy: 3.3
confidence: medium
examples:
@ -13,7 +19,13 @@ rules:
- name: Age Identity (X22519 secret key)
id: kingfisher.age.2
pattern: '\b(AGE-SECRET-KEY-1[0-9A-Z]{58})\b'
pattern: |
(?xi)
\b
(
AGE-SECRET-KEY-1[0-9A-Z]{58}
)
\b
min_entropy: 3.3
confidence: medium
examples:

View file

@ -2,14 +2,14 @@ rules:
- name: Airbrake User Key
id: kingfisher.airbrake.1
pattern: |
(?x)(?i)
(?xi)
\b
airbrake
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
(
[a-zA-Z0-9-]{40}
[A-Z0-9-]{40}
)
\b
min_entropy: 4.5

View file

@ -39,7 +39,7 @@ rules:
(?xi)
\b
(
[a-zA-Z0-9]+\.v1\.[a-zA-Z0-9_-]+\.[a-f0-9]+
[A-Z0-9]+\.v1\.[A-Z0-9_-]+\.[a-f0-9]+
)
\b
min_entropy: 3.5

View file

@ -2,8 +2,7 @@ rules:
- name: Aiven API Key
id: kingfisher.aiven.1
pattern: |
(?x)
(?i)
(?xi)
aiven
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)

View file

@ -2,8 +2,7 @@ rules:
- name: Algolia Admin API Key
id: kingfisher.algolia.1
pattern: |
(?x)
(?i)
(?xi)
algolia
(?:.|[\n\r]){0,32}?
\b
@ -36,8 +35,7 @@ rules:
- name: Algolia Application ID
id: kingfisher.algolia.2
pattern: |
(?x)
(?i)
(?xi)
algolia
(?:.|[\n\r]){0,16}?
\b

View file

@ -2,8 +2,7 @@ rules:
- name: Alibaba Access Key ID
id: kingfisher.alibabacloud.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
LTAI[a-z0-9]{17,21}
@ -17,8 +16,7 @@ rules:
- name: Alibaba Access Key Secret
id: kingfisher.alibabacloud.2
pattern: |
(?x)
(?i)
(?xi)
\b
alibaba
(?:.|[\n\r]){0,16}?

View file

@ -2,8 +2,7 @@ rules:
- name: Anthropic API Key
id: kingfisher.anthropic.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
sk-ant-api

View file

@ -2,8 +2,7 @@ rules:
- name: Anypoint API Key
id: kingfisher.anypoint.1
pattern: |
(?x)
(?i)
(?xi)
anypoint
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)

View file

@ -2,11 +2,10 @@ rules:
- name: Artifactory Access Token
id: kingfisher.artifactory.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
AKC[a-zA-Z0-9]{64,74}
AKC[A-Z0-9]{64,74}
)
\b
min_entropy: 3.5
@ -37,7 +36,7 @@ rules:
- name: Artifactory JFrog URL
id: kingfisher.artifactory.2
pattern: |
(?x)
(?xi)
\b
(
[a-z0-9]

View file

@ -2,8 +2,7 @@ rules:
- name: Asana Client ID
id: kingfisher.asana.1
pattern: |
(?x)
(?i)
(?xi)
\b
asana
(?:.|[\n\r]){0,32}?
@ -25,8 +24,7 @@ rules:
- name: Asana Client Secret
id: kingfisher.asana.2
pattern: |
(?x)
(?i)
(?xi)
\b
asana
(?:.|[\n\r]){0,64}?
@ -44,8 +42,7 @@ rules:
- name: Asana OAuth / Personal Access Token
id: kingfisher.asana.3
pattern: |
(?x)
(?i)
(?xi)
\b
asana
(?:.|[\n\r]){0,64}?

View file

@ -2,8 +2,7 @@ rules:
- name: Atlassian API token
id: kingfisher.atlassian.1
pattern: |
(?x)
(?i)
(?xi)
\b
atlassian
(?:.|[\n\r]){0,32}?

View file

@ -2,8 +2,7 @@ rules:
- name: Auth0 Client ID
id: kingfisher.auth0.1
pattern: |
(?x)
(?i)
(?xi)
\b
auth0
(?:.|[\n\r]){0,32}?
@ -24,8 +23,7 @@ rules:
- name: Auth0 Client Secret
id: kingfisher.auth0.2
pattern: |
(?x)
(?i)
(?xi)
\b
auth0
(?:.|[\n\r]){0,16}?
@ -66,8 +64,7 @@ rules:
- name: Auth0 Domain
id: kingfisher.auth0.3
pattern: |
(?x)
(?i)
(?xi)
\b
(
[a-z0-9]

View file

@ -2,8 +2,7 @@ rules:
- name: AWS Access Key ID
id: kingfisher.aws.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
(?:AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)
@ -26,7 +25,7 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
[A-Za-z0-9/+=]{40}
[A-Z0-9/+=]{40}
)
\b
|
@ -38,7 +37,7 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
[A-Za-z0-9/+=]{40}
[A-Z0-9/+=]{40}
)
\b
)

View file

@ -42,10 +42,10 @@ rules:
- name: Azure App Configuration Connection String
id: kingfisher.azure.2
pattern: |
(?x)
(https://[a-zA-Z0-9-]+\.azconfig\.io);
Id=(.{4}-.{2}-.{2}:[a-zA-Z0-9+/]{18,22});
Secret=([a-zA-Z0-9+/]{36,50}=)
(?xi)
(https://[A-Z0-9-]+\.azconfig\.io);
Id=(.{4}-.{2}-.{2}:[A-Z0-9+/]{18,22});
Secret=([A-Z0-9+/]{36,50}=)
min_entropy: 3.3
confidence: medium
examples:
@ -69,7 +69,7 @@ rules:
- name: Azure Personal Access Token
id: kingfisher.azure.3
pattern: |
(?x)
(?xi)
(?i: ADO_PAT | pat_token | personal_?access_?token | \$token )
\s* = \s*
["']

View file

@ -37,7 +37,7 @@ rules:
- name: Azure Search URL
id: kingfisher.azuresearch.url.1
pattern: |
(?x)
(?xi)
\b
azure
(?:.|[\n\r]){0,32}?

View file

@ -2,7 +2,7 @@ rules:
- name: Azure Storage Account Name
id: kingfisher.azurestorage.name.1
pattern: |
(?x)
(?xi)
(?:
(?i:
(?:Account|Storage)
@ -25,11 +25,11 @@ rules:
- name: Azure Storage Account Key
id: kingfisher.azurestorage.key.1
pattern: |
(?x)
(?xi)
(?i:(?:Access|Account|Storage)[_.-]?Key)
(?:.|[\n\r]){0,25}?
(
[a-zA-Z0-9+\\/-]{86,88}={0,2}
[A-Z0-9+\\/-]{86,88}={0,2}
)
min_entropy: 4.0
confidence: medium

View file

@ -2,8 +2,7 @@ rules:
- name: Baremetrics API Key
id: kingfisher.baremetrics.1
pattern: |
(?x)
(?i)
(?xi)
\b
baremetrics
(?:.|[\n\r]){0,32}?

View file

@ -8,7 +8,7 @@ rules:
(?:.|[\n\r]){0,64}?
\b
(
b_[a-zA-Z0-9=_\\/\\\-+]{44}
b_[A-Z0-9=_\\/\\\-+]{44}
)
min_entropy: 3.0
confidence: medium

View file

@ -2,8 +2,7 @@ rules:
- name: Bitbucket Client ID
id: kingfisher.bitbucket.1
pattern: |
(?x)
(?i)
(?xi)
\b
bitbucket
(?:.|[\n\r]){0,16}?
@ -34,8 +33,7 @@ rules:
- name: Bitbucket Secret
id: kingfisher.bitbucket.3
pattern: |
(?x)
(?i)
(?xi)
\b
bitbucket
(?:.|[\n\r]){0,32}?

View file

@ -2,9 +2,9 @@ rules:
- name: Blynk Device Access Token
id: kingfisher.blynk.1
pattern: |
(?x)
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/external/api/[a-zA-Z0-9/]*\?token=
([a-zA-Z0-9_\-]{32})
(?xi)
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/external/api/[A-Z0-9/]*\?token=
([A-Z0-9_\-]{32})
&
min_entropy: 3.3
confidence: medium
@ -16,10 +16,10 @@ rules:
- name: Blynk Organization Access Token
id: kingfisher.blynk.2
pattern: |
(?x)
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/api/[a-zA-Z0-9_\-\s/\\]*
(?xi)
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/api/[A-Z0-9_\-\s/\\]*
-H\s*"Authorization:\s*Bearer\s*
([a-zA-Z0-9_\-]{40})
([A-Z0-9_\-]{40})
"
min_entropy: 3.3
confidence: medium
@ -31,9 +31,9 @@ rules:
- name: Blynk Organization Access Token
id: kingfisher.blynk.3
pattern: |
(?x)
(?xi)
-H\s*"Authorization:\s*Bearer\s*
([a-zA-Z0-9_\-]{40})
([A-Z0-9_\-]{40})
"[\s\\]*https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/api
min_entropy: 3.3
confidence: medium
@ -45,11 +45,11 @@ rules:
- name: Blynk Organization Client Credentials
id: kingfisher.blynk.8
pattern: |
(?x)
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/oauth2/[a-zA-Z0-9_\-\s/\\?=&]*
(oa2-client-id_[a-zA-Z0-9_\-]{32})
(?xi)
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/oauth2/[A-Z0-9_\-\s/\\?=&]*
(oa2-client-id_[A-Z0-9_\-]{32})
(?: : | &client_secret= )
([a-zA-Z0-9_\-]{40})
([A-Z0-9_\-]{40})
min_entropy: 3.3
confidence: medium
examples:
@ -61,10 +61,10 @@ rules:
- name: Blynk Organization Client Credentials
id: kingfisher.blynk.9
pattern: |
(?x)
(?xi)
\b
(oa2-client-id_[a-zA-Z0-9_\-]{32})
:([a-zA-Z0-9_\-]{40})
(oa2-client-id_[A-Z0-9_\-]{32})
:([A-Z0-9_\-]{40})
[\s\\]*https://(fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/oauth2
min_entropy: 3.3
confidence: medium

View file

@ -2,8 +2,7 @@ rules:
- name: CircleCI API Personal Access Token
id: kingfisher.circleci.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
CCIPAT_

View file

@ -2,8 +2,7 @@ rules:
- name: Cloudflare API Token
id: kingfisher.cloudflare.1
pattern: |
(?x)
(?i)
(?xi)
\b
cloudflare
(?:.|[\n\r]){0,32}?

View file

@ -2,8 +2,7 @@ rules:
- name: CloudSight API Key
id: kingfisher.cloudsight.1
pattern: |
(?x)
(?i)
(?xi)
\b
cloudsight
(?:.|[\n\r]){0,32}?

View file

@ -10,7 +10,7 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
[0-9A-Za-z]{20,24}
[0-9A-Z]{20,24}
)
min_entropy: 3.5
confidence: medium

View file

@ -2,7 +2,7 @@ rules:
- name: CodeClimate Reporter ID
id: kingfisher.codeclimate.1
pattern: |
(?x)
(?xi)
(?: CODECLIMATE| CC_TEST_REPORTER_ID)
(?:.|[\n\r]){0,64}?
(

View file

@ -2,12 +2,12 @@ rules:
- name: Confluent Client ID
id: kingfisher.confluent.1
pattern: |
(?x)(?i)
(?xi)
\b(?:confluent|ccloud|cpdev|kafka)
(?:.|[\n\r]){0,32}?
\b
(
[a-zA-Z0-9]{16}
[A-Z0-9]{16}
)
\b
min_entropy: 3
@ -21,14 +21,14 @@ rules:
- name: Confluent API Secret
id: kingfisher.confluent.2
pattern: |
(?x)(?i)
(?xi)
(?:confluent|ccloud|cpdev|kafka)
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
[a-zA-Z0-9\+/]{64}
[A-Z0-9\+/]{64}
)
min_entropy: 3.3
confidence: medium

View file

@ -1,7 +1,13 @@
rules:
- name: crates.io API Key
id: kingfisher.cratesio.1
pattern: '\b(cio[a-zA-Z0-9]{32})\b'
pattern: |
(?xi)
\b
(
cio[A-Z0-9]{32}
)
\b
min_entropy: 3.3
confidence: medium
examples:

View file

@ -2,8 +2,7 @@ rules:
- name: Credentials in a URL
id: kingfisher.credentials.1
pattern: |
(?x)
(?i)
(?xi)
https?:\/\/
(
[a-z0-9._~-]+

View file

@ -2,8 +2,7 @@ rules:
- name: Databricks API token
id: kingfisher.databricks.1
pattern: |
(?x)
(?i)
(?xi)
(
dapi
[a-f0-9]{32}
@ -54,7 +53,7 @@ rules:
- name: Databricks Domain
id: kingfisher.databricks.3
pattern: |
(?x)
(?xi)
\b
(
[a-z0-9-]+

View file

@ -1,7 +1,13 @@
rules:
- name: Dependency-Track API Key
id: kingfisher.dtrack.1
pattern: '\b(odt_[A-Za-z0-9]{32,255})\b'
pattern: |
(?xi)
\b
(
odt_[A-Z0-9]{32,255}
)
\b
min_entropy: 3.3
confidence: medium
examples:

View file

@ -2,7 +2,7 @@ rules:
- name: DigitalOcean API Key
id: kingfisher.digitalocean.1
pattern: |
(?x)
(?xi)
\b
(
(?:dop|doo)_v1_
@ -32,7 +32,7 @@ rules:
- name: DigitalOcean Refresh Token
id: kingfisher.digitalocean.2
pattern: |
(?x)
(?xi)
\b
(
dor_v1_

View file

@ -34,7 +34,7 @@ rules:
(?xi)
\b
(
[MNO][A-Za-z0-9_-]{23}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27}
[MNO][A-Z0-9_-]{23}\.[A-Z0-9_-]{6}\.[A-Z0-9_-]{27}
)
\b
min_entropy: 3.3

View file

@ -7,7 +7,7 @@ rules:
.{1,16}?
\b
(
[a-zA-Z0-9*!$@\#&_%^-]{45,55}
[A-Za-z0-9*!$@\#&_%^-]{45,55}
)
\b
min_entropy: 4.5

View file

@ -2,12 +2,12 @@ rules:
- name: Docker Hub Personal Access Token
id: kingfisher.dockerhub.1
pattern: |
(?x)
(?xi)
\b
(
dckr_pat_[a-zA-Z0-9_-]{27}
dckr_pat_[A-Z0-9_-]{27}
)
(?: $ | [^a-zA-Z0-9_-] )
(?: $ | [^A-Z0-9_-] )
min_entropy: 3.3
confidence: medium
examples:

View file

@ -2,9 +2,9 @@ rules:
- name: Doppler CLI Token
id: kingfisher.doppler.1
pattern: |
(?x)
(?xi)
\b
(dp\.ct\.[a-zA-Z0-9]{40,44})
(dp\.ct\.[A-Z0-9]{40,44})
\b
min_entropy: 3.3
confidence: medium
@ -31,9 +31,9 @@ rules:
- name: Doppler Personal Token
id: kingfisher.doppler.2
pattern: |
(?x)
(?xi)
\b
(dp\.pt\.[a-zA-Z0-9]{40,44})
(dp\.pt\.[A-Z0-9]{40,44})
\b
min_entropy: 3.3
confidence: medium
@ -61,9 +61,9 @@ rules:
- name: Doppler Service Token
id: kingfisher.doppler.3
pattern: |
(?x)
(?xi)
\b
(dp\.st\.(?:[a-z0-9\-_]{2,35}\.)?[a-zA-Z0-9]{40,44})
(dp\.st\.(?:[a-z0-9\-_]{2,35}\.)?[A-Z0-9]{40,44})
\b
min_entropy: 3.3
confidence: medium
@ -91,9 +91,9 @@ rules:
- name: Doppler Service Account Token
id: kingfisher.doppler.4
pattern: |
(?x)
(?xi)
\b
(dp\.sa\.[a-zA-Z0-9]{40,44})
(dp\.sa\.[A-Z0-9]{40,44})
\b
min_entropy: 3.3
confidence: medium
@ -121,9 +121,9 @@ rules:
- name: Doppler SCIM Token
id: kingfisher.doppler.5
pattern: |
(?x)
(?xi)
\b
(dp\.scim\.[a-zA-Z0-9]{40,44})
(dp\.scim\.[A-Z0-9]{40,44})
\b
min_entropy: 3.3
confidence: medium
@ -151,9 +151,9 @@ rules:
- name: Doppler Audit Token
id: kingfisher.doppler.6
pattern: |
(?x)
(?xi)
\b
(dp\.audit\.[a-zA-Z0-9]{40,44})
(dp\.audit\.[A-Z0-9]{40,44})
\b
min_entropy: 3.3
confidence: medium

View file

@ -6,7 +6,7 @@ rules:
\b
(
EZ[AT]K
[a-zA-Z0-9]{54}
[A-Za-z0-9]{54}
)
\b
min_entropy: 3.5
@ -14,9 +14,6 @@ rules:
examples:
- '"EZTKXxNbJDeDLDyrXuIgHd3cr1YmP7MFqY9cHAPYMOXhUN8nJ671JKaGME"'
- EZAK1234abcd5678efgh9012ijkl3456mnop7890qrst1234uvwx5678yz
categories:
- api
- secret
validation:
type: Http
content:

View file

@ -2,8 +2,7 @@ rules:
- name: Facebook App ID
id: kingfisher.facebook.1
pattern: |
(?x)
(?i)
(?xi)
\b
(?:facebook|fb)
(?:.|[\n\r]){0,8}?
@ -25,7 +24,7 @@ rules:
- name: Facebook Secret Key
id: kingfisher.facebook.2
pattern: |
(?x)(?i)
(?xi)
\b (?: facebook | fb )
.?
(?: api | app | application | client | consumer | customer | secret | key )
@ -62,7 +61,7 @@ rules:
- name: Facebook Access Token
id: kingfisher.facebook.3
pattern: |
(?x)
(?xi)
(?:
\b
(?:facebook|fb\b)
@ -71,7 +70,7 @@ rules:
(?:.|[\n\r]){0,32}?
)?
\b
(EAACEdEose0cBA[a-zA-Z0-9]{20,})
(EAACEdEose0cBA[A-Z0-9]{20,})
\b
min_entropy: 3.3
confidence: medium

View file

@ -2,7 +2,7 @@ rules:
- name: Fastly API token
id: kingfisher.fastly.1
pattern: |
(?x)(?i)
(?xi)
\b
fastly
(?:.|[\n\r]){0,32}?

View file

@ -2,10 +2,10 @@ rules:
- name: Figma Personal Access Token
id: kingfisher.figma.1
pattern: |
(?x)
(?xi)
\b
(
figd_[A-Za-z0-9_-]{38,42}
figd_[A-Z0-9_-]{38,42}
)
\b
min_entropy: 3.5
@ -33,7 +33,7 @@ rules:
- name: Figma Personal Access Header Token
id: kingfisher.figma.2
pattern: |
(?x)(?i)
(?xi)
figma
(?:.|[\n\r]){0,32}?
\b

View file

@ -17,7 +17,7 @@ rules:
min_entropy: 3.3
confidence: medium
examples:
- fileio SECRETKEY = Z9Y8X7W6V5U4T3S2R1Q0.P9O8N7M6L5K4J3H2G1F
- fileio SECRETKEY = Z9Y8X7W6V5U4T3S2R1Q0.P9O8N7M6L5K4J3H2G1FV
- fileio.PRIVATE.TOKEN = F0E1D2C3B4A596877869.5E4D3C2B1A0Z9Y8X7W6V
- fileio_key = M8N6B4V2C0X9Z7L5K3J1.H2G4F6D8S0A9P7O5I3U1
validation:

View file

@ -35,8 +35,7 @@ rules:
- name: Finicity client secret
id: kingfisher.finicity.2
pattern: |
(?x)
(?i)
(?xi)
\b
finicity
(?:.|[\n\r]){0,64}?

View file

@ -2,10 +2,7 @@ rules:
- name: GCP API Token
id: kingfisher.gcp.1
pattern: |
(?x)
(?m)
(?i)
(?s)
(?xims)
(
\{[^{}]*
\"auth_provider_x509_cert_url\":
@ -40,8 +37,7 @@ rules:
- name: GCP Private Key ID
id: kingfisher.gcp.3
pattern: |
(?x)
(?i)
(?xi)
\b
gcp
(?:

View file

@ -87,7 +87,7 @@ rules:
- name: Generic Password
id: kingfisher.generic.5
pattern: |
(?x)(?i)
(?xi)
password
\b
(?:.|[\n\r]){0,16}?

View file

@ -2,8 +2,7 @@ rules:
- name: GitHub Personal Access Token
id: kingfisher.github.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
(?: # for token prefixes
@ -53,11 +52,11 @@ rules:
- name: GitHub OAuth Access Token
id: kingfisher.github.2
pattern: |
(?x)
(?xi)
\b
(
gho_
[a-zA-Z0-9]{36}
[A-Z0-9]{36}
)
\b
min_entropy: 3.5
@ -89,7 +88,13 @@ rules:
- '"login"'
- name: GitHub App Token
id: kingfisher.github.3
pattern: '\b((?:ghu|ghs)_[a-zA-Z0-9]{36})\b'
pattern: |
(?xi)
\b
(
(?:ghu|ghs)_[A-Z0-9]{36}
)
\b
examples:
- ' "token": "ghu_16C7e42F292c69C2E7C10c838347Ae178B4a",'
- |
@ -119,7 +124,13 @@ rules:
- '"login"'
- name: GitHub Refresh Token
id: kingfisher.github.4
pattern: '\b(ghr_[a-zA-Z0-9]{76})\b'
pattern: |
(?xi)
\b
(
ghr_[A-Z0-9]{76}
)
\b
examples:
- ' "refresh_token": "ghr_1B4a2e77838347a7E420ce178F2E7c6912E169246c3CE1ccbF66C46812d16D5B1A9Dc86A1498",'
references:
@ -147,7 +158,7 @@ rules:
- name: GitHub Client ID
id: kingfisher.github.5
pattern: |
(?x)(?i)
(?xi)
(?:github)
.?
(?: api | app | application | client | consumer | customer )?
@ -162,7 +173,7 @@ rules:
- name: GitHub Secret Key
id: kingfisher.github.6
pattern: |
(?x)(?i)
(?xi)
github
.?
(?: api | app | application | client | consumer | customer | secret | key )
@ -177,9 +188,11 @@ rules:
- name: GitHub Personal Access Token (fine-grained permissions)
id: kingfisher.github.7
pattern: |
(?x)
(?xi)
\b
(github_pat_[0-9a-zA-Z_]{82})
(
github_pat_[0-9A-Z_]{82}
)
\b
examples:
- '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'

View file

@ -2,11 +2,11 @@ rules:
- name: GitLab Private Token
id: kingfisher.gitlab.1
pattern: |
(?x)
(?xi)
\b
(
glpat-
[0-9a-zA-Z_-]{20}
[0-9A-Z_-]{20}
)
(?:\b|$)
min_entropy: 3.5
@ -18,7 +18,8 @@ rules:
-f Dockerfile \
--build-arg 'GO_REPO_TOKEN=glpat-tFrjFXD7soVU2fqxuDMh' \
references:
- https://docs.gitlab.com/api/users/#get-your-user-status
- https://github.com/diffblue/gitlab/blob/39c63ee83369bf5353256a6b95f3116728edd102/doc/api/personal_access_tokens.md
- https://docs.gitlab.com/api/personal_access_tokens/
validation:
type: Http
content:
@ -30,12 +31,18 @@ rules:
- report_response: true
- type: WordMatch
words:
- '"message"'
url: https://gitlab.com/api/v4/user/status
- '"id"'
url: https://gitlab.com/api/v4/personal_access_tokens/self
- name: GitLab Runner Registration Token
id: kingfisher.gitlab.2
pattern: '\b(GR1348941[0-9a-zA-Z_-]{20})(?:\b|$)'
pattern: |
(?xi)
\b
(
GR1348941[0-9A-Z_-]{20}
)
\b
examples:
- |
sudo gitlab-runner register \
@ -49,6 +56,8 @@ rules:
--run-untagged="true" \
--locked="false" \
--access-level="not_protected"
references:
- https://docs.gitlab.com/api/runners/
validation:
type: Http
content:
@ -61,12 +70,18 @@ rules:
response_matcher:
- report_response: true
- type: StatusMatch
status: [200, 201]
status: 200
url: https://gitlab.com/api/v4/runners/verify
- name: GitLab Pipeline Trigger Token
id: kingfisher.gitlab.3
pattern: '\b(glptt-[0-9a-f]{40})\b'
pattern: |
(?xi)
\b
(
glptt-[0-9a-f]{40}
)
\b
examples:
- |
curl \
@ -75,6 +90,8 @@ rules:
--no-progress-meter \
-F token=glptt-0d66598d696a02da33fb65e2a041f607c68ea50d \
-F ref=main
references:
- https://docs.gitlab.com/api/pipeline_triggers/
validation:
type: Http
content:
@ -85,5 +102,6 @@ rules:
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
status:
- 200
url: https://gitlab.com/api/v4/ci/pipeline_triggers/{{ TOKEN }}

View file

@ -9,9 +9,9 @@ rules:
\b
(
live_
[A-Za-z0-9=_-]{16}
(?:[A-Za-z0-9=_-]{8}){3}
[A-Za-z0-9=_-]{0,2}
[A-Z0-9=_-]{16}
(?:[A-Z0-9=_-]{8}){3}
[A-Z0-9=_-]{0,2}
)
\b
min_entropy: 3.5

View file

@ -14,10 +14,10 @@ rules:
- name: Google OAuth Client Secret
id: kingfisher.google.2
pattern: |
(?x)
(?xi)
\b
(GOCSPX-[a-zA-Z0-9_-]{28})
(?:[^a-zA-Z0-9_-] | $)
(GOCSPX-[A-Z0-9_-]{28})
(?:[^A-Z0-9_-] | $)
min_entropy: 3.3
confidence: medium
examples:
@ -26,7 +26,7 @@ rules:
- name: Google OAuth Client Secret
id: kingfisher.google.3
pattern: |
(?x)(?i)
(?xi)
client.?secret .{0,10}
\b
([a-z0-9_-]{24})
@ -41,10 +41,10 @@ rules:
- name: Google OAuth Access Token
id: kingfisher.google.4
pattern: |
(?x)
(?xi)
\b
(ya29\.[0-9A-Za-z_-]{20,1024})
(?: [^0-9A-Za-z_-]|$)
(ya29\.[0-9A-Z_-]{20,1024})
(?: [^0-9A-Z_-]|$)
min_entropy: 3.3
confidence: medium
examples:
@ -65,7 +65,7 @@ rules:
- name: Google OAuth Credentials
id: kingfisher.google.6
pattern: |
(?x)
(?xi)
\b
([0-9]+-[a-z0-9_]{32}\.apps\.googleusercontent\.com)
(?:
@ -73,13 +73,13 @@ rules:
)
\b
(?:
(GOCSPX-[a-zA-Z0-9_-]{28})
(GOCSPX-[A-Z0-9_-]{28})
|
(?:
(?i) client.?secret .{0,10} \b ([a-zA-Z0-9_-]{24})
(?i) client.?secret .{0,10} \b ([A-Z0-9_-]{24})
)
)
(?:[^a-zA-Z0-9_-] | $)
(?:[^A-Z0-9_-] | $)
min_entropy: 3.3
confidence: medium
examples:

View file

@ -50,9 +50,11 @@ rules:
- name: Grafana Service Account Token
id: kingfisher.grafana.3
pattern: |
(?x)
(?xi)
\b
(glsa_[a-zA-Z0-9]{32}_[a-fA-F0-9]{8})
(
glsa_[A-Z0-9]{32}_[A-F0-9]{8}
)
\b
min_entropy: 3.3
confidence: medium
@ -93,8 +95,8 @@ rules:
pattern: |
(?xi)
(?:https?://)?
(?:[A-Za-z0-9-]+\.)*
grafana\.[A-Za-z0-9.-]+
(?:[A-Z0-9-]+\.)*
grafana\.[A-Z0-9.-]+
(?::\d{2,5})?
(?:[/?\#]\S*)?
min_entropy: 3.0

View file

@ -106,4 +106,4 @@ rules:
- '$krb5asrep$23$8cf8eb5287e28a4006c064892150c4fb$3e05ecc13548bec8e1eeb900dea5429cc6931bae9b8524490eb3a8801560871fe44355ed556202afbb39872e1bbb5c3c4f1b37dcd68fda89a23ebad917d4bbb0933edd94331598939e5d0c0c98c7e219a2e9dd6b877280d1bd7c51131413be577a167208bcc21e9fe7ae8f393278d740e72ca5c44c42d5cb0bf6bab0a36f1b88b7ddc4abbc6f152e652f6ba35c2955fb4132e11b7e566f3b422c3740f79847b77783d245a4e570b8a621b4ff6ff4815566446af70313ee78133707a76a4e4424783bd7c04920aa822a1a36b29f7e25cef186e6439fc46e42e23d6bd918969ef49b8388aef158e443b3a57dbde7ada631fbef7326f9046a9b'
- '$krb5asrep$23$c447eddaebf22ebf006a8fc6f986488c$eb3a17eb56287b474cecad5d4e0490d949977ba3f5015220bcd3080444d5601d67b76c5453b678e8527624e40c273bea4cfe4a7303e136b9bc3b9e63b6fb492ee4b4d2f830c5fa5a55466b57a678f708438f6712354a2deb851792b09270f4941966b82a2fd5ad8fa1fbd95a60b0f9bcd57774b3e55467a02ffcb3f1379104c24e468342f83df20b571e6f34f9a9842b43735d58d94514dcefa76719c0f5c7c3a3bfa770380924625aa0a3472d7c02d10dbb278fd946f7efcfe59a4d4cb7bdb9c5dbddc027611fe333d3ac940ec5b4ed43b55ab54b03cd2df0a9a2a7b5d235c226b259bd5ff8e0e49680351d4f0c4d13e258bc8d383cad6fc2711be0'
- '$krb5asrep$23$771adbc2397abddef676742924414f2b$2df6eb2d9c71820dc3fa2c098e071d920f0e412f5f12411632c5ee70e004da1be6f003b78661f8e4507e173552a52da751c45887c19bc1661ed334e0ccb4ef33975d4bd68b3d24746f281b4ca4fdf98fca0e50a8e845ad7d834e020c05b1495bc473b0295c6e9b94963cb912d3ff0f2f48c9075b0f52d9a31e5f4cc67c7af1d816b6ccfda0da5ccf35820a4d7d79073fa404726407ac840910357ef210fcf19ed81660106dfc3f4d9166a89d59d274f31619ddd9a1e2712c879a4e9c471965098842b44fae7ca6dd389d5d98b7fd7aca566ca399d072025e81cf0ef5075447687f80100307145fade7a8'
- '$krb5asrep$23$user@domain.com:3e156ada591263b8aab0965f5aebd837$007497cb51b6c8116d6407a782ea0e1c5402b17db7afa6b05a6d30ed164a9933c754d720e279c6c573679bd27128fe77e5fea1f72334c1193c8ff0b370fadc6368bf2d49bbfdba4c5dccab95e8c8ebfdc75f438a0797dbfb2f8a1a5f4c423f9bfc1fea483342a11bd56a216f4d5158ccc4b224b52894fadfba3957dfe4b6b8f5f9f9fe422811a314768673e0c924340b8ccb84775ce9defaa3baa0910b676ad0036d13032b0dd94e3b13903cc738a7b6d00b0b3c210d1f972a6c7cae9bd3c959acf7565be528fc179118f28c679f6deeee1456f0781eb8154e18e49cb27b64bf74cd7112a0ebae2102ac'
- '$krb5asrep$23$user@domain.com:3e156ada591263b8aab0965f5aebd837$007497cb51b6c8116d6407a782ea0e1c5402b17db7afa6b05a6d30ed164a9933c754d720e279c6c573679bd27128fe77e5fea1f72334c1193c8ff0b370fadc6368bf2d49bbfdba4c5dccab95e8c8ebfdc75f438a0797dbfb2f8a1a5f4c423f9bfc1fea483342a11bd56a216f4d5158ccc4b224b52894fadfba3957dfe4b6b8f5f9f9fe422811a314768673e0c924340b8ccb84775ce9defaa3baa0910b676ad0036d13032b0dd94e3b13903cc738a7b6d00b0b3c210d1f972a6c7cae9bd3c959acf7565be528fc179118f28c679f6deeee1456f0781eb8154e18e49cb27b64bf74cd7112a0ebae2102ac'

View file

@ -2,8 +2,7 @@ rules:
- name: Jira Domain
id: kingfisher.jira.1
pattern: |
(?x)
(?i)
(?xi)
(
[a-z][a-z0-9-]{5,24}\.atlassian\.net
)
@ -18,8 +17,7 @@ rules:
- name: Jira Token
id: kingfisher.jira.2
pattern: |
(?x)
(?i)
(?xi)
\b
jira
(?:.|[\n\r]){0,8}?

View file

@ -5,13 +5,13 @@ rules:
(?x)
\b
(
ey[a-zA-Z0-9_-]{12,} (?# header )
ey[A-Za-z0-9_-]{12,} (?# header )
\.
ey[a-zA-Z0-9_-]{12,} (?# payload )
ey[A-Za-z0-9_-]{12,} (?# payload )
\.
[a-zA-Z0-9_-]{12,} (?# signature )
[A-Za-z0-9_-]{12,} (?# signature )
)
(?:[^a-zA-Z0-9_-]|$) (?# this instead of a \b anchor because that doesn't play nicely with `-` )
(?:[^A-Z0-9_-]|$) (?# this instead of a \b anchor because that doesn't play nicely with `-` )
min_entropy: 3.3
confidence: medium
examples:

View file

@ -2,8 +2,7 @@ rules:
- name: Line Messaging API Token
id: kingfisher.line.1
pattern: |
(?x)
(?i)
(?xi)
\b
line
(?:.|[\n\r]){0,32}?
@ -11,7 +10,7 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
(?:[0-9A-Za-z+/]{57}){3}=?
(?:[0-9A-Z+/]{57}){3}=?
)
min_entropy: 3.5
confidence: medium

View file

@ -2,12 +2,11 @@ rules:
- name: Linear API Key
id: kingfisher.linear.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
lin_api_
(?:[0-9A-Za-z]{8}){5}
(?:[0-9A-Z]{8}){5}
)
\b
min_entropy: 3.5

View file

@ -2,7 +2,7 @@ rules:
- name: LinkedIn Client ID
id: kingfisher.linkedin.1
pattern: |
(?x)(?i)
(?xi)
linkedin
.?
(?: api | app | application | client | consumer | customer )?
@ -36,7 +36,7 @@ rules:
- name: LinkedIn Secret Key
id: kingfisher.linkedin.2
pattern: |
(?x)(?i)
(?xi)
linkedin
.?
(?: api | app | application | client | consumer | customer | secret | key )

View file

@ -2,8 +2,7 @@ rules:
- name: MailGun Token
id: kingfisher.mailgun.1
pattern: |
(?x)
(?i)
(?xi)
\b
mailgun
(?:.|[\n\r]){0,32}?
@ -11,7 +10,7 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
(?:[0-9A-Za-z-]{24}){3}
(?:[0-9A-Z-]{24}){3}
)
min_entropy: 3.5
confidence: medium
@ -34,8 +33,7 @@ rules:
- name: MailGun Primary Key
id: kingfisher.mailgun.2
pattern: |
(?x)
(?i)
(?xi)
(?:mailgun|mg)
(?:.|[\n\r]){0,64}?
\b

View file

@ -2,8 +2,7 @@ rules:
- name: Mandrill API Key
id: kingfisher.mandrill.1
pattern: |
(?x)
(?i)
(?xi)
\b
mandrill
(?:.|[\n\r]){0,32}?
@ -11,7 +10,7 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
(?:[0-9A-Za-z_-]{11}){2}
(?:[0-9A-Z_-]{11}){2}
)
min_entropy: 3.5
confidence: medium

View file

@ -2,18 +2,18 @@ rules:
- name: Microsoft Teams Webhook
id: kingfisher.microsoftteamswebhook.1
pattern: |
(?x)
https://[a-zA-Z0-9]+\.webhook\.office\.com/webhookb2
(?xi)
https://[A-Z0-9]+\.webhook\.office\.com/webhookb2
/
[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}
[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}
@
[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}
[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}
/
IncomingWebhook
/
[a-zA-Z0-9]{32}
[A-Z0-9]{32}
/
[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}
[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}
min_entropy: 3.3
confidence: medium
examples:

View file

@ -2,8 +2,7 @@ rules:
- name: MongoDB API Private Key
id: kingfisher.mongodb.1
pattern: |
(?x)
(?i)
(?xi)
(?:
(?:\b|_|-|\.)
(?:mongodb|atlas)
@ -59,9 +58,9 @@ rules:
(?:public|pub|user|id)
(?:.|[\n\r]){0,4}?
(
[a-zA-Z]+
[A-Z]+
)
(?:$|[^a-zA-Z0-9/+=-])
(?:$|[^A-Z0-9/+=-])
min_entropy: 2.0
confidence: medium
visible: false
@ -70,8 +69,7 @@ rules:
- name: MongoDB URI Connection String
id: kingfisher.mongodb.3
pattern: |
(?x)
(?i)
(?xi)
\b
(
mongodb(?:\+srv)?://[\S]{3,50}:(?:[\S]{3,88})@[-.%\w/:]+
@ -88,7 +86,7 @@ rules:
(?xi)
\b
(
mdb_sa_sk_[0-9a-zA-Z_-]{6}[0-9a-zA-Z]{34}
mdb_sa_sk_[0-9A-Z_-]{6}[0-9A-Z]{34}
)
\b
min_entropy: 3.5

View file

@ -39,7 +39,7 @@ rules:
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
([A-Za-z0-9_-]{43,45})
([A-Z0-9_-]{43,45})
\b
min_entropy: 3.5
confidence: medium

View file

@ -2,7 +2,7 @@ rules:
- name: netrc Credentials
id: kingfisher.netrc.1
pattern: |
(?x)
(?xi)
( machine \s+ [^\s]+ | default )
\s+
login \s+ ([^\s]+)

View file

@ -2,7 +2,7 @@ rules:
- name: Ngrok API Key
id: kingfisher.ngrok.1
pattern: |
(?x)(?i)
(?xi)
ngrok
(?:.|[\\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)

View file

@ -2,10 +2,10 @@ rules:
- name: NPM Access Token (fine-grained)
id: kingfisher.npm.1
pattern: |
(?x)
(?xi)
\b
(
npm_[A-Za-z0-9]{36}
npm_[A-Z0-9]{36}
)
\b
references:

View file

@ -2,7 +2,7 @@ rules:
- name: NuGet API Key
id: kingfisher.nuget.1
pattern: |
(?x)
(?xi)
\b
(
oy2[a-z0-9]{43}

View file

@ -2,7 +2,7 @@ rules:
- name: Credentials in ODBC Connection String
id: kingfisher.odbc.1
pattern: |
(?x)(?i)
(?xi)
(?: User | User\ Id | UserId | Uid) \s*=\s* ([^\s;]{3,100}) \s* ;
[\ \t]* .{0,10} [\ \t]*
(?: Password | Pwd) \s*=\s* ([^\t\ ;]{3,100}) \s* (?: [;] | $)

View file

@ -2,10 +2,10 @@ rules:
- name: OpenAI API Key
id: kingfisher.openai.1
pattern: |
(?x)
(?xi)
\b
(
sk-[a-zA-Z0-9]{48}
sk-[A-Z0-9]{48}
)
\b
min_entropy: 3.3

View file

@ -2,8 +2,7 @@ rules:
- name: OpsGenie API Key
id: kingfisher.opsgenie.1
pattern: |
(?x)
(?i)
(?xi)
\b
opsgenie
(?:.|[\\n\r]){0,32}?

View file

@ -2,11 +2,11 @@ rules:
- name: particle.io Access Token
id: kingfisher.particleio.1
pattern: |
(?x)
https://api\.particle\.io/v1/[a-zA-Z0-9_\-\s/"\\?]*
(?xi)
https://api\.particle\.io/v1/[A-Z0-9_\-\s/"\\?]*
(?:access_token=|Authorization:\s*Bearer\s*)
\b
([a-zA-Z0-9]{40})
([A-Z0-9]{40})
\b
min_entropy: 3.3
confidence: medium
@ -40,10 +40,10 @@ rules:
- name: particle.io Access Token
id: kingfisher.particleio.2
pattern: |
(?x)
(?xi)
(?:access_token=|Authorization:\s*Bearer\s*)
\b
([a-zA-Z0-9]{40})
([A-Z0-9]{40})
\b
[\s"\\]*https://api\.particle\.io/v1
min_entropy: 3.3

View file

@ -10,7 +10,7 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
[a-zA-Z0-9_]{32}
[A-Z0-9_]{32}
)
\b
min_entropy: 3.5

View file

@ -9,13 +9,13 @@ rules:
(?:.|[\n\r]){0,16}?
\b
(
A[A-Z0-9_-]{79,99}
A[A-Z0-9_-]{78,99}
)
\b
min_entropy: 3.5
visible: false
examples:
- paypal_client_id=AZJ6y8Dpr1TYbqAIdhkPzyhjXoY6m8GplL7C3zZ3lPrkTIdhkPzyhjXo_Dx3
- paypal_client_id=AZJ6y8Dpr1TYbqAIdhkPzyhjXoY6mIdhkPzyhjXoY6m8GplL7C3zZ3lPrkTIdhkPzyhjXo_Dx3IdhkPzyhjXoY6m
- name: PayPal OAuth Secret
id: kingfisher.paypal.2
@ -27,12 +27,12 @@ rules:
(?:.|[\n\r]){0,32}?
\b
(
[A-Z0-9_.-]{80,120}
[A-Z0-9_.-]{78,120}
)
\b
min_entropy: 3.5
examples:
- paypal_secret=EDe5J6y8Dpr1TYbqAIdhkPzyhjXoY6m8GplL7C3zZ3lPrkT1XlV6hYPSeJL5b1T1
- paypal_secret=EP0uwUsACKVPcbDRaXFYerX2ij6nbsha71cSdynuQWoSt1pIy4qtIs7gJQRmHwKXu5Icv3g1YQZzAywf
validation:
type: Http

View file

@ -2,8 +2,7 @@ rules:
- name: PlanetScale API Token
id: kingfisher.planetscale.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
pscale_tkn_[a-z0-9-_]{43}
@ -37,8 +36,7 @@ rules:
- name: PlanetScale Username
id: kingfisher.planetscale.2
pattern: |
(?x)
(?i)
(?xi)
(?:pscale|planetscale)
(?:.|[\n\r]){0,16}?
(?:USER|ID|NAME)

View file

@ -2,7 +2,7 @@ rules:
- name: Postman API Key
id: kingfisher.postman.1
pattern: |
(?x)
(?xi)
\b
(
PMAK-[A-Z0-9]{24}-[A-Z0-9]{34}

View file

@ -2,7 +2,7 @@ rules:
- name: Contains encrypted RSA private key
id: kingfisher.privkey.1
pattern: |
(?x)
(?xi)
(?msi)
(
-----BEGIN\s
@ -45,7 +45,7 @@ rules:
- name: Contains Private Key
id: kingfisher.privkey.2
pattern: |
(?x)
(?xi)
(?ims)
(
-----BEGIN\s

51
data/rules/pubnub.yml Normal file
View file

@ -0,0 +1,51 @@
rules:
- name: PubNub Publish Key
id: kingfisher.pubnub.1
pattern: |
(?xi)
\b
(
pub-c-[a-z0-9]{8}(?:-[a-z0-9]{4}){3}-[a-z0-9]{12}
)
\b
min_entropy: 3.5
examples:
- pub-c-12345678-1234-1234-1234-123456789012
validation:
type: Http
content:
request:
method: GET
response_matcher:
- report_response: true
- status:
- 200
type: StatusMatch
url: "https://ps.pndsn.com/publish/{{ TOKEN }}/{{ SUBSCRIPTIONTOKEN }}/0/kingfisher/0/%22ping%22?uuid=kingfisher_validate"
depends_on_rule:
- rule_id: "kingfisher.pubnub.2"
variable: SUBSCRIPTIONTOKEN
- name: PubNub Subscription Key
id: kingfisher.pubnub.2
pattern: |
(?xi)
\b
(
sub-c-[a-z0-9]{8}(?:-[a-z0-9]{4}){3}-[a-z0-9]{12}
)
\b
min_entropy: 3.5
confidence: medium
examples:
- sub-c-12345678-abcd-1234-efgh-567890abcdef
validation:
type: Http
content:
request:
method: GET
url: "https://ps.pndsn.com/v2/objects/{{ TOKEN }}/uuids/kingfisher_validate"
response_matcher:
- report_response: true
- status:
- 200
type: StatusMatch

30
data/rules/pulumi.yml Normal file
View file

@ -0,0 +1,30 @@
rules:
- name: Pulumi API Key
id: kingfisher.pulumi.1
pattern: |
(?x)
\b
(
pul-[a-f0-9]{40}
)
\b
min_entropy: 3.3
examples:
- pul-18e13e3eebebeb94eac318d421ca8ecc5ca78d5f
references:
- https://www.pulumi.com/docs/pulumi-cloud/reference/api-basics/
validation:
type: Http
content:
request:
headers:
Accept: application/vnd.pulumi+8
Authorization: token {{ TOKEN }}
Content-Type: application/json
method: GET
response_matcher:
- report_response: true
- status:
- 200
type: StatusMatch
url: "https://api.pulumi.com/api/user"

View file

@ -2,12 +2,12 @@ rules:
- name: PyPI Upload Token
id: kingfisher.pypi.1
pattern: |
(?x)
(?xi)
\b
(
pypi-AgEIcHlwaS5vcmc[a-zA-Z0-9_-]{50,}
pypi-AgEIcHlwaS5vcmc[A-Z0-9_-]{50,}
)
(?:[^a-zA-Z0-9_-]|$)
(?:[^A-Z0-9_-]|$)
min_entropy: 3.3
confidence: medium
examples:

View file

@ -2,7 +2,7 @@ rules:
- name: React App Username
id: kingfisher.reactapp.1
pattern: |
(?x)(?i)
(?xi)
\b
REACT_APP (?: _[A-Z0-9]+)* _USER (?: NAME)? (?# variable name )
\s* = \s*
@ -28,7 +28,7 @@ rules:
- name: React App Password
id: kingfisher.reactapp.2
pattern: |
(?x)(?i)
(?xi)
\b
REACT_APP (?: _[A-Z0-9]+)* _PASS (?: WORD)? (?# variable name )
\s* = \s*

30
data/rules/readme.yml Normal file
View file

@ -0,0 +1,30 @@
rules:
- name: ReadMe API Key
id: kingfisher.readme.1
pattern: |
(?x)(?i)
\b
(
rdme_(?P<RDMVAL>[a-z0-9]{70})
)
min_entropy: 3.3
confidence: medium
examples:
- rdme_abcdefghijklmnopqrstuvwxyzabcdef1234567890abcdef1234567890abcdef123456
- rdme_xn8s9he60fb31e9d290403d2707cce88fa820042d425fc6eb2baed4191dd88a5405987
references:
- https://docs.readme.com/main/reference/getproject-1
validation:
type: Http
content:
request:
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
method: GET
response_matcher:
- report_response: true
- status:
- 200
type: StatusMatch
url: https://api.readme.com/v2/projects/me

36
data/rules/rubygems.yml Normal file
View file

@ -0,0 +1,36 @@
rules:
- name: RubyGems API Key
id: kingfisher.rubygems.1
pattern: |
(?x)
(?i)
\b
(
rubygems_
[a-z0-9]{42,52}
)
\b
min_entropy: 3.5
confidence: medium
categories: [api, secret]
references:
- https://guides.rubygems.org/rubygems-org-api/
- https://guides.rubygems.org/api-key-scopes/
examples:
- |
$ curl -H 'Authorization:rubygems_b9ce70c306b3a2e248679fbbbd66723d408d3c8c5f00566c' \
https://rubygems.org/api/v1/web_hooks.json
- 'apikey: rubygems_123abc01a15f32b0be0103de4c9b3dcb3f2fea0fa8a84f23'
validation:
type: Http
content:
request:
headers:
Authorization: '{{ TOKEN }}'
method: GET
response_matcher:
- report_response: true
- status:
- 200
type: StatusMatch
url: https://rubygems.org/api/v1/web_hooks.json

63
data/rules/salesforce.yml Normal file
View file

@ -0,0 +1,63 @@
rules:
- name: Salesforce Access / Refresh Token
id: kingfisher.salesforce.1
pattern: |
(?xi)
\b
(
00
[A-Z0-9]{13}
!
[A-Z0-9._-]{90,120}
)
min_entropy: 3.3
confidence: medium
examples:
- 00DE0X0A0M0PeLE!CJoAQOx1GCLf1UIt4UU9y0VOPLUZAYN6I8DsdGEDyHh5cO02egObcAhIDHYiGCfi94c53oFbr4HB.xZfuYRGhvNuxobAAXRe
- |
=== Org Description
KEY VALUE
──────────────── ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Access Token 00DE0X0A0M0PeLE!AQcAQH0dMHEXAMPLEzmpkb58urFRkgeBGsxL_QJWwYMfAbUeeG7c1EXAMPLEDUkWe6H34r1AAwOR8B8fLEz6nEXAMPLEAAAA
Client Id PlatformCLI
Connected Status Connected
Id 00D5fORGIDEXAMPLE
Instance Url https://MyDomainName.my.salesforce.com
Username juliet.capulet@empathetic-wolf-g5qddtr.com
validation:
type: Http
content:
request:
headers:
Authorization: 'Bearer {{ TOKEN }}'
method: GET
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
words: ["DailyApiRequests"]
match_all_words: true
url: "https://{{ INSTANCE }}.my.salesforce.com/services/data/v60.0/limits"
depends_on_rule:
- rule_id: "kingfisher.salesforce.2"
variable: INSTANCE
- name: Salesforce Instance URL
id: kingfisher.salesforce.2
pattern: |
(?xi)
\b
(?:https?://)?
(
[0-9A-Z-]{5,128}
)
\.
my\.salesforce\.com
\b
min_entropy: 2.5
confidence: medium
visible: false
examples:
- https://example123.my.salesforce.com
- mydomainname.my.salesforce.com

62
data/rules/segment.yml Normal file
View file

@ -0,0 +1,62 @@
rules:
- name: Segment Public API Token
id: kingfisher.segment.1
pattern: |
(?xi)
\b
(
sgp_[A-Z0-9_-]{60,70}
)
\b
min_entropy: 3.3
confidence: medium
examples:
- sgp_pOqmnKCOAdIxlEbeRLlJKUOE4ravQJ3ZEijxzK4bpPrWaMNPP35kz4OU7ZVsDtgU
validation:
type: Http
content:
request:
headers:
Authorization: "Bearer {{ TOKEN }}"
method: GET
response_matcher:
- report_response: true
- status:
- 200
type: StatusMatch
url: https://api.segmentapis.com/
references:
- https://segment.com/docs/api/public-api/
- https://segment.com/blog/how-segment-proactively-protects-customer-api-tokens/
- name: Segment API Key
id: kingfisher.segment.2
pattern: |
(?xi)
\b
(?:segment|sgmt)
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,16}?
\b
(
[A-Z0-9_-]{40,50}\.[A-Z0-9_-]{40,50}
)
\b
min_entropy: 3.3
confidence: medium
examples:
- segment_token=FYbcC23QtDKym0b_bapKDaYKcIv5Ggu0B9icU9cfVud.1mSaWEYOh1GIKw11-VVtS3TVXzI04BkCvyijbHWdZK7
validation:
type: Http
content:
request:
headers:
Authorization: "Bearer {{ TOKEN }}"
method: GET
response_matcher:
- report_response: true
- status:
- 200
type: StatusMatch
url: https://api.segmentapis.com/

View file

@ -2,8 +2,7 @@ rules:
- name: Slack App Token
id: kingfisher.slack.1
pattern: |
(?x)
(?i)
(?xi)
(?:
.{0,24}[=:]
\s{0,8}
@ -37,8 +36,7 @@ rules:
- name: Slack Token
id: kingfisher.slack.2
pattern: |
(?x)
(?i)
(?xi)
\b
(
xox[pbarose]
@ -83,8 +81,7 @@ rules:
- name: Slack Webhook
id: kingfisher.slack.4
pattern: |
(?x)
(?i)
(?xi)
\b
(
https://hooks\.slack\.com/services/

34
data/rules/snyk.yml Normal file
View file

@ -0,0 +1,34 @@
rules:
- name: Snyk API Key
id: kingfisher.snyk.1
pattern: |
(?xi)
\b
snyk
(?:.|[\n\r]){0,32}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
\b
(
[A-Z0-9]{8}-(?:[A-Z0-9]{4}-){3}[A-Z0-9]{12}
)
min_entropy: 3.5
examples:
- snyk_token = 123e4567-e89b-12d3-a456-426614174000
- snyk_key = 123e4567-e89b-12d3-a456-426614174abc
validation:
type: Http
content:
request:
method: GET
url: "https://api.snyk.io/rest/self?version=2024-10-15"
headers:
Authorization: "Bearer {{ TOKEN }}"
Accept: application/json
response_matcher:
- report_response: true
- type: StatusMatch
status: [200]
- type: WordMatch
words: ['"username"']
match_all_words: true

View file

@ -5,11 +5,12 @@ rules:
pattern: |
(?xi)
(?:stripe|strp)
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
(?:.|[\n\r]){0,16}?
(
pk_live_
(?:[0-9A-Za-z]{6}){4,30}
(?:[0-9A-Z]{6}){4,30}
)
min_entropy: 3.3
confidence: medium
@ -21,16 +22,16 @@ rules:
id: kingfisher.stripe.2
pattern: |
(?ix)
(?:^|[\s"'=])
(?xi)
(?:stripe|strp)
(?:.|[\n\r]){0,16}?
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
(?:.|[\n\r]){0,32}?
(?:.|[\n\r]){0,16}?
(
(?:
sk|rk
)_live_
(?:[0-9A-Za-z]{8}){3,25}
(?:[0-9A-Z]{8}){3,25}
)
min_entropy: 3.3
confidence: medium

View file

@ -2,11 +2,10 @@ rules:
- name: Tailscale API Key
id: kingfisher.tailscale.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
tskey-[a-z]+-[A-Za-z0-9_-]{20,24}
tskey-[a-z]+-[A-Z0-9_-]{20,24}
)
\b
min_entropy: 3.0

View file

@ -2,8 +2,7 @@ rules:
- name: Travis CI Token
id: kingfisher.travisci.1
pattern: |
(?x)
(?i)
(?xi)
\b
travis
(?:.|[\\n\r]){0,16}?
@ -11,13 +10,13 @@ rules:
(?:.|[\n\r]){0,16}?
\b
(
[a-z0-9-_]{22}
[A-Z-_0-9]{22}
)
\b
min_entropy: 3.0
confidence: medium
examples:
- "travis_token splendid21RANDOMCONTENT_token"
- "travis_token splendid21RANDOMCONTEN"
validation:
type: Http
content:

View file

@ -2,10 +2,10 @@ rules:
- name: Twilio API ID
id: kingfisher.twilio.1
pattern: |
(?x)
(?xi)
\b
(
(?:SK|AC)[a-fA-F0-9]{32}
(?:SK|AC)[A-F0-9]{32}
)
\b
min_entropy: 3.5
@ -21,8 +21,7 @@ rules:
- name: Twilio API Key
id: kingfisher.twilio.2
pattern: |
(?x)
(?i)
(?xi)
\b
twilio
(?:.|[\n\r]){0,32}?

View file

@ -117,8 +117,7 @@ rules:
- name: Algolia Admin API Key
id: kingfisher.algolia.1
pattern: |
(?x)
(?i)
(?xi)
algolia
(?:.|[\n\r]){0,32}?
\b
@ -151,8 +150,7 @@ rules:
- name: Algolia Application ID
id: kingfisher.algolia.2
pattern: |
(?x)
(?i)
(?xi)
algolia
(?:.|[\n\r]){0,16}?
\b
@ -215,8 +213,7 @@ rules:
- name: Anthropic API Key
id: kingfisher.anthropic.1
pattern: |
(?x)
(?i)
(?xi)
\b
(
sk-ant-api

View file

@ -190,6 +190,10 @@ async fn async_main(args: CommandLineArgs) -> Result<()> {
let rules_db = Arc::new(load_and_record_rules(&scan_args, &datastore)?);
run_scan(&args.global_args, &scan_args, &rules_db, Arc::clone(&datastore)).await?;
let exit_code = determine_exit_code(&datastore);
if let Err(e) = temp_dir.close() {
eprintln!("Failed to close temporary directory: {}", e);
}
std::process::exit(exit_code);
}
Command::Rules(ref rule_args) => match &rule_args.command {

View file

@ -1,36 +1,74 @@
use std::{fs, io::ErrorKind, path::PathBuf};
// This module checks GitHub for a newer Kingfisher release and (optionally)
// selfupdates. Our release assets use short, userfriendly names such as
// `kingfisher-linux-arm64.tgz`, `kingfisher-darwin-x64.tgz`, etc. Those names
// do **not** match the full Rust target triple that the `self_update` crate
// expects (e.g. `aarch64-unknown-linux-musl`). We therefore map the compile
// time target to the corresponding asset suffix via `builder.target()`.
//
// Version handling logic covers three scenarios:
// 1. Running version == latest release → "up to date".
// 2. Running version > latest release → print a notice that the binary is
// **newer** than anything on GitHub (e.g. a dev build).
// 3. Latest release > running version → offer to selfupdate.
//
// All informational messages are printed with the
// `style_finding_active_heading` style so that they stand out alongside normal
// scan output.
use std::{
fs,
io::{ErrorKind, IsTerminal},
path::PathBuf,
};
use self_update::{backends::github::Update, cargo_crate_version, errors::Error as UpdError};
use semver::Version;
use tracing::{error, info, warn};
use crate::cli::global::GlobalArgs;
use crate::{
cli::global::GlobalArgs,
reporter::styles::Styles,
};
/// Return `true` when the canonical executable path lives inside a Homebrew Cellar.
/// Works for Intel macOS (/usr/local/Cellar), Apple-Silicon macOS (/opt/homebrew/Cellar)
/// Works for Intel macOS (/usr/local/Cellar), AppleSilicon macOS (/opt/homebrew/Cellar)
/// and Linuxbrew (~/.linuxbrew/Cellar).
fn installed_via_homebrew() -> bool {
fn canonical_exe() -> Option<PathBuf> {
std::env::current_exe().ok().and_then(|p| fs::canonicalize(p).ok())
}
canonical_exe().map(|p| p.components().any(|c| c.as_os_str() == "Cellar")).unwrap_or(false)
canonical_exe()
.map(|p| p.components().any(|c| c.as_os_str() == "Cellar"))
.unwrap_or(false)
}
/// Check GitHub for a newer Kingfisher release.
/// Check GitHub for a newer Kingfisher release and optionally selfupdate.
///
/// * `base_url` lets tests point at a mock server.
/// * Self-update is performed unless the user disabled it **or** the binary is a Homebrew install.
/// * Selfupdate is skipped when the user disabled it **or** the binary is a
/// Homebrew install.
pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Option<String> {
if global_args.no_update_check {
return None;
}
// Decide once whether we want coloured output.
let use_color = std::io::stderr().is_terminal() && !global_args.quiet;
let styles = Styles::new(use_color);
let is_brew = installed_via_homebrew();
if is_brew {
info!("Homebrew install detected will notify about updates but not self-update");
info!(
"{}",
styles
.style_finding_active_heading
.apply_to("Homebrew install detected will notify about updates but not selfupdate")
);
}
info!("Checking for updates…");
info!(
"{}","Checking for updates…");
let mut builder = Update::configure();
builder
@ -40,48 +78,102 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt
.show_download_progress(false)
.current_version(cargo_crate_version!());
// Allow tests to point at a mock HTTP server.
if let Some(url) = base_url {
builder.with_url(url);
}
// ──────────────────────────────────────────────────────
// Map the current Rust target triple to our simplified asset names.
// ──────────────────────────────────────────────────────
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
builder.target("linux-arm64");
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
builder.target("linux-x64");
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
builder.target("darwin-arm64");
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
builder.target("darwin-x64");
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
builder.target("windows-x64");
// Build the updater.
let Ok(updater) = builder.build() else {
warn!("Failed to configure update checker");
return None;
};
// Query GitHub.
let Ok(release) = updater.get_latest_release() else {
warn!("Failed to check for updates");
return None;
};
if release.version == cargo_crate_version!() {
let msg = format!("Kingfisher {} is up to date", release.version);
info!("{msg}");
return Some(msg);
let running_v = cargo_crate_version!();
// ───────────── Case 1: running == latest ─────────────
if release.version == running_v {
let plain = format!("Kingfisher {running_v} is up to date");
info!("{}", styles.style_finding_active_heading.apply_to(&plain));
return Some(plain);
}
// There is a newer release.
let msg = format!("New Kingfisher release {} available", release.version);
info!("{msg}");
// Try semantic version comparison. If parsing fails, fall back to the
// selfupdate codepath (which will treat the strings lexicographically).
if let (Ok(curr), Ok(latest)) = (
Version::parse(running_v),
Version::parse(&release.version),
) {
// ───────── Case 2: running > latest (dev build) ─────────
if curr > latest {
let plain = format!(
"Running Kingfisher {curr} which is newer than latest released {latest}"
);
info!("{}", styles.style_finding_active_heading.apply_to(&plain));
return Some(plain);
}
// else fall through to Case 3 (latest > running)
}
// Decide whether to perform the update in place.
// ───────────── Case 3: latest > running ─────────────
let plain = format!("New Kingfisher release {} available", release.version);
info!("{}", styles.style_finding_active_heading.apply_to(&plain));
// Attempt selfupdate when allowed and feasible.
if global_args.self_update && !is_brew {
match updater.update() {
Ok(status) => info!("Updated to version {}", status.version()),
Ok(status) => info!(
"{}",
styles
.style_finding_active_heading
.apply_to(&format!("Updated to version {}", status.version()))
),
Err(e) => match e {
UpdError::Io(ref io_err) if io_err.kind() == ErrorKind::PermissionDenied => {
warn!(
"Cannot replace the current binary permission denied.\n\
If you installed via a package manager, run its upgrade command.\n\
Otherwise reinstall to a user-writable directory or re-run with sudo."
"{}",
styles.style_finding_active_heading.apply_to(
"Cannot replace the current binary permission denied.\n\
If you installed via a package manager, run its upgrade command.\n\
Otherwise reinstall to a userwritable directory or rerun with sudo."
)
);
}
_ => error!("Failed to update: {e}"),
},
}
} else if is_brew {
info!("Run `brew upgrade kingfisher` to install the new version.");
info!(
"{}",
styles
.style_finding_active_heading
.apply_to("Run `brew upgrade kingfisher` to install the new version.")
);
}
Some(msg)
Some(plain)
}

View file

@ -1,76 +0,0 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
regexp "github.com/wasilibs/go-re2"
)
func main() {
// fmt.Println(">> [*] Testing 'kingfisher local-git' functionality against owasp/wrongsecrets repo.")
// Remove the existing /tmp/wrongsecrets directory
if err := os.RemoveAll("/tmp/wrongsecrets"); err != nil {
fmt.Printf("Error removing /tmp/wrongsecrets: %s\n", err)
return
}
// Clone the owasp/wrongsecrets repository
gitCloneCmd := exec.Command("git", "clone", "https://github.com/OWASP/wrongsecrets.git", "/tmp/wrongsecrets", "--depth", "1")
if err := gitCloneCmd.Run(); err != nil {
fmt.Printf("Error cloning repository: %s\n", err)
return
}
defer os.RemoveAll("/tmp/wrongsecrets")
// Get the current working directory
cwd, err := os.Getwd()
if err != nil {
fmt.Printf("Error getting current directory: %s\n", err)
return
}
// Construct the path to main.go
mainGoPath := filepath.Join(cwd, "main.go")
// Run the main.go with local-git command
mainGoCmd := exec.Command("go", "run", mainGoPath, "local-git", "--path", "/tmp/wrongsecrets", "--silent", "--debug", "--confidence", "low")
outputBytes, err := mainGoCmd.CombinedOutput()
if err != nil {
fmt.Printf("Error running main.go: %s\nOutput: %s\n", err, string(outputBytes))
return
}
output := string(outputBytes)
// Print output
// fmt.Println(output)
// Extract the number of files processed
re := regexp.MustCompile(`Files Read\.*?: (\d+)`)
matches := re.FindStringSubmatch(output)
if len(matches) < 2 {
fmt.Println("Error: Could not find files count")
os.Exit(1)
return
}
filesCount, err := strconv.Atoi(matches[1])
if err != nil {
fmt.Printf("Error parsing files count: %s\n", err)
os.Exit(1)
return
}
// Check if the files count is greater than 10
if filesCount <= 10 {
fmt.Printf("Error: Files count (%d) is not greater than 10\n", filesCount)
os.Exit(1)
return
}
fmt.Println("Test completed successfully.")
}

View file

@ -0,0 +1,23 @@
// tests/smoke_check_rules.rs
use std::process::Command;
use assert_cmd::prelude::*;
use predicates::prelude::*;
#[test]
fn check_rules() -> anyhow::Result<()> {
// ── run kingfisher ────────────────────────────────────────────────
Command::cargo_bin("kingfisher")?
.args([
"rules",
"check",
"--no-update-check", // skip update check to avoid network calls
])
.assert()
.code(0) // no findings present
.stdout(predicate::str::contains("All rules passed validation successfully"));
Ok(())
}

View file

@ -16,9 +16,9 @@ async fn detects_new_release() {
let server = MockServer::start().await;
let body = serde_json::json!({
"tag_name": "v1.99.0",
"tag_name": "v99.999.0",
"created_at": "2025-01-01T00:00:00Z",
"name": "Kingfisher 1.99.0",
"name": "Kingfisher 99.999.0",
"body": "",
"assets": [{"url": "http://example.com/bin", "name": "bin"}]
});
@ -42,5 +42,5 @@ async fn detects_new_release() {
.expect("blocking task panicked")
.expect("update checker returned None");
assert!(msg.contains("1.99.0"));
assert!(msg.contains("99.999.0"));
}