From 482a60bb9d2d74ae13a4da9d92875cfc4eb92258 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Sun, 29 Mar 2026 10:41:54 -0700 Subject: [PATCH] fixed github actions --- .github/workflows/release-provenance.yml | 49 -------- .github/workflows/release.yml | 40 ++++++ CHANGELOG.md | 4 +- README.md | 36 +++++- crates/kingfisher-rules/data/rules/AGENTS.md | 21 +++- .../kingfisher-rules/data/rules/anthropic.yml | 36 +++++- .../data/rules/azurespeech.yml | 84 +++++++++++++ .../data/rules/azuretranslator.yml | 97 +++++++++++++++ .../kingfisher-rules/data/rules/databento.yml | 34 +++++ .../kingfisher-rules/data/rules/datastax.yml | 33 +++++ .../kingfisher-rules/data/rules/devcycle.yml | 117 ++++++++++++++++++ .../kingfisher-rules/data/rules/fullstory.yml | 41 ++++++ .../kingfisher-rules/data/rules/gcnotify.yml | 41 ++++++ crates/kingfisher-rules/data/rules/heroku.yml | 60 +++++++++ crates/kingfisher-rules/data/rules/stytch.yml | 76 ++++++++++++ 15 files changed, 716 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/release-provenance.yml create mode 100644 crates/kingfisher-rules/data/rules/azurespeech.yml create mode 100644 crates/kingfisher-rules/data/rules/azuretranslator.yml create mode 100644 crates/kingfisher-rules/data/rules/databento.yml create mode 100644 crates/kingfisher-rules/data/rules/datastax.yml create mode 100644 crates/kingfisher-rules/data/rules/devcycle.yml create mode 100644 crates/kingfisher-rules/data/rules/fullstory.yml create mode 100644 crates/kingfisher-rules/data/rules/gcnotify.yml create mode 100644 crates/kingfisher-rules/data/rules/stytch.yml diff --git a/.github/workflows/release-provenance.yml b/.github/workflows/release-provenance.yml deleted file mode 100644 index c3adb37..0000000 --- a/.github/workflows/release-provenance.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: SLSA Provenance - -on: - release: - types: [published] - -permissions: {} - -jobs: - # Compute SHA256 hashes of all release assets - hash: - name: Compute artifact hashes - runs-on: ubuntu-24.04 - permissions: - contents: read - outputs: - hashes: ${{ steps.hash.outputs.hashes }} - steps: - - name: Download release assets - env: - GH_TOKEN: ${{ github.token }} - TAG_NAME: ${{ github.event.release.tag_name }} - run: | - set -euo pipefail - mkdir -p assets - gh release download "${TAG_NAME}" \ - --repo "${{ github.repository }}" \ - --dir assets - - - name: Compute SHA256 hashes - id: hash - run: | - set -euo pipefail - cd assets - # Base64-encode the SHA256 hashes for SLSA provenance - echo "hashes=$(sha256sum -- * | base64 -w0)" >> "$GITHUB_OUTPUT" - - # Generate SLSA provenance for the release artifacts - provenance: - name: Generate SLSA provenance - needs: [hash] - permissions: - actions: read - id-token: write - contents: write - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a # v2.1.0 - with: - base64-subjects: "${{ needs.hash.outputs.hashes }}" - upload-assets: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ea626f..f58df1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -408,6 +408,46 @@ jobs: with: subject-path: 'target/release/*' + # ──────────────── SLSA Provenance ──────────────── + hash: + name: Compute artifact hashes + needs: [release] + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + hashes: ${{ steps.hash.outputs.hashes }} + steps: + - name: Download release assets + env: + GH_TOKEN: ${{ github.token }} + TAG_NAME: ${{ needs.release.outputs.tag }} + run: | + set -euo pipefail + mkdir -p assets + gh release download "${TAG_NAME}" \ + --repo "${{ github.repository }}" \ + --dir assets + + - name: Compute SHA256 hashes + id: hash + run: | + set -euo pipefail + cd assets + echo "hashes=$(sha256sum -- * | base64 -w0)" >> "$GITHUB_OUTPUT" + + provenance: + name: Generate SLSA provenance + needs: [hash] + permissions: + actions: read + id-token: write + contents: write + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@f7dd8c54c2067bafc12ca7a55595d5ee9b75204a # v2.1.0 + with: + base64-subjects: "${{ needs.hash.outputs.hashes }}" + upload-assets: true + # ──────────────── Publish Docker image ──────────────── publish-docker: needs: [release] diff --git a/CHANGELOG.md b/CHANGELOG.md index 6954865..97b5ed7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,10 @@ All notable changes to this project will be documented in this file. ## [v1.92.0] -- Added new built-in rules for Etsy, Flutterwave, Freemius, JFrog, Kraken, KuCoin, and Trello, plus recent Betterleaks-derived additions including Octopus Deploy, OpenShift, Private AI, SettleMint, Sidekiq, and Polymarket. +- Added new built-in rules for Etsy, Flutterwave, Freemius, JFrog, Kraken, KuCoin, Trello, Octopus Deploy, OpenShift, Private AI, SettleMint, Sidekiq, and Polymarket. - Added live HTTP validation for Etsy, JFrog, Octopus Deploy, OpenShift, and Private AI where provider documentation supported reliable token-only checks. +- Added detection + validation rules for Anthropic Admin, Azure Speech, Azure Translator, Databento, DataStax Astra, DevCycle, Fullstory, GC Notify, and Stytch; built-in runtime rule count is now 601 with `--confidence=low`. +- Added Heroku token revocation support for both legacy UUID-format tokens and `HRKU-` platform tokens via the OAuth authorizations API. ## [v1.91.0] - Added SSRF protection for credential validation: outbound HTTP requests now block connections to loopback, private, link-local, and other non-public IP addresses. HTTP redirect targets are DNS-resolved and validated against the same SSRF rules. Use `--allow-internal-ips` to opt out when scanning internal infrastructure. diff --git a/README.md b/README.md index d5cb266..e208ae9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Kingfisher Logo [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -[![Detection Rules](https://img.shields.io/badge/Detection%20Rules-548-2ea043.svg)](https://github.com/mongodb/kingfisher)
+[![Detection Rules](https://img.shields.io/badge/Detection%20Rules-601-2ea043.svg)](https://github.com/mongodb/kingfisher)
[![ghcr downloads](https://ghcr-badge.elias.eu.org/shield/mongodb/kingfisher/kingfisher)](https://github.com/mongodb/kingfisher/pkgs/container/kingfisher)
@@ -302,6 +302,40 @@ Kingfisher supports multiple installation methods: **For complete installation instructions and pre-commit hook setup, see [docs/INSTALLATION.md](docs/INSTALLATION.md).** +## Verifying Releases + +Every Kingfisher release includes [SLSA v3](https://slsa.dev) provenance and GitHub build attestations so you can verify that artifacts were built by our CI pipeline and haven't been tampered with. + +### SLSA provenance + +Each GitHub release includes a `multiple.intoto.jsonl` provenance file. Verify any release artifact with [`slsa-verifier`](https://github.com/slsa-framework/slsa-verifier): + +```bash +# Install slsa-verifier +go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest + +# Download the artifact and provenance from the release +gh release download v1.91.0 --repo mongodb/kingfisher \ + --pattern 'kingfisher-linux-x64.tgz' \ + --pattern 'multiple.intoto.jsonl' + +# Verify +slsa-verifier verify-artifact kingfisher-linux-x64.tgz \ + --provenance-path multiple.intoto.jsonl \ + --source-uri github.com/mongodb/kingfisher +``` + +### GitHub attestations + +Release artifacts also have GitHub build attestations, verifiable with the GitHub CLI: + +```bash +gh release download v1.91.0 --repo mongodb/kingfisher \ + --pattern 'kingfisher-linux-x64.tgz' + +gh attestation verify kingfisher-linux-x64.tgz --repo mongodb/kingfisher +``` + # Detection Rules Kingfisher ships with [hundreds of rules](crates/kingfisher-rules/data/rules/) that cover everything from classic cloud keys to the latest AI SaaS tokens. Below is an overview: diff --git a/crates/kingfisher-rules/data/rules/AGENTS.md b/crates/kingfisher-rules/data/rules/AGENTS.md index 80a9522..80ecbaf 100644 --- a/crates/kingfisher-rules/data/rules/AGENTS.md +++ b/crates/kingfisher-rules/data/rules/AGENTS.md @@ -30,8 +30,27 @@ Strongly recommended fields: ## Pattern Quality Rules - Prefer specific anchors/prefixes and provider context over broad generic regex. +- When the token format is generic or common-looking (for example bare 32-hex keys), prefer contextual patterns of the form: provider keyword -> short flexible gap -> key/secret label -> short flexible gap -> token. A good default is: + - `\b` + - provider identifier (for example `amplitude`, `azure`, `speech`, `translator`) + - `(?:.|[\n\r]){0,N}?` + - common credential labels such as `(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|AUTHORIZATION|API)` + - `(?:.|[\n\r]){0,M}?` + - the token capture wrapped in a single unnamed capture group +- Do not add surrounding context when the token is already strongly self-identifying by prefix or structure (for example `sk-ant-api...`, `AstraCS:...`, `dvc_client_...`, `secret-test-...`). In those cases, prefer the tighter self-identifying regex. - Use `pattern_requirements` to enforce quality constraints (`min_digits`, `min_uppercase`, `min_lowercase`, `min_special_chars`, `ignore_if_contains`, `checksum`). -- Use checksum validation in `pattern_requirements.checksum` when token formats support it. +- Use checksum validation in `pattern_requirements.checksum` when token formats support it. This is preferred when the provider token format includes a documented or reverse-engineered check segment, because it can sharply reduce false positives without adding brittle surrounding context. +- For checksum-based rules, prefer named captures for the main token body and checksum suffix/prefix, then compute the expected checksum in Liquid. A typical pattern is: + - `( + prefix_(?P...)(?P...) + )` + - with: + - `actual.template: "{{ checksum }}"` + - `actual.requires_capture: checksum` + - `expected: "{{ body | | }}"` + - `skip_if_missing: true` +- Example: GitHub PATs use a CRC32-derived base62 checksum. The rule in `github.yml` captures `body` and `checksum`, then compares `{{ checksum }}` against `{{ body | crc32 | base62: 6 }}`. +- Prefer checksum validation over extra loose context whenever the token structure itself supports it. If the checksum is only present on some token generations, keep `skip_if_missing: true` so older examples continue to load safely. - Use `visible: false` for helper/non-secret captures used only by dependent rules. - Use `depends_on_rule` for multi-part credential validation (for example ID + secret). diff --git a/crates/kingfisher-rules/data/rules/anthropic.yml b/crates/kingfisher-rules/data/rules/anthropic.yml index 3aed2b1..1a2784a 100644 --- a/crates/kingfisher-rules/data/rules/anthropic.yml +++ b/crates/kingfisher-rules/data/rules/anthropic.yml @@ -47,4 +47,38 @@ rules: words: - '"type":"message"' - 'credit balance is too low' - url: https://api.anthropic.com/v1/messages \ No newline at end of file + url: https://api.anthropic.com/v1/messages + + - name: Anthropic Admin API Key + id: kingfisher.anthropic.2 + pattern: | + (?xi) + ( + sk-ant-admin(?:\d{2,4})?-[A-Za-z0-9_-]{40,} + ) + pattern_requirements: + min_digits: 2 + min_uppercase: 1 + min_lowercase: 1 + min_entropy: 3.5 + confidence: medium + examples: + - sk-ant-admin03-4mB9zY2Qx8LmN7pR5sT1uV6wX0aBcDeFgHiJkLmNoPqRsTuVwXyZ1234 + references: + - https://docs.anthropic.com/en/api/administration-api + - https://docs.anthropic.com/en/api/admin-api/organization/get-me + validation: + type: Http + content: + request: + method: GET + url: https://api.anthropic.com/v1/organizations/me + headers: + x-api-key: '{{ TOKEN }}' + anthropic-version: "2023-06-01" + content-type: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/azurespeech.yml b/crates/kingfisher-rules/data/rules/azurespeech.yml new file mode 100644 index 0000000..4a661cf --- /dev/null +++ b/crates/kingfisher-rules/data/rules/azurespeech.yml @@ -0,0 +1,84 @@ +rules: + - name: Azure Speech Region + id: kingfisher.azurespeech.1 + visible: false + pattern: | + (?xi) + \b + (?: + SPEECH_REGION + | + AZURE_SPEECH_REGION + | + speech[_-]?region + | + azure[_-]?speech[_-]?region + ) + \b + (?:.|[\n\r]){0,16}? + [=:] + \s*["']? + ( + [a-z0-9-]{4,32} + ) + ["']? + min_entropy: 1.5 + confidence: medium + examples: + - SPEECH_REGION=eastus + - azure_speech_region="westus2" + references: + - https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech + + - name: Azure Speech API Key + id: kingfisher.azurespeech.2 + pattern: | + (?xi) + \b + (?: + speech + | + azure[_-]?speech + ) + (?:.|[\n\r]){0,24}? + (?: + key + | + api[_-]?key + | + subscription[_-]?key + | + secret + ) + (?:.|[\n\r]){0,16}? + ( + [a-f0-9]{32} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 2 + min_entropy: 3.5 + confidence: medium + examples: + - AZURE_SPEECH_KEY=1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d + - speech_subscription_key="abcdef0123456789abcdef0123456789" + references: + - https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech + depends_on_rule: + - rule_id: kingfisher.azurespeech.1 + variable: AZURE_SPEECH_REGION + validation: + type: Http + content: + request: + method: POST + url: https://{{ AZURE_SPEECH_REGION }}.api.cognitive.microsoft.com/sts/v1.0/issueToken + headers: + Ocp-Apim-Subscription-Key: "{{ TOKEN }}" + Content-Type: application/x-www-form-urlencoded + Content-Length: "0" + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] diff --git a/crates/kingfisher-rules/data/rules/azuretranslator.yml b/crates/kingfisher-rules/data/rules/azuretranslator.yml new file mode 100644 index 0000000..7a9ccf3 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/azuretranslator.yml @@ -0,0 +1,97 @@ +rules: + - name: Azure Translator Region + id: kingfisher.azuretranslator.1 + visible: false + pattern: | + (?xi) + \b + (?: + TRANSLATOR_REGION + | + AZURE_TRANSLATOR_REGION + | + translator[_-]?region + | + translation[_-]?region + | + Ocp-Apim-Subscription-Region + ) + \b + (?:.|[\n\r]){0,16}? + [=:] + \s*["']? + ( + [a-z0-9-]{4,32} + ) + ["']? + min_entropy: 1.5 + confidence: medium + examples: + - TRANSLATOR_REGION=eastus + - azure_translator_region="westeurope" + references: + - https://learn.microsoft.com/en-us/azure/ai-services/translator/text-translation/reference/authentication + - https://learn.microsoft.com/en-us/azure/ai-services/translator/reference/v3-0-reference + + - name: Azure Translator API Key + id: kingfisher.azuretranslator.2 + pattern: | + (?xi) + \b + (?: + translator + | + translation + | + azure[_-]?(?:translator|translation) + ) + (?:.|[\n\r]){0,24}? + (?: + key + | + api[_-]?key + | + subscription[_-]?key + | + secret + ) + (?:.|[\n\r]){0,16}? + ( + [a-f0-9]{32} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 2 + min_entropy: 3.5 + confidence: medium + examples: + - AZURE_TRANSLATOR_KEY=1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d + - translator_subscription_key="abcdef0123456789abcdef0123456789" + references: + - https://learn.microsoft.com/en-us/azure/ai-services/translator/text-translation/reference/authentication + - https://learn.microsoft.com/en-us/azure/ai-services/translator/reference/v3-0-reference + depends_on_rule: + - rule_id: kingfisher.azuretranslator.1 + variable: AZURE_TRANSLATOR_REGION + validation: + type: Http + content: + request: + method: POST + url: https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=es + headers: + Ocp-Apim-Subscription-Key: "{{ TOKEN }}" + Ocp-Apim-Subscription-Region: "{{ AZURE_TRANSLATOR_REGION }}" + Content-Type: application/json + body: | + [ + { + "Text": "hello" + } + ] + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/databento.yml b/crates/kingfisher-rules/data/rules/databento.yml new file mode 100644 index 0000000..2249cb0 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/databento.yml @@ -0,0 +1,34 @@ +rules: + - name: Databento API Key + id: kingfisher.databento.1 + pattern: | + (?x) + \b + ( + db-[A-Za-z0-9]{29} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 2 + min_entropy: 3.3 + confidence: medium + examples: + - DATABENTO_API_KEY=db-abc123def456ghi789jkl012mno34 + references: + - https://databento.com/docs/api-reference-historical + - https://databento.com/docs/portal/api-keys + validation: + type: Http + content: + request: + method: GET + url: https://hist.databento.com/v0/metadata.list_datasets + headers: + Authorization: "Basic {{ TOKEN | append: ':' | b64enc }}" + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/datastax.yml b/crates/kingfisher-rules/data/rules/datastax.yml new file mode 100644 index 0000000..62b4537 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/datastax.yml @@ -0,0 +1,33 @@ +rules: + - name: DataStax Astra Application Token + id: kingfisher.datastax.1 + pattern: | + (?x) + \b + ( + AstraCS:[A-Za-z0-9]{20,} + ) + \b + pattern_requirements: + min_digits: 2 + min_entropy: 4.0 + confidence: medium + examples: + - ASTRA_DB_APPLICATION_TOKEN=AstraCS:Q29kZXhWYWxpZGF0aW9uVG9rZW5FeGFtcGxlMTIzNDU2Nzg5 + references: + - https://docs.datastax.com/en/astra-db-serverless/administration/manage-application-tokens.html + - https://docs.datastax.com/en/astra-db-classic/api-reference/devops-api.html + validation: + type: Http + content: + request: + method: GET + url: https://api.astra.datastax.com/v2/databases + 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/devcycle.yml b/crates/kingfisher-rules/data/rules/devcycle.yml new file mode 100644 index 0000000..0e71e76 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/devcycle.yml @@ -0,0 +1,117 @@ +rules: + - name: DevCycle Client SDK Key + id: kingfisher.devcycle.1 + pattern: | + (?x) + \b + ( + dvc_client_[A-Za-z0-9]{8,32} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 2 + min_entropy: 3.0 + confidence: medium + examples: + - dvc_client_abc12345 + - 'sdkKey: "dvc_client_abcdefg1234"' + references: + - https://docs.devcycle.com/cli-guides/environments/ + - https://docs.devcycle.com/bucketing-api/ + validation: + type: Http + content: + request: + method: POST + url: https://bucketing-api.devcycle.com/v1/variables + headers: + Authorization: Bearer {{ TOKEN }} + Content-Type: application/json + body: | + { + "user_id": "kingfisher-validation-user" + } + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + + - name: DevCycle Mobile SDK Key + id: kingfisher.devcycle.2 + pattern: | + (?x) + \b + ( + dvc_mobile_[A-Za-z0-9]{8,32} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 2 + min_entropy: 3.0 + confidence: medium + examples: + - dvc_mobile_abc12345 + - 'mobileKey: "dvc_mobile_abcdefg1234"' + references: + - https://docs.devcycle.com/cli-guides/environments/ + - https://docs.devcycle.com/bucketing-api/ + validation: + type: Http + content: + request: + method: POST + url: https://bucketing-api.devcycle.com/v1/variables + headers: + Authorization: Bearer {{ TOKEN }} + Content-Type: application/json + body: | + { + "user_id": "kingfisher-validation-user" + } + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + + - name: DevCycle Server SDK Key + id: kingfisher.devcycle.3 + pattern: | + (?x) + \b + ( + dvc_server_[A-Za-z0-9]{8,32} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 2 + min_entropy: 3.0 + confidence: medium + examples: + - dvc_server_abc12345 + - 'serverKey: "dvc_server_abcdefg1234"' + references: + - https://docs.devcycle.com/cli-guides/environments/ + - https://docs.devcycle.com/bucketing-api/ + validation: + type: Http + content: + request: + method: POST + url: https://bucketing-api.devcycle.com/v1/variables + headers: + Authorization: Bearer {{ TOKEN }} + Content-Type: application/json + body: | + { + "user_id": "kingfisher-validation-user" + } + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/fullstory.yml b/crates/kingfisher-rules/data/rules/fullstory.yml new file mode 100644 index 0000000..b9c4571 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/fullstory.yml @@ -0,0 +1,41 @@ +rules: + - name: Fullstory API Key + id: kingfisher.fullstory.1 + pattern: | + (?xi) + \b + (?:fullstory|fs_api_key|fullstory_api_key) + (?:.|[\n\r]){0,32}? + (?:SECRET|PRIVATE|ACCESS|KEY|TOKEN|AUTHORIZATION|API) + (?:.|[\n\r]){0,16}? + \b + ( + (?:na1|eu1)\.[A-Za-z0-9]{20,} + ) + \b + pattern_requirements: + min_digits: 2 + min_lowercase: 2 + min_entropy: 3.3 + confidence: medium + examples: + - FULLSTORY_API_KEY=na1.Abcd1234Efgh5678Ijkl9012Mnop3456 + - 'fs_api_key: "eu1.Abcd1234Efgh5678Ijkl9012Mnop3456"' + references: + - https://developer.fullstory.com/server/v1/getting-started/ + - https://developer.fullstory.com/server/authentication/ + - https://developer.fullstory.com/server/v1/authentication/me/ + validation: + type: Http + content: + request: + method: GET + url: https://api.fullstory.com/me + headers: + Authorization: Basic {{ TOKEN }} + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid diff --git a/crates/kingfisher-rules/data/rules/gcnotify.yml b/crates/kingfisher-rules/data/rules/gcnotify.yml new file mode 100644 index 0000000..1d83850 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/gcnotify.yml @@ -0,0 +1,41 @@ +rules: + - name: GC Notify API Key + id: kingfisher.gcnotify.1 + pattern: | + (?xi) + \b + ( + ApiKey-v1 + \s+ + gcntfy-[a-z0-9_]+ + - + [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} + - + [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} + ) + \b + pattern_requirements: + min_digits: 4 + min_lowercase: 2 + min_entropy: 3.5 + confidence: medium + examples: + - 'Authorization: "ApiKey-v1 gcntfy-my_test_key-26785a09-ab16-4eb0-8407-a37497a57506-3d844edf-8d35-48ac-975b-e847b4f122b0"' + references: + - https://documentation.notification.canada.ca/en/start.html + - https://documentation.notification.canada.ca/en/status.html + - https://documentation.notification.canada.ca/en/keys.html + validation: + type: Http + content: + request: + method: GET + url: https://api.notification.canada.ca/v2/notifications + 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/heroku.yml b/crates/kingfisher-rules/data/rules/heroku.yml index 96e94e4..d10ff87 100644 --- a/crates/kingfisher-rules/data/rules/heroku.yml +++ b/crates/kingfisher-rules/data/rules/heroku.yml @@ -20,6 +20,7 @@ rules: - 'HEROKU_API_KEY: c55dbac4-e0e8-4a06-b892-75cac2387ce5' references: - https://devcenter.heroku.com/articles/authentication + - https://devcenter.heroku.com/articles/oauth validation: type: Http content: @@ -33,6 +34,35 @@ rules: - report_response: true - type: StatusMatch status: [200] + revocation: + type: HttpMultiStep + content: + steps: + - name: lookup_authorization_id + request: + method: GET + url: https://api.heroku.com/oauth/authorizations + headers: + Accept: application/vnd.heroku+json; version=3 + Authorization: Bearer {{ TOKEN }} + response_matcher: + - type: StatusMatch + status: [200] + extract: + AUTHORIZATION_ID: + type: Regex + pattern: '"id":"([^"]+)"(?:.|[\n\r]){0,2048}?"token":"{{ TOKEN }}"' + - name: revoke_authorization + request: + method: DELETE + url: https://api.heroku.com/oauth/authorizations/{{ AUTHORIZATION_ID }} + headers: + Accept: application/vnd.heroku+json; version=3 + Authorization: Bearer {{ TOKEN }} + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] - name: Heroku API Key (Platform Key) id: kingfisher.heroku.2 pattern: | @@ -61,8 +91,38 @@ rules: - '"id":' - '"name":' match_all_words: true + revocation: + type: HttpMultiStep + content: + steps: + - name: lookup_authorization_id + request: + method: GET + url: https://api.heroku.com/oauth/authorizations + headers: + Accept: application/vnd.heroku+json; version=3 + Authorization: Bearer {{TOKEN}} + response_matcher: + - type: StatusMatch + status: [200] + extract: + AUTHORIZATION_ID: + type: Regex + pattern: '"id":"([^"]+)"(?:.|[\n\r]){0,2048}?"token":"{{ TOKEN }}"' + - name: revoke_authorization + request: + method: DELETE + url: https://api.heroku.com/oauth/authorizations/{{ AUTHORIZATION_ID }} + headers: + Accept: application/vnd.heroku+json; version=3 + Authorization: "Bearer {{TOKEN}}" + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] references: - https://devcenter.heroku.com/articles/platform-api-quickstart + - https://devcenter.heroku.com/articles/oauth examples: - "HRKU-AADVTUYvfjT4nhuJ07bEfAUq9GS3PkTdyWuNBiXYmYMg_____wgAf6OTnGyh" - "HRKU-AABW9W1iH9NHEIlAABq9nZUq9GS3PkTdyWuNBiXYmYMg_____wV2XYIXxm5p" diff --git a/crates/kingfisher-rules/data/rules/stytch.yml b/crates/kingfisher-rules/data/rules/stytch.yml new file mode 100644 index 0000000..97c7a57 --- /dev/null +++ b/crates/kingfisher-rules/data/rules/stytch.yml @@ -0,0 +1,76 @@ +rules: + - name: Stytch Project ID + id: kingfisher.stytch.1 + visible: false + pattern: | + (?xi) + \b + ( + project-(?:test|live)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} + ) + \b + min_entropy: 2.0 + confidence: medium + examples: + - project-test-8aed2e54-0266-4793-9b5e-0cc9c56064da + references: + - https://stytch.com/docs/api-reference/consumer/api/overview + - https://stytch.com/docs/api-reference/b2b/api/sessions/authenticate-jwt + + - name: Stytch Project Secret + id: kingfisher.stytch.2 + pattern: | + (?xi) + \b + ( + secret-(?:test|live)-[A-Za-z0-9_-]{35}=? + ) + \b + pattern_requirements: + min_digits: 2 + min_uppercase: 1 + min_lowercase: 1 + min_entropy: 3.5 + confidence: medium + examples: + - secret-test-IJ7zLTgXp8xoS7yXO2xavNxZTbYfvm-2nZM= + references: + - https://stytch.com/docs/api-reference/consumer/api/overview + - https://stytch.com/docs/api-reference/b2b/api/m2m/overview + - https://stytch.com/docs/api-reference/b2b/api/sessions/authenticate-jwt + depends_on_rule: + - rule_id: kingfisher.stytch.1 + variable: STYTCH_PROJECT_ID + validation: + type: Http + content: + request: + method: POST + url: > + {%- if TOKEN contains "-live-" -%} + https://api.stytch.com/v1/m2m/clients/search + {%- else -%} + https://test.stytch.com/v1/m2m/clients/search + {%- endif -%} + headers: + Authorization: "Basic {{ STYTCH_PROJECT_ID | append: ':' | append: TOKEN | b64enc }}" + Content-Type: application/json + body: | + { + "query": { + "operator": "AND", + "operands": [ + { + "filter_name": "status", + "filter_value": ["active"] + } + ] + } + } + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: WordMatch + words: + - '"m2m_clients"'