From d5f9d400272dc3ec96b336f97f0af3b4f6799bb2 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Thu, 26 Jun 2025 09:45:14 -0700 Subject: [PATCH 1/7] Ensuring temp files are cleaned up. Applying visual style to the update check output --- CHANGELOG.md | 4 ++ src/main.rs | 2 + src/update.rs | 48 +++++++++++++++++------ testdata/e2e/e2e_localgit.go | 76 ------------------------------------ tests/smoke_update.rs | 6 +-- 5 files changed, 44 insertions(+), 92 deletions(-) delete mode 100644 testdata/e2e/e2e_localgit.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ba1012..f72ec6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ 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 + ## [1.14.0] - Fixed several malformed rules - Now validating that response_matcher is present in validation section of all rules diff --git a/src/main.rs b/src/main.rs index 673a99b..7314aa2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -190,6 +190,8 @@ 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); + + temp_dir.close()?; std::process::exit(exit_code); } Command::Rules(ref rule_args) => match &rule_args.command { diff --git a/src/update.rs b/src/update.rs index 59cbea0..5648855 100644 --- a/src/update.rs +++ b/src/update.rs @@ -1,25 +1,32 @@ -use std::{fs, io::ErrorKind, path::PathBuf}; +use std::{ + fs, + io::{ErrorKind, IsTerminal}, + path::PathBuf, +}; use self_update::{backends::github::Update, cargo_crate_version, errors::Error as UpdError}; use tracing::{error, info, warn}; use crate::cli::global::GlobalArgs; +use crate::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), Apple‑Silicon macOS (/opt/homebrew/Cellar) /// and Linuxbrew (~/.linuxbrew/Cellar). fn installed_via_homebrew() -> bool { fn canonical_exe() -> Option { 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. /// /// * `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. +/// * Self‑update is performed unless the user disabled it **or** the binary is a Homebrew install. pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Option { if global_args.no_update_check { return None; @@ -27,11 +34,19 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt let is_brew = installed_via_homebrew(); if is_brew { - info!("Homebrew install detected – will notify about updates but not self-update"); + info!( + "Homebrew install detected – will notify about updates but not self‑update" + ); } info!("Checking for updates…"); + // ------------------------------------------------------------- + // Prepare colour/style helper so every message looks consistent + // ------------------------------------------------------------- + let use_color = std::io::stderr().is_terminal() && !global_args.quiet; + let styles = Styles::new(use_color); + let mut builder = Update::configure(); builder .repo_owner("mongodb") @@ -54,15 +69,22 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt return None; }; + // ---------------------------- + // Already on the latest version + // ---------------------------- if release.version == cargo_crate_version!() { - let msg = format!("Kingfisher {} is up to date", release.version); - info!("{msg}"); - return Some(msg); + let plain = format!("Kingfisher {} is up to date", release.version); + let styled = styles.style_finding_active_heading.apply_to(&plain); + info!("{}", styled); + return Some(plain); } - // There is a newer release. - let msg = format!("New Kingfisher release {} available", release.version); - info!("{msg}"); + // ---------------------------- + // A newer version is available + // ---------------------------- + let plain = format!("New Kingfisher release {} available", release.version); + let styled = styles.style_finding_active_heading.apply_to(&plain); + info!("{}", styled); // Decide whether to perform the update in place. if global_args.self_update && !is_brew { @@ -73,7 +95,7 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt 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." + Otherwise reinstall to a user‑writable directory or re‑run with sudo." ); } _ => error!("Failed to update: {e}"), @@ -83,5 +105,5 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt info!("Run `brew upgrade kingfisher` to install the new version."); } - Some(msg) + Some(plain) } diff --git a/testdata/e2e/e2e_localgit.go b/testdata/e2e/e2e_localgit.go deleted file mode 100644 index 8d78145..0000000 --- a/testdata/e2e/e2e_localgit.go +++ /dev/null @@ -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.") -} diff --git a/tests/smoke_update.rs b/tests/smoke_update.rs index 29ae49c..8224d42 100644 --- a/tests/smoke_update.rs +++ b/tests/smoke_update.rs @@ -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")); } From a6b64307bf841837e5a86e89c49e03a37f5007a1 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Thu, 26 Jun 2025 11:31:41 -0700 Subject: [PATCH 2/7] Updated formatting of several rules --- data/rules/adafruitio.yml | 2 +- data/rules/airbrake.yml | 4 ++-- data/rules/airtable.yml | 2 +- data/rules/aiven.yml | 3 +-- data/rules/algolia.yml | 6 ++---- data/rules/alibaba.yml | 6 ++---- data/rules/anthropic.yml | 3 +-- data/rules/anypoint.yml | 3 +-- data/rules/artifactory.yml | 7 +++---- data/rules/asana.yml | 9 +++------ data/rules/atlassian.yml | 3 +-- data/rules/auth0.yml | 9 +++------ data/rules/aws.yml | 7 +++---- data/rules/azure.yml | 10 +++++----- data/rules/azuresearchquery.yml | 2 +- data/rules/azurestorage.yml | 6 +++--- data/rules/baremetrics.yml | 3 +-- data/rules/beamer.yml | 2 +- data/rules/bitbucket.yml | 6 ++---- data/rules/blynk.yml | 30 ++++++++++++++-------------- data/rules/circleci.yml | 3 +-- data/rules/cloudflare.yml | 3 +-- data/rules/cloudsight.yml | 3 +-- data/rules/codacy.yml | 2 +- data/rules/codeclimate.yml | 2 +- data/rules/confluent.yml | 8 ++++---- data/rules/crates.io.yml | 8 +++++++- data/rules/credentials.yml | 3 +-- data/rules/databricks.yml | 5 ++--- data/rules/dependency_track.yml | 8 +++++++- data/rules/digitalocean.yml | 4 ++-- data/rules/discord.yml | 2 +- data/rules/django.yml | 2 +- data/rules/dockerhub.yml | 6 +++--- data/rules/doppler.yml | 24 +++++++++++----------- data/rules/easypost.yml | 5 +---- data/rules/facebook.yml | 9 ++++----- data/rules/fastly.yml | 2 +- data/rules/figma.yml | 6 +++--- data/rules/finicity.yml | 3 +-- data/rules/gcp.yml | 8 ++------ data/rules/generic.yml | 2 +- data/rules/github.yml | 21 +++++++++---------- data/rules/gitlab.yml | 13 ++++++------ data/rules/gocardless.yml | 6 +++--- data/rules/google.yml | 16 +++++++-------- data/rules/grafana.yml | 10 ++++++---- data/rules/hashes.yml | 16 +++++++-------- data/rules/jira.yml | 6 ++---- data/rules/jwt.yml | 8 ++++---- data/rules/line.yml | 5 ++--- data/rules/linear.yml | 5 ++--- data/rules/linkedin.yml | 4 ++-- data/rules/mailgun.yml | 8 +++----- data/rules/mandrill.yml | 5 ++--- data/rules/microsoftteamswebhook.yml | 12 +++++------ data/rules/mongodb.yml | 12 +++++------ data/rules/netlify.yml | 2 +- data/rules/netrc.yml | 2 +- data/rules/ngrok.yml | 2 +- data/rules/npm.yml | 4 ++-- data/rules/nuget.yml | 2 +- data/rules/odbc.yml | 2 +- data/rules/openai.yml | 4 ++-- data/rules/opsgenie.yml | 3 +-- data/rules/particle.io.yml | 10 +++++----- data/rules/pastebin.yml | 2 +- data/rules/pem.yml | 6 +++--- data/rules/planetscale.yml | 6 ++---- data/rules/pypi.yml | 6 +++--- data/rules/react.yml | 4 ++-- data/rules/slack.yml | 9 +++------ data/rules/stripe.yml | 4 ++-- data/rules/tailscale.yml | 5 ++--- data/rules/travisci.yml | 3 +-- data/rules/twilio.yml | 7 +++---- docs/RULES.md | 9 +++------ 77 files changed, 221 insertions(+), 259 deletions(-) diff --git a/data/rules/adafruitio.yml b/data/rules/adafruitio.yml index 17b271f..4982d86 100644 --- a/data/rules/adafruitio.yml +++ b/data/rules/adafruitio.yml @@ -6,7 +6,7 @@ rules: \b ( aio_ - [a-zA-Z0-9]{28} + [A-Z0-9]{28} ) \b min_entropy: 3.5 diff --git a/data/rules/airbrake.yml b/data/rules/airbrake.yml index 172a22c..9d55e24 100644 --- a/data/rules/airbrake.yml +++ b/data/rules/airbrake.yml @@ -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 diff --git a/data/rules/airtable.yml b/data/rules/airtable.yml index 60e7907..7b8b037 100644 --- a/data/rules/airtable.yml +++ b/data/rules/airtable.yml @@ -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 diff --git a/data/rules/aiven.yml b/data/rules/aiven.yml index 5410fab..0f4c727 100644 --- a/data/rules/aiven.yml +++ b/data/rules/aiven.yml @@ -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) diff --git a/data/rules/algolia.yml b/data/rules/algolia.yml index 679fc1e..dd65cf7 100644 --- a/data/rules/algolia.yml +++ b/data/rules/algolia.yml @@ -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 diff --git a/data/rules/alibaba.yml b/data/rules/alibaba.yml index 27ae47f..927d783 100644 --- a/data/rules/alibaba.yml +++ b/data/rules/alibaba.yml @@ -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}? diff --git a/data/rules/anthropic.yml b/data/rules/anthropic.yml index dcfff7d..0bf11fa 100644 --- a/data/rules/anthropic.yml +++ b/data/rules/anthropic.yml @@ -2,8 +2,7 @@ rules: - name: Anthropic API Key id: kingfisher.anthropic.1 pattern: | - (?x) - (?i) + (?xi) \b ( sk-ant-api diff --git a/data/rules/anypoint.yml b/data/rules/anypoint.yml index c3ae66b..4b3b8ab 100644 --- a/data/rules/anypoint.yml +++ b/data/rules/anypoint.yml @@ -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) diff --git a/data/rules/artifactory.yml b/data/rules/artifactory.yml index 1341b10..f53174e 100644 --- a/data/rules/artifactory.yml +++ b/data/rules/artifactory.yml @@ -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] diff --git a/data/rules/asana.yml b/data/rules/asana.yml index 1db0b54..0824711 100644 --- a/data/rules/asana.yml +++ b/data/rules/asana.yml @@ -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}? diff --git a/data/rules/atlassian.yml b/data/rules/atlassian.yml index 8a05db2..6d4ac9c 100644 --- a/data/rules/atlassian.yml +++ b/data/rules/atlassian.yml @@ -2,8 +2,7 @@ rules: - name: Atlassian API token id: kingfisher.atlassian.1 pattern: | - (?x) - (?i) + (?xi) \b atlassian (?:.|[\n\r]){0,32}? diff --git a/data/rules/auth0.yml b/data/rules/auth0.yml index 7ce0f07..39a0b43 100644 --- a/data/rules/auth0.yml +++ b/data/rules/auth0.yml @@ -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] diff --git a/data/rules/aws.yml b/data/rules/aws.yml index 284f2e0..b324dcc 100644 --- a/data/rules/aws.yml +++ b/data/rules/aws.yml @@ -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 ) diff --git a/data/rules/azure.yml b/data/rules/azure.yml index 949a8f3..277bea7 100644 --- a/data/rules/azure.yml +++ b/data/rules/azure.yml @@ -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* ["'] diff --git a/data/rules/azuresearchquery.yml b/data/rules/azuresearchquery.yml index 87272fb..45b84b3 100644 --- a/data/rules/azuresearchquery.yml +++ b/data/rules/azuresearchquery.yml @@ -37,7 +37,7 @@ rules: - name: Azure Search URL id: kingfisher.azuresearch.url.1 pattern: | - (?x) + (?xi) \b azure (?:.|[\n\r]){0,32}? diff --git a/data/rules/azurestorage.yml b/data/rules/azurestorage.yml index 696c80a..8f2921f 100644 --- a/data/rules/azurestorage.yml +++ b/data/rules/azurestorage.yml @@ -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 diff --git a/data/rules/baremetrics.yml b/data/rules/baremetrics.yml index 4262e08..415731e 100644 --- a/data/rules/baremetrics.yml +++ b/data/rules/baremetrics.yml @@ -2,8 +2,7 @@ rules: - name: Baremetrics API Key id: kingfisher.baremetrics.1 pattern: | - (?x) - (?i) + (?xi) \b baremetrics (?:.|[\n\r]){0,32}? diff --git a/data/rules/beamer.yml b/data/rules/beamer.yml index c1996bb..f051ff2 100644 --- a/data/rules/beamer.yml +++ b/data/rules/beamer.yml @@ -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 diff --git a/data/rules/bitbucket.yml b/data/rules/bitbucket.yml index 1727f25..ad7e74a 100644 --- a/data/rules/bitbucket.yml +++ b/data/rules/bitbucket.yml @@ -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}? diff --git a/data/rules/blynk.yml b/data/rules/blynk.yml index 1ac51be..4af12c6 100644 --- a/data/rules/blynk.yml +++ b/data/rules/blynk.yml @@ -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 diff --git a/data/rules/circleci.yml b/data/rules/circleci.yml index 729340f..f3f2d2a 100644 --- a/data/rules/circleci.yml +++ b/data/rules/circleci.yml @@ -2,8 +2,7 @@ rules: - name: CircleCI API Personal Access Token id: kingfisher.circleci.1 pattern: | - (?x) - (?i) + (?xi) \b ( CCIPAT_ diff --git a/data/rules/cloudflare.yml b/data/rules/cloudflare.yml index 07381d5..776c1bd 100644 --- a/data/rules/cloudflare.yml +++ b/data/rules/cloudflare.yml @@ -2,8 +2,7 @@ rules: - name: Cloudflare API Token id: kingfisher.cloudflare.1 pattern: | - (?x) - (?i) + (?xi) \b cloudflare (?:.|[\n\r]){0,32}? diff --git a/data/rules/cloudsight.yml b/data/rules/cloudsight.yml index 10d13f1..b06e922 100644 --- a/data/rules/cloudsight.yml +++ b/data/rules/cloudsight.yml @@ -2,8 +2,7 @@ rules: - name: CloudSight API Key id: kingfisher.cloudsight.1 pattern: | - (?x) - (?i) + (?xi) \b cloudsight (?:.|[\n\r]){0,32}? diff --git a/data/rules/codacy.yml b/data/rules/codacy.yml index 75d2411..121dbf4 100644 --- a/data/rules/codacy.yml +++ b/data/rules/codacy.yml @@ -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 diff --git a/data/rules/codeclimate.yml b/data/rules/codeclimate.yml index 7d207bc..677cbc5 100644 --- a/data/rules/codeclimate.yml +++ b/data/rules/codeclimate.yml @@ -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}? ( diff --git a/data/rules/confluent.yml b/data/rules/confluent.yml index 0fcc2c7..7deda55 100644 --- a/data/rules/confluent.yml +++ b/data/rules/confluent.yml @@ -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 diff --git a/data/rules/crates.io.yml b/data/rules/crates.io.yml index 6688704..7f6b8f2 100644 --- a/data/rules/crates.io.yml +++ b/data/rules/crates.io.yml @@ -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: diff --git a/data/rules/credentials.yml b/data/rules/credentials.yml index 19a53bd..38c1057 100644 --- a/data/rules/credentials.yml +++ b/data/rules/credentials.yml @@ -2,8 +2,7 @@ rules: - name: Credentials in a URL id: kingfisher.credentials.1 pattern: | - (?x) - (?i) + (?xi) https?:\/\/ ( [a-z0-9._~-]+ diff --git a/data/rules/databricks.yml b/data/rules/databricks.yml index a85f81b..9cec5c3 100644 --- a/data/rules/databricks.yml +++ b/data/rules/databricks.yml @@ -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-]+ diff --git a/data/rules/dependency_track.yml b/data/rules/dependency_track.yml index bce278d..e41aa32 100644 --- a/data/rules/dependency_track.yml +++ b/data/rules/dependency_track.yml @@ -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: diff --git a/data/rules/digitalocean.yml b/data/rules/digitalocean.yml index b02bb52..1ae7471 100644 --- a/data/rules/digitalocean.yml +++ b/data/rules/digitalocean.yml @@ -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_ diff --git a/data/rules/discord.yml b/data/rules/discord.yml index c2ed19f..6b8809b 100644 --- a/data/rules/discord.yml +++ b/data/rules/discord.yml @@ -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 diff --git a/data/rules/django.yml b/data/rules/django.yml index d332bc9..c335542 100644 --- a/data/rules/django.yml +++ b/data/rules/django.yml @@ -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 diff --git a/data/rules/dockerhub.yml b/data/rules/dockerhub.yml index 266efc8..e26d108 100644 --- a/data/rules/dockerhub.yml +++ b/data/rules/dockerhub.yml @@ -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: diff --git a/data/rules/doppler.yml b/data/rules/doppler.yml index 0afdba5..c8c302a 100644 --- a/data/rules/doppler.yml +++ b/data/rules/doppler.yml @@ -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 diff --git a/data/rules/easypost.yml b/data/rules/easypost.yml index 84361c8..757a70a 100644 --- a/data/rules/easypost.yml +++ b/data/rules/easypost.yml @@ -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: diff --git a/data/rules/facebook.yml b/data/rules/facebook.yml index 82075c4..d3ef582 100644 --- a/data/rules/facebook.yml +++ b/data/rules/facebook.yml @@ -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 diff --git a/data/rules/fastly.yml b/data/rules/fastly.yml index 875cc24..c5d4fcb 100644 --- a/data/rules/fastly.yml +++ b/data/rules/fastly.yml @@ -2,7 +2,7 @@ rules: - name: Fastly API token id: kingfisher.fastly.1 pattern: | - (?x)(?i) + (?xi) \b fastly (?:.|[\n\r]){0,32}? diff --git a/data/rules/figma.yml b/data/rules/figma.yml index 9863b6f..7cc6f00 100644 --- a/data/rules/figma.yml +++ b/data/rules/figma.yml @@ -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 diff --git a/data/rules/finicity.yml b/data/rules/finicity.yml index 20794cd..5273fbf 100644 --- a/data/rules/finicity.yml +++ b/data/rules/finicity.yml @@ -35,8 +35,7 @@ rules: - name: Finicity client secret id: kingfisher.finicity.2 pattern: | - (?x) - (?i) + (?xi) \b finicity (?:.|[\n\r]){0,64}? diff --git a/data/rules/gcp.yml b/data/rules/gcp.yml index 7c4052d..e23acb4 100644 --- a/data/rules/gcp.yml +++ b/data/rules/gcp.yml @@ -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 (?: diff --git a/data/rules/generic.yml b/data/rules/generic.yml index 330893b..9338797 100644 --- a/data/rules/generic.yml +++ b/data/rules/generic.yml @@ -87,7 +87,7 @@ rules: - name: Generic Password id: kingfisher.generic.5 pattern: | - (?x)(?i) + (?xi) password \b (?:.|[\n\r]){0,16}? diff --git a/data/rules/github.yml b/data/rules/github.yml index 3baca1d..3bcb58f 100644 --- a/data/rules/github.yml +++ b/data/rules/github.yml @@ -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,7 @@ rules: - '"login"' - name: GitHub App Token id: kingfisher.github.3 - pattern: '\b((?:ghu|ghs)_[a-zA-Z0-9]{36})\b' + pattern: '\b((?:ghu|ghs)_[A-Z0-9]{36})\b' examples: - ' "token": "ghu_16C7e42F292c69C2E7C10c838347Ae178B4a",' - | @@ -119,7 +118,7 @@ rules: - '"login"' - name: GitHub Refresh Token id: kingfisher.github.4 - pattern: '\b(ghr_[a-zA-Z0-9]{76})\b' + pattern: '\b(ghr_[A-Z0-9]{76})\b' examples: - ' "refresh_token": "ghr_1B4a2e77838347a7E420ce178F2E7c6912E169246c3CE1ccbF66C46812d16D5B1A9Dc86A1498",' references: @@ -147,7 +146,7 @@ rules: - name: GitHub Client ID id: kingfisher.github.5 pattern: | - (?x)(?i) + (?xi) (?:github) .? (?: api | app | application | client | consumer | customer )? @@ -162,7 +161,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 +176,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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' diff --git a/data/rules/gitlab.yml b/data/rules/gitlab.yml index c0c247c..336d29c 100644 --- a/data/rules/gitlab.yml +++ b/data/rules/gitlab.yml @@ -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,12 @@ 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: '\b(GR1348941[0-9A-Z_-]{20})(?:\b|$)' examples: - | sudo gitlab-runner register \ diff --git a/data/rules/gocardless.yml b/data/rules/gocardless.yml index 4f1cf74..e261372 100644 --- a/data/rules/gocardless.yml +++ b/data/rules/gocardless.yml @@ -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 diff --git a/data/rules/google.yml b/data/rules/google.yml index 1e60cf4..f617845 100644 --- a/data/rules/google.yml +++ b/data/rules/google.yml @@ -16,8 +16,8 @@ rules: pattern: | (?x) \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}) @@ -43,8 +43,8 @@ rules: pattern: | (?x) \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: @@ -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: diff --git a/data/rules/grafana.yml b/data/rules/grafana.yml index 8a3b859..4cb5461 100644 --- a/data/rules/grafana.yml +++ b/data/rules/grafana.yml @@ -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 diff --git a/data/rules/hashes.yml b/data/rules/hashes.yml index ab7cdeb..f952d36 100644 --- a/data/rules/hashes.yml +++ b/data/rules/hashes.yml @@ -1,7 +1,7 @@ rules: - name: Password Hash (md5crypt) id: kingfisher.pwhash.1 - pattern: '(\$1\$[./A-Za-z0-9]{8}\$[./A-Za-z0-9]{22})' + pattern: '(\$1\$[./A-Z0-9]{8}\$[./A-Z0-9]{22})' references: - https://en.wikipedia.org/wiki/Crypt_(C)#MD5-based_scheme - https://unix.stackexchange.com/a/511017 @@ -16,7 +16,7 @@ rules: id: kingfisher.pwhash.2 # Format from Wikipedia: # $2$[cost]$[22 character salt][31 character hash] - pattern: '(\$2[abxy]\$\d+\$[./A-Za-z0-9]{53})' + pattern: '(\$2[abxy]\$\d+\$[./A-Z0-9]{53})' references: - https://en.wikipedia.org/wiki/Bcrypt - https://hashcat.net/wiki/doku.php?id=example_hashes @@ -35,8 +35,8 @@ rules: ( \$ 5 (?: \$ rounds=\d+ )? - \$ [./A-Za-z0-9]{8,16} - \$ [./A-Za-z0-9]{43} + \$ [./A-Z0-9]{8,16} + \$ [./A-Z0-9]{43} ) references: - https://en.wikipedia.org/wiki/Crypt_(C)#Key_derivation_functions_supported_by_crypt @@ -55,8 +55,8 @@ rules: ( \$ 6 (?: \$ rounds=\d+ )? - \$ [./A-Za-z0-9]{8,16} - \$ [./A-Za-z0-9]{86} + \$ [./A-Z0-9]{8,16} + \$ [./A-Z0-9]{86} ) references: - https://en.wikipedia.org/wiki/Crypt_(C)#Key_derivation_functions_supported_by_crypt @@ -73,8 +73,8 @@ rules: (?x) ( \$ 8 - \$ [./A-Za-z0-9]{8,16} - \$ [./A-Za-z0-9]{43} + \$ [./A-Z0-9]{8,16} + \$ [./A-Z0-9]{43} ) references: - https://en.wikipedia.org/wiki/Crypt_(C)#Key_derivation_functions_supported_by_crypt diff --git a/data/rules/jira.yml b/data/rules/jira.yml index e106e77..c0b14a2 100644 --- a/data/rules/jira.yml +++ b/data/rules/jira.yml @@ -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}? diff --git a/data/rules/jwt.yml b/data/rules/jwt.yml index 3d360a1..cd3f78d 100644 --- a/data/rules/jwt.yml +++ b/data/rules/jwt.yml @@ -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: diff --git a/data/rules/line.yml b/data/rules/line.yml index 3dd453a..c3b43dd 100644 --- a/data/rules/line.yml +++ b/data/rules/line.yml @@ -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 diff --git a/data/rules/linear.yml b/data/rules/linear.yml index 62749ea..fdaa30d 100644 --- a/data/rules/linear.yml +++ b/data/rules/linear.yml @@ -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 diff --git a/data/rules/linkedin.yml b/data/rules/linkedin.yml index 97b4865..7663c28 100644 --- a/data/rules/linkedin.yml +++ b/data/rules/linkedin.yml @@ -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 ) diff --git a/data/rules/mailgun.yml b/data/rules/mailgun.yml index 4bac90e..c17c40b 100644 --- a/data/rules/mailgun.yml +++ b/data/rules/mailgun.yml @@ -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 diff --git a/data/rules/mandrill.yml b/data/rules/mandrill.yml index 4f0228c..4abd63d 100644 --- a/data/rules/mandrill.yml +++ b/data/rules/mandrill.yml @@ -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 diff --git a/data/rules/microsoftteamswebhook.yml b/data/rules/microsoftteamswebhook.yml index 3fc349c..82fbb19 100644 --- a/data/rules/microsoftteamswebhook.yml +++ b/data/rules/microsoftteamswebhook.yml @@ -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: diff --git a/data/rules/mongodb.yml b/data/rules/mongodb.yml index 507f028..63e4775 100644 --- a/data/rules/mongodb.yml +++ b/data/rules/mongodb.yml @@ -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 diff --git a/data/rules/netlify.yml b/data/rules/netlify.yml index f7736a3..eaf8399 100644 --- a/data/rules/netlify.yml +++ b/data/rules/netlify.yml @@ -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 diff --git a/data/rules/netrc.yml b/data/rules/netrc.yml index 35ed66e..6b6be12 100644 --- a/data/rules/netrc.yml +++ b/data/rules/netrc.yml @@ -2,7 +2,7 @@ rules: - name: netrc Credentials id: kingfisher.netrc.1 pattern: | - (?x) + (?xi) ( machine \s+ [^\s]+ | default ) \s+ login \s+ ([^\s]+) diff --git a/data/rules/ngrok.yml b/data/rules/ngrok.yml index 3635d08..56f1b62 100644 --- a/data/rules/ngrok.yml +++ b/data/rules/ngrok.yml @@ -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) diff --git a/data/rules/npm.yml b/data/rules/npm.yml index af94dfe..5d2e8c8 100644 --- a/data/rules/npm.yml +++ b/data/rules/npm.yml @@ -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: diff --git a/data/rules/nuget.yml b/data/rules/nuget.yml index 8d39cce..663a415 100644 --- a/data/rules/nuget.yml +++ b/data/rules/nuget.yml @@ -2,7 +2,7 @@ rules: - name: NuGet API Key id: kingfisher.nuget.1 pattern: | - (?x) + (?xi) \b ( oy2[a-z0-9]{43} diff --git a/data/rules/odbc.yml b/data/rules/odbc.yml index ffbfb4a..df536ea 100644 --- a/data/rules/odbc.yml +++ b/data/rules/odbc.yml @@ -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* (?: [;] | $) diff --git a/data/rules/openai.yml b/data/rules/openai.yml index 27c4b35..ef78a3f 100644 --- a/data/rules/openai.yml +++ b/data/rules/openai.yml @@ -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 diff --git a/data/rules/opsgenie.yml b/data/rules/opsgenie.yml index fafef06..012f296 100644 --- a/data/rules/opsgenie.yml +++ b/data/rules/opsgenie.yml @@ -2,8 +2,7 @@ rules: - name: OpsGenie API Key id: kingfisher.opsgenie.1 pattern: | - (?x) - (?i) + (?xi) \b opsgenie (?:.|[\\n\r]){0,32}? diff --git a/data/rules/particle.io.yml b/data/rules/particle.io.yml index 76d6f27..c4fdfaf 100644 --- a/data/rules/particle.io.yml +++ b/data/rules/particle.io.yml @@ -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 diff --git a/data/rules/pastebin.yml b/data/rules/pastebin.yml index 2173766..1ae302e 100644 --- a/data/rules/pastebin.yml +++ b/data/rules/pastebin.yml @@ -10,7 +10,7 @@ rules: (?:.|[\n\r]){0,32}? \b ( - [a-zA-Z0-9_]{32} + [A-Z0-9_]{32} ) \b min_entropy: 3.5 diff --git a/data/rules/pem.yml b/data/rules/pem.yml index fb51a06..23e0144 100644 --- a/data/rules/pem.yml +++ b/data/rules/pem.yml @@ -5,7 +5,7 @@ rules: (?x) -----BEGIN\ .{0,20}\ ?PRIVATE\ KEY\ ?.{0,20}----- \s* - ( (?: [a-zA-Z0-9+/=\s"',] | \\r | \\n ) {50,} ) + ( (?: [A-Z0-9+/=\s"',] | \\r | \\n ) {50,} ) \s* -----END\ .{0,20}\ ?PRIVATE\ KEY\ ?.{0,20}----- min_entropy: 4.5 @@ -57,9 +57,9 @@ rules: | LS0tLS1CRUdJTiBEU0EgUFJJVkFURSBLRVktLS0t (?# prefix of base64 encoding of `-----BEGIN DSA PRIVATE KEY-----` ) | LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0t (?# prefix of base64 encoding of `-----BEGIN EC PRIVATE KEY-----` ) ) - [a-zA-Z0-9+/=]{50,} + [A-Z0-9+/=]{50,} ) - (?: [^a-zA-Z0-9+/=] | $ ) + (?: [^A-Z0-9+/=] | $ ) min_entropy: 4.5 confidence: high prevalidated: false diff --git a/data/rules/planetscale.yml b/data/rules/planetscale.yml index 119957c..ecbee4f 100644 --- a/data/rules/planetscale.yml +++ b/data/rules/planetscale.yml @@ -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) diff --git a/data/rules/pypi.yml b/data/rules/pypi.yml index d0be047..85884ed 100644 --- a/data/rules/pypi.yml +++ b/data/rules/pypi.yml @@ -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: diff --git a/data/rules/react.yml b/data/rules/react.yml index bcd0bb4..c7d5a40 100644 --- a/data/rules/react.yml +++ b/data/rules/react.yml @@ -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* diff --git a/data/rules/slack.yml b/data/rules/slack.yml index 0abfd9d..9f3db01 100644 --- a/data/rules/slack.yml +++ b/data/rules/slack.yml @@ -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/ diff --git a/data/rules/stripe.yml b/data/rules/stripe.yml index 0cb2072..f127777 100644 --- a/data/rules/stripe.yml +++ b/data/rules/stripe.yml @@ -9,7 +9,7 @@ rules: (?:.|[\n\r]){0,32}? ( pk_live_ - (?:[0-9A-Za-z]{6}){4,30} + (?:[0-9A-Z]{6}){4,30} ) min_entropy: 3.3 confidence: medium @@ -30,7 +30,7 @@ rules: (?: sk|rk )_live_ - (?:[0-9A-Za-z]{8}){3,25} + (?:[0-9A-Z]{8}){3,25} ) min_entropy: 3.3 confidence: medium diff --git a/data/rules/tailscale.yml b/data/rules/tailscale.yml index b06f16b..f393da7 100644 --- a/data/rules/tailscale.yml +++ b/data/rules/tailscale.yml @@ -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 diff --git a/data/rules/travisci.yml b/data/rules/travisci.yml index dd0bd05..e23817e 100644 --- a/data/rules/travisci.yml +++ b/data/rules/travisci.yml @@ -2,8 +2,7 @@ rules: - name: Travis CI Token id: kingfisher.travisci.1 pattern: | - (?x) - (?i) + (?xi) \b travis (?:.|[\\n\r]){0,16}? diff --git a/data/rules/twilio.yml b/data/rules/twilio.yml index c5fb308..b3b2d32 100644 --- a/data/rules/twilio.yml +++ b/data/rules/twilio.yml @@ -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}? diff --git a/docs/RULES.md b/docs/RULES.md index 392156a..7b0650d 100644 --- a/docs/RULES.md +++ b/docs/RULES.md @@ -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 From 17acf2dccb35f310ff4d2e63a259b782f4ab32d7 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Thu, 26 Jun 2025 11:35:36 -0700 Subject: [PATCH 3/7] Updated formatting of several rules --- data/rules/gitlab.yml | 9 +++++++-- src/main.rs | 2 +- src/update.rs | 11 +++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/data/rules/gitlab.yml b/data/rules/gitlab.yml index 336d29c..e616a93 100644 --- a/data/rules/gitlab.yml +++ b/data/rules/gitlab.yml @@ -50,6 +50,8 @@ rules: --run-untagged="true" \ --locked="false" \ --access-level="not_protected" + references: + - https://docs.gitlab.com/api/runners/ validation: type: Http content: @@ -62,7 +64,7 @@ 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 @@ -76,6 +78,8 @@ rules: --no-progress-meter \ -F token=glptt-0d66598d696a02da33fb65e2a041f607c68ea50d \ -F ref=main + references: + - https://docs.gitlab.com/api/pipeline_triggers/ validation: type: Http content: @@ -86,5 +90,6 @@ rules: response_matcher: - report_response: true - type: StatusMatch - status: [200] + status: + - 200 url: https://gitlab.com/api/v4/ci/pipeline_triggers/{{ TOKEN }} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 7314aa2..5be8672 100644 --- a/src/main.rs +++ b/src/main.rs @@ -190,7 +190,7 @@ 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); - + temp_dir.close()?; std::process::exit(exit_code); } diff --git a/src/update.rs b/src/update.rs index 5648855..8bfc11a 100644 --- a/src/update.rs +++ b/src/update.rs @@ -7,8 +7,7 @@ use std::{ use self_update::{backends::github::Update, cargo_crate_version, errors::Error as UpdError}; use tracing::{error, info, warn}; -use crate::cli::global::GlobalArgs; -use crate::reporter::styles::Styles; +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) @@ -18,9 +17,7 @@ fn installed_via_homebrew() -> bool { 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. @@ -34,9 +31,7 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt let is_brew = installed_via_homebrew(); if is_brew { - info!( - "Homebrew install detected – will notify about updates but not self‑update" - ); + info!("Homebrew install detected – will notify about updates but not self‑update"); } info!("Checking for updates…"); From 37cdf1fb6944054bb03360bcb30ec00c3778783c Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Thu, 26 Jun 2025 14:29:36 -0700 Subject: [PATCH 4/7] Improved Updater text. Cleaned up more rules and the examples included with them. --- CHANGELOG.md | 2 + Cargo.toml | 2 +- data/rules/age.yml | 16 ++++- data/rules/fileio.yml | 2 +- data/rules/github.yml | 16 ++++- data/rules/gitlab.yml | 16 ++++- data/rules/google.yml | 6 +- data/rules/hashes.yml | 18 ++--- data/rules/paypal.yml | 8 +-- data/rules/pem.yml | 6 +- data/rules/postman.yml | 2 +- data/rules/privkey.yml | 4 +- data/rules/stripe.yml | 9 +-- data/rules/travisci.yml | 4 +- src/update.rs | 141 ++++++++++++++++++++++++++++++---------- 15 files changed, 183 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f72ec6d..ff2c109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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 diff --git a/Cargo.toml b/Cargo.toml index 7ced54e..78c7574 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/data/rules/age.yml b/data/rules/age.yml index b85e4a1..f296a26 100644 --- a/data/rules/age.yml +++ b/data/rules/age.yml @@ -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: diff --git a/data/rules/fileio.yml b/data/rules/fileio.yml index 6bdd8dd..7bf255e 100644 --- a/data/rules/fileio.yml +++ b/data/rules/fileio.yml @@ -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: diff --git a/data/rules/github.yml b/data/rules/github.yml index 3bcb58f..90c9c3b 100644 --- a/data/rules/github.yml +++ b/data/rules/github.yml @@ -88,7 +88,13 @@ rules: - '"login"' - name: GitHub App Token id: kingfisher.github.3 - pattern: '\b((?:ghu|ghs)_[A-Z0-9]{36})\b' + pattern: | + (?xi) + \b + ( + (?:ghu|ghs)_[A-Z0-9]{36} + ) + \b examples: - ' "token": "ghu_16C7e42F292c69C2E7C10c838347Ae178B4a",' - | @@ -118,7 +124,13 @@ rules: - '"login"' - name: GitHub Refresh Token id: kingfisher.github.4 - pattern: '\b(ghr_[A-Z0-9]{76})\b' + pattern: | + (?xi) + \b + ( + ghr_[A-Z0-9]{76} + ) + \b examples: - ' "refresh_token": "ghr_1B4a2e77838347a7E420ce178F2E7c6912E169246c3CE1ccbF66C46812d16D5B1A9Dc86A1498",' references: diff --git a/data/rules/gitlab.yml b/data/rules/gitlab.yml index e616a93..d7c3ca2 100644 --- a/data/rules/gitlab.yml +++ b/data/rules/gitlab.yml @@ -36,7 +36,13 @@ rules: - name: GitLab Runner Registration Token id: kingfisher.gitlab.2 - pattern: '\b(GR1348941[0-9A-Z_-]{20})(?:\b|$)' + pattern: | + (?xi) + \b + ( + GR1348941[0-9A-Z_-]{20} + ) + \b examples: - | sudo gitlab-runner register \ @@ -69,7 +75,13 @@ rules: - 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 \ diff --git a/data/rules/google.yml b/data/rules/google.yml index f617845..812a903 100644 --- a/data/rules/google.yml +++ b/data/rules/google.yml @@ -14,7 +14,7 @@ rules: - name: Google OAuth Client Secret id: kingfisher.google.2 pattern: | - (?x) + (?xi) \b (GOCSPX-[A-Z0-9_-]{28}) (?:[^A-Z0-9_-] | $) @@ -41,7 +41,7 @@ rules: - name: Google OAuth Access Token id: kingfisher.google.4 pattern: | - (?x) + (?xi) \b (ya29\.[0-9A-Z_-]{20,1024}) (?: [^0-9A-Z_-]|$) @@ -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) (?: diff --git a/data/rules/hashes.yml b/data/rules/hashes.yml index f952d36..60b0f4a 100644 --- a/data/rules/hashes.yml +++ b/data/rules/hashes.yml @@ -1,7 +1,7 @@ rules: - name: Password Hash (md5crypt) id: kingfisher.pwhash.1 - pattern: '(\$1\$[./A-Z0-9]{8}\$[./A-Z0-9]{22})' + pattern: '(\$1\$[./A-Za-z0-9]{8}\$[./A-Za-z0-9]{22})' references: - https://en.wikipedia.org/wiki/Crypt_(C)#MD5-based_scheme - https://unix.stackexchange.com/a/511017 @@ -16,7 +16,7 @@ rules: id: kingfisher.pwhash.2 # Format from Wikipedia: # $2$[cost]$[22 character salt][31 character hash] - pattern: '(\$2[abxy]\$\d+\$[./A-Z0-9]{53})' + pattern: '(\$2[abxy]\$\d+\$[./A-Za-z0-9]{53})' references: - https://en.wikipedia.org/wiki/Bcrypt - https://hashcat.net/wiki/doku.php?id=example_hashes @@ -35,8 +35,8 @@ rules: ( \$ 5 (?: \$ rounds=\d+ )? - \$ [./A-Z0-9]{8,16} - \$ [./A-Z0-9]{43} + \$ [./A-Za-z0-9]{8,16} + \$ [./A-Za-z0-9]{43} ) references: - https://en.wikipedia.org/wiki/Crypt_(C)#Key_derivation_functions_supported_by_crypt @@ -55,8 +55,8 @@ rules: ( \$ 6 (?: \$ rounds=\d+ )? - \$ [./A-Z0-9]{8,16} - \$ [./A-Z0-9]{86} + \$ [./A-Za-z0-9]{8,16} + \$ [./A-Za-z0-9]{86} ) references: - https://en.wikipedia.org/wiki/Crypt_(C)#Key_derivation_functions_supported_by_crypt @@ -73,8 +73,8 @@ rules: (?x) ( \$ 8 - \$ [./A-Z0-9]{8,16} - \$ [./A-Z0-9]{43} + \$ [./A-Za-z0-9]{8,16} + \$ [./A-Za-z0-9]{43} ) references: - https://en.wikipedia.org/wiki/Crypt_(C)#Key_derivation_functions_supported_by_crypt @@ -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' \ No newline at end of file diff --git a/data/rules/paypal.yml b/data/rules/paypal.yml index 4011eee..ef65d2d 100644 --- a/data/rules/paypal.yml +++ b/data/rules/paypal.yml @@ -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 diff --git a/data/rules/pem.yml b/data/rules/pem.yml index 23e0144..fb51a06 100644 --- a/data/rules/pem.yml +++ b/data/rules/pem.yml @@ -5,7 +5,7 @@ rules: (?x) -----BEGIN\ .{0,20}\ ?PRIVATE\ KEY\ ?.{0,20}----- \s* - ( (?: [A-Z0-9+/=\s"',] | \\r | \\n ) {50,} ) + ( (?: [a-zA-Z0-9+/=\s"',] | \\r | \\n ) {50,} ) \s* -----END\ .{0,20}\ ?PRIVATE\ KEY\ ?.{0,20}----- min_entropy: 4.5 @@ -57,9 +57,9 @@ rules: | LS0tLS1CRUdJTiBEU0EgUFJJVkFURSBLRVktLS0t (?# prefix of base64 encoding of `-----BEGIN DSA PRIVATE KEY-----` ) | LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0t (?# prefix of base64 encoding of `-----BEGIN EC PRIVATE KEY-----` ) ) - [A-Z0-9+/=]{50,} + [a-zA-Z0-9+/=]{50,} ) - (?: [^A-Z0-9+/=] | $ ) + (?: [^a-zA-Z0-9+/=] | $ ) min_entropy: 4.5 confidence: high prevalidated: false diff --git a/data/rules/postman.yml b/data/rules/postman.yml index 707b4e2..ae7ae7c 100644 --- a/data/rules/postman.yml +++ b/data/rules/postman.yml @@ -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} diff --git a/data/rules/privkey.yml b/data/rules/privkey.yml index 9815384..6a5fe32 100644 --- a/data/rules/privkey.yml +++ b/data/rules/privkey.yml @@ -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 diff --git a/data/rules/stripe.yml b/data/rules/stripe.yml index f127777..faaa1ab 100644 --- a/data/rules/stripe.yml +++ b/data/rules/stripe.yml @@ -5,8 +5,9 @@ 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-Z]{6}){4,30} @@ -21,11 +22,11 @@ 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 diff --git a/data/rules/travisci.yml b/data/rules/travisci.yml index e23817e..3461fbb 100644 --- a/data/rules/travisci.yml +++ b/data/rules/travisci.yml @@ -10,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: diff --git a/src/update.rs b/src/update.rs index 8bfc11a..165120c 100644 --- a/src/update.rs +++ b/src/update.rs @@ -1,3 +1,20 @@ +// This module checks GitHub for a newer Kingfisher release and (optionally) +// self‑updates. Our release assets use short, user‑friendly 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 self‑update. +// +// 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}, @@ -5,9 +22,13 @@ use std::{ }; 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, reporter::styles::Styles}; +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) @@ -17,31 +38,38 @@ fn installed_via_homebrew() -> bool { 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 self‑update. /// /// * `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. +/// * Self‑update 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 { if global_args.no_update_check { return None; } - let is_brew = installed_via_homebrew(); - if is_brew { - info!("Homebrew install detected – will notify about updates but not self‑update"); - } - - info!("Checking for updates…"); - - // ------------------------------------------------------------- - // Prepare colour/style helper so every message looks consistent - // ------------------------------------------------------------- + // 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!( + "{}", + styles + .style_finding_active_heading + .apply_to("Homebrew install detected – will notify about updates but not self‑update") + ); + } + + info!( + "{}","Checking for updates…"); + let mut builder = Update::configure(); builder .repo_owner("mongodb") @@ -50,54 +78,101 @@ 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; }; - // ---------------------------- - // Already on the latest version - // ---------------------------- - if release.version == cargo_crate_version!() { - let plain = format!("Kingfisher {} is up to date", release.version); - let styled = styles.style_finding_active_heading.apply_to(&plain); - info!("{}", styled); + 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); } - // ---------------------------- - // A newer version is available - // ---------------------------- - let plain = format!("New Kingfisher release {} available", release.version); - let styled = styles.style_finding_active_heading.apply_to(&plain); - info!("{}", styled); + // Try semantic version comparison. If parsing fails, fall back to the + // self‑update code‑path (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 self‑update 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 user‑writable directory or re‑run 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(plain) From 579828c5201f5057c1f0073590f7c8e6c80fecd0 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Thu, 26 Jun 2025 14:55:35 -0700 Subject: [PATCH 5/7] Improved Updater text. Cleaned up more rules and the examples included with them. --- data/rules/pubnub.yml | 51 +++++++++++++++++++++++++++++++++++++++++ data/rules/pulumi.yml | 30 ++++++++++++++++++++++++ data/rules/readme.yml | 30 ++++++++++++++++++++++++ data/rules/rubygems.yml | 36 +++++++++++++++++++++++++++++ src/update.rs | 2 +- 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 data/rules/pubnub.yml create mode 100644 data/rules/pulumi.yml create mode 100644 data/rules/readme.yml create mode 100644 data/rules/rubygems.yml diff --git a/data/rules/pubnub.yml b/data/rules/pubnub.yml new file mode 100644 index 0000000..f29310a --- /dev/null +++ b/data/rules/pubnub.yml @@ -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 \ No newline at end of file diff --git a/data/rules/pulumi.yml b/data/rules/pulumi.yml new file mode 100644 index 0000000..2298d1d --- /dev/null +++ b/data/rules/pulumi.yml @@ -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" \ No newline at end of file diff --git a/data/rules/readme.yml b/data/rules/readme.yml new file mode 100644 index 0000000..0e415ec --- /dev/null +++ b/data/rules/readme.yml @@ -0,0 +1,30 @@ +rules: + - name: ReadMe API Key + id: kingfisher.readme.1 + pattern: | + (?x)(?i) + \b + ( + rdme_(?P[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 \ No newline at end of file diff --git a/data/rules/rubygems.yml b/data/rules/rubygems.yml new file mode 100644 index 0000000..93ace54 --- /dev/null +++ b/data/rules/rubygems.yml @@ -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 \ No newline at end of file diff --git a/src/update.rs b/src/update.rs index 165120c..4758ab1 100644 --- a/src/update.rs +++ b/src/update.rs @@ -171,7 +171,7 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt "{}", styles .style_finding_active_heading - .apply_to("Run `brew upgrade kingfisher` to install the new version.") + .apply_to("Run `brew upgrade kingfisher` to install the new version.") ); } From 3add34be788a3155da6ef103657586e9656d3043 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Thu, 26 Jun 2025 15:37:51 -0700 Subject: [PATCH 6/7] Added rules for readme, rubygems, salesforce, segment, snyk, pulumi, pubnub --- data/rules/salesforce.yml | 63 ++++++++++++++++++++++++++++++++++++++ data/rules/segment.yml | 62 +++++++++++++++++++++++++++++++++++++ data/rules/snyk.yml | 34 ++++++++++++++++++++ tests/smoke_check_rules.rs | 23 ++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 data/rules/salesforce.yml create mode 100644 data/rules/segment.yml create mode 100644 data/rules/snyk.yml create mode 100644 tests/smoke_check_rules.rs diff --git a/data/rules/salesforce.yml b/data/rules/salesforce.yml new file mode 100644 index 0000000..d532346 --- /dev/null +++ b/data/rules/salesforce.yml @@ -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 \ No newline at end of file diff --git a/data/rules/segment.yml b/data/rules/segment.yml new file mode 100644 index 0000000..1a2518f --- /dev/null +++ b/data/rules/segment.yml @@ -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/ \ No newline at end of file diff --git a/data/rules/snyk.yml b/data/rules/snyk.yml new file mode 100644 index 0000000..dfadaaa --- /dev/null +++ b/data/rules/snyk.yml @@ -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 \ No newline at end of file diff --git a/tests/smoke_check_rules.rs b/tests/smoke_check_rules.rs new file mode 100644 index 0000000..d220285 --- /dev/null +++ b/tests/smoke_check_rules.rs @@ -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(()) +} From abeac9cde9b7ba4dfb4de8f84f166b9111e929de Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Thu, 26 Jun 2025 15:46:14 -0700 Subject: [PATCH 7/7] Fixed Snyk rule --- data/rules/snyk.yml | 14 +++++++------- src/main.rs | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/data/rules/snyk.yml b/data/rules/snyk.yml index dfadaaa..e6237e3 100644 --- a/data/rules/snyk.yml +++ b/data/rules/snyk.yml @@ -25,10 +25,10 @@ rules: headers: Authorization: "Bearer {{ TOKEN }}" Accept: application/json - response_matcher: - - report_response: true - - type: StatusMatch - status: [200] - - type: WordMatch - words: ['"username"'] - match_all_words: true \ No newline at end of file + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: ['"username"'] + match_all_words: true \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5be8672..0a1e07b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -191,7 +191,9 @@ async fn async_main(args: CommandLineArgs) -> Result<()> { run_scan(&args.global_args, &scan_args, &rules_db, Arc::clone(&datastore)).await?; let exit_code = determine_exit_code(&datastore); - temp_dir.close()?; + 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 {