diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b681f1b..14bb7a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,19 +21,37 @@ jobs: - uses: swatinem/rust-cache@v2 + - name: Install packaging tools + run: cargo install cargo-deb cargo-generate-rpm + - name: Build (Makefile linux-x64) run: make linux-x64 + - name: Build Debian package + run: | + cargo deb --no-build --target x86_64-unknown-linux-musl \ + --output target/release/kingfisher-amd64.deb + + - name: Build RPM package + run: | + cargo generate-rpm --target x86_64-unknown-linux-musl \ + --output target/release/kingfisher-amd64.rpm + - name: Move artifact to dist shell: bash run: | mkdir -p dist cp target/release/kingfisher-linux-x64.tgz dist/ + cp target/release/kingfisher-amd64.deb dist/ + cp target/release/kingfisher-amd64.rpm dist/ - uses: actions/upload-artifact@v4 with: name: kingfisher-linux-x64 - path: dist/kingfisher-*linux-x64*.* + path: | + dist/kingfisher-linux-x64.tgz + dist/kingfisher-amd64.deb + dist/kingfisher-amd64.rpm linux-arm64: name: Linux arm64 @@ -49,19 +67,37 @@ jobs: - uses: swatinem/rust-cache@v2 + - name: Install packaging tools + run: cargo install cargo-deb cargo-generate-rpm + - name: Build (Makefile linux-arm64) run: make linux-arm64 + - name: Build Debian package + run: | + cargo deb --no-build --target aarch64-unknown-linux-musl \ + --output target/release/kingfisher-arm64.deb + + - name: Build RPM package + run: | + cargo generate-rpm --target aarch64-unknown-linux-musl \ + --output target/release/kingfisher-arm64.rpm + - name: Move artifact to dist shell: bash run: | mkdir -p dist cp target/release/kingfisher-linux-arm64.tgz dist/ + cp target/release/kingfisher-arm64.deb dist/ + cp target/release/kingfisher-arm64.rpm dist/ - uses: actions/upload-artifact@v4 with: name: kingfisher-linux-arm64 - path: dist/kingfisher-*linux-arm64*.* + path: | + dist/kingfisher-linux-arm64.tgz + dist/kingfisher-arm64.deb + dist/kingfisher-arm64.rpm macos-x64: diff --git a/CHANGELOG.md b/CHANGELOG.md index dffce78..9f609a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [1.24.0] +- Now generating DEB and RPM packages +- Now releasing Docker images, and updated README +- Added rule for Scale, Deepgram, AssemblyAI + + ## [1.23.0] - Updating GitHub Action to generate Docker image - Added rules for Diffbot, ai21, baseten diff --git a/Cargo.toml b/Cargo.toml index 3d6bed2..5ab6f6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace.package] edition = "2021" -rust-version = "1.83" +rust-version = "1.88" license = "Apache-2.0" authors = ["Mick Grove "] homepage = "https://github.com/mongodb/kingfisher" @@ -19,6 +19,25 @@ homepage.workspace = true repository.workspace = true publish.workspace = true +[package.metadata.deb] +name = "kingfisher" +maintainer = "Mick Grove " +depends = "$auto" +section = "utils" +priority = "optional" +assets = [ + ["target/release/kingfisher", "/usr/bin/kingfisher", "755"] +] + +[package.metadata.generate-rpm] +package = "kingfisher" +summary = "MongoDB's blazingly fast secret scanning and validation tool" +license = "Apache-2.0" +url = "https://github.com/mongodb/kingfisher" +assets = [ + { source = "target/release/kingfisher", dest = "/usr/bin/kingfisher", mode = "755" } +] + [dependencies] clap = { version = "4.5", features = [ "cargo", @@ -27,6 +46,7 @@ clap = { version = "4.5", features = [ "unicode", "wrap_help", ] } + anyhow = "1.0" bstr = { version = "1.12", features = ["serde"] } fixedbitset = "0.5" diff --git a/README.md b/README.md index 854aa97..fba254f 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,58 @@ make darwin-all # builds both x64 and arm64 make all # builds for every OS and architecture supported ``` + +### Run Kingfisher in Docker + + +Run the dockerized Kingfisher container: +```bash +# GitHub Container Registry +docker run --rm ghcr.io/mongodb/kingfisher:latest --version + +# Scan the current working directory +# (mounts your code at /src and scans it) +docker run --rm \ + -v "$PWD":/src \ + ghcr.io/mongodb/kingfisher:latest scan /src + + +# Scan while providing a GitHub token +# Mounts your working dir at /proj and passes in the token: +docker run --rm \ + -e KF_GITHUB_TOKEN=ghp_… \ + -v "$PWD":/proj \ + ghcr.io/mongodb/kingfisher:latest \ + scan --git-url https://github.com/org/private_repo.git + +# Scan and write a JSON report locally +# Here we: +# 1. Mount $PWD → /proj +# 2. Tell Kingfisher to write findings.json inside /proj/reports +# 3. Ensure ./reports exists on your host so Docker can mount it +mkdir -p reports + +# run and output into host’s ./reports directory +docker run --rm \ + -v "$PWD":/proj \ + ghcr.io/mongodb/kingfisher:latest \ + scan /proj \ + --format json \ + --output /proj/reports/findings.json + + +# Tip: you can combine multiple mounts if you prefer separating source vs. output: +# Here /src is read‑only, and /out holds your generated reports +docker run --rm \ + -v "$PWD":/src:ro \ + -v "$PWD/reports":/out \ + ghcr.io/mongodb/kingfisher:latest \ + scan /src \ + --format json \ + --output /out/findings.json + +``` + # 🔐 Detection Rules at a Glance Kingfisher ships with hundreds of rules that cover everything from classic cloud keys to the latest LLM-API secrets. Below is an overview: diff --git a/data/rules/assemblyai.yml b/data/rules/assemblyai.yml new file mode 100644 index 0000000..3c81111 --- /dev/null +++ b/data/rules/assemblyai.yml @@ -0,0 +1,38 @@ +rules: + - name: AssemblyAI API Key + id: kingfisher.assemblyai.1 + pattern: | + (?xi) + \b + assemblyai + (?:.|[\n\r]){0,32}? + \b + ( + [0-9a-z]{32} + ) + \b + min_entropy: 3.0 + confidence: medium + examples: + - assemblyai = fa0ed91518b345468f9df7570f31f18a + - assemblyai_token = a741b921ae1f4446826a784726b6a71a + references: + - https://www.assemblyai.com/docs/api-reference/overview + - https://www.assemblyai.com/docs/api-reference/transcripts/list + + validation: + type: Http + content: + request: + method: GET + url: https://api.assemblyai.com/v2/transcript?limit=1 + headers: + Authorization: '{{ TOKEN }}' + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + - type: WordMatch + match_all_words: true + words: ['"page_details"', '"transcripts"'] diff --git a/data/rules/deepgram.yml b/data/rules/deepgram.yml new file mode 100644 index 0000000..850afa9 --- /dev/null +++ b/data/rules/deepgram.yml @@ -0,0 +1,41 @@ +rules: + - name: Deepgram API Key + id: kingfisher.deepgram.1 + pattern: | + (?xi) + \b + deepgram + (?:.|[\n\r]){0,32}? + \b + ( + [0-9a-f]{40} + ) + \b + min_entropy: 3.5 + confidence: medium + examples: + - deepgram_key = 948c19ecde2818a1a357fffb14d2fc2a03d3c56e + - 'deepgram-api: 6c8ba06cb14a32d508948606d8b5d9c8f70e493b' + - deepgram = 12e217f37eb173f0c8f1b7309f4207c7dca20186 + - deepgram token 1 == 1f8946087e64b14dffd069b78554e217b3ed34d4 + references: + - https://developers.deepgram.com/docs/authenticating + - https://developers.deepgram.com/reference/management-api/models/list + - https://developers.deepgram.com/reference/list-keys + + validation: + type: Http + content: + request: + method: GET + url: https://api.deepgram.com/v1/projects + headers: + Authorization: Token {{ TOKEN }} + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + - type: WordMatch + match_all_words: true + words: ['"name"'] diff --git a/data/rules/pagerdutyapikey.yml b/data/rules/pagerdutyapikey.yml index d65bced..d51814e 100644 --- a/data/rules/pagerdutyapikey.yml +++ b/data/rules/pagerdutyapikey.yml @@ -4,9 +4,7 @@ rules: pattern: | (?xi) \b - (?: - Token | - Authorization | + (?: pd[_-]? | pd[_-]? | pagerduty[_-]? | @@ -19,7 +17,7 @@ rules: ( u\+[A-Z0-9_+-]{18} | # personal user token (20 chars) [A-Z0-9_-]{20} | # legacy PAT (20 chars, mixed case) - [a-f0-9]{32} # integration / routing key (32 hex, lower case) + [a-f0-9]{32} # integration / routing key (32 hex, lower case) ) \b min_entropy: 3.5 diff --git a/data/rules/scale.yml b/data/rules/scale.yml new file mode 100644 index 0000000..b0d9b90 --- /dev/null +++ b/data/rules/scale.yml @@ -0,0 +1,60 @@ +rules: + - name: Scale API Key + id: kingfisher.scale.1 + pattern: | + (?x) + \b + ( # capture => TOKEN + live_ # live-mode prefix per docs + [0-9a-f]{32} # 32 lowercase hex chars + ) + \b + min_entropy: 3.1 + confidence: medium + examples: + - live_8df31399ec4a4755a7cf9e0fb59f967a + - live_54d1bd2d1e62430bb2d521d298ec4231 + - live_1b9fc721a4624a478211ce613c674a03 + references: + - https://scale.com/docs/api-reference/authentication + - https://scale.com/docs/api-reference/studio#list-all-teammates + - https://scale.com/docs/api-reference/authentication#test-and-live-modes + + validation: + type: Http + content: + request: + method: GET + url: https://api.scale.com/v1/teams + headers: + Authorization: 'Basic {{ TOKEN | append: ":" | b64enc }}' + Accept: application/json + response_matcher: + - report_response: true + - type: StatusMatch + status: [200] + - type: JsonValid + - type: WordMatch + match_all_words: true + words: + - '"email"' + - '"role"' + + - name: Scale Callback Auth Key + id: kingfisher.scale.2 + pattern: | + (?x) + \b + ( + live_auth_ + [0-9a-f]{32} + ) + \b + min_entropy: 3.1 + confidence: medium + examples: + - live_auth_250ae896ada542c08a95734f935c871a + references: + - https://scale.com/docs/api-reference/authentication#callback-authentication + # Callback keys are *only* echoed by Scale in webhook headers and + # can’t be validated via an API call, so no `validation:` block. diff --git a/docker/Dockerfile b/docker/Dockerfile index 317f5d1..f44e792 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 FROM alpine:latest -RUN apk add --no-cache curl tar +RUN apk add --no-cache curl tar git sh ARG TARGETARCH # set automatically by BuildKit ENV TARGETARCH=${TARGETARCH}