From c7f7adb223857b0d87327197696c070a1f8f7a49 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Tue, 14 Apr 2026 12:52:27 -0700 Subject: [PATCH] added more rules + validators --- CHANGELOG.md | 7 +- THREAT_MODEL_PROMPT.md | 45 +++++++++++ .../data/rules/adafruitio.yml | 2 +- crates/kingfisher-rules/data/rules/aikido.yml | 34 +++++++++ crates/kingfisher-rules/data/rules/akamai.yml | 2 +- .../kingfisher-rules/data/rules/anthropic.yml | 2 +- crates/kingfisher-rules/data/rules/arcjet.yml | 19 +++++ crates/kingfisher-rules/data/rules/axiom.yml | 61 +++++++++++++++ .../data/rules/browseruse.yml | 40 ++++++++++ crates/kingfisher-rules/data/rules/calcom.yml | 34 +++++++++ crates/kingfisher-rules/data/rules/canva.yml | 2 +- .../kingfisher-rules/data/rules/cloudant.yml | 2 +- .../data/rules/cloudflare.yml | 30 ++++++++ .../kingfisher-rules/data/rules/composio.yml | 48 ++++++++++++ crates/kingfisher-rules/data/rules/convex.yml | 16 ++++ .../kingfisher-rules/data/rules/courier.yml | 33 ++++++++ .../kingfisher-rules/data/rules/crossmint.yml | 55 ++++++++++++++ crates/kingfisher-rules/data/rules/deepl.yml | 66 ++++++++++++++++ crates/kingfisher-rules/data/rules/dub.yml | 33 ++++++++ crates/kingfisher-rules/data/rules/e2b.yml | 34 +++++++++ .../kingfisher-rules/data/rules/facebook.yml | 13 ++++ crates/kingfisher-rules/data/rules/falai.yml | 18 +++++ .../kingfisher-rules/data/rules/flagsmith.yml | 35 +++++++++ crates/kingfisher-rules/data/rules/gitlab.yml | 20 +++++ .../data/rules/guardsquare.yml | 16 ++++ .../kingfisher-rules/data/rules/hackclub.yml | 16 ++++ crates/kingfisher-rules/data/rules/hexpm.yml | 58 ++++++++++++++ crates/kingfisher-rules/data/rules/hop.yml | 28 +++++++ .../kingfisher-rules/data/rules/infisical.yml | 16 ++++ .../kingfisher-rules/data/rules/inngest.yml | 34 +++++++++ .../data/rules/liveblocks.yml | 33 ++++++++ .../kingfisher-rules/data/rules/mappedin.yml | 32 ++++++++ crates/kingfisher-rules/data/rules/mastra.yml | 37 +++++++++ crates/kingfisher-rules/data/rules/mem0.yml | 33 ++++++++ .../kingfisher-rules/data/rules/minimax.yml | 30 ++++++++ .../kingfisher-rules/data/rules/mintlify.yml | 19 +++++ .../kingfisher-rules/data/rules/netlify.yml | 30 ++++++++ .../kingfisher-rules/data/rules/nightfall.yml | 31 ++++++++ crates/kingfisher-rules/data/rules/ory.yml | 74 ++++++++++++++++++ crates/kingfisher-rules/data/rules/pirsch.yml | 23 ++++++ .../data/rules/pollinations.yml | 36 +++++++++ .../kingfisher-rules/data/rules/posthog.yml | 14 ++++ crates/kingfisher-rules/data/rules/ramp.yml | 32 ++++++++ .../data/rules/redirectpizza.yml | 37 +++++++++ crates/kingfisher-rules/data/rules/seam.yml | 31 ++++++++ .../kingfisher-rules/data/rules/supabase.yml | 16 ++++ crates/kingfisher-rules/data/rules/svix.yml | 17 +++++ .../kingfisher-rules/data/rules/tinybird.yml | 39 ++++++++++ crates/kingfisher-rules/data/rules/tolgee.yml | 61 +++++++++++++++ .../data/rules/triggerdev.yml | 71 +++++++++++++++++ crates/kingfisher-rules/data/rules/unkey.yml | 16 ++++ .../kingfisher-rules/data/rules/upstash.yml | 76 +++++++++++++++++++ .../data/rules/warpstream.yml | 32 ++++++++ .../data/rules/woocommerce.yml | 33 ++++++++ crates/kingfisher-rules/data/rules/workos.yml | 45 +++++++++++ crates/kingfisher-rules/data/rules/xata.yml | 33 ++++++++ crates/kingfisher-rules/data/rules/xendit.yml | 31 ++++++++ docs-site/docs/changelog.md | 2 +- 58 files changed, 1773 insertions(+), 10 deletions(-) create mode 100644 THREAT_MODEL_PROMPT.md create mode 100644 crates/kingfisher-rules/data/rules/aikido.yml create mode 100644 crates/kingfisher-rules/data/rules/arcjet.yml create mode 100644 crates/kingfisher-rules/data/rules/axiom.yml create mode 100644 crates/kingfisher-rules/data/rules/browseruse.yml create mode 100644 crates/kingfisher-rules/data/rules/calcom.yml create mode 100644 crates/kingfisher-rules/data/rules/composio.yml create mode 100644 crates/kingfisher-rules/data/rules/convex.yml create mode 100644 crates/kingfisher-rules/data/rules/courier.yml create mode 100644 crates/kingfisher-rules/data/rules/crossmint.yml create mode 100644 crates/kingfisher-rules/data/rules/deepl.yml create mode 100644 crates/kingfisher-rules/data/rules/dub.yml create mode 100644 crates/kingfisher-rules/data/rules/e2b.yml create mode 100644 crates/kingfisher-rules/data/rules/falai.yml create mode 100644 crates/kingfisher-rules/data/rules/flagsmith.yml create mode 100644 crates/kingfisher-rules/data/rules/guardsquare.yml create mode 100644 crates/kingfisher-rules/data/rules/hackclub.yml create mode 100644 crates/kingfisher-rules/data/rules/hexpm.yml create mode 100644 crates/kingfisher-rules/data/rules/infisical.yml create mode 100644 crates/kingfisher-rules/data/rules/inngest.yml create mode 100644 crates/kingfisher-rules/data/rules/liveblocks.yml create mode 100644 crates/kingfisher-rules/data/rules/mappedin.yml create mode 100644 crates/kingfisher-rules/data/rules/mastra.yml create mode 100644 crates/kingfisher-rules/data/rules/mem0.yml create mode 100644 crates/kingfisher-rules/data/rules/minimax.yml create mode 100644 crates/kingfisher-rules/data/rules/mintlify.yml create mode 100644 crates/kingfisher-rules/data/rules/nightfall.yml create mode 100644 crates/kingfisher-rules/data/rules/ory.yml create mode 100644 crates/kingfisher-rules/data/rules/pirsch.yml create mode 100644 crates/kingfisher-rules/data/rules/pollinations.yml create mode 100644 crates/kingfisher-rules/data/rules/ramp.yml create mode 100644 crates/kingfisher-rules/data/rules/redirectpizza.yml create mode 100644 crates/kingfisher-rules/data/rules/seam.yml create mode 100644 crates/kingfisher-rules/data/rules/svix.yml create mode 100644 crates/kingfisher-rules/data/rules/tinybird.yml create mode 100644 crates/kingfisher-rules/data/rules/tolgee.yml create mode 100644 crates/kingfisher-rules/data/rules/triggerdev.yml create mode 100644 crates/kingfisher-rules/data/rules/upstash.yml create mode 100644 crates/kingfisher-rules/data/rules/warpstream.yml create mode 100644 crates/kingfisher-rules/data/rules/woocommerce.yml create mode 100644 crates/kingfisher-rules/data/rules/workos.yml create mode 100644 crates/kingfisher-rules/data/rules/xata.yml create mode 100644 crates/kingfisher-rules/data/rules/xendit.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index a01d831..1032b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,10 @@ All notable changes to this project will be documented in this file. -## [v1.97.0] -- Added live HTTP validation for 12 rules across 10 providers: Val Town, Polar, hCaptcha, Thunderstore, Elastic Cloud (2 rules), LlamaCloud, Gemfury (2 rules), Vonage, ThingsBoard, and Zapier. -- Added revocation support for 7 rules across 6 providers: Discord webhooks (single-step DELETE), DigitalOcean PATs (self-revoke via OAuth), and multi-step HttpMultiStep revocation for LaunchDarkly, Resend, Linode, and Netlify (2 rules). Built-in revocation coverage is now 34 provider families with 53 revocation-enabled rules. - ## [v1.96.0] +- Added live HTTP validation for 18 rules across 15 providers: Val Town, Polar, hCaptcha, Thunderstore, Elastic Cloud (2 rules), LlamaCloud, Gemfury (2 rules), Vonage, ThingsBoard, Zapier, Facebook Access Token, GitLab Session Cookie, PostHog Feature Flags, Unkey API Key, and Hop.io (2 rules). +- Added revocation support for 7 rules across 6 providers: Discord webhooks (single-step DELETE), DigitalOcean PATs (self-revoke via OAuth), and multi-step HttpMultiStep revocation for LaunchDarkly, Resend, Linode, and Netlify (2 rules). Built-in revocation coverage is now 34 provider families with 53 revocation-enabled rules. +- Added 59 new detection rules across 44 providers: Axiom (API token + PAT), Trigger.dev (secret key + PAT), Dub.co, Svix webhook signing secret, Liveblocks, Inngest (signing key + event key), Seam, Courier, Cal.com, Arcjet, WarpStream, Mem0, Mintlify, Pirsch, Tinybird, Tolgee (project key + PAT), Ory (API key + session + OAuth2 tokens), Xendit, Xata, Crossmint (server + client keys), DeepL (Free + Pro), Flagsmith, E2B, Infisical, WooCommerce (consumer key + secret), Nightfall AI, Ramp (client ID + secret), Hex.pm (personal + workspace tokens), Convex deploy key, MiniMax, Mappedin (key + secret), Pollinations (secret + publishable), Fal.ai, Aikido, Hack Club, GuardSquare, Browser Use, Composio, Mastra, redirect.pizza, Upstash, and WorkOS. Also added new prefixed-token rules for Netlify (`nfp_`), Cloudflare (`cfut_`), and Supabase (`sb_publishable_`). Added live HTTP validation for 28 of these rules. - Removed 17 direct dependencies from the root crate by dropping unused deps (`p256`, `ed25519-dalek`, `jsonwebtoken`, `gitlab`, `lazy_static`, `base32`, `pem`, `byteorder`, `reqwest-middleware`, `sha1`, `time`, `ring`, `num_cpus`, `strum_macros`), replacing `once_cell` with `std::sync::{LazyLock, OnceLock}`, and using `std::thread::available_parallelism()` in place of `num_cpus`. Salt generation now uses `rand` instead of `ring`, and all `strum_macros::Display` imports are consolidated under `strum::Display`. ## [v1.95.0] diff --git a/THREAT_MODEL_PROMPT.md b/THREAT_MODEL_PROMPT.md new file mode 100644 index 0000000..5896a15 --- /dev/null +++ b/THREAT_MODEL_PROMPT.md @@ -0,0 +1,45 @@ +# Threat Model Prompt (STRIDE) + +Perform a practical STRIDE threat model of this system. + +**Principles:** +- Only realistic, relevant risks. No generic boilerplate. +- Depth over breadth. Be concrete and opinionated. +- Skip STRIDE categories that aren't meaningfully relevant — say so briefly and move on. + +## Step 1: Architecture Summary + +Summarize the system in short bullets: +- Purpose +- Main components +- Data flows +- Trust boundaries +- Sensitive assets + +## Step 2: STRIDE Analysis + +For each relevant threat: +- **STRIDE category** +- **Threat scenario** — specific, not generic +- **Impacted asset / component** +- **Why it matters** — not just "it's bad", but the concrete consequence +- **Severity:** Low / Medium / High / Critical +- **Recommended mitigations** + +Focus especially on: +- Authn / authz failures +- Trust boundary crossings +- Secret handling +- Multi-tenant isolation +- Injection / unsafe input handling +- Data exfiltration paths +- Abuse / privilege escalation +- Logging / detection gaps +- Supply chain and execution risks + +## Step 3: Prioritized Output + +End with: +1. **Top 5 risks to fix first** — ordered by impact, with brief rationale +2. **Assumptions made** — what you assumed about the deployment, environment, or usage +3. **Open questions** — things that, if answered differently, would change the threat model diff --git a/crates/kingfisher-rules/data/rules/adafruitio.yml b/crates/kingfisher-rules/data/rules/adafruitio.yml index 7bbed0e..99a2cbe 100644 --- a/crates/kingfisher-rules/data/rules/adafruitio.yml +++ b/crates/kingfisher-rules/data/rules/adafruitio.yml @@ -12,7 +12,7 @@ rules: ) \b min_entropy: 3.5 - confidence: high + confidence: medium examples: - ADAFRUIT_AIO_KEY = "aio_giXk31KzM05IVxHRwJwtpNGClUE5" validation: diff --git a/crates/kingfisher-rules/data/rules/aikido.yml b/crates/kingfisher-rules/data/rules/aikido.yml new file mode 100644 index 0000000..e1eb35a --- /dev/null +++ b/crates/kingfisher-rules/data/rules/aikido.yml @@ -0,0 +1,34 @@ +rules: + - name: Aikido CI Token + id: kingfisher.aikido.1 + pattern: | + (?x) + \b + ( + AIK_CI_[a-zA-Z0-9]{20,44} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'AIKIDO_TOKEN=AIK_CI_a1B2c3D4e5F6g7H8i9J0k1L2' + references: + - https://help.aikido.dev/pr-and-release-gating/cli-for-pr-and-release-gating/aikido-ci-api + validation: + type: Http + content: + request: + method: POST + url: https://app.aikido.dev/api/integrations/ci/scan/start + headers: + X-AIK-API-SECRET: "{{ TOKEN }}" + Content-Type: application/json + body: '{}' + response_matcher: + - report_response: true + - type: StatusMatch + status: [200, 400, 403] + - type: WordMatch + words: + - '"Unauthorized"' + negative: true diff --git a/crates/kingfisher-rules/data/rules/akamai.yml b/crates/kingfisher-rules/data/rules/akamai.yml index 573072c..4a60fc4 100644 --- a/crates/kingfisher-rules/data/rules/akamai.yml +++ b/crates/kingfisher-rules/data/rules/akamai.yml @@ -14,7 +14,7 @@ rules: pattern_requirements: min_digits: 2 min_entropy: 3.0 - confidence: high + confidence: medium visible: false examples: - "client-token=akab-sXedJBTOf0dHl27vVOd" diff --git a/crates/kingfisher-rules/data/rules/anthropic.yml b/crates/kingfisher-rules/data/rules/anthropic.yml index 1a2784a..d87c149 100644 --- a/crates/kingfisher-rules/data/rules/anthropic.yml +++ b/crates/kingfisher-rules/data/rules/anthropic.yml @@ -15,7 +15,7 @@ rules: min_uppercase: 1 min_lowercase: 1 min_entropy: 3.3 - confidence: high + confidence: medium examples: - sk-ant-api668-Clm512odot9WDD7itfUU9R880nefA1EtYZDbpE-C9b0XQEWpqFKf9DQUo03vOfXl16oSmyar1CLF1SzV3YzpZJ6bahcpLAA references: diff --git a/crates/kingfisher-rules/data/rules/arcjet.yml b/crates/kingfisher-rules/data/rules/arcjet.yml new file mode 100644 index 0000000..1709b66 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/arcjet.yml @@ -0,0 +1,19 @@ +rules: + - name: Arcjet API Key + id: kingfisher.arcjet.1 + pattern: | + (?x) + \b + ( + ajkey_[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'ARCJET_KEY=ajkey_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'ARCJET_KEY="ajkey_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h"' + references: + - https://docs.arcjet.com/get-started diff --git a/crates/kingfisher-rules/data/rules/axiom.yml b/crates/kingfisher-rules/data/rules/axiom.yml new file mode 100644 index 0000000..0c06458 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/axiom.yml @@ -0,0 +1,61 @@ +rules: + - name: Axiom API Token + id: kingfisher.axiom.1 + pattern: | + (?x) + \b + ( + xaat-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12} + ) + \b + confidence: medium + examples: + - 'AXIOM_TOKEN=xaat-a1b2c3d4-e5f6-7890-abcd-ef1234567890' + - 'Authorization: Bearer xaat-deadbeef-1234-5678-9abc-def012345678' + references: + - https://axiom.co/docs/reference/tokens + - https://axiom.co/docs/restapi/endpoints/getDatasets + validation: + type: Http + content: + request: + method: GET + url: https://api.axiom.co/v2/datasets + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + + - name: Axiom Personal Access Token + id: kingfisher.axiom.2 + pattern: | + (?x) + \b + ( + xapt-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12} + ) + \b + confidence: medium + examples: + - 'AXIOM_TOKEN=xapt-a1b2c3d4-e5f6-7890-abcd-ef1234567890' + - 'Authorization: Bearer xapt-deadbeef-1234-5678-9abc-def012345678' + references: + - https://axiom.co/docs/reference/tokens + validation: + type: Http + content: + request: + method: GET + url: https://api.axiom.co/v2/datasets + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/browseruse.yml b/crates/kingfisher-rules/data/rules/browseruse.yml new file mode 100644 index 0000000..be13e63 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/browseruse.yml @@ -0,0 +1,40 @@ +rules: + - name: Browser Use API Key + id: kingfisher.browseruse.1 + pattern: | + (?xi) + \b + (?:browser[-_\s]?use|BROWSER_USE_API_KEY) + (?:.|[\n\r]){0,48}? + \b + ( + bu_[A-Za-z0-9_-]{32}(?:[A-Za-z0-9_-]{16}){0,4} + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 4 + min_uppercase: 4 + min_entropy: 3.5 + confidence: medium + examples: + - 'BROWSER_USE_API_KEY="bu_w82Kp7LzR4QnV6tYp9CmX3aSb5DgHjK1"' + - 'browser-use apiKey: "bu_P7rT2mK9vL4qN8sX6cA3dF5gH1jZ0QaB"' + references: + - https://docs.browser-use.com/cloud/quickstart + - https://docs.browser-use.com/llms-full.txt + validation: + type: Http + content: + request: + method: GET + url: https://api.browser-use.com/api/v3/sessions + headers: + X-Browser-Use-API-Key: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + # Revocation is managed from Browser Use Cloud settings; no public key-revocation endpoint is documented. diff --git a/crates/kingfisher-rules/data/rules/calcom.yml b/crates/kingfisher-rules/data/rules/calcom.yml new file mode 100644 index 0000000..502f82f --- /dev/null +++ b/crates/kingfisher-rules/data/rules/calcom.yml @@ -0,0 +1,34 @@ +rules: + - name: Cal.com API Key + id: kingfisher.calcom.1 + pattern: | + (?x) + \b + ( + cal_live_[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'CAL_API_KEY=cal_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'Authorization: Bearer cal_live_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + references: + - https://cal.com/docs/api-reference/v2/introduction + validation: + type: Http + content: + request: + method: GET + url: https://api.cal.com/v2/me + headers: + Authorization: "Bearer {{ TOKEN }}" + cal-api-version: "2024-08-13" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/canva.yml b/crates/kingfisher-rules/data/rules/canva.yml index 6986fba..1b41e42 100644 --- a/crates/kingfisher-rules/data/rules/canva.yml +++ b/crates/kingfisher-rules/data/rules/canva.yml @@ -11,7 +11,7 @@ rules: pattern_requirements: min_digits: 2 min_entropy: 3.5 - confidence: high + confidence: medium categories: [api, key] examples: - 'CANVA_CLIENT_SECRET=cnvcaAbCdEfGhIjKlMnOpQrStUvWxYz123456' diff --git a/crates/kingfisher-rules/data/rules/cloudant.yml b/crates/kingfisher-rules/data/rules/cloudant.yml index cc5b46f..0e43b62 100644 --- a/crates/kingfisher-rules/data/rules/cloudant.yml +++ b/crates/kingfisher-rules/data/rules/cloudant.yml @@ -20,7 +20,7 @@ rules: pattern_requirements: min_digits: 8 min_entropy: 3.5 - confidence: high + confidence: medium examples: - "new PouchDB(\"https://4f621480-c3c9-41c6-bb2e-38fd4cce291f-bluemix:9a1b75eae487515172acc27e1203bd19d094f359bebb0f20ff69bb173cee3d4a@4f621480-c3c9-41c6-bb2e-38fd4cce291f-bluemix.cloudantnosqldb.appdomain.cloud/mydb\")" - "CLOUDANT_URL=https://a1b2c3d4-e5f6-7890-abcd-ef1234567890-bluemix:abc123def456789012345678901234567890123456789012345678901234abcd@a1b2c3d4-e5f6-7890-abcd-ef1234567890-bluemix.cloudantnosqldb.appdomain.cloud" diff --git a/crates/kingfisher-rules/data/rules/cloudflare.yml b/crates/kingfisher-rules/data/rules/cloudflare.yml index 2458191..1ef2a1b 100644 --- a/crates/kingfisher-rules/data/rules/cloudflare.yml +++ b/crates/kingfisher-rules/data/rules/cloudflare.yml @@ -110,3 +110,33 @@ rules: - 200 type: StatusMatch url: https://api.cloudflare.com/client/v4/certificates?per_page=1 + + - name: Cloudflare User API Token (cfut_ prefix) + id: kingfisher.cloudflare.3 + pattern: | + (?x) + \b + ( + cfut_[a-zA-Z0-9_-]{36,44} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'CF_API_TOKEN=cfut_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9' + references: + - https://developers.cloudflare.com/fundamentals/api/get-started/token-formats/ + validation: + type: Http + content: + request: + method: GET + url: https://api.cloudflare.com/client/v4/user/tokens/verify + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/composio.yml b/crates/kingfisher-rules/data/rules/composio.yml new file mode 100644 index 0000000..af67b81 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/composio.yml @@ -0,0 +1,48 @@ +rules: + - name: Composio API Key + id: kingfisher.composio.1 + pattern: | + (?xi) + \b + (?:composio|COMPOSIO_API_KEY) + (?:.|[\n\r]){0,48}? + \b + ( + (?: + comp_[A-Za-z0-9_-]{32}(?:[A-Za-z0-9_-]{16}){0,3} + | + sk_(?:live|test)_[A-Za-z0-9_-]{16}(?:[A-Za-z0-9_-]{16}){0,3} + ) + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 4 + min_entropy: 3.5 + confidence: medium + examples: + - 'COMPOSIO_API_KEY="comp_a7B2c9D4eF6gH8jK1mN3pQ5rS7tV9xY0"' + - 'composio x-api-key: sk_live_Qj7mN4vK8sL2xP6zT9aBcD3eF5gH1jK2' + references: + - https://docs.composio.dev/reference/api-reference/authentication/getAuthSessionInfo + - https://composio.dev/content/secure-ai-agent-infrastructure-guide + validation: + type: Http + content: + request: + method: GET + url: https://backend.composio.dev/api/v3/auth/session/info + headers: + x-api-key: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + - type: WordMatch + words: + - '"project"' + - '"api_key"' + match_all_words: true + # Current Composio API docs expose session verification, but not a same-key revocation flow. diff --git a/crates/kingfisher-rules/data/rules/convex.yml b/crates/kingfisher-rules/data/rules/convex.yml new file mode 100644 index 0000000..d6d0b35 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/convex.yml @@ -0,0 +1,16 @@ +rules: + - name: Convex Deploy Key + id: kingfisher.convex.1 + pattern: | + (?x) + \b + ( + (?:prod|dev|preview):[a-z]+-[a-z]+-\d+\|eyJ[A-Za-z0-9_.-]{50,500} + ) + (?:\b|$) + min_entropy: 3.5 + confidence: medium + examples: + - 'CONVEX_DEPLOY_KEY=prod:qualified-jaguar-123|eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIwMThmNmY5NThmNTI3NjkwYjE1M2NkNDMiLCJpYXQiOjE3MjQ3NzA4MTJ9.bQkJz3sXt' + references: + - https://docs.convex.dev/production/hosting/deploy-key diff --git a/crates/kingfisher-rules/data/rules/courier.yml b/crates/kingfisher-rules/data/rules/courier.yml new file mode 100644 index 0000000..0087e32 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/courier.yml @@ -0,0 +1,33 @@ +rules: + - name: Courier API Key + id: kingfisher.courier.1 + pattern: | + (?x) + \b + ( + (?:pk|dk)_(?:prod|test)_[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'COURIER_API_KEY=pk_prod_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'COURIER_AUTH_TOKEN="dk_test_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h"' + references: + - https://www.courier.com/docs/platform/workspaces/environments-api-keys + validation: + type: Http + content: + request: + method: GET + url: https://api.courier.com/profiles?limit=1 + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/crossmint.yml b/crates/kingfisher-rules/data/rules/crossmint.yml new file mode 100644 index 0000000..175bb5d --- /dev/null +++ b/crates/kingfisher-rules/data/rules/crossmint.yml @@ -0,0 +1,55 @@ +rules: + - name: Crossmint Server API Key + id: kingfisher.crossmint.1 + pattern: | + (?x) + \b + ( + sk_(?:staging|production)_[A-Za-z0-9]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'CROSSMINT_SECRET_KEY=sk_production_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'x-api-key: sk_staging_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + references: + - https://docs.crossmint.com/introduction/platform/api-keys + validation: + type: Http + content: + request: + method: GET + url: https://www.crossmint.com/api/v1-alpha2/wallets + headers: + x-api-key: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200, 403] + - type: WordMatch + words: + - '"Unauthorized"' + negative: true + + - name: Crossmint Client API Key + id: kingfisher.crossmint.2 + pattern: | + (?x) + \b + ( + ck_(?:staging|production)_[A-Za-z0-9]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'CROSSMINT_CLIENT_KEY=ck_production_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'ck_staging_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + references: + - https://docs.crossmint.com/introduction/platform/api-keys diff --git a/crates/kingfisher-rules/data/rules/deepl.yml b/crates/kingfisher-rules/data/rules/deepl.yml new file mode 100644 index 0000000..5b352bf --- /dev/null +++ b/crates/kingfisher-rules/data/rules/deepl.yml @@ -0,0 +1,66 @@ +rules: + - name: DeepL API Key (Free) + id: kingfisher.deepl.1 + pattern: | + (?x) + \b + ( + [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}:fx + ) + \b + confidence: medium + examples: + - 'DEEPL_API_KEY=279a2e9d-83b3-c416-7e2d-f721593e42a0:fx' + - 'DeepL-Auth-Key 5ef4a0d1-7e1f-47b2-ac0a-1282002aa2a1:fx' + references: + - https://developers.deepl.com/docs/getting-started/auth + validation: + type: Http + content: + request: + method: GET + url: https://api-free.deepl.com/v2/usage + headers: + Authorization: "DeepL-Auth-Key {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + + - name: DeepL API Key (Pro) + id: kingfisher.deepl.2 + pattern: | + (?xi) + deepl + (?:.|[\n\r]){0,32}? + (?:SECRET|AUTH|KEY|TOKEN|API) + (?:.|[\n\r]){0,16}? + \b + ( + [a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12} + ) + \b + confidence: medium + examples: + - 'DEEPL_AUTH_KEY=279a2e9d-83b3-c416-7e2d-f721593e42a0' + - 'deepl_api_key: "5ef4a0d1-7e1f-47b2-ac0a-1282002aa2a1"' + negative_examples: + - 'SOME_UUID=279a2e9d-83b3-c416-7e2d-f721593e42a0' + references: + - https://developers.deepl.com/docs/getting-started/auth + validation: + type: Http + content: + request: + method: GET + url: https://api.deepl.com/v2/usage + headers: + Authorization: "DeepL-Auth-Key {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/dub.yml b/crates/kingfisher-rules/data/rules/dub.yml new file mode 100644 index 0000000..5d54357 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/dub.yml @@ -0,0 +1,33 @@ +rules: + - name: Dub.co API Key + id: kingfisher.dub.1 + pattern: | + (?x) + \b + ( + dub_[A-Za-z0-9]{24,36} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'DUB_API_KEY=dub_a1b2c3d4e5f6g7h8i9j0k1l2' + - 'Authorization: Bearer dub_xK8m2LpQr5nW0vYz3cJ7aB4d' + references: + - https://dub.co/docs/api-reference/tokens + validation: + type: Http + content: + request: + method: GET + url: https://api.dub.co/me + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/e2b.yml b/crates/kingfisher-rules/data/rules/e2b.yml new file mode 100644 index 0000000..f377dd6 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/e2b.yml @@ -0,0 +1,34 @@ +rules: + - name: E2B API Key + id: kingfisher.e2b.1 + pattern: | + (?x) + \b + ( + e2b_[A-Za-z0-9]{32,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'E2B_API_KEY=e2b_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'X-API-Key: e2b_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8hI9' + references: + - https://e2b.dev/docs/getting-started/api-key + - https://e2b.dev/docs/api-reference/sandboxes/list-sandboxes + validation: + type: Http + content: + request: + method: GET + url: https://api.e2b.dev/sandboxes + headers: + X-API-Key: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/facebook.yml b/crates/kingfisher-rules/data/rules/facebook.yml index 8c1e1f1..3f5d80b 100644 --- a/crates/kingfisher-rules/data/rules/facebook.yml +++ b/crates/kingfisher-rules/data/rules/facebook.yml @@ -95,3 +95,16 @@ rules: - 'FACEBOOK_ACCESS_TOKEN=EAACEdEose0cBAZAQW123456789abcdefghijklmnopqrstuvwxyzASDFGHJKL' references: - https://developers.facebook.com/docs/facebook-login/access-tokens/ + validation: + type: Http + content: + request: + method: GET + url: "https://graph.facebook.com/me?access_token={{ TOKEN }}" + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: + - '"id"' diff --git a/crates/kingfisher-rules/data/rules/falai.yml b/crates/kingfisher-rules/data/rules/falai.yml new file mode 100644 index 0000000..662d1a8 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/falai.yml @@ -0,0 +1,18 @@ +rules: + - name: Fal.ai API Key + id: kingfisher.falai.1 + pattern: | + (?x) + \b + ( + fal_sk_[a-zA-Z0-9_-]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'FAL_KEY=fal_sk_a1B2c3D4e5F6g7H8i9J0k1L2m3N4' + references: + - https://docs.fal.ai/platform-apis/v1/keys/create diff --git a/crates/kingfisher-rules/data/rules/flagsmith.yml b/crates/kingfisher-rules/data/rules/flagsmith.yml new file mode 100644 index 0000000..0bf5e61 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/flagsmith.yml @@ -0,0 +1,35 @@ +rules: + - name: Flagsmith Server-Side Environment Key + id: kingfisher.flagsmith.1 + pattern: | + (?x) + \b + ( + ser\.[A-Za-z0-9]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_uppercase: 1 + min_lowercase: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'FLAGSMITH_SERVER_SIDE_ENVIRONMENT_KEY=ser.xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + - 'X-Environment-Key: ser.a1b2C3d4E5f6G7h8I9j0K1l2' + references: + - https://docs.flagsmith.com/clients/server-side + validation: + type: Http + content: + request: + method: GET + url: https://edge.api.flagsmith.com/api/v1/flags/ + headers: + X-Environment-Key: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/gitlab.yml b/crates/kingfisher-rules/data/rules/gitlab.yml index 2ac05bb..995bac0 100644 --- a/crates/kingfisher-rules/data/rules/gitlab.yml +++ b/crates/kingfisher-rules/data/rules/gitlab.yml @@ -254,6 +254,9 @@ rules: - 'DEPLOY_TOKEN=gldt-3dEfGhIjK4MnOpQrStUv' references: - https://docs.gitlab.com/user/project/deploy_tokens/ + # Validation not added: deploy tokens authenticate via HTTP basic auth + # (username + token as password) and require the deploy token username, + # which varies per token and is not captured by this rule. - name: GitLab Feature Flag Client Token id: kingfisher.gitlab.7 @@ -473,3 +476,20 @@ rules: - 'Cookie: _gitlab_session=0f1e2d3c4b5a69788796a5b4c3d2e1f0' references: - https://docs.gitlab.com/ + validation: + type: Http + content: + request: + method: GET + url: https://gitlab.com/api/v4/user + headers: + Cookie: "_gitlab_session={{ TOKEN }}" + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: + - '"id"' + - '"username"' + match_all_words: true diff --git a/crates/kingfisher-rules/data/rules/guardsquare.yml b/crates/kingfisher-rules/data/rules/guardsquare.yml new file mode 100644 index 0000000..7371557 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/guardsquare.yml @@ -0,0 +1,16 @@ +rules: + - name: GuardSquare AppSweep API Key + id: kingfisher.guardsquare.1 + pattern: | + (?x) + \b + ( + gs_appsweep_[a-zA-Z0-9_-]{24,48} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'APPSWEEP_API_KEY=gs_appsweep_a1B2c3D4e5F6g7H8i9J0k1L2' + references: + - https://www.guardsquare.com/appsweep diff --git a/crates/kingfisher-rules/data/rules/hackclub.yml b/crates/kingfisher-rules/data/rules/hackclub.yml new file mode 100644 index 0000000..e9c8b8c --- /dev/null +++ b/crates/kingfisher-rules/data/rules/hackclub.yml @@ -0,0 +1,16 @@ +rules: + - name: Hack Club AI API Key + id: kingfisher.hackclub.1 + pattern: | + (?x) + \b + ( + sk-hc-v1-[a-f0-9]{64} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'HACKCLUB_API_KEY=sk-hc-v1-a1b2c3d4e5f60708a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f60708a9b0c1d2' + references: + - https://jams.hackclub.com/jam/ai-travel diff --git a/crates/kingfisher-rules/data/rules/hexpm.yml b/crates/kingfisher-rules/data/rules/hexpm.yml new file mode 100644 index 0000000..e9c5cca --- /dev/null +++ b/crates/kingfisher-rules/data/rules/hexpm.yml @@ -0,0 +1,58 @@ +rules: + - name: Hex.pm Personal Token + id: kingfisher.hexpm.1 + pattern: | + (?x) + \b + ( + hxtp_[a-f0-9]{64} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'HEX_API_KEY=hxtp_a1b2c3d4e5f60708a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f60708a9b0c1d2' + references: + - https://hex.pm/docs/faq + validation: + type: Http + content: + request: + method: GET + url: https://hex.pm/api/auth + headers: + Authorization: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + + - name: Hex.pm Workspace Token + id: kingfisher.hexpm.2 + pattern: | + (?x) + \b + ( + hxtw_[a-f0-9]{64} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'HEX_API_KEY=hxtw_a1b2c3d4e5f60708a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f60708a9b0c1d2' + references: + - https://hex.pm/docs/faq + validation: + type: Http + content: + request: + method: GET + url: https://hex.pm/api/auth + headers: + Authorization: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] diff --git a/crates/kingfisher-rules/data/rules/hop.yml b/crates/kingfisher-rules/data/rules/hop.yml index 06fbd0b..912a43f 100644 --- a/crates/kingfisher-rules/data/rules/hop.yml +++ b/crates/kingfisher-rules/data/rules/hop.yml @@ -17,6 +17,20 @@ rules: - 'HOP_TOKEN=ptk_AbCdEfGhIjKlMnOpQrStUvWxYz123456' references: - https://docs.hop.io/reference/rest-api + validation: + type: Http + content: + request: + method: GET + url: https://api.hop.io/v1/projects/@this + headers: + Authorization: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid - name: HOP Personal Access Token id: kingfisher.hop.2 @@ -36,3 +50,17 @@ rules: - 'HOP_PAT="hop_pat_AbCdEfGhIjKlMnOpQrStUvWxYz123456"' references: - https://docs.hop.io/reference/rest-api + validation: + type: Http + content: + request: + method: GET + url: https://api.hop.io/v1/users/@me + headers: + Authorization: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/infisical.yml b/crates/kingfisher-rules/data/rules/infisical.yml new file mode 100644 index 0000000..0f7a5f9 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/infisical.yml @@ -0,0 +1,16 @@ +rules: + - name: Infisical Service Token + id: kingfisher.infisical.1 + pattern: | + (?x) + \b + ( + st\.[a-f0-9]{8,32}\.[a-f0-9]{8,32}\.[a-f0-9]{16,48} + ) + (?:\b|$) + min_entropy: 3.5 + confidence: medium + examples: + - 'INFISICAL_TOKEN=st.a1b2c3d4e5f6.7890abcdef12.f1e2d3c4b5a69788796a5b4c3d2e1f0' + references: + - https://infisical.com/docs/internals/service-tokens diff --git a/crates/kingfisher-rules/data/rules/inngest.yml b/crates/kingfisher-rules/data/rules/inngest.yml new file mode 100644 index 0000000..60a8e5c --- /dev/null +++ b/crates/kingfisher-rules/data/rules/inngest.yml @@ -0,0 +1,34 @@ +rules: + - name: Inngest Signing Key + id: kingfisher.inngest.1 + pattern: | + (?x) + \b + ( + signkey-[A-Za-z0-9_]+-[a-f0-9]{32,68} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'INNGEST_SIGNING_KEY=signkey-prod-b2ed992186a5cb19f6668aade821f502c1d00970dfd0e35128d51bac4649916c' + - 'INNGEST_SIGNING_KEY="signkey-branch-12345678abcdef0123456789abcdef01"' + references: + - https://www.inngest.com/docs/platform/signing-keys + + - name: Inngest Event Key + id: kingfisher.inngest.2 + pattern: | + (?x) + \b + ( + evtkey-(?:dev|prod|test|staging|branch)-[a-f0-9]{24,40} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'INNGEST_EVENT_KEY=evtkey-dev-a1b2c3d4e5f60708a9b0c1d2' + - 'INNGEST_EVENT_KEY="evtkey-prod-deadbeefcafebabe1234567890abcdef"' + references: + - https://www.inngest.com/docs/events/creating-an-event diff --git a/crates/kingfisher-rules/data/rules/liveblocks.yml b/crates/kingfisher-rules/data/rules/liveblocks.yml new file mode 100644 index 0000000..104d8bc --- /dev/null +++ b/crates/kingfisher-rules/data/rules/liveblocks.yml @@ -0,0 +1,33 @@ +rules: + - name: Liveblocks Secret Key + id: kingfisher.liveblocks.1 + pattern: | + (?x) + \b + ( + sk_(?:prod|dev)_[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'LIVEBLOCKS_SECRET_KEY=sk_prod_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'LIVEBLOCKS_SECRET_KEY="sk_dev_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h"' + references: + - https://liveblocks.io/docs/api-reference/rest-api-endpoints + validation: + type: Http + content: + request: + method: GET + url: https://api.liveblocks.io/v2/rooms?limit=1 + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/mappedin.yml b/crates/kingfisher-rules/data/rules/mappedin.yml new file mode 100644 index 0000000..46e1f10 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/mappedin.yml @@ -0,0 +1,32 @@ +rules: + - name: Mappedin API Key + id: kingfisher.mappedin.1 + pattern: | + (?x) + \b + ( + mik_[a-zA-Z0-9]{26} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'MAPPEDIN_KEY=mik_a1B2c3D4e5F6g7H8i9J0k1L2m3' + references: + - https://developer.mappedin.com/docs/mvf/v2/getting-started + + - name: Mappedin API Secret + id: kingfisher.mappedin.2 + pattern: | + (?x) + \b + ( + mis_[a-zA-Z0-9]{52} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'MAPPEDIN_SECRET=mis_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0u1V2w3X4y5Z6' + references: + - https://developer.mappedin.com/docs/mvf/v2/getting-started diff --git a/crates/kingfisher-rules/data/rules/mastra.yml b/crates/kingfisher-rules/data/rules/mastra.yml new file mode 100644 index 0000000..f8a707f --- /dev/null +++ b/crates/kingfisher-rules/data/rules/mastra.yml @@ -0,0 +1,37 @@ +rules: + - name: Mastra Memory Gateway API Key + id: kingfisher.mastra.1 + pattern: | + (?x) + \b + ( + msk_[A-Za-z0-9]{32}(?:[A-Za-z0-9]{16}){0,2} + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 4 + min_uppercase: 4 + min_entropy: 3.5 + confidence: medium + examples: + - 'MASTRA_API_KEY="msk_7fKp2Rz9Qa4Vn8Lm3Tx6Yc5Bg1Hd0JsW"' + - 'Authorization: Bearer msk_P7rT2mK9vL4qN8sX6cA3dF5gH1jZ0QaB' + references: + - https://gateway.mastra.ai/docs + - https://gateway.mastra.ai/docs/models + validation: + type: Http + content: + request: + method: GET + url: https://gateway-api.mastra.ai/v1/models + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + # Mastra Memory Gateway docs do not document a public API-key revocation endpoint. diff --git a/crates/kingfisher-rules/data/rules/mem0.yml b/crates/kingfisher-rules/data/rules/mem0.yml new file mode 100644 index 0000000..e8a7718 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/mem0.yml @@ -0,0 +1,33 @@ +rules: + - name: Mem0 API Key + id: kingfisher.mem0.1 + pattern: | + (?x) + \b + ( + m0-[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'MEM0_API_KEY=m0-a1b2c3d4e5f6g7h8i9j0k1l2m3n4' + - 'Authorization: Token m0-xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + references: + - https://docs.mem0.ai/api-reference + validation: + type: Http + content: + request: + method: GET + url: https://api.mem0.ai/v1/memories/?limit=1 + headers: + Authorization: "Token {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/minimax.yml b/crates/kingfisher-rules/data/rules/minimax.yml new file mode 100644 index 0000000..520b827 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/minimax.yml @@ -0,0 +1,30 @@ +rules: + - name: MiniMax API Key + id: kingfisher.minimax.1 + pattern: | + (?x) + \b + ( + MINIMAX-[A-Z0-9]{8}-[A-Z0-9]{8}-[A-Z0-9]{8}-[A-Z0-9]{8} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'MINIMAX_API_KEY=MINIMAX-A1B2C3D4-E5F6G7H8-I9J0K1L2-M3N4O5P6' + references: + - https://platform.minimax.io/docs/token-plan/quickstart + validation: + type: Http + content: + request: + method: GET + url: https://api.minimax.io/v1/models + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/mintlify.yml b/crates/kingfisher-rules/data/rules/mintlify.yml new file mode 100644 index 0000000..2e0f611 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/mintlify.yml @@ -0,0 +1,19 @@ +rules: + - name: Mintlify API Key + id: kingfisher.mintlify.1 + pattern: | + (?x) + \b + ( + mint_(?:dsc_)?[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'MINTLIFY_API_KEY=mint_a1b2c3d4e5f6g7h8i9j0k1l2' + - 'MINTLIFY_KEY="mint_dsc_xK8m2LpQr5nW0vYz3cJ7aB4d"' + references: + - https://mintlify.com/docs/api/introduction diff --git a/crates/kingfisher-rules/data/rules/netlify.yml b/crates/kingfisher-rules/data/rules/netlify.yml index 427d841..b87f35f 100644 --- a/crates/kingfisher-rules/data/rules/netlify.yml +++ b/crates/kingfisher-rules/data/rules/netlify.yml @@ -124,3 +124,33 @@ rules: - report_response: true - type: StatusMatch status: [204] + + - name: Netlify Personal Access Token (nfp_ prefix) + id: kingfisher.netlify.3 + pattern: | + (?x) + \b + ( + nfp_[a-zA-Z0-9]{40,48} + ) + \b + pattern_requirements: + min_digits: 2 + min_entropy: 3.3 + confidence: medium + examples: + - 'NETLIFY_AUTH_TOKEN=nfp_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0' + references: + - https://docs.netlify.com/api/get-started/#authentication + validation: + type: Http + content: + request: + method: GET + url: https://api.netlify.com/api/v1/user + headers: + Authorization: "Bearer {{ TOKEN }}" + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] diff --git a/crates/kingfisher-rules/data/rules/nightfall.yml b/crates/kingfisher-rules/data/rules/nightfall.yml new file mode 100644 index 0000000..8fe80e7 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/nightfall.yml @@ -0,0 +1,31 @@ +rules: + - name: Nightfall AI API Key + id: kingfisher.nightfall.1 + pattern: | + (?x) + \b + ( + NF-[a-zA-Z0-9]{32} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'NIGHTFALL_API_KEY=NF-a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6' + - 'Authorization: Bearer NF-xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h0i' + references: + - https://docs.nightfall.ai/docs/creating-an-api-key + validation: + type: Http + content: + request: + method: POST + url: https://api.nightfall.ai/v3/upload + headers: + Authorization: "Bearer {{ TOKEN }}" + Content-Type: application/json + body: '{"fileSizeBytes":256}' + response_matcher: + - report_response: true + - type: StatusMatch + status: [200, 201] diff --git a/crates/kingfisher-rules/data/rules/ory.yml b/crates/kingfisher-rules/data/rules/ory.yml new file mode 100644 index 0000000..605aaa0 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/ory.yml @@ -0,0 +1,74 @@ +rules: + - name: Ory API Key + id: kingfisher.ory.1 + pattern: | + (?x) + \b + ( + ory_(?:pat|apikey|wak)_[A-Za-z0-9_-]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'ORY_API_KEY=ory_pat_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + - 'ORY_ACCESS_KEY="ory_wak_a1b2c3d4e5f6g7h8i9j0k1l2"' + - 'Authorization: Bearer ory_apikey_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + references: + - https://www.ory.sh/docs/security-compliance/token-formats + validation: + type: Http + content: + request: + method: GET + url: https://api.console.ory.sh/projects + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200, 403] + - type: WordMatch + words: + - '"Unauthorized"' + negative: true + + - name: Ory Session Token + id: kingfisher.ory.2 + pattern: | + (?x) + \b + ( + ory_st_[A-Za-z0-9_-]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'ory_session_token=ory_st_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + references: + - https://www.ory.sh/docs/security-compliance/token-formats + + - name: Ory OAuth2 Token + id: kingfisher.ory.3 + pattern: | + (?x) + \b + ( + ory_(?:at|rt|ac)_[A-Za-z0-9_-]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'access_token=ory_at_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + - 'refresh_token="ory_rt_a1b2c3d4e5f6g7h8i9j0k1l2"' + references: + - https://www.ory.sh/docs/security-compliance/token-formats diff --git a/crates/kingfisher-rules/data/rules/pirsch.yml b/crates/kingfisher-rules/data/rules/pirsch.yml new file mode 100644 index 0000000..d8515ff --- /dev/null +++ b/crates/kingfisher-rules/data/rules/pirsch.yml @@ -0,0 +1,23 @@ +rules: + - name: Pirsch Analytics Access Key + id: kingfisher.pirsch.1 + pattern: | + (?xi) + pirsch + (?:.|[\n\r]){0,32}? + (?:SECRET|ACCESS|KEY|TOKEN|CLIENT) + (?:.|[\n\r]){0,16}? + \b + ( + pa_[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'PIRSCH_ACCESS_KEY=pa_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'pirsch_client_secret = "pa_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8hI0"' + references: + - https://docs.pirsch.io/api-sdks/api-guide diff --git a/crates/kingfisher-rules/data/rules/pollinations.yml b/crates/kingfisher-rules/data/rules/pollinations.yml new file mode 100644 index 0000000..f28c715 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/pollinations.yml @@ -0,0 +1,36 @@ +rules: + - name: Pollinations Secret Key + id: kingfisher.pollinations.1 + pattern: | + (?x) + \b + ( + plln_sk_[a-zA-Z0-9]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'POLLINATIONS_KEY=plln_sk_a1B2c3D4e5F6g7H8i9J0k1L2' + references: + - https://pollinations.ai/pricing + + - name: Pollinations Publishable Key + id: kingfisher.pollinations.2 + pattern: | + (?x) + \b + ( + plln_pk_[a-zA-Z0-9]{24,48} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'POLLINATIONS_PUBLIC_KEY=plln_pk_a1B2c3D4e5F6g7H8i9J0k1L2' + references: + - https://pollinations.ai/pricing diff --git a/crates/kingfisher-rules/data/rules/posthog.yml b/crates/kingfisher-rules/data/rules/posthog.yml index 6f65b0e..859a0e2 100644 --- a/crates/kingfisher-rules/data/rules/posthog.yml +++ b/crates/kingfisher-rules/data/rules/posthog.yml @@ -41,6 +41,20 @@ rules: examples: - "phs_8BamSCGAJL4J0hBl2WmkcswecSArJAXO20xzcpYhdiPto9B" - "phs_FsG1YzDpCu64PFUcbW1CiEEfiFg1IIBRsME3qqehpZ5GpoT" + validation: + type: Http + content: + request: + method: GET + url: https://app.posthog.com/api/users/@me/ + headers: + Authorization: "Bearer {{ TOKEN }}" + Content-Type: "application/json" + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid references: - https://posthog.com/docs/api - https://github.com/PostHog/posthog/blob/e408aac5debe02b39a6a67cfd028f16a2ca7bc90/posthog/models/utils.py#L260-L290 diff --git a/crates/kingfisher-rules/data/rules/ramp.yml b/crates/kingfisher-rules/data/rules/ramp.yml new file mode 100644 index 0000000..d053828 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/ramp.yml @@ -0,0 +1,32 @@ +rules: + - name: Ramp Client ID + id: kingfisher.ramp.1 + pattern: | + (?x) + \b + ( + ramp_id_[a-zA-Z0-9]{40} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'RAMP_CLIENT_ID=ramp_id_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0' + references: + - https://docs.ramp.com/reference/authentication + + - name: Ramp Client Secret + id: kingfisher.ramp.2 + pattern: | + (?x) + \b + ( + ramp_sec_[a-zA-Z0-9]{48} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'RAMP_CLIENT_SECRET=ramp_sec_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0u1V2w3X4' + references: + - https://docs.ramp.com/reference/authentication diff --git a/crates/kingfisher-rules/data/rules/redirectpizza.yml b/crates/kingfisher-rules/data/rules/redirectpizza.yml new file mode 100644 index 0000000..23fa44f --- /dev/null +++ b/crates/kingfisher-rules/data/rules/redirectpizza.yml @@ -0,0 +1,37 @@ +rules: + - name: redirect.pizza API Token + id: kingfisher.redirectpizza.1 + pattern: | + (?x) + \b + ( + rpa_[A-Za-z0-9]{30} + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 4 + min_uppercase: 4 + min_entropy: 3.5 + confidence: medium + examples: + - 'REDIRECT_PIZZA_TOKEN="rpa_Qj7mN4vK8sL2xP6zT9aBcD3eF5gH1j"' + - 'Authorization: Bearer rpa_P7rT2mK9vL4qN8sX6cA3dF5gH1jZ0Q' + references: + - https://docs.github.com/en/code-security/reference/secret-security/supported-secret-scanning-patterns + - https://redirect.pizza/api/v1/domains + validation: + type: Http + content: + request: + method: GET + url: https://redirect.pizza/api/v1/domains + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + # API-token deletion is dashboard-managed; the public redirect.pizza API does not document self-revocation. diff --git a/crates/kingfisher-rules/data/rules/seam.yml b/crates/kingfisher-rules/data/rules/seam.yml new file mode 100644 index 0000000..906c5c8 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/seam.yml @@ -0,0 +1,31 @@ +rules: + - name: Seam API Key + id: kingfisher.seam.1 + pattern: | + (?x) + \b + ( + seam_(?:test2|live)[a-zA-Z0-9]{1,8}_[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{20,32} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'SEAM_API_KEY=seam_test2bMS_94SrGUXuNR2JmJkjtvBQDg5c' + - 'SEAM_API_KEY="seam_liveK3_7Zum8CdnFeJi2CysRnTBh3"' + references: + - https://docs.seam.co/latest/core-concepts/authentication/api-keys + validation: + type: Http + content: + request: + method: GET + url: https://connect.getseam.com/workspaces/get + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/supabase.yml b/crates/kingfisher-rules/data/rules/supabase.yml index a2af991..1f29fae 100644 --- a/crates/kingfisher-rules/data/rules/supabase.yml +++ b/crates/kingfisher-rules/data/rules/supabase.yml @@ -80,3 +80,19 @@ rules: - https://supabase.com/docs/guides/api examples: - "https://ejcvydfyxzmbtfbfstnq.supabase.co" + + - name: Supabase Publishable Key + id: kingfisher.supabase.4 + pattern: | + (?x) + \b + ( + sb_publishable_[a-zA-Z0-9_-]{24,40} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'NEXT_PUBLIC_SUPABASE_ANON_KEY=sb_publishable_a1B2c3D4e5F6g7H8i9J0k1L2' + references: + - https://supabase.com/docs/guides/api diff --git a/crates/kingfisher-rules/data/rules/svix.yml b/crates/kingfisher-rules/data/rules/svix.yml new file mode 100644 index 0000000..2f2ad7f --- /dev/null +++ b/crates/kingfisher-rules/data/rules/svix.yml @@ -0,0 +1,17 @@ +rules: + - name: Svix Webhook Signing Secret + id: kingfisher.svix.1 + pattern: | + (?x) + \b + ( + whsec_(?:[A-Za-z0-9+/]{32}|[A-Za-z0-9+/]{43}=|[A-Za-z0-9+/]{42}==) + ) + (?:\b|$) + min_entropy: 3.0 + confidence: medium + examples: + - 'WEBHOOK_SECRET=whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw' + - 'SVIX_WEBHOOK_SECRET="whsec_C2FVsBQIhrscChlQIMV+b5sSYspob7oD"' + references: + - https://docs.svix.com/receiving/verifying-payloads/how-manual diff --git a/crates/kingfisher-rules/data/rules/tinybird.yml b/crates/kingfisher-rules/data/rules/tinybird.yml new file mode 100644 index 0000000..6351e87 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/tinybird.yml @@ -0,0 +1,39 @@ +rules: + - name: Tinybird Admin Token + id: kingfisher.tinybird.1 + pattern: | + (?x) + \b + (?i:tinybird) + (?:.|[\n\r]){0,32}? + (?:ADMIN|SECRET|TOKEN|KEY) + (?:.|[\n\r]){0,16}? + \b + ( + p\.[A-Za-z0-9]{40,100} + ) + (?:\b|$) + pattern_requirements: + min_digits: 1 + min_uppercase: 1 + min_lowercase: 1 + min_entropy: 3.5 + confidence: medium + examples: + - 'TINYBIRD_TOKEN=p.eyJ1IjogIjczMjA3MzYwLWVmMDAtNGIxOS1hZjQ3LWRlMmU3OTI2ZTdmOCJ9' + references: + - https://www.tinybird.co/docs/api-reference/token-api + validation: + type: Http + content: + request: + method: GET + url: https://api.tinybird.co/v0/datasources + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/tolgee.yml b/crates/kingfisher-rules/data/rules/tolgee.yml new file mode 100644 index 0000000..c8d1da3 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/tolgee.yml @@ -0,0 +1,61 @@ +rules: + - name: Tolgee Project API Key + id: kingfisher.tolgee.1 + pattern: | + (?x) + \b + ( + tgpak_[a-z2-7]{40,64} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'TOLGEE_API_KEY=tgpak_ha3tkxzton2wm4twmrsgkmtgmnsgw4bqgvttemttnrwwoyld' + - 'X-API-Key: tgpak_gm2tgnrqgi3tsnrvheytomrvme4dgmrtgntdgntfmu3to' + references: + - https://tolgee.io/platform/account_settings/api_keys_and_pat_tokens + validation: + type: Http + content: + request: + method: GET + url: https://app.tolgee.io/v2/projects + headers: + X-API-Key: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + + - name: Tolgee Personal Access Token + id: kingfisher.tolgee.2 + pattern: | + (?x) + \b + ( + tgpat_[a-z2-7]{40,64} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - 'TOLGEE_PAT=tgpat_gm2tgnrqgi3tsnrvheytomrvme4dgmrtgntdgntfmu3to' + references: + - https://tolgee.io/platform/account_settings/api_keys_and_pat_tokens + validation: + type: Http + content: + request: + method: GET + url: https://app.tolgee.io/v2/projects + headers: + X-API-Key: "{{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/triggerdev.yml b/crates/kingfisher-rules/data/rules/triggerdev.yml new file mode 100644 index 0000000..14f2618 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/triggerdev.yml @@ -0,0 +1,71 @@ +rules: + - name: Trigger.dev Secret Key + id: kingfisher.triggerdev.1 + pattern: | + (?x) + \b + ( + tr_(?:dev|prod|stg)_[A-Za-z0-9]{20,40} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'TRIGGER_SECRET_KEY=tr_dev_1a2b3c4d5e6f7g8h9i0j' + - 'TRIGGER_SECRET_KEY=tr_prod_xK8m2LpQr5nW0vYz3cJ7' + references: + - https://trigger.dev/docs/management/authentication + validation: + type: Http + content: + request: + method: GET + url: https://api.trigger.dev/api/v1/tasks + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200, 401] + - type: WordMatch + words: + - '"Unauthorized"' + negative: true + + - name: Trigger.dev Personal Access Token + id: kingfisher.triggerdev.2 + pattern: | + (?x) + \b + ( + tr_pat_[A-Za-z0-9]{20,40} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'TRIGGER_ACCESS_TOKEN=tr_pat_xK8m2LpQr5nW0vYz3cJ7aB4d' + references: + - https://trigger.dev/docs/management/authentication + validation: + type: Http + content: + request: + method: GET + url: https://api.trigger.dev/api/v1/tasks + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200, 401] + - type: WordMatch + words: + - '"Unauthorized"' + negative: true diff --git a/crates/kingfisher-rules/data/rules/unkey.yml b/crates/kingfisher-rules/data/rules/unkey.yml index dabcfbf..858a192 100644 --- a/crates/kingfisher-rules/data/rules/unkey.yml +++ b/crates/kingfisher-rules/data/rules/unkey.yml @@ -78,6 +78,22 @@ rules: - report_response: true - type: StatusMatch status: [200] + validation: + type: Http + content: + request: + method: POST + url: https://api.unkey.com/v2/keys.verifyKey + headers: + Content-Type: application/json + body: '{"key":"{{ TOKEN }}"}' + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: + - '"valid"' references: - https://www.unkey.com/docs/api-reference/v2/keys/create-api-key - https://www.unkey.com/docs/api-reference/v2/keys/verify-api-key diff --git a/crates/kingfisher-rules/data/rules/upstash.yml b/crates/kingfisher-rules/data/rules/upstash.yml new file mode 100644 index 0000000..27d44b2 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/upstash.yml @@ -0,0 +1,76 @@ +rules: + - name: Upstash Redis REST Token + id: kingfisher.upstash.1 + pattern: | + (?x) + \b + (?: + UPSTASH_REDIS_REST_TOKEN + | + (?i:upstash) + (?:.|[\n\r]){0,40}? + (?i:(?:redis|rest)) + (?:.|[\n\r]){0,40}? + (?i:token) + ) + (?:.|[\n\r]){0,24}? + ( + (?: + [A-Za-z0-9]{32,48} + | + AYNgAS[A-Za-z0-9+/_-]{26,90}={0,2} + ) + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 4 + min_entropy: 3.5 + confidence: medium + examples: + - 'UPSTASH_REDIS_REST_TOKEN="2553feg6a2d9842h2a0gcdb5f8efe9934"' + - 'upstash redis token: "AYNgAS2553feg6a2d9842h2a0gcdb5f8efe9934DQ="' + references: + - https://upstash.com/docs/redis/features/restapi + - https://upstash.com/docs/redis/howto/connectwithupstashredis + depends_on_rule: + - rule_id: kingfisher.upstash.2 + variable: UPSTASH_REDIS_REST_URL + validation: + type: Http + content: + request: + method: POST + url: "{{ UPSTASH_REDIS_REST_URL }}/ping" + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + - type: WordMatch + words: + - PONG + # Upstash Redis REST tokens are database credentials; revocation is done by rotating/resetting the database token. + + - name: Upstash Redis REST URL + id: kingfisher.upstash.2 + pattern: | + (?xi) + \b + (?:UPSTASH_REDIS_REST_URL|upstash) + (?:.|[\n\r]){0,48}? + ( + https://[a-z0-9][a-z0-9-]{2,63}\.upstash\.io + ) + \b + min_entropy: 3.0 + confidence: medium + visible: false + examples: + - 'UPSTASH_REDIS_REST_URL="https://steady-cobra-12345.upstash.io"' + - 'upstash_url=https://sharp-raven-67890.upstash.io' + references: + - https://upstash.com/docs/redis/features/restapi diff --git a/crates/kingfisher-rules/data/rules/warpstream.yml b/crates/kingfisher-rules/data/rules/warpstream.yml new file mode 100644 index 0000000..5225a04 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/warpstream.yml @@ -0,0 +1,32 @@ +rules: + - name: WarpStream API Key Secret + id: kingfisher.warpstream.1 + pattern: | + (?x) + \b + ( + aks_[a-f0-9]{64} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'WARPSTREAM_API_KEY=aks_a51b1fb20ac4a3af549503bb08cd309672fcada1030c5a494850e87ce5d4c613' + references: + - https://docs.warpstream.com/warpstream/reference/api-reference/api-keys + - https://docs.warpstream.com/warpstream/reference/api-reference/api-keys/list + validation: + type: Http + content: + request: + method: GET + url: https://api.warpstream.com/api/v1/list_api_keys + headers: + warpstream-api-key: "{{ TOKEN }}" + Content-Type: application/json + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/woocommerce.yml b/crates/kingfisher-rules/data/rules/woocommerce.yml new file mode 100644 index 0000000..ab49c68 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/woocommerce.yml @@ -0,0 +1,33 @@ +rules: + - name: WooCommerce Consumer Secret + id: kingfisher.woocommerce.1 + pattern: | + (?x) + \b + ( + cs_[a-f0-9]{40} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'WC_CONSUMER_SECRET=cs_a1b2c3d4e5f60708a9b0c1d2e3f4a5b6c7d8e9f0' + - 'consumer_secret: "cs_deadbeefcafebabe1234567890abcdef01234567"' + references: + - https://woocommerce.github.io/woocommerce-rest-api-docs/#authentication + + - name: WooCommerce Consumer Key + id: kingfisher.woocommerce.2 + pattern: | + (?x) + \b + ( + ck_[a-f0-9]{40} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - 'WC_CONSUMER_KEY=ck_a1b2c3d4e5f60708a9b0c1d2e3f4a5b6c7d8e9f0' + references: + - https://woocommerce.github.io/woocommerce-rest-api-docs/#authentication diff --git a/crates/kingfisher-rules/data/rules/workos.yml b/crates/kingfisher-rules/data/rules/workos.yml new file mode 100644 index 0000000..744ccc3 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/workos.yml @@ -0,0 +1,45 @@ +rules: + - name: WorkOS API Key + id: kingfisher.workos.1 + pattern: | + (?xi) + \b + (?:workos|WORKOS_API_KEY|WORKOS_SECRET_KEY) + (?:.|[\n\r]){0,48}? + \b + ( + sk_(?:live|test|staging|example)_[A-Za-z0-9_-]{16}(?:[A-Za-z0-9_-]{16}){0,3} + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 4 + min_entropy: 3.5 + confidence: medium + examples: + - 'WORKOS_API_KEY="sk_live_Qj7mN4vK8sL2xP6zT9aBcD3eF5gH1jK2"' + - 'workos apiKey: "sk_test_P7rT2mK9vL4qN8sX6cA3dF5gH1jZ0QaB"' + references: + - https://workos.com/docs/reference/organization + - https://workos.com/docs/reference/authkit/user + - https://docs.github.com/en/code-security/reference/secret-security/supported-secret-scanning-patterns + validation: + type: Http + content: + request: + method: GET + url: https://api.workos.com/organizations + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + - type: WordMatch + words: + - '"object"' + - '"data"' + match_all_words: true + # WorkOS account API keys are managed from the WorkOS dashboard; no self-revocation endpoint is documented. diff --git a/crates/kingfisher-rules/data/rules/xata.yml b/crates/kingfisher-rules/data/rules/xata.yml new file mode 100644 index 0000000..7267740 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/xata.yml @@ -0,0 +1,33 @@ +rules: + - name: Xata API Key + id: kingfisher.xata.1 + pattern: | + (?x) + \b + ( + xau_[A-Za-z0-9]{24,44} + ) + \b + pattern_requirements: + min_digits: 1 + min_entropy: 3.0 + confidence: medium + examples: + - 'XATA_API_KEY=xau_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6' + - 'Authorization: Bearer xau_xK8m2LpQr5nW0vYz3cJ7aB4dE6fG8h' + references: + - https://xata.io/docs/concepts/api-keys + validation: + type: Http + content: + request: + method: GET + url: https://api.xata.io/user + headers: + Authorization: "Bearer {{ TOKEN }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/xendit.yml b/crates/kingfisher-rules/data/rules/xendit.yml new file mode 100644 index 0000000..628cc70 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/xendit.yml @@ -0,0 +1,31 @@ +rules: + - name: Xendit API Key + id: kingfisher.xendit.1 + pattern: | + (?x) + \b + ( + xnd_(?:production|development)_[A-Za-z0-9_-]{32,52} + ) + (?:\b|$) + min_entropy: 3.0 + confidence: medium + examples: + - 'XENDIT_SECRET_KEY=xnd_development_P4qDfOss0OCpl8RtKrROHjaQYNCk9dN5lSfk' + - 'XENDIT_API_KEY="xnd_production_aB3cD4eF5gH6iJ7kL8mN9oP0qR1sT2uV3wX4y"' + references: + - https://docs.xendit.co/api-integration/api-keys + validation: + type: Http + content: + request: + method: GET + url: https://api.xendit.co/balance + headers: + Authorization: "Basic {{ TOKEN | append: ':' | b64enc }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/docs-site/docs/changelog.md b/docs-site/docs/changelog.md index 57582be..ff04027 100644 --- a/docs-site/docs/changelog.md +++ b/docs-site/docs/changelog.md @@ -8,7 +8,7 @@ description: "Kingfisher release history: new features, rules, bug fixes, and im All notable changes to this project will be documented in this file. ## [v1.97.0] -- Added live HTTP validation for 12 rules across 10 providers: Val Town, Polar, hCaptcha, Thunderstore, Elastic Cloud (2 rules), LlamaCloud, Gemfury (2 rules), Vonage, ThingsBoard, and Zapier. +- Added live HTTP validation for 18 rules across 15 providers: Val Town, Polar, hCaptcha, Thunderstore, Elastic Cloud (2 rules), LlamaCloud, Gemfury (2 rules), Vonage, ThingsBoard, Zapier, Facebook Access Token, GitLab Session Cookie, PostHog Feature Flags, Unkey API Key, and Hop.io (2 rules). - Added revocation support for 7 rules across 6 providers: Discord webhooks (single-step DELETE), DigitalOcean PATs (self-revoke via OAuth), and multi-step HttpMultiStep revocation for LaunchDarkly, Resend, Linode, and Netlify (2 rules). Built-in revocation coverage is now 34 provider families with 53 revocation-enabled rules. ## [v1.95.0]