diff --git a/README.md b/README.md index 3af9c20..8f7af32 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ Kingfisher is a blazingly fast secret‑scanning and validation tool built in Ru
-Kingfisher originated as a fork of **[Nosey Parker](https://github.com/praetorian-inc/noseyparker)** by Praetorian Security, Inc, and is built atop the incredible work contributed by the Nosey Parker community. +Kingfisher originated as a fork of **[Nosey Parker](https://github.com/praetorian-inc/noseyparker)** by Praetorian Security, Inc, and is built atop their incredible work and the work contributed by the Nosey Parker community. -- **MongoDB Blog**: [Introducing Kingfisher: Real-Time Secret Detection and Validation](https://www.mongodb.com/blog/post/product-release-announcements/introducing-kingfisher-real-time-secret-detection-validation) +**MongoDB Blog**: [Introducing Kingfisher: Real-Time Secret Detection and Validation](https://www.mongodb.com/blog/post/product-release-announcements/introducing-kingfisher-real-time-secret-detection-validation) ## Key Features diff --git a/data/rules/netlify.yml b/data/rules/netlify.yml index eaf8399..0a29d25 100644 --- a/data/rules/netlify.yml +++ b/data/rules/netlify.yml @@ -14,8 +14,6 @@ rules: examples: - netlify_token=3cdfad7b885a6daceff3fb820389115750b373763fb30b10ca0382648b55872d - netlify_secret=7a9ef2c84d6b3e5f1c8a0b9d2e4f6a8c7b3d5e9f2a1c8b4d6e3f5a9c7b2d8e4 - references: - - https://howtorotate.com/docs/tutorials/netlify/ validation: type: Http content: @@ -39,15 +37,15 @@ rules: (?:SECRET|PRIVATE|ACCESS|KEY|TOKEN) (?:.|[\n\r]){0,32}? \b - ([A-Z0-9_-]{43,45}) + ( + [A-Z0-9_-]{43,45} + ) \b min_entropy: 3.5 confidence: medium examples: - netlify_token=G5yT54abRasekrOpe7SaArsowiuHTeR45sfEhsH-K1L2 - netlify_key=H7xZ98cdWbsemqNpv8UaXtsnyjKgVeQ34rsDkpM-N5P6 - references: - - https://howtorotate.com/docs/tutorials/netlify/ validation: type: Http content: diff --git a/data/rules/sonarcloud.yml b/data/rules/sonarcloud.yml new file mode 100644 index 0000000..f0b874d --- /dev/null +++ b/data/rules/sonarcloud.yml @@ -0,0 +1,35 @@ +rules: + - name: SonarCloud API Token + id: kingfisher.sonarcloud.1 + pattern: | + (?xi) + \b + sonar + (?:.|[\n\r]){0,32}? + (?:SECRET|PRIVATE|ACCESS|KEY|TOKEN) + (?:.|[\n\r]){0,32}? + \b + ( + [0-9a-z]{40} + ) + \b + min_entropy: 2.5 + examples: + - sonar_api_token=abcdef0123456789abcdef0123456789abcdef23 + validation: + type: Http + content: + request: + headers: + Authorization: "Basic {{ TOKEN | append: ':' | b64enc }}" + Accept: application/json + method: GET + url: https://sonarcloud.io/api/user_tokens/search + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + match_all_words: true + words: + - '"tokens":' diff --git a/data/rules/sonarqube.yml b/data/rules/sonarqube.yml new file mode 100644 index 0000000..ceddbee --- /dev/null +++ b/data/rules/sonarqube.yml @@ -0,0 +1,58 @@ +rules: + - name: SonarQube API Key + id: kingfisher.sonarqube.1 + pattern: | + (?xi) + \b + ( + (?:sq[pua]) + _[a-z0-9]{40} + ) + min_entropy: 3.5 + examples: + - sonar.login=sqp_4b78f8494075e310d62dfdcaeb14be2c78fca2fc + - sonar.login=squ_4b78f8494075e310d62dfdcaeb14be2c78fca2fc + validation: + type: Http + content: + request: + headers: + Authorization: "Basic {{ TOKEN | append: ':' | b64enc }}" + method: GET + response_matcher: + - report_response: true + - status: + - 200 + type: StatusMatch + url: '{{ SONARHOST }}/api/user_tokens/search' + depends_on_rule: + - rule_id: kingfisher.sonarqube.2 + variable: SONARHOST + + - name: SonarQube Host + id: kingfisher.sonarqube.2 + pattern: | + (?xi) + sonar.{0,8}host + (?:.|[\n\r]){0,64}? + \b + ( + https?://.*?:\d{2,6} + ) + \b + min_entropy: 3.5 + visible: false + examples: + - sonar.host=https://sonar.internal.company.com:9000 + + - name: SonarQube Token + id: kingfisher.sonarqube.3 + pattern: '(?i)sonar.{0,5}login.{0,5}\s*\b([a-f0-9]{40})\b' + min_entropy: 3.3 + confidence: medium + examples: + - 'sonar.host.url=https://sonarcloud.io -Dsonar.login=5524bf449ca45fcace54698371466398321f3a82' + - "sonar.login', '826de5590c75919a8317fdface58206eebe7ebbc" + - '$sonarLogin = "4924be8f51f3e738c97db2c4ace51db7e938f28b"' + references: + - https://docs.sonarqube.org/latest/user-guide/user-token/ \ No newline at end of file diff --git a/data/rules/sourcegraph.yml b/data/rules/sourcegraph.yml new file mode 100644 index 0000000..965d99a --- /dev/null +++ b/data/rules/sourcegraph.yml @@ -0,0 +1,85 @@ +rules: + - name: Sourcegraph Access Token + id: kingfisher.sourcegraph.1 + pattern: | + (?xi) + \b + sgp_(?:[a-f0-9]{16}_local_)?[a-f0-9]{40} + \b + min_entropy: 3.3 + examples: + - sgp_210f1131b08e93adcfc3f05faa2d768ff883a61f + validation: + type: Http + content: + request: + method: POST + url: https://sourcegraph.com/.api/graphql + headers: + Authorization: "token {{ TOKEN }}" + Content-Type: application/json + body: | + { "query": "query ValidateToken { site { id } }" } + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: ['"site":{'] + match_all_words: true + + - name: Sourcegraph _Legacy_ API Key + id: kingfisher.sourcegraph.2 + pattern: | + (?xi) + \b + (?:sgp_(?:[a-f0-9]{16}_local_)?[a-f0-9]{40}|[a-f0-9]{40}) + \b + min_entropy: 3.5 + confidence: medium + examples: + - sgp_abcdef1234567890_local_abcdef12345678901234567890abcdef12345678 + validation: + type: Http + content: + request: + method: POST + url: https://sourcegraph.com/.api/graphql + headers: + Authorization: "token {{ TOKEN }}" + Content-Type: application/json + body: | + { "query": "query ValidateToken { site { id } }" } + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: ['"site":{'] + + - name: Sourcegraph Cody Gateway Key + id: kingfisher.sourcegraph.3 + pattern: | + (?xi) + \b + slk_[a-f0-9]{64} + \b + min_entropy: 3.5 + confidence: medium + examples: + - slk_27b0a1f1926e7376dd8bdfcb0ade3c397c462b6e68c854a5521a17dd2b704ce6 + validation: + type: Http + content: + request: + method: GET + url: https://cody-gateway.sourcegraph.com/v1/limits + headers: + Authorization: "Bearer {{ TOKEN }}" + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: ['"token"', '"limit"'] + match_all_words: true diff --git a/src/matcher.rs b/src/matcher.rs index d7ae76d..d90c5ef 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -1,6 +1,5 @@ use std::{ borrow::Cow, - // collections::{HashMap, HashSet}, hash::{Hash, Hasher}, io::Write, str, @@ -60,7 +59,6 @@ pub struct OwnedBlobMatch { pub blob_id: BlobId, /// The unique content-based identifier of this match pub finding_fingerprint: u64, - // pub matching_input: Vec