From 26b5b7918fa87cc88dc0ae75ba365fba62863704 Mon Sep 17 00:00:00 2001
From: Mick Grove
Date: Sat, 28 Jun 2025 08:34:15 -0700
Subject: [PATCH] Added ruels for sonarcloud, sonarqube, sourcegraph
---
README.md | 4 +-
data/rules/netlify.yml | 8 ++--
data/rules/sonarcloud.yml | 35 ++++++++++++++++
data/rules/sonarqube.yml | 58 ++++++++++++++++++++++++++
data/rules/sourcegraph.yml | 85 ++++++++++++++++++++++++++++++++++++++
src/matcher.rs | 2 -
6 files changed, 183 insertions(+), 9 deletions(-)
create mode 100644 data/rules/sonarcloud.yml
create mode 100644 data/rules/sonarqube.yml
create mode 100644 data/rules/sourcegraph.yml
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,
pub matching_input_offset_span: OffsetSpan,
pub captures: SerializableCaptures,
pub validation_response_body: String,