diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b4462f..6954865 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [v1.92.0] +- Added new built-in rules for Etsy, Flutterwave, Freemius, JFrog, Kraken, KuCoin, and Trello, plus recent Betterleaks-derived additions including Octopus Deploy, OpenShift, Private AI, SettleMint, Sidekiq, and Polymarket. +- Added live HTTP validation for Etsy, JFrog, Octopus Deploy, OpenShift, and Private AI where provider documentation supported reliable token-only checks. + ## [v1.91.0] - Added SSRF protection for credential validation: outbound HTTP requests now block connections to loopback, private, link-local, and other non-public IP addresses. HTTP redirect targets are DNS-resolved and validated against the same SSRF rules. Use `--allow-internal-ips` to opt out when scanning internal infrastructure. - Consolidated JWT SSRF checks to use the shared `is_ssrf_safe_ip` function, covering additional reserved ranges (CGNAT, documentation, benchmarking, IPv6 unique-local). diff --git a/Cargo.toml b/Cargo.toml index 162bc34..f37bf5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ http = "1.4" [package] name = "kingfisher" -version = "1.91.0" +version = "1.92.0" description = "MongoDB's blazingly fast and accurate secret scanning and validation tool" edition.workspace = true rust-version.workspace = true diff --git a/NOTICE b/NOTICE index f47f0bb..d98a5b3 100644 --- a/NOTICE +++ b/NOTICE @@ -113,3 +113,39 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------- + +Certain detection rules: + * crates/kingfisher-rules/data/rules/adafruit.yml + * crates/kingfisher-rules/data/rules/etsy.yml + * crates/kingfisher-rules/data/rules/flutterwave.yml + * crates/kingfisher-rules/data/rules/freemius.yml + * crates/kingfisher-rules/data/rules/jfrog.yml + * crates/kingfisher-rules/data/rules/kraken.yml + * crates/kingfisher-rules/data/rules/kucoin.yml + * crates/kingfisher-rules/data/rules/trello.yml + +are derived in part from Gitleaks (https://github.com/gitleaks/gitleaks), +which is licensed under the MIT License. + +Gitleaks +Copyright (c) 2019 Zachary Rice + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/kingfisher-rules/data/rules/azure-notification-hub.yml b/crates/kingfisher-rules/data/rules/azure-notification-hub.yml index 5bd443e..c090b03 100644 --- a/crates/kingfisher-rules/data/rules/azure-notification-hub.yml +++ b/crates/kingfisher-rules/data/rules/azure-notification-hub.yml @@ -63,7 +63,9 @@ rules: pattern: | (?xi) \b + ["']? SharedAccessKeyName + ["']? \s*[:=]\s* ["']? ( diff --git a/crates/kingfisher-rules/data/rules/etsy.yml b/crates/kingfisher-rules/data/rules/etsy.yml new file mode 100644 index 0000000..7b89bca --- /dev/null +++ b/crates/kingfisher-rules/data/rules/etsy.yml @@ -0,0 +1,51 @@ +rules: + - name: Etsy Open API Key + id: kingfisher.etsy.1 + pattern: | + (?xi) + \b + (?: + etsy + (?:.|[\n\r]){0,32}? + (?: + api[_-]?key | + keystring | + x-api-key + ) + | + x-api-key + ) + (?:.|[\n\r]){0,12}? + ( + [a-z0-9]{24}:[A-Za-z0-9]{10,64} + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 8 + ignore_if_contains: + - your_api_key + - your_key_here + - placeholder + min_entropy: 3.4 + confidence: medium + examples: + - 'x-api-key: 1aa2bb33c44d55eeeeee6fff:a1b2c3d4e5' + - ETSY_API_KEY=1aa2bb33c44d55eeeeee6fff:a1b2c3d4e5 + references: + - https://developers.etsy.com/documentation/tutorials/quickstart/ + validation: + type: Http + content: + request: + method: GET + url: https://api.etsy.com/v3/application/openapi-ping + headers: + x-api-key: '{{ TOKEN }}' + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + diff --git a/crates/kingfisher-rules/data/rules/firebase.yml b/crates/kingfisher-rules/data/rules/firebase.yml index 952b392..1140506 100644 --- a/crates/kingfisher-rules/data/rules/firebase.yml +++ b/crates/kingfisher-rules/data/rules/firebase.yml @@ -52,7 +52,7 @@ rules: confidence: medium examples: - FCM_DEVICE_TOKEN=AbCdEfGhIjKlMnOpQrStUv:APA91bZaYxWvUtSrQpOnMlKjIhGfEdCbA9876543210ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-AaBbCcDdEeFfGgHhIiJj - - 'registrationToken: "ZyXwVuTsRqPoNmLkJiHgFe:APA91bAbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-KkLlMmNnOoPpQqRrSs"' + - 'registrationToken: "AbCdEfGhIjKlMnOpQrStUv:APA91bZaYxWvUtSrQpOnMlKjIhGfEdCbA9876543210ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-AaBbCcDdEeFfGgHhIiJj"' references: - https://firebase.google.com/docs/cloud-messaging/manage-tokens # Registration tokens can’t be safely live-validated using only the token value. diff --git a/crates/kingfisher-rules/data/rules/flutterwave.yml b/crates/kingfisher-rules/data/rules/flutterwave.yml new file mode 100644 index 0000000..ae0819d --- /dev/null +++ b/crates/kingfisher-rules/data/rules/flutterwave.yml @@ -0,0 +1,43 @@ +rules: + - name: Flutterwave Public Key + id: kingfisher.flutterwave.1 + pattern: | + (?x) + \b + ( + FLWPUBK(?:_TEST)?-[a-f0-9]{32}-X + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 4 + min_entropy: 3.1 + confidence: medium + examples: + - FLW_PUBLIC_KEY=FLWPUBK_TEST-32193bba8dab84e3d9c4525c85ea7a12-X + - data-PBFPubKey="FLWPUBK_TEST-589490616a6297324231c5e89b58f3f6-X" + references: + - https://developer.flutterwave.com/docs/authentication + - https://developer.flutterwave.com/v2.0/docs/api-keys + + - name: Flutterwave Secret Key + id: kingfisher.flutterwave.2 + pattern: | + (?x) + \b + ( + FLWSECK(?:_TEST)?-[a-f0-9]{32}-X + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 8 + min_entropy: 3.3 + confidence: medium + examples: + - FLW_SECRET_KEY=FLWSECK_TEST-a514d8f1abd080db1502a144f22954dc-X + - 'Authorization: Bearer FLWSECK_TEST-5b1f0a33de9c41748c2a7e9b51d3c6af-X' + - seckey=FLWSECK-e6db11d1f8a6208de8cb2f94e293450e-X + references: + - https://developer.flutterwave.com/docs/authentication + - https://developer.flutterwave.com/v2.0/reference/api-request-and-response-standards diff --git a/crates/kingfisher-rules/data/rules/freemius.yml b/crates/kingfisher-rules/data/rules/freemius.yml new file mode 100644 index 0000000..766d627 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/freemius.yml @@ -0,0 +1,29 @@ +rules: + - name: Freemius Secret Key + id: kingfisher.freemius.1 + pattern: | + (?xi) + ["']secret_key["'] + \s*=>\s* + ["'] + ( + sk_[^"' \t\r\n]{29} + ) + ["'] + pattern_requirements: + min_digits: 2 + min_lowercase: 6 + min_special_chars: 2 + ignore_if_contains: + - xxxxxxxxx + - placeholder + min_entropy: 3.6 + confidence: medium + examples: + - | + $config = array( + "secret_key" => "sk_ubb4yN3mzqGR2x8#P7r5&@*xC$utE", + ); + references: + - https://freemius.com/help/documentation/wordpress-sdk/integrating-freemius-sdk/ + diff --git a/crates/kingfisher-rules/data/rules/helpscout.yml b/crates/kingfisher-rules/data/rules/helpscout.yml index e08a38c..6a63bb7 100644 --- a/crates/kingfisher-rules/data/rules/helpscout.yml +++ b/crates/kingfisher-rules/data/rules/helpscout.yml @@ -56,7 +56,7 @@ rules: confidence: medium examples: - HELPSCOUT_CLIENT_SECRET=a3B8f29E4d1C6a0578e23D9f41b6C8e2 - - 'helpscout_secret: "E7d2A1f849c3B05d6e81F2a794c3D5b0"' + - 'helpscout_client_secret: "E7d2A1f849c3B05d6e81F2a794c3D5b0"' references: - https://developer.helpscout.com/mailbox-api/ depends_on_rule: diff --git a/crates/kingfisher-rules/data/rules/jfrog.yml b/crates/kingfisher-rules/data/rules/jfrog.yml new file mode 100644 index 0000000..4285f49 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/jfrog.yml @@ -0,0 +1,129 @@ +rules: + - name: JFrog Cloud Host + id: kingfisher.jfrog.1 + pattern: | + (?xi) + \b + ( + [a-z0-9] + (?: + [a-z0-9\-]{0,61} + [a-z0-9] + )? + \.jfrog\.io + ) + \b + min_entropy: 2.5 + confidence: medium + visible: false + examples: + - company.jfrog.io + - my-team.jfrog.io + references: + - https://jfrog.com/help/api/khub/documents/xrOb4ANk_fqUw5nctnsIww/content + + - name: JFrog API Key + id: kingfisher.jfrog.2 + pattern: | + (?xi) + \b + (?: + jfrog | + artifactory | + bintray | + xray + ) + (?:.|[\n\r]){0,32}? + (?: + api[_-]?key | + password | + token | + secret + ) + (?:.|[\n\r]){0,12}? + ( + [A-Za-z0-9]{73} + ) + \b + pattern_requirements: + min_digits: 4 + min_uppercase: 2 + min_lowercase: 6 + min_entropy: 3.5 + confidence: medium + examples: + - jfrog_api_key=Ab12Cd34Ef56Gh78Ij90Kl12Mn34Op56Qr78St90Uv12Wx34Yz56Ab78Cd90Ef12Gh34Ij5Kl + - jfrog_password=Ab12Cd34Ef56Gh78Ij90Kl12Mn34Op56Qr78St90Uv12Wx34Yz56Ab78Cd90Ef12Gh34Ij5Kl + references: + - https://jfrog.com/help/api/khub/documents/xrOb4ANk_fqUw5nctnsIww/content + - https://jfrog.com/article/access-service/ + depends_on_rule: + - rule_id: kingfisher.jfrog.1 + variable: JFROG_HOST + validation: + type: Http + content: + request: + method: GET + url: https://{{ JFROG_HOST }}/artifactory/api/repositories + headers: + X-JFrog-Art-Api: '{{ TOKEN }}' + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + + - name: JFrog Identity Token + id: kingfisher.jfrog.3 + pattern: | + (?xi) + \b + (?: + jfrog | + artifactory | + bintray | + xray + ) + (?:.|[\n\r]){0,32}? + (?: + identity[_-]?token | + access[_-]?token | + bearer | + token + ) + (?:.|[\n\r]){0,12}? + ( + [A-Za-z0-9]{64} + ) + \b + pattern_requirements: + min_digits: 4 + min_uppercase: 2 + min_lowercase: 6 + min_entropy: 3.4 + confidence: medium + examples: + - jfrog_identity_token=Ab12Cd34Ef56Gh78Ij90Kl12Mn34Op56Qr78St90Uv12Wx34Yz56Ab78Cd90Ef12 + - artifactory_access_token=Zx12Cv34Bn56Mm78Aa90Ss12Dd34Ff56Gg78Hh90Jj12Kk34Ll56Qq78Ww90Ee12 + references: + - https://jfrog.com/help/api/khub/documents/xrOb4ANk_fqUw5nctnsIww/content + - https://jfrog.com/article/access-service/ + depends_on_rule: + - rule_id: kingfisher.jfrog.1 + variable: JFROG_HOST + validation: + type: Http + content: + request: + method: GET + url: https://{{ JFROG_HOST }}/artifactory/api/repositories + headers: + Authorization: 'Bearer {{ TOKEN }}' + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/kraken.yml b/crates/kingfisher-rules/data/rules/kraken.yml new file mode 100644 index 0000000..bd3fc2e --- /dev/null +++ b/crates/kingfisher-rules/data/rules/kraken.yml @@ -0,0 +1,31 @@ +rules: + - name: Kraken API Secret + id: kingfisher.kraken.1 + pattern: | + (?xi) + \b + kraken + (?:.|[\n\r]){0,32}? + (?: + api[_-]?secret | + secret | + private[_-]?key | + token + ) + (?:.|[\n\r]){0,12}? + ( + [A-Za-z0-9+/=_-]{80,90} + ) + (?:[^A-Za-z0-9+/=_-]|$) + pattern_requirements: + min_digits: 4 + min_uppercase: 2 + min_lowercase: 8 + min_entropy: 4.0 + confidence: medium + examples: + - KRAKEN_API_SECRET=dGhpcy1sb29rcy1saWtlLWEtYmFzZTY0LWtyYWtlbi1zZWNyZXQtdGhhdC1pcy1sb25nLWVub3VnaA== + - kraken_secret="Aq1Bq2Cr3Ds4Et5Fu6Gv7Hw8Ix9Jy0Kz1La2Mb3Nc4Od5Pe6Qf7Rg8Sh9Ti0Uj1Vk2Wm3Xn4Yo5Za6Bc7" + references: + - https://docs.kraken.com/api/docs/guides/spot-rest-auth/ + - https://docs.kraken.com/api/docs/rest-api/get-account-balance/ diff --git a/crates/kingfisher-rules/data/rules/kucoin.yml b/crates/kingfisher-rules/data/rules/kucoin.yml new file mode 100644 index 0000000..c2d92ed --- /dev/null +++ b/crates/kingfisher-rules/data/rules/kucoin.yml @@ -0,0 +1,62 @@ +rules: + - name: KuCoin API Key + id: kingfisher.kucoin.1 + pattern: | + (?xi) + \b + kucoin + (?:.|[\n\r]){0,32}? + (?: + api[_-]?key | + key + ) + (?:.|[\n\r]){0,12}? + ( + [a-f0-9]{24} + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 8 + ignore_if_contains: + - xxxxxx + - your_api_key + min_entropy: 3.0 + confidence: medium + examples: + - KUCOIN_API_KEY=4f4ecb6f11b1a70001c8e2ff + - 'kucoin_api_key: a1b2c3d4e5f60718293a4b5c' + references: + - https://www.kucoin.com/docs-new/authentication + - https://www.kucoin.com/docs-new/api-3470125 + + - name: KuCoin API Secret + id: kingfisher.kucoin.2 + pattern: | + (?xi) + \b + kucoin + (?:.|[\n\r]){0,32}? + (?: + api[_-]?secret | + secret + ) + (?:.|[\n\r]){0,12}? + ( + [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12} + ) + \b + pattern_requirements: + min_digits: 6 + min_lowercase: 8 + ignore_if_contains: + - 00000000-0000-0000-0000-000000000000 + - xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + min_entropy: 3.3 + confidence: medium + examples: + - KUCOIN_API_SECRET=7d70f6c7-42e9-4261-8a8d-8ca2d5028d4f + - 'kucoin_secret: a1b2c3d4-e5f6-7890-abcd-ef1234567890' + references: + - https://www.kucoin.com/docs-new/authentication + diff --git a/crates/kingfisher-rules/data/rules/openshift.yml b/crates/kingfisher-rules/data/rules/openshift.yml index 442c27b..63aa3c4 100644 --- a/crates/kingfisher-rules/data/rules/openshift.yml +++ b/crates/kingfisher-rules/data/rules/openshift.yml @@ -8,9 +8,9 @@ rules: (?:.|[\n\r]){0,128}? --server= | - \bopenshift\b + \bopenshift(?:[_-]?(?:server|api(?:[_-]?server)?|cluster(?:[_-]?url)?))?\b (?:.|[\n\r]){0,32}? - \b(?:server|api(?:[_-]?server)?|cluster(?:[_-]?url)?)\b + \b(?:server|api(?:[_-]?server)?|cluster(?:[_-]?url)?)?\b \s*[:=]\s* ) ["']? @@ -43,7 +43,7 @@ rules: ( sha256~[A-Za-z0-9_-]{43} ) - \b + (?:[^A-Za-z0-9_-]|$) pattern_requirements: min_digits: 3 min_uppercase: 1 diff --git a/crates/kingfisher-rules/data/rules/privateai.yml b/crates/kingfisher-rules/data/rules/privateai.yml index ce7d5d6..209fb7e 100644 --- a/crates/kingfisher-rules/data/rules/privateai.yml +++ b/crates/kingfisher-rules/data/rules/privateai.yml @@ -34,7 +34,7 @@ rules: confidence: medium examples: - PRIVATEAI_API_KEY=4fa2d7c81be9063d4ea8bc1f6d2a7e9c - - 'x-api-key: 2ab4d6e8f0c1a3b5d7e9f1a2b4c6d8e0' + - 'privateai_x_api_key: 2ab4d6e8f0c1a3b5d7e9f1a2b4c6d8e0' references: - https://docs.private-ai.com/fundamentals/getting-started - https://docs.private-ai.com/reference/4.0.0/operation/ner_text_ner_text_post/ diff --git a/crates/kingfisher-rules/data/rules/sidekiq.yml b/crates/kingfisher-rules/data/rules/sidekiq.yml index 9e0eb2b..0a982dc 100644 --- a/crates/kingfisher-rules/data/rules/sidekiq.yml +++ b/crates/kingfisher-rules/data/rules/sidekiq.yml @@ -22,8 +22,8 @@ rules: min_entropy: 2.8 confidence: medium examples: - - BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafebabe:deadbeef - - 'export BUNDLE_GEMS__CONTRIBSYS__COM="cafeb4b3:d3adb33f"' + - BUNDLE_ENTERPRISE__CONTRIBSYS__COM=cafe1234:dead5678 + - 'export BUNDLE_GEMS__CONTRIBSYS__COM="ca1eb4b3:d3ad533f"' - name: Sidekiq Sensitive URL id: kingfisher.sidekiq.2 @@ -54,5 +54,5 @@ rules: min_entropy: 2.8 confidence: medium examples: - - https://cafebabe:deadbeef@gems.contribsys.com/ - - http://cafeb4b3:d3adb33f@enterprise.contribsys.com:80/path?param1=true + - https://cafe1234:dead5678@gems.contribsys.com/ + - http://ca1eb4b3:d3ad533f@enterprise.contribsys.com:80/path?param1=true diff --git a/crates/kingfisher-rules/data/rules/trello.yml b/crates/kingfisher-rules/data/rules/trello.yml new file mode 100644 index 0000000..fbe8821 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/trello.yml @@ -0,0 +1,31 @@ +rules: + - name: Trello API Token + id: kingfisher.trello.1 + pattern: | + (?xi) + \b + trello + (?:.|[\n\r]){0,32}? + (?: + token | + api[_-]?token | + access[_-]?token + ) + (?:.|[\n\r]){0,12}? + ( + [A-Za-z0-9]{32} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 6 + ignore_if_contains: + - yourtoken + - placeholder + min_entropy: 3.1 + confidence: medium + examples: + - TRELLO_TOKEN=0a1b2c3d4e5f6g7h8i9j0k1l2m3n4p5q + - trello_access_token="Ab12Cd34Ef56Gh78Ij90Kl12Mn34Op56" + references: + - https://developer.atlassian.com/cloud/trello/guides/rest-api/api-introduction/