From cd4f6265027c8598edf573bdde3b1bccbba89d28 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Tue, 8 Jul 2025 17:49:12 -0700 Subject: [PATCH] Added support for HTTP request bodies in rule validation. Added mistral and perplexity rule --- CHANGELOG.md | 6 +++++ Cargo.toml | 2 +- README.md | 13 +++++++++- data/rules/alibaba.yml | 37 ++++++++++++++++++++++++++-- data/rules/mistral.yml | 41 ++++++++++++++++++++++++++++++++ data/rules/perplexity.yml | 37 ++++++++++++++++++++++++++++ src/matcher.rs | 1 + src/rules/rule.rs | 2 ++ src/update.rs | 1 + src/validation.rs | 1 + src/validation/httpvalidation.rs | 24 ++++++++++++++++++- 11 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 data/rules/mistral.yml create mode 100644 data/rules/perplexity.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index dde30d3..a4c8196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. + +## [1.20.0] +- Removed confirmation prompt when user provides --self-update flag +- Added support for HTTP request bodies in rule validation +- Added rules for mistral, perplexity + ## [1.19.0] - JSON output was missing committer name and email - Fixed Gitlab rule which was incorrectly identifying certain tokens as valid diff --git a/Cargo.toml b/Cargo.toml index 779f8a9..19c6f95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ publish = false [package] name = "kingfisher" -version = "1.19.0" +version = "1.20.0" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/README.md b/README.md index 8e45179..80e2224 100644 --- a/README.md +++ b/README.md @@ -230,7 +230,18 @@ _If no token is provided Kingfisher still works for public repositories._ ### Update Checks -Kingfisher checks for newer releases on GitHub each time it starts and exits, printing whether a new version is available. Use `--self-update` to automatically download and replace the binary when an update is found. Add `--no-update-check` to disable these checks entirely. +Kingfisher automatically queries GitHub for a newer release when it starts and tells you whether an update is available. + +- **Hands-free updates** – Add `--self-update` to any Kingfisher command + + If a newer version exists, Kingfisher will download it, replace the running + binary, and re-launch itself with the **exact same arguments**. + + If the update fails or no newer release is found, the current run + proceeds as normal + +- **Disable version checks** – Pass `--no-update-check` to skip both the + startup and shutdown checks entirely --- diff --git a/data/rules/alibaba.yml b/data/rules/alibaba.yml index 927d783..3c2c4aa 100644 --- a/data/rules/alibaba.yml +++ b/data/rules/alibaba.yml @@ -19,7 +19,7 @@ rules: (?xi) \b alibaba - (?:.|[\n\r]){0,16}? + (?:.|[\n\r]){0,32}? \b ( [a-z0-9]{30} @@ -30,4 +30,37 @@ rules: examples: - alibaba_secret = 7jkWdTjKLnSlGddwPR5gBn65PHcZG6 - alibaba-token = aJHKLnSlGddwPR5g7jkWdTBn65PHc5 - \ No newline at end of file + validation: + type: Http + content: + request: + method: GET + url: > + {% assign nonce = "" | uuid | url_encode -%} + {% assign ts = "" | iso_timestamp | url_encode -%} + {% capture qs -%} + AccessKeyId={{ AKID }}& + Action=GetCallerIdentity& + Format=JSON& + SignatureMethod=HMAC-SHA1& + SignatureNonce={{ nonce }}& + SignatureVersion=1.0& + Timestamp={{ ts }}& + Version=2015-04-01 + {%- endcapture %} + {% capture sts -%}GET&%2F&{{ qs | url_encode }}{%- endcapture %} + {% assign key = TOKEN | append: '&' -%} + {% assign sig = sts | hmac_sha256: key | b64enc | url_encode -%} + https://sts.aliyuncs.com/?{{ qs }}&Signature={{ sig }} + headers: + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: ['"Arn"'] + match_all_words: true + depends_on_rule: + - rule_id: kingfisher.alibabacloud.1 + variable: AKID \ No newline at end of file diff --git a/data/rules/mistral.yml b/data/rules/mistral.yml new file mode 100644 index 0000000..07b1af8 --- /dev/null +++ b/data/rules/mistral.yml @@ -0,0 +1,41 @@ +rules: + - name: Mistral AI API Key + id: kingfisher.mistral.1 + pattern: | + (?xi) + \b + mistral + (?:.|[\n\r]){0,32}? + (?:SECRET|PRIVATE|ACCESS|KEY|TOKEN) + (?:.|[\n\r]){0,32}? + \b + ( + [A-Z0-9]{32} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - mistral_token = 47cFZMzkoEo9DBapfvhrmMst3zfV2EIh + - mistral_api_key = olI6zlb3F0jO8jjDQUi34skQLPzvox84 + - 'mistral secret: kMrbwHjG78fYzneT934Pn34NQxVSxniq' + references: + - https://docs.mistral.ai/getting-started/quickstart :contentReference[oaicite:1]{index=1} + - https://docs.mistral.ai/api/ :contentReference[oaicite:2]{index=2} + - https://medium.com/@stephane.giron/explore-mistral-ai-api-with-google-apps-script-d41b851c55e3 :contentReference[oaicite:3]{index=3} + - https://apidog.com/blog/mistral-ai-api/ :contentReference[oaicite:4]{index=4} + validation: + type: Http + content: + request: + method: GET + url: https://api.mistral.ai/v1/models + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: ['"data"'] diff --git a/data/rules/perplexity.yml b/data/rules/perplexity.yml new file mode 100644 index 0000000..5b1beae --- /dev/null +++ b/data/rules/perplexity.yml @@ -0,0 +1,37 @@ +rules: + - name: Perplexity AI API Key + id: kingfisher.perplexity.1 + pattern: | + (?xi) + \b + ( + pplx-[A-Za-z0-9]{48} + ) + \b + min_entropy: 3.8 + confidence: medium + examples: + - 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 + - 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 + - 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 + - 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 + references: + - https://docs.perplexity.ai/guides/getting-started + - https://pplx.readme.io/reference/post_chat_completions + - https://www.perplexity.ai/hub/blog/introducing-pplx-api + - https://docs.litellm.ai/docs/providers/perplexity + - https://developers.cloudflare.com/ai-gateway/providers/perplexity/ + validation: + type: Http + content: + request: + method: POST + url: https://api.perplexity.ai/chat/completions + headers: + Authorization: "Bearer {{ TOKEN }}" + Content-Type: application/json + body: '{"model": "kingfisher", "messages": [{ "role": "user", "content": "." }]}' + response_matcher: + - report_response: false + - type: WordMatch + match_all_words: false \ No newline at end of file diff --git a/src/matcher.rs b/src/matcher.rs index d90c5ef..33e2425 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -979,6 +979,7 @@ mod test { method: "GET".to_string(), url: "https://example.com".to_string(), headers: BTreeMap::new(), + body: None, response_matcher: Some(vec![]), multipart: None, response_is_html: false, diff --git a/src/rules/rule.rs b/src/rules/rule.rs index 83bb844..bf923f3 100644 --- a/src/rules/rule.rs +++ b/src/rules/rule.rs @@ -65,6 +65,8 @@ pub struct HttpRequest { #[serde(default)] pub headers: BTreeMap, #[serde(default)] + pub body: Option, + #[serde(default)] pub response_matcher: Option>, #[serde(default)] pub multipart: Option, diff --git a/src/update.rs b/src/update.rs index bd0f212..9914d8c 100644 --- a/src/update.rs +++ b/src/update.rs @@ -69,6 +69,7 @@ pub fn check_for_update(global_args: &GlobalArgs, base_url: Option<&str>) -> Opt .repo_name("kingfisher") .bin_name("kingfisher") .show_download_progress(false) + .no_confirm(true) // Don't prompt for confirmation when self‑updating .current_version(cargo_crate_version!()); // Allow tests to point at a mock HTTP server. diff --git a/src/validation.rs b/src/validation.rs index 4a056f3..44a7a6f 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -353,6 +353,7 @@ async fn timed_validate_single_match<'a>( &http_validation.request.method, &url, &http_validation.request.headers, + &http_validation.request.body, parser, &globals, ) { diff --git a/src/validation/httpvalidation.rs b/src/validation/httpvalidation.rs index 03fc10f..2633ff1 100644 --- a/src/validation/httpvalidation.rs +++ b/src/validation/httpvalidation.rs @@ -63,6 +63,7 @@ pub fn build_request_builder( method_str: &str, url: &Url, headers: &BTreeMap, + body: &Option, parser: &liquid::Parser, globals: &liquid::Object, ) -> Result { @@ -99,6 +100,19 @@ pub fn build_request_builder( } } request_builder = request_builder.headers(combined_headers); + + // If a body template is provided, parse and render it + if let Some(body_template) = body { + let template = parser + .parse(body_template) + .map_err(|e| format!("Error parsing body template: {}", e))?; + let rendered_body = template + .render(globals) + .map_err(|e| format!("Error rendering body template: {}", e))?; + request_builder = request_builder.body(rendered_body); + } + + Ok(request_builder) } @@ -427,7 +441,15 @@ mod tests { let headers = BTreeMap::from([("Content-Type".to_string(), "application/json".to_string())]); let url = Url::from_str("https://example.com").unwrap(); - let result = build_request_builder(&client, "GET", &url, &headers, &parser, &globals); + let result = build_request_builder( + &client, + "GET", + &url, + &headers, + &None, + &parser, + &globals, + ); assert!(result.is_ok()); } #[tokio::test]