forked from mirrors/kingfisher
preparing for v1.12
This commit is contained in:
commit
fc4aee9e41
249 changed files with 121395 additions and 0 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
testdata/* linguist-vendored
|
||||
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
name: "🐛 Bug report"
|
||||
about: "Something isn’t working as expected"
|
||||
title: "[BUG] <short description>"
|
||||
labels: [bug]
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### 📋 Checklist
|
||||
|
||||
- [ ] I’m running **the latest `main` or a recent release**.
|
||||
- [ ] I’ve searched existing issues and **no open issue covers this bug**.
|
||||
- [ ] If this is a build problem, I attached the **full error log**.
|
||||
|
||||
---
|
||||
|
||||
### What version?
|
||||
|
||||
`kingfisher --version` output
|
||||
|
||||
### 🐞 What happened?
|
||||
|
||||
_A clear and concise description of what went wrong._
|
||||
|
||||
### ✅ What did you expect to happen?
|
||||
|
||||
_What *should* have happened?_
|
||||
|
||||
### 🔢 Reproduction steps
|
||||
|
||||
1. …
|
||||
2. …
|
||||
3. …
|
||||
|
||||
### 💻 Environment
|
||||
|
||||
| Item | Value |
|
||||
| --------------- | ----- |
|
||||
| OS / Distro | `<!-- e.g. Windows 11 22H2 / Ubuntu 24.04 → arm64 -->` |
|
||||
| Rust toolchain | `rustc --version` → |
|
||||
| kingfisher ver. | `<!-- kingfisher --version OR git rev-parse HEAD OR release tag -->` |
|
||||
|
||||
### 📎 Log / stack trace
|
||||
|
||||
<details>
|
||||
<summary>Click to expand</summary>
|
||||
|
||||
```text
|
||||
# Paste or drag-and-drop here
|
||||
```
|
||||
</details>
|
||||
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
---
|
||||
|
||||
#### `.github/ISSUE_TEMPLATE/feature_request.md`
|
||||
|
||||
```md
|
||||
---
|
||||
name: "🚀 Feature request"
|
||||
about: "Suggest an idea or improvement"
|
||||
title: "[FEAT] <short description>"
|
||||
labels: [enhancement]
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
### 🌟 Is your feature request related to a problem?
|
||||
|
||||
_A short statement of *why* this feature matters. Example: “Cross-compiling on
|
||||
macOS requires Homebrew’s outdated musl gcc; integrating Zig would remove that
|
||||
dependency.”_
|
||||
|
||||
### 📝 Describe the solution you’d like
|
||||
|
||||
_A clear, concise description of what you want to happen, including interface
|
||||
changes, flags, or user-visible behavior._
|
||||
|
||||
### 🔄 Describe alternatives you’ve considered
|
||||
|
||||
- **Option A:** …
|
||||
- **Option B:** …
|
||||
|
||||
### 📚 Additional context
|
||||
|
||||
_Anything else—diagrams, links, prior art, screenshots—that helps explain the
|
||||
request._
|
||||
42
.github/workflows/ci.yml
vendored
Normal file
42
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
name: CI Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# This workflow runs on pull requests to the main branch
|
||||
# It builds the project for 2 platforms, Linux arm64 and macOS arm64,
|
||||
# and runs tests for each platform. All platforms tested on merge to main
|
||||
jobs:
|
||||
linux-arm64:
|
||||
name: Linux arm64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.85.0
|
||||
profile: minimal
|
||||
override: true
|
||||
- uses: swatinem/rust-cache@v2
|
||||
- name: Build (Makefile linux-arm64)
|
||||
run: make ubuntu-arm64
|
||||
- name: Run tests
|
||||
run: make tests
|
||||
|
||||
macos-arm64:
|
||||
name: macOS arm64
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.85.0
|
||||
profile: minimal
|
||||
override: true
|
||||
- uses: swatinem/rust-cache@v2
|
||||
- name: Build (Makefile darwin-arm64)
|
||||
run: make darwin-arm64
|
||||
- name: Run tests
|
||||
run: make tests
|
||||
199
.github/workflows/release.yml
vendored
Normal file
199
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
name: build-and-release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
# ──────────────── Linux (via Makefile) ────────────────
|
||||
linux-x64:
|
||||
name: Linux x64
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.85.0
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: swatinem/rust-cache@v2
|
||||
|
||||
- name: Build (Makefile linux-x64)
|
||||
run: make linux-x64
|
||||
|
||||
- name: Move artifact to dist
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp target/release/kingfisher-linux-x64.tgz dist/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kingfisher-linux-x64
|
||||
path: dist/kingfisher-*linux-x64*.*
|
||||
|
||||
linux-arm64:
|
||||
name: Linux arm64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.85.0
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: swatinem/rust-cache@v2
|
||||
|
||||
- name: Build (Makefile linux-arm64)
|
||||
run: make linux-arm64
|
||||
|
||||
- name: Move artifact to dist
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp target/release/kingfisher-linux-arm64.tgz dist/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kingfisher-linux-arm64
|
||||
path: dist/kingfisher-*linux-arm64*.*
|
||||
|
||||
|
||||
macos-x64:
|
||||
name: macOS x64
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.85.0
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: swatinem/rust-cache@v2
|
||||
|
||||
- name: Build Darwin x64
|
||||
run: make darwin-x64
|
||||
|
||||
- name: Move artifacts to dist
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp target/release/kingfisher-darwin-x64.tgz dist/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kingfisher-darwin-x64.tgz
|
||||
path: dist/kingfisher-darwin-x64.tgz
|
||||
|
||||
|
||||
macos-arm64:
|
||||
name: macOS arm64
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.85.0
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- uses: swatinem/rust-cache@v2
|
||||
|
||||
- name: Build Darwin arm64
|
||||
run: make darwin-arm64
|
||||
|
||||
- name: Move artifacts to dist
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp target/release/kingfisher-darwin-arm64.tgz dist/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kingfisher-darwin-arm64.tgz
|
||||
path: dist/kingfisher-darwin-arm64.tgz
|
||||
|
||||
|
||||
# ──────────────── Windows ────────────────
|
||||
windows:
|
||||
name: Windows x64
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.85.0
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Cache vcpkg artifacts
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
# Adjust these paths if your vcpkg root is somewhere else
|
||||
path: |
|
||||
C:\vcpkg\buildtrees
|
||||
C:\vcpkg\packages
|
||||
C:\vcpkg\installed
|
||||
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.cmake') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Build
|
||||
run: .\buildwin.bat -force
|
||||
shell: cmd
|
||||
|
||||
- name: Run tests
|
||||
shell: pwsh
|
||||
run: |
|
||||
if (-not (Get-Command cargo-nextest -ErrorAction SilentlyContinue)) {
|
||||
cargo install --locked cargo-nextest
|
||||
}
|
||||
Write-Host "▶ cargo nextest run --release --workspace --all-targets"
|
||||
cargo nextest run --release --workspace --all-targets
|
||||
|
||||
- name: Move artifact to dist
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp target/release/kingfisher-windows-x64.zip dist/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kingfisher-windows-x64
|
||||
path: dist/kingfisher-*windows-x64*.*
|
||||
|
||||
# ──────────────── Draft public release ────────────────
|
||||
release:
|
||||
name: Public GitHub Release
|
||||
needs: [linux-x64, linux-arm64, windows, macos-x64, macos-arm64] # wait for all builds to finish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # allow release upload
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Read version from Cargo.toml
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(grep -m1 '^version\s*=' Cargo.toml | cut -d '"' -f2)
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: target/release/kingfisher-*
|
||||
merge-multiple: true
|
||||
- name: Create release & upload assets
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: v${{ steps.version.outputs.version }}
|
||||
name: "Kingfisher v${{ steps.version.outputs.version }}"
|
||||
bodyFile: CHANGELOG.md # use existing changelog
|
||||
generateReleaseNotes: false # turn off auto-notes
|
||||
artifacts: target/release/**
|
||||
205
.gitignore
vendored
Normal file
205
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,rust
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,visualstudiocode,rust
|
||||
|
||||
*.log
|
||||
*.sarif
|
||||
*.profile.json
|
||||
*.json
|
||||
*.jsonl
|
||||
*.bson
|
||||
.prettierrc
|
||||
custom.py
|
||||
logs/*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
### Rust ###
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
bin/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,rust
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/intellij,rust-analyzer
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,rust-analyzer
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
# Azure Toolkit for IntelliJ plugin
|
||||
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
|
||||
.idea/**/azureSettings.xml
|
||||
|
||||
### rust-analyzer ###
|
||||
# Can be generated by other build systems other than cargo (ex: bazelbuild/rust_rules)
|
||||
rust-project.json
|
||||
.dockerignore
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/intellij,rust-analyzer
|
||||
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [1.12.0]
|
||||
- Added automatic update checks using GitHub releases
|
||||
- New `--self-update` flag installs updates when available
|
||||
- New `--no-update-check` flag disables update checks
|
||||
- Updated rules
|
||||
|
||||
## [1.11.0] 2025-06-21
|
||||
- Increased default value for number of scanning jobs to improve validation speed
|
||||
- Fixed issue where some API responses (e.g. GitHub's `/user` endpoint) include required fields like `"name"` beyond the first 512 bytes. Truncating earlier causes `WordMatch` checks to fail even for active credentials. Increased the limit to keep a larger slice of the body while still bounding memory usage.
|
||||
|
||||
## [1.10.0] 2025-06-20
|
||||
- Updated de-dupe fingerprint to include the content of the match
|
||||
- Updated Makefile
|
||||
- Adding GitHub Actions
|
||||
|
||||
## [1.9.0] 2025-06-16
|
||||
- Initial public release of Kingfisher
|
||||
204
Cargo.toml
Normal file
204
Cargo.toml
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.83"
|
||||
license = "Apache-2.0"
|
||||
authors = ["Mick Grove <mick.grove@mongodb.com>"]
|
||||
homepage = "https://github.com/mongodb/kingfisher"
|
||||
repository = "https://github.com/mongodb/kingfisher"
|
||||
publish = false
|
||||
|
||||
|
||||
[package]
|
||||
name = "kingfisher"
|
||||
version = "1.12.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.3", features = [
|
||||
"cargo",
|
||||
"derive",
|
||||
"env",
|
||||
"unicode",
|
||||
"wrap_help",
|
||||
] }
|
||||
anyhow = "1.0"
|
||||
bstr = { version = "1.0", features = ["serde"] }
|
||||
fixedbitset = "0.5"
|
||||
gix = { version = "0.72", features = ["max-performance", "serde", "blocking-network-client"] }
|
||||
ignore = "0.4"
|
||||
petgraph = "0.6"
|
||||
roaring = "0.10"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
smallvec = { version = "1", features = [
|
||||
"const_generics",
|
||||
"const_new",
|
||||
"union",
|
||||
] }
|
||||
tracing = "0.1.41"
|
||||
indicatif = { version = "0.17", features = ["improved_unicode"] }
|
||||
rayon = "1.10"
|
||||
sha1 = "0.10.6"
|
||||
hex = "0.4.3"
|
||||
vectorscan-rs = "0.0.5"
|
||||
regex = "1.10.6"
|
||||
serde_json = "1.0.128"
|
||||
lazy_static = "1.5.0"
|
||||
url = "2.5.2"
|
||||
include_dir = { version = "0.7", features = ["glob"] }
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
sysinfo = "0.31.2"
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"json",
|
||||
"gzip",
|
||||
"brotli",
|
||||
"deflate",
|
||||
"stream",
|
||||
"rustls-tls",
|
||||
"blocking",
|
||||
"multipart",
|
||||
"rustls-tls",
|
||||
] }
|
||||
|
||||
|
||||
chrono = "0.4.38"
|
||||
thiserror = "1.0.63"
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
base64 = "0.22.1"
|
||||
crossbeam-channel = "0.5.13"
|
||||
indenter = "0.3.3"
|
||||
serde-sarif = "0.4"
|
||||
console = "0.15.8"
|
||||
time = "0.3.36"
|
||||
tempfile = "3.12.0"
|
||||
num_cpus = "1.16.0"
|
||||
once_cell = "1.19.0"
|
||||
http = "1.1.0"
|
||||
liquid = "0.26.4"
|
||||
liquid-core = "0.26.4"
|
||||
flate2 = "1.0.33"
|
||||
brotli = "6.0.0"
|
||||
thousands = "0.2.0"
|
||||
base32 = "0.5.1"
|
||||
crossbeam-skiplist = "0.1.3"
|
||||
tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"] }
|
||||
mongodb = { version = "3.2", default-features = false, features = ["rustls-tls", "aws-auth", "compat-3-0-0", "dns-resolver"] }
|
||||
bson = "2.13.0"
|
||||
ring = "0.17.8"
|
||||
pem = "3.0.4"
|
||||
aws-config = "1.5.10"
|
||||
aws-credential-types = "1.2.1"
|
||||
aws-sdk-sts = "1.21.0"
|
||||
aws-types = "1.3.3"
|
||||
byteorder = "1.5.0"
|
||||
parking_lot = "0.12.3"
|
||||
octorust = "0.9.0"
|
||||
reqwest-middleware = "0.4.1"
|
||||
tracing-subscriber = {version = "0.3.19", features = ["env-filter"] }
|
||||
tracing-core = "0.1.33"
|
||||
tree-sitter = "0.24.4"
|
||||
tree-sitter-bash = "0.23.3"
|
||||
tree-sitter-c = "0.23.2"
|
||||
tree-sitter-c-sharp = "0.23.1"
|
||||
tree-sitter-cpp = "0.23.4"
|
||||
tree-sitter-css = "0.23.1"
|
||||
tree-sitter-go = "0.23.4"
|
||||
tree-sitter-html = "0.23.2"
|
||||
tree-sitter-java = "0.23.4"
|
||||
tree-sitter-javascript = "0.23.1"
|
||||
tree-sitter-php = "0.23.11"
|
||||
tree-sitter-python = "0.23.4"
|
||||
tree-sitter-ruby = "0.23.1"
|
||||
tree-sitter-rust = "0.23.2"
|
||||
tree-sitter-toml-ng = "0.7.0"
|
||||
tree-sitter-typescript = "0.23.2"
|
||||
tree-sitter-yaml = "0.6.1"
|
||||
streaming-iterator = "0.1.9"
|
||||
tree-sitter-regex = "0.24.3"
|
||||
content_inspector = "0.2.4"
|
||||
rustc-hash = "2.1.0"
|
||||
term_size = "0.3.2"
|
||||
bzip2 = "0.5.0"
|
||||
zip = "2.2.2"
|
||||
tar = "0.4.43"
|
||||
xz2 = "0.1.7"
|
||||
asar = "0.3.0"
|
||||
blake3 = "1.5.5"
|
||||
memmap2 = "0.9.5"
|
||||
futures = "0.3.31"
|
||||
dashmap = "6.1.0"
|
||||
xxhash-rust = { version = "0.8.15", features = ["xxh3", "const_xxh3"] }
|
||||
serde_yaml = "0.9.34"
|
||||
hmac = "0.12.1"
|
||||
sha2 = "0.10.8"
|
||||
strum_macros = "0.27.1"
|
||||
humantime = "2.2.0"
|
||||
path-dedot = "3.1.1"
|
||||
quick-xml = {version = "0.37.5", features = ["serde","serialize"] }
|
||||
rustls = "0.23.26"
|
||||
tokio-postgres-rustls = "0.13.0"
|
||||
rustls-native-certs = "0.8.1"
|
||||
predicates = "3.1.3"
|
||||
assert_cmd = "2.0.17"
|
||||
proptest = "1.6.0"
|
||||
color-backtrace = "0.7.0"
|
||||
gitlab = "0.1711.0"
|
||||
mimalloc = {version = "0.1.46", features = ["override"]}
|
||||
thread_local = "1.1.8"
|
||||
crc32fast = "1.4.2"
|
||||
bloomfilter = "3.0.1"
|
||||
uuid = "1.17.0"
|
||||
urlencoding = "2.1.3"
|
||||
rand = "0.9.1"
|
||||
percent-encoding = "2.3.1"
|
||||
trust-dns-resolver = { version = "0.23.2", default-features = false, features = ["tokio-runtime"] }
|
||||
atty = "0.2.14"
|
||||
self_update = { version = "0.42.0", default-features = false, features = ["rustls"] }
|
||||
|
||||
[dependencies.tikv-jemallocator]
|
||||
version = "0.6"
|
||||
optional = true
|
||||
|
||||
|
||||
[features]
|
||||
default = ["use-mimalloc"]
|
||||
use-mimalloc = ["mimalloc/override"]
|
||||
use-jemalloc = ["tikv-jemallocator"]
|
||||
system-alloc = [] # forces System allocator
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.3"
|
||||
temp-env = "0.3.6"
|
||||
wiremock = "0.6.2"
|
||||
git2 = "0.20.2"
|
||||
rand_chacha = "0.9.0"
|
||||
|
||||
[profile.release]
|
||||
debug = false
|
||||
strip = "debuginfo"
|
||||
opt-level = 3 # Maximum optimization for performance
|
||||
lto = true # Enable Link Time Optimization
|
||||
codegen-units = 1 # Optimize for size but slower compilation
|
||||
# panic = "abort" # Remove unwind tables for panics
|
||||
rpath = false # Don't embed path dependencies
|
||||
incremental = false
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
debug = true
|
||||
incremental = true
|
||||
codegen-units = 256
|
||||
|
||||
[patch.crates-io]
|
||||
vectorscan-rs = { path = "vendor/vectorscan-rs/vectorscan-rs" }
|
||||
vectorscan-rs-sys = { path = "vendor/vectorscan-rs/vectorscan-rs-sys" }
|
||||
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
201
LICENSE
Normal file
201
LICENSE
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
429
Makefile
Normal file
429
Makefile
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
SHELL := /usr/bin/env bash
|
||||
.SHELLFLAGS := -eu -o pipefail -c
|
||||
|
||||
# Detect project name from Cargo.toml
|
||||
PROJECT_NAME := $(shell grep '^name' Cargo.toml | cut -d '"' -f 2)
|
||||
|
||||
# Determine OS and whether to use gtar on darwin
|
||||
OS := $(shell uname)
|
||||
ifneq ($(OS),darwin)
|
||||
USE_GTAR := 0
|
||||
TAR_CMD := tar
|
||||
TAR_OPTS := $(shell if tar --help 2>/dev/null | grep -q -- '--no-xattrs'; then echo '--no-xattrs -czf'; else echo '-czf'; fi)
|
||||
else
|
||||
ifneq ($(shell command -v gtar 2>/dev/null),)
|
||||
USE_GTAR := 1
|
||||
TAR_CMD := gtar
|
||||
TAR_OPTS := --no-xattrs -czf
|
||||
else
|
||||
USE_GTAR := 0
|
||||
TAR_CMD := tar
|
||||
TAR_OPTS := -czf
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(OS),darwin)
|
||||
export HOMEBREW_NO_INSTALL_CLEANUP=1
|
||||
export HOMEBREW_NO_ENV_HINTS=1
|
||||
endif
|
||||
|
||||
# detect host architecture and map to our target suffixes
|
||||
UNAME_M := $(shell uname -m)
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
ARCH := x64
|
||||
else ifeq ($(UNAME_M),amd64)
|
||||
ARCH := x64
|
||||
else ifeq ($(UNAME_M),arm64)
|
||||
ARCH := arm64
|
||||
else ifeq ($(UNAME_M),aarch64)
|
||||
ARCH := arm64
|
||||
else
|
||||
$(error Unsupported architecture: $(UNAME_M))
|
||||
endif
|
||||
|
||||
ARCHIVE_CMD = $(TAR_CMD) $(TAR_OPTS)
|
||||
SUDO_CMD := $(shell command -v sudo 2>/dev/null)
|
||||
|
||||
.PHONY: default help create-dockerignore ubuntu-x64 ubuntu-arm64 linux-x64 linux-arm64 darwin-arm64 darwin-x64 windows-x64 windows \
|
||||
linux darwin all list-archives check-docker check-rust clean tests
|
||||
|
||||
default: help
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " create-dockerignore"
|
||||
@echo " linux-x64"
|
||||
@echo " linux-arm64"
|
||||
@echo " linux"
|
||||
@echo " darwin-arm64"
|
||||
@echo " darwin-x64"
|
||||
@echo " darwin"
|
||||
@echo " windows-x64"
|
||||
@echo " windows"
|
||||
@echo " all"
|
||||
@echo " list-archives"
|
||||
@echo " tests"
|
||||
|
||||
create-dockerignore:
|
||||
@echo "target/" > .dockerignore
|
||||
@echo ".git/" >> .dockerignore
|
||||
@echo ".vscode/" >> .dockerignore
|
||||
@echo "bin/" >> .dockerignore
|
||||
|
||||
|
||||
.PHONY: setup-zig
|
||||
setup-zig:
|
||||
@command -v zig >/dev/null 2>&1 || { \
|
||||
echo "⬇️ Installing Zig 0.14.0 …"; \
|
||||
if $(SUDO_CMD) apt-get update -qq && \
|
||||
$(SUDO_CMD) apt-get install -y --no-install-recommends zig 2>/dev/null ; then \
|
||||
echo "✓ Zig installed via apt"; \
|
||||
else \
|
||||
echo "⚠️ Package 'zig' not in apt repos – falling back to manual install"; \
|
||||
arch=$$(uname -m); \
|
||||
case "$$arch" in \
|
||||
x86_64) pkg=zig-linux-x86_64-0.14.0 ;; \
|
||||
aarch64|arm64) pkg=zig-linux-aarch64-0.14.0 ;; \
|
||||
*) echo "Unsupported architecture: $$arch"; exit 1 ;; \
|
||||
esac; \
|
||||
curl -L -o /tmp/zig.tar.xz https://ziglang.org/download/0.14.0/$${pkg}.tar.xz; \
|
||||
tar -C /tmp -xf /tmp/zig.tar.xz; \
|
||||
$(SUDO_CMD) mv /tmp/$${pkg} /opt/zig; \
|
||||
$(SUDO_CMD) ln -sf /opt/zig/zig /usr/local/bin/zig; \
|
||||
echo "✓ Zig installed to /usr/local/bin/zig"; \
|
||||
fi; \
|
||||
}
|
||||
|
||||
@if [ -f "$$HOME/.cargo/env" ]; then . $$HOME/.cargo/env; fi && \
|
||||
(cargo zigbuild --help >/dev/null 2>&1 || { \
|
||||
echo "⬇️ Installing cargo-zigbuild …"; \
|
||||
cargo install --locked cargo-zigbuild; \
|
||||
})
|
||||
|
||||
|
||||
# ============= BAREMETAL BUILDS (Check Rust first, install if missing) =============
|
||||
#
|
||||
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
# ubuntu-x64 — native static build for x86_64-unknown-linux-musl via Zig. Tested on Ubuntu 24.04.
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
ubuntu-x64: setup-zig # ensures Zig & cargo-zigbuild exist
|
||||
@echo "Checking Rust toolchain…"
|
||||
@$(MAKE) check-rust || { \
|
||||
echo "🦀 Installing Rust 1.85.0 …"; \
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; \
|
||||
. $$HOME/.cargo/env; \
|
||||
rustup toolchain install 1.85.0; \
|
||||
rustup default 1.85.0; \
|
||||
}
|
||||
|
||||
@echo "📦 Installing build dependencies (musl, cmake, etc.)…"
|
||||
@$(SUDO_CMD) DEBIAN_FRONTEND=noninteractive apt-get update -qq
|
||||
@$(SUDO_CMD) DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential musl-tools musl-dev cmake pkg-config \
|
||||
zlib1g-dev libbz2-dev liblzma-dev libboost-all-dev \
|
||||
patch perl ragel
|
||||
|
||||
@echo "🔨 Building $(PROJECT_NAME) for x86_64-unknown-linux-musl …"
|
||||
@. $$HOME/.cargo/env && \
|
||||
rustup target add x86_64-unknown-linux-musl && \
|
||||
export PKG_CONFIG_ALLOW_CROSS=1 && \
|
||||
cargo zigbuild --release --target x86_64-unknown-linux-musl
|
||||
|
||||
@echo "🗜️ Packaging archive …"
|
||||
@cd target/x86_64-unknown-linux-musl/release && \
|
||||
find ./$(PROJECT_NAME) -type f -executable -exec sha256sum {} \; > CHECKSUM.txt
|
||||
@mkdir -p target/release
|
||||
@cp target/x86_64-unknown-linux-musl/release/$(PROJECT_NAME) target/release/
|
||||
@cp target/x86_64-unknown-linux-musl/release/CHECKSUM.txt target/release/CHECKSUM-linux-x64.txt
|
||||
@cd target/release && \
|
||||
rm -rf $(PROJECT_NAME)-linux-x64.tgz && \
|
||||
$(ARCHIVE_CMD) $(PROJECT_NAME)-linux-x64.tgz $(PROJECT_NAME) CHECKSUM-linux-x64.txt && \
|
||||
sha256sum $(PROJECT_NAME)-linux-x64.tgz >> CHECKSUM-linux-x64.txt
|
||||
|
||||
$(MAKE) list-archives
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
# ubuntu-arm64 — native cross-compile to aarch64-unknown-linux-musl via Zig. Tested on Ubuntu 24.04.
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
ubuntu-arm64: setup-zig # ensures Zig & cargo-zigbuild exist
|
||||
@echo "Checking Rust toolchain…"
|
||||
@$(MAKE) check-rust || { \
|
||||
echo "🦀 Installing Rust 1.85.0 …"; \
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; \
|
||||
. $$HOME/.cargo/env; \
|
||||
rustup toolchain install 1.85.0; \
|
||||
rustup default 1.85.0; \
|
||||
}
|
||||
|
||||
@echo "📦 Installing build dependencies (musl, cmake, etc.)…"
|
||||
@$(SUDO_CMD) DEBIAN_FRONTEND=noninteractive apt-get update -qq
|
||||
@$(SUDO_CMD) DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
build-essential musl-tools musl-dev cmake pkg-config \
|
||||
zlib1g-dev libbz2-dev liblzma-dev libboost-all-dev \
|
||||
patch perl ragel
|
||||
|
||||
@echo "🔨 Building $(PROJECT_NAME) for aarch64-unknown-linux-musl …"
|
||||
@. $$HOME/.cargo/env && \
|
||||
rustup target add aarch64-unknown-linux-musl && \
|
||||
export PKG_CONFIG_ALLOW_CROSS=1 && \
|
||||
cargo zigbuild --release --target aarch64-unknown-linux-musl
|
||||
|
||||
@echo "🗜️ Packaging archive …"
|
||||
@cd target/aarch64-unknown-linux-musl/release && \
|
||||
find ./$(PROJECT_NAME) -type f -executable -exec sha256sum {} \; > CHECKSUM.txt
|
||||
@mkdir -p target/release
|
||||
@cp target/aarch64-unknown-linux-musl/release/$(PROJECT_NAME) target/release/
|
||||
@cp target/aarch64-unknown-linux-musl/release/CHECKSUM.txt target/release/CHECKSUM-linux-arm64.txt
|
||||
@cd target/release && \
|
||||
rm -rf $(PROJECT_NAME)-linux-arm64.tgz && \
|
||||
$(ARCHIVE_CMD) $(PROJECT_NAME)-linux-arm64.tgz $(PROJECT_NAME) CHECKSUM-linux-arm64.txt && \
|
||||
sha256sum $(PROJECT_NAME)-linux-arm64.tgz >> CHECKSUM-linux-arm64.txt
|
||||
|
||||
$(MAKE) list-archives
|
||||
|
||||
|
||||
darwin-arm64:
|
||||
@echo "Checking Rust for darwin-arm64..."
|
||||
@$(MAKE) check-rust || ( \
|
||||
echo "Rust not found or out-of-date. Installing via Homebrew..." && \
|
||||
brew install rust \
|
||||
)
|
||||
@brew install boost cmake gcc libpcap pkg-config ragel sqlite coreutils gnu-tar || true
|
||||
@rustup target add aarch64-apple-darwin
|
||||
cargo build --release --target aarch64-apple-darwin --features system-alloc
|
||||
@cd target/aarch64-apple-darwin/release && \
|
||||
find ./$(PROJECT_NAME) -type f -not -name "*.d" -not -name "*.rlib" -exec shasum -a 256 {} \; > CHECKSUM.txt
|
||||
@mkdir -p target/release
|
||||
@cp target/aarch64-apple-darwin/release/$(PROJECT_NAME) target/release/
|
||||
@cp target/aarch64-apple-darwin/release/CHECKSUM.txt target/release/CHECKSUM-darwin-arm64.txt
|
||||
@cd target/release && \
|
||||
rm -rf $(PROJECT_NAME)-darwin-arm64.tgz && \
|
||||
$(ARCHIVE_CMD) $(PROJECT_NAME)-darwin-arm64.tgz $(PROJECT_NAME) CHECKSUM-darwin-arm64.txt && \
|
||||
if [ -f $(PROJECT_NAME)-darwin-arm64.tgz ]; then \
|
||||
shasum -a 256 $(PROJECT_NAME)-darwin-arm64.tgz >> CHECKSUM-darwin-arm64.txt; \
|
||||
fi
|
||||
$(MAKE) list-archives
|
||||
|
||||
darwin-x64:
|
||||
@echo "Checking Rust for darwin-x64..."
|
||||
@$(MAKE) check-rust || ( \
|
||||
echo "Rust not found or out-of-date. Installing via Homebrew..." && \
|
||||
brew install rust \
|
||||
)
|
||||
@brew install boost cmake gcc libpcap pkg-config ragel sqlite coreutils gnu-tar || true
|
||||
@rustup target add x86_64-apple-darwin
|
||||
source $$HOME/.cargo/env && cargo build --release --target x86_64-apple-darwin --features system-alloc
|
||||
@cd target/x86_64-apple-darwin/release && \
|
||||
find ./$(PROJECT_NAME) -type f -not -name "*.d" -not -name "*.rlib" -exec shasum -a 256 {} \; > CHECKSUM.txt
|
||||
@mkdir -p target/release
|
||||
@cp target/x86_64-apple-darwin/release/$(PROJECT_NAME) target/release/
|
||||
@cp target/x86_64-apple-darwin/release/CHECKSUM.txt target/release/CHECKSUM-darwin-x64.txt
|
||||
@cd target/release && \
|
||||
rm -rf $(PROJECT_NAME)-darwin-x64.tgz && \
|
||||
$(ARCHIVE_CMD) $(PROJECT_NAME)-darwin-x64.tgz $(PROJECT_NAME) CHECKSUM-darwin-x64.txt && \
|
||||
if [ -f $(PROJECT_NAME)-darwin-x64.tgz ]; then \
|
||||
shasum -a 256 $(PROJECT_NAME)-darwin-x64.tgz >> CHECKSUM-darwin-x64.txt; \
|
||||
fi
|
||||
$(MAKE) list-archives
|
||||
|
||||
windows-x64:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@echo "Detected Windows host."
|
||||
else
|
||||
$(error "This target can only run on Windows.")
|
||||
endif
|
||||
buildwin.bat -force
|
||||
#
|
||||
# ============= DOCKER-BASED BUILDS =============
|
||||
# #
|
||||
|
||||
linux-x64: check-docker create-dockerignore
|
||||
@mkdir -p target/release
|
||||
docker run --platform linux/amd64 --rm \
|
||||
-v "$$(pwd):/src" -w /src rust:1.85-alpine sh -eu -c '\
|
||||
apk add --no-cache \
|
||||
musl-dev \
|
||||
gcc g++ make cmake pkgconfig \
|
||||
zlib-dev zlib-static \
|
||||
bzip2-dev bzip2-static \
|
||||
xz-dev xz-static \
|
||||
boost-dev linux-headers \
|
||||
patch perl ragel && \
|
||||
git openssl-dev curl && \
|
||||
\
|
||||
cargo test --workspace --all-targets --release ; \
|
||||
\
|
||||
rustup target add x86_64-unknown-linux-musl && \
|
||||
\
|
||||
export PKG_CONFIG_ALLOW_CROSS=1 ; \
|
||||
export RUSTFLAGS="-C target-feature=+crt-static" ; \
|
||||
\
|
||||
cargo build --release --target x86_64-unknown-linux-musl && \
|
||||
cd target/x86_64-unknown-linux-musl/release && \
|
||||
find "./$(PROJECT_NAME)" -type f -executable \
|
||||
-not -name "*.d" -not -name "*.rlib" \
|
||||
-exec sha256sum {} \; > CHECKSUM.txt \
|
||||
'
|
||||
@cd target/release && \
|
||||
rm -rf $(PROJECT_NAME)-linux-x64.tgz && \
|
||||
cp ../x86_64-unknown-linux-musl/release/$(PROJECT_NAME) . && \
|
||||
cp ../x86_64-unknown-linux-musl/release/CHECKSUM.txt CHECKSUM-linux-x64.txt && \
|
||||
tar --no-xattrs -czf $(PROJECT_NAME)-linux-x64.tgz \
|
||||
$(PROJECT_NAME) CHECKSUM-linux-x64.txt && \
|
||||
rm $(PROJECT_NAME) && \
|
||||
sha256sum $(PROJECT_NAME)-linux-x64.tgz >> CHECKSUM-linux-x64.txt
|
||||
$(MAKE) list-archives
|
||||
|
||||
linux-arm64: check-docker create-dockerignore
|
||||
@mkdir -p target/release
|
||||
docker run --platform linux/arm64 --rm \
|
||||
-v "$$(pwd):/src" -w /src rust:1.85-alpine sh -eu -c '\
|
||||
apk add --no-cache \
|
||||
musl-dev \
|
||||
gcc g++ make cmake pkgconfig \
|
||||
zlib-dev zlib-static \
|
||||
bzip2-dev bzip2-static \
|
||||
xz-dev xz-static \
|
||||
boost-dev linux-headers \
|
||||
patch perl ragel && \
|
||||
git openssl-dev curl && \
|
||||
\
|
||||
rustup target add aarch64-unknown-linux-musl && \
|
||||
\
|
||||
cargo test --workspace --all-targets --release ; \
|
||||
\
|
||||
export PKG_CONFIG_ALLOW_CROSS=1 ; \
|
||||
export RUSTFLAGS="-C target-feature=+crt-static" ; \
|
||||
\
|
||||
cargo build --release --target aarch64-unknown-linux-musl && \
|
||||
\
|
||||
cd target/aarch64-unknown-linux-musl/release && \
|
||||
find "./$(PROJECT_NAME)" -type f -executable \
|
||||
-not -name "*.d" -not -name "*.rlib" \
|
||||
-exec sha256sum {} \; > CHECKSUM.txt \
|
||||
'
|
||||
@cd target/release && \
|
||||
rm -rf $(PROJECT_NAME)-linux-arm64.tgz && \
|
||||
cp ../aarch64-unknown-linux-musl/release/$(PROJECT_NAME) . && \
|
||||
cp ../aarch64-unknown-linux-musl/release/CHECKSUM.txt CHECKSUM-linux-arm64.txt && \
|
||||
tar --no-xattrs -czf $(PROJECT_NAME)-linux-arm64.tgz \
|
||||
$(PROJECT_NAME) CHECKSUM-linux-arm64.txt && \
|
||||
rm $(PROJECT_NAME) && \
|
||||
sha256sum $(PROJECT_NAME)-linux-arm64.tgz >> CHECKSUM-linux-arm64.txt
|
||||
$(MAKE) list-archives
|
||||
|
||||
|
||||
# ============= AGGREGATE TARGETS =============
|
||||
#
|
||||
|
||||
windows: windows-x64
|
||||
@echo "# Windows builds:" > target/release/CHECKSUMS-windows.txt
|
||||
@echo -e "\n# x86_64-windows build:" >> target/release/CHECKSUMS-windows.txt
|
||||
@cat target/release/CHECKSUM-windows-x64.txt >> target/release/CHECKSUMS-windows.txt
|
||||
@echo -e "\nBuilt Windows archives:"
|
||||
@ls -lh target/release/*.tgz
|
||||
@echo -e "\nWindows Checksums:"
|
||||
@cat target/release/CHECKSUMS-windows.txt
|
||||
|
||||
linux:
|
||||
$(MAKE) linux-$(ARCH)
|
||||
|
||||
linux-all: linux-x64 linux-arm64
|
||||
@echo "# Linux builds:" > target/release/CHECKSUMS-linux.txt
|
||||
@echo -e "\n# x86_64-linux build:" >> target/release/CHECKSUMS-linux.txt
|
||||
@cat target/release/CHECKSUM-linux-x64.txt >> target/release/CHECKSUMS-linux.txt
|
||||
@echo -e "\n# arm64-linux build:" >> target/release/CHECKSUMS-linux.txt
|
||||
@cat target/release/CHECKSUM-linux-arm64.txt >> target/release/CHECKSUMS-linux.txt
|
||||
@echo -e "\nBuilt Linux archives:"
|
||||
@ls -lh target/release/*.tgz
|
||||
@echo -e "\nLinux Checksums:"
|
||||
@cat target/release/CHECKSUMS-linux.txt
|
||||
|
||||
darwin:
|
||||
$(MAKE) darwin-$(ARCH)
|
||||
|
||||
darwin-all: darwin-arm64 darwin-x64
|
||||
@echo "# darwin builds:" > target/release/CHECKSUMS-darwin.txt
|
||||
@echo -e "\n# arm64-darwin build:" >> target/release/CHECKSUMS-darwin.txt
|
||||
@cat target/release/CHECKSUM-darwin-arm64.txt >> target/release/CHECKSUMS-darwin.txt
|
||||
@echo -e "\n# x86_64-darwin build:" >> target/release/CHECKSUMS-darwin.txt
|
||||
@cat target/release/CHECKSUM-darwin-x64.txt >> target/release/CHECKSUMS-darwin.txt
|
||||
@echo -e "\nBuilt darwin archives:"
|
||||
@ls -lh target/release/*.tgz
|
||||
@echo -e "\ndarwin Checksums:"
|
||||
@cat target/release/CHECKSUMS-darwin.txt
|
||||
|
||||
all: linux darwin
|
||||
@echo "# All builds:" > target/release/CHECKSUMS.txt
|
||||
@echo -e "\n# Linux builds:" >> target/release/CHECKSUMS.txt
|
||||
@cat target/release/CHECKSUMS-linux.txt >> target/release/CHECKSUMS.txt
|
||||
@echo -e "\n# darwin builds:" >> target/release/CHECKSUMS.txt
|
||||
@cat target/release/CHECKSUMS-darwin.txt >> target/release/CHECKSUMS.txt
|
||||
@echo -e "\nBuilt archives:"
|
||||
@ls -lh target/release/*.tgz
|
||||
@echo -e "\nCombined Checksums:"
|
||||
@cat target/release/CHECKSUMS.txt
|
||||
|
||||
list-archives:
|
||||
@echo -e "\n=== Built archives ==="
|
||||
@found=0; \
|
||||
for f in target/release/*.tgz; do \
|
||||
if [ -e "$$f" ]; then \
|
||||
found=1; \
|
||||
realpath "$$f"; \
|
||||
fi; \
|
||||
done; \
|
||||
if [ $$found -eq 0 ]; then \
|
||||
echo "No archives found."; \
|
||||
fi
|
||||
|
||||
check-docker:
|
||||
@command -v docker >/dev/null 2>&1 || { \
|
||||
echo "Docker is not installed. Please install Docker."; \
|
||||
exit 1; \
|
||||
}
|
||||
|
||||
check-rust:
|
||||
@version=$$(rustc --version 2>/dev/null | awk '{print $$2}'); \
|
||||
if [ -z "$$version" ]; then \
|
||||
echo "Rust not found."; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
required=1.85.0; \
|
||||
if [ $$(printf '%s\n' "$$required" "$$version" | sort -V | head -n1) != "$$required" ]; then \
|
||||
echo "Rust version $$version is older than required $$required."; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Rust version $$version is acceptable."; \
|
||||
fi
|
||||
|
||||
tests:
|
||||
@echo "🔍 checking for cargo-nextest …"
|
||||
@if command -v cargo-nextest >/dev/null 2>&1; then \
|
||||
echo "✅ cargo-nextest already present"; \
|
||||
else \
|
||||
echo "📦 installing cargo-nextest …"; \
|
||||
cargo install --locked cargo-nextest || true; \
|
||||
fi
|
||||
@echo "▶ running tests …"; \
|
||||
if command -v cargo-nextest >/dev/null 2>&1; then \
|
||||
cargo nextest run --workspace --all-targets; \
|
||||
else \
|
||||
echo "⚠️ cargo-nextest unavailable – falling back to cargo test"; \
|
||||
cargo test --workspace --all-targets; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
@echo "Cleaning build artifacts..."
|
||||
cargo clean
|
||||
rm -f .dockerignore
|
||||
|
||||
notices:
|
||||
@echo "Generating third-party notices..."
|
||||
@cargo install cargo-bundle-licenses
|
||||
@cargo bundle-licenses --format yaml --output THIRD_PARTY_NOTICES
|
||||
|
||||
evergreen-patch:
|
||||
@evergreen patch --project kingfisher --variants all --tasks build
|
||||
308
README.md
Normal file
308
README.md
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
# Kingfisher
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/kingfisher_logo.png" alt="Kingfisher Logo" width="126" height="173" style="vertical-align: right;" />
|
||||
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
Kingfisher is a blazingly fast secret‑scanning and validation tool built in Rust. It combines Intel’s hardware‑accelerated Hyperscan regex engine with language‑aware parsing via Tree‑Sitter, and **ships with hundreds of built‑in rules** to detect, validate, and triage secrets before they ever reach production
|
||||
</p>
|
||||
|
||||
**MongoDB Blog**: [Introducing Kingfisher: Real-Time Secret Detection and Validation](https://www.mongodb.com/blog/post/product-release-announcements/introducing-kingfisher-real-time-secret-detection-validation)
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Performance**: Multi‑threaded, Hyperscan‑powered scanning for massive codebases
|
||||
- **Language‑Aware Accuracy**: AST parsing in 20+ languages via Tree‑Sitter reduces contextless regex matches. see [docs/PARSING.md](/docs/PARSING.md)
|
||||
- **Built-In Validation**: Hundreds of built-in detection rules, many with live-credential validators that call the relevant service APIs (AWS, Azure, GCP, Stripe, etc.) to confirm a secret is active. You can extend or override the library by adding YAML-defined rules on the command line—see [docs/RULES.md](/docs/RULES.md) for details
|
||||
- **Git History Scanning**: Scan local repos, remote GitHub/GitLab orgs/users, or arbitrary GitHub/GitLab repos
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
On macOS, you can simply
|
||||
```bash
|
||||
brew install kingfisher
|
||||
```
|
||||
|
||||
Pre-built binaries are also available on the [Releases](https://github.com/mongodb/kingfisher/releases) section of this page.
|
||||
|
||||
Or you may compile for your platform via `make`:
|
||||
|
||||
```bash
|
||||
# NOTE: Requires Docker
|
||||
make linux
|
||||
```
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
make darwin
|
||||
```
|
||||
|
||||
```bash
|
||||
# Windows x64 --- requires building from a Windows host with Visual Studio installed
|
||||
./buildwin.bat -force
|
||||
```
|
||||
|
||||
```bash
|
||||
# Build all targets
|
||||
make linux-all # builds both x64 and arm64
|
||||
make darwin-all # builds both x64 and arm64
|
||||
make all # builds for every OS and architecture supported
|
||||
```
|
||||
|
||||
|
||||
# Write Custom Rules!
|
||||
|
||||
Kingfisher ships with hundreds of rules with HTTP and service‑specific validation checks (AWS, Azure, GCP, etc.) to confirm if a detected string is a live credential.
|
||||
|
||||
However, you may want to add your own custom rules, or modify a detection to better suit your needs / environment.
|
||||
|
||||
First, review [docs/RULES.md](/docs/RULES.md) to learn how to create custom Kingfisher rules.
|
||||
|
||||
Once you've done that, you can provide your custom rules (defined in a YAML file) and provide it to Kingfisher at runtime --- no recompiling required!
|
||||
|
||||
# Usage
|
||||
|
||||
## Basic Examples
|
||||
|
||||
> **Note** `kingfisher scan` detects whether the input is a Git repository or a plain directory—no extra flags required.
|
||||
|
||||
### Scan with secret validation
|
||||
```bash
|
||||
kingfisher scan /path/to/code
|
||||
## NOTE: This path can refer to:
|
||||
# 1. a local git repo
|
||||
# 2. a directory with many git repos
|
||||
# 3. or just a folder with files and subdirectories
|
||||
|
||||
## To explicitly prevent scanning git commit history add:
|
||||
# `--git-history=none`
|
||||
```
|
||||
|
||||
|
||||
### Scan a directory containing multiple Git repositories
|
||||
```bash
|
||||
kingfisher scan /projects/mono‑repo‑dir
|
||||
```
|
||||
|
||||
### Scan a Git repository without validation
|
||||
```bash
|
||||
kingfisher scan ~/src/myrepo --no-validate
|
||||
```
|
||||
|
||||
### Display only secrets confirmed active by third‑party APIs
|
||||
```bash
|
||||
kingfisher scan ./service --only-valid
|
||||
```
|
||||
|
||||
### Output JSON and capture to a file
|
||||
```bash
|
||||
kingfisher scan . --format json | tee kingfisher.json
|
||||
```
|
||||
|
||||
### Output SARIF directly to disk
|
||||
```bash
|
||||
kingfisher scan . --format sarif --output findings.sarif
|
||||
```
|
||||
|
||||
### Pipe any text directly into Kingfisher by passing `-`
|
||||
|
||||
```bash
|
||||
cat /path/to/file.py | kingfisher scan -
|
||||
```
|
||||
|
||||
### Scan using a rule *family* with one flag
|
||||
*(prefix matching: `--rule kingfisher.aws` loads `kingfisher.aws.*`)*
|
||||
|
||||
```bash
|
||||
# Only apply AWS-related rules (kingfisher.aws.1 + kingfisher.aws.2)
|
||||
kingfisher scan /path/to/repo --rule kingfisher.aws
|
||||
```
|
||||
|
||||
---
|
||||
## Scanning GitHub
|
||||
|
||||
### Scan GitHub organisation (requires `KF_GITHUB_TOKEN`)
|
||||
```bash
|
||||
kingfisher scan --github-organization my-org
|
||||
```
|
||||
|
||||
### Scan remote GitHub repository
|
||||
```bash
|
||||
kingfisher scan --git-url https://github.com/org/repo.git
|
||||
|
||||
# Optionally provide a GitHub Token
|
||||
KF_GITHUB_TOKEN="ghp_…" kingfisher scan --git-url https://github.com/org/private_repo.git
|
||||
|
||||
```
|
||||
---
|
||||
## Scanning GitLab
|
||||
|
||||
### Scan GitLab group (requires `KF_GITLAB_TOKEN`)
|
||||
```bash
|
||||
kingfisher scan --gitlab-group my-group
|
||||
```
|
||||
|
||||
### Scan GitLab user
|
||||
```bash
|
||||
kingfisher scan --gitlab-user johndoe
|
||||
```
|
||||
|
||||
### Scan remote GitLab repository by URL
|
||||
```bash
|
||||
kingfisher scan --git-url https://gitlab.com/group/project.git
|
||||
```
|
||||
|
||||
### List GitLab repositories
|
||||
```bash
|
||||
kingfisher gitlab repos list --group my-group
|
||||
```
|
||||
|
||||
---
|
||||
## Environment Variables for Tokens
|
||||
|
||||
| Variable | Purpose |
|
||||
|---------------------|---------------------------------------|
|
||||
| `KF_GITHUB_TOKEN` | GitHub Personal Access Token |
|
||||
| `KF_GITLAB_TOKEN` | GitLab Personal Access Token |
|
||||
|
||||
Set them temporarily per command:
|
||||
```bash
|
||||
KF_GITLAB_TOKEN="glpat-…" kingfisher scan --gitlab-group my-group
|
||||
```
|
||||
Or export for the session:
|
||||
```bash
|
||||
export KF_GITLAB_TOKEN="glpat-…"
|
||||
```
|
||||
|
||||
*If no token is provided Kingfisher still works for public repositories.*
|
||||
|
||||
---
|
||||
## Exit Codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|-------------------------------------|
|
||||
| 0 | No findings |
|
||||
| 200 | Findings discovered |
|
||||
| 205 | Validated findings discovered |
|
||||
|
||||
---
|
||||
|
||||
### Update Checks
|
||||
Kingfisher checks for newer releases on GitHub each time it starts and exits, printing whether a new version is available. Use `--self-update` to automatically download and replace the binary when an update is found. Add `--no-update-check` to disable these checks entirely.
|
||||
|
||||
---
|
||||
|
||||
|
||||
### List Builtin Rules
|
||||
```bash
|
||||
kingfisher rules list
|
||||
```
|
||||
### To scan using **only** your own `my_rules.yaml` you could run:
|
||||
```bash
|
||||
kingfisher scan \
|
||||
--load-builtins=false \
|
||||
--rules-path path/to/my_rules.yaml \
|
||||
./src/
|
||||
```
|
||||
|
||||
### To add your rules alongside the built‑ins:
|
||||
|
||||
```bash
|
||||
kingfisher scan \
|
||||
--rules-path ./custom-rules/ \
|
||||
--rules-path my_rules.yml \
|
||||
~/path/to/project-dir/
|
||||
```
|
||||
|
||||
## Other Examples
|
||||
```bash
|
||||
# Check custom rules - this ensures all regular expressions compile, and can match the rule's `examples` in the YML file
|
||||
kingfisher rules check --rules-path ./my_rules.yml
|
||||
|
||||
# List GitHub repos
|
||||
kingfisher github repos list --user my-user
|
||||
kingfisher github repos list --organization my-org
|
||||
|
||||
```
|
||||
|
||||
## Notable Scan Options
|
||||
- `--no-dedup`: Report every occurrence of a finding (disable the default de-duplicate behavior)
|
||||
- `--confidence <LEVEL>`: (low|medium|high)
|
||||
- `--min-entropy <VAL>`: Override default threshold
|
||||
- `--no-binary`: Skip binary files
|
||||
- `--no-extract-archives`: Do not scan inside archives
|
||||
- `--extraction-depth <N>`: Specifies how deep nested archives should be extracted and scanned (default: 2)
|
||||
- `--redact`: Replaces discovered secrets with a one-way hash for secure output
|
||||
|
||||
## Finding Fingerprint
|
||||
|
||||
The document below details the four-field formula (rule SHA-1, origin label, start & end offsets) hashed with XXH3-64 to create Kingfisher’s 64-bit finding fingerprint, and explains how this ID powers safe deduplication; plus how `--no-dedup` can be used shows every raw match.
|
||||
See ([docs/FINGERPRINT.md](docs/FINGERPRINT.md))
|
||||
|
||||
|
||||
## CLI Options
|
||||
```bash
|
||||
kingfisher scan --help
|
||||
```
|
||||
|
||||
## Business Value
|
||||
|
||||
By integrating Kingfisher into your development lifecycle, you can:
|
||||
|
||||
- **Prevent Costly Breaches**
|
||||
Early detection of embedded credentials avoids expensive incident response, legal fees, and reputation damage
|
||||
- **Automate Compliance**
|
||||
Enforce secret‑scanning policies across GitOps, CI/CD, and pull requests to help satisfy SOC 2, PCI‑DSS, GDPR, and other standards
|
||||
- **Reduce Noise, Focus on Real Threats**
|
||||
Validation logic filters out false positives and highlights only active, valid secrets (`--only-valid`)
|
||||
- **Accelerate Dev Workflows**
|
||||
Run in parallel across dozens of languages, integrate with GitHub Actions or any pipeline, and shift security left to minimize delays
|
||||
|
||||
|
||||
## The Risk of Leaked Secrets
|
||||
|
||||
Embedding credentials in code repositories is a pervasive, ever‑present risk that leads directly to data breaches:
|
||||
|
||||
1. **Uber (2016)**
|
||||
- *Incident*: Attackers stole GitHub credentials, retrieved an AWS key from a developer’s private repo, and accessed data on 57 million riders and 600 000 drivers.
|
||||
- *Sources*: [BBC News](https://www.bbc.com/news/technology-42075306), [Ars Technica](https://arstechnica.com/tech-policy/2017/11/report-uber-paid-hackers-100000-to-keep-2016-data-breach-quiet/)
|
||||
|
||||
2. **AWS**
|
||||
- *Incident*: An AWS engineer accidentally published log files and CloudFormation templates containing AWS key pairs (including “rootkey.csv”) to a public GitHub repo.
|
||||
- *Sources*: [The Register](https://www.theregister.com/2020/01/23/aws_engineer_credentials_github/), [UpGuard](https://www.upguard.com/breaches/identity-and-access-misstep-how-an-amazon-engineer-exposed-credentials-and-more)
|
||||
|
||||
3. **Infosys**
|
||||
- *Incident*: Infosys published an internal PyPI package embedding a FullAdminAccess AWS key for a Johns Hopkins data bucket; the key remained active for over a year.
|
||||
- *Sources*: [The Stack](https://www.thestack.technology/infosys-leak-aws-key-exposed-on-pypi/), [Tom Forbes Blog](https://tomforb.es/blog/infosys-leak/)
|
||||
|
||||
4. **Microsoft**
|
||||
- *Incident*: Microsoft’s AI research GitHub repo included an overly permissive Azure SAS token, exposing 38 TB of private data (workstation backups, 30,000+ Teams messages).
|
||||
- *Sources*: [Wiz Blog](https://www.wiz.io/blog/38-terabytes-of-private-data-accidentally-exposed-by-microsoft-ai-researchers), [TechCrunch](https://techcrunch.com/2023/09/18/microsoft-ai-researchers-accidentally-exposed-terabytes-of-internal-sensitive-data/)
|
||||
|
||||
5. **GitHub**
|
||||
- *Incident*: GitHub discovered its RSA SSH host private key was briefly exposed in a public repository and rotated it out of caution.
|
||||
- *Sources*: [GitHub Blog](https://github.blog/news-insights/company-news/we-updated-our-rsa-ssh-host-key/)
|
||||
|
||||
Left unchecked, leaked secrets can lead to unauthorized access, pivoting within your environment, regulatory fines, and brand‑damaging incident response costs.
|
||||
|
||||
# Benchmark Results
|
||||
|
||||
See ([docs/COMPARISON.md](docs/COMPARISON.md))
|
||||
|
||||
# Roadmap
|
||||
|
||||
- More rules
|
||||
- Auto-updater
|
||||
- Packages for Linux (deb, rpm)
|
||||
- Please file a [feature request](https://github.com/mongodb/kingfisher/issues) if you have specific features you'd like added
|
||||
|
||||
|
||||
# License
|
||||
|
||||
[Apache2 License](LICENSE)
|
||||
|
||||
|
||||
73305
THIRD_PARTY_NOTICES
Normal file
73305
THIRD_PARTY_NOTICES
Normal file
File diff suppressed because one or more lines are too long
140
buildwin.bat
Normal file
140
buildwin.bat
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
@echo off
|
||||
REM This script builds a Windows x64 release binary and creates a tarball with checksum.
|
||||
REM It requires vcpkg to be installed at root of C: drive (https://github.com/microsoft/vcpkg).
|
||||
REM This script will install Rust (using chocolatey) if it is not already installed.
|
||||
REM
|
||||
REM Call with -force to clone and bootstrap vcpkg if it is not found
|
||||
REM
|
||||
|
||||
setlocal
|
||||
|
||||
REM Set your Cargo project name manually here if desired:
|
||||
set "PROJECT_NAME=kingfisher"
|
||||
|
||||
REM Optional check for OS:
|
||||
if NOT "%OS%"=="Windows_NT" (
|
||||
echo This script must be run on Windows.
|
||||
exit /b 1
|
||||
)
|
||||
if "%VCINSTALLDIR%"=="" (
|
||||
echo VCINSTALLDIR not set - attempting auto-detection…
|
||||
for %%P in (
|
||||
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC"
|
||||
"C:\Program Files\Microsoft Visual Studio\2022\Professional\VC"
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC"
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC"
|
||||
) do (
|
||||
if exist "%%~P\Auxiliary\Build\vcvars64.bat" (
|
||||
set "VCINSTALLDIR=%%~P"
|
||||
echo Found Visual C++ Build Tools at: %%~P
|
||||
goto :vc_found
|
||||
)
|
||||
)
|
||||
echo ERROR: Could not find a suitable Visual Studio installation.
|
||||
echo Install “Desktop development with C++” or set VCINSTALLDIR.
|
||||
exit /b 1
|
||||
)
|
||||
:vc_found
|
||||
|
||||
REM Strip trailing backslash if present
|
||||
if "%VCINSTALLDIR:~-1%"=="\" set "VCINSTALLDIR=%VCINSTALLDIR:~0,-1%"
|
||||
|
||||
echo Initialising MSVC environment…
|
||||
call "%VCINSTALLDIR%\Auxiliary\Build\vcvars64.bat" || (
|
||||
echo ERROR: Failed to initialise MSVC toolchain.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Locate vcpkg.exe
|
||||
where vcpkg.exe >nul 2>nul
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
if exist "%HOMEDRIVE%\vcpkg\vcpkg.exe" (
|
||||
set "VCPKG_EXE=%HOMEDRIVE%\vcpkg\vcpkg.exe"
|
||||
echo Found vcpkg at: %VCPKG_EXE%
|
||||
) else (
|
||||
if "%~1"=="-force" (
|
||||
echo Cloning and bootstrapping vcpkg...
|
||||
if exist "%HOMEDRIVE%\vcpkg" (
|
||||
rmdir /s /q "%HOMEDRIVE%\vcpkg"
|
||||
)
|
||||
git clone https://github.com/microsoft/vcpkg.git "%HOMEDRIVE%\vcpkg"
|
||||
pushd "%HOMEDRIVE%\vcpkg"
|
||||
dir
|
||||
call .\bootstrap-vcpkg.bat
|
||||
set "VCPKG_EXE=%CD%\vcpkg.exe"
|
||||
popd
|
||||
echo Installed vcpkg at: %VCPKG_EXE%
|
||||
) else (
|
||||
echo ERROR: vcpkg not found. Please install it or re-run script with -force.
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
) else (
|
||||
for /f "tokens=*" %%i in ('where vcpkg.exe') do (
|
||||
set "VCPKG_EXE=%%i"
|
||||
goto :found_vcpkg
|
||||
)
|
||||
:found_vcpkg
|
||||
echo Found vcpkg at: %VCPKG_EXE%
|
||||
)
|
||||
|
||||
REM Check if LOCALAPPDATA starts with a drive letter, if not set it to APPDATA
|
||||
if /I not "%LOCALAPPDATA:~1,1%"==":" (
|
||||
echo LOCALAPPDATA does not start with a drive letter. Setting it to APPDATA.
|
||||
set "LOCALAPPDATA=%APPDATA%"
|
||||
)
|
||||
|
||||
echo Installing hyperscan via vcpkg...
|
||||
set
|
||||
"%HOMEDRIVE%\vcpkg\vcpkg.exe" install hyperscan:x64-windows
|
||||
set "LIBHS_NO_PKG_CONFIG=1"
|
||||
|
||||
REM Point vectorscan-rs-sys at the Hyperscan install from vcpkg
|
||||
set "HYPERSCAN_ROOT=%HOMEDRIVE%\vcpkg\installed\x64-windows"
|
||||
set "LIB=%HYPERSCAN_ROOT%\lib;%LIB%"
|
||||
set "INCLUDE=%HYPERSCAN_ROOT%\include;%INCLUDE%"
|
||||
|
||||
REM Check for Rust, install if missing
|
||||
where rustc.exe >nul 2>nul
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo Installing Rust...
|
||||
choco install rust-ms -y
|
||||
choco install cmake -y --installargs "ADD_CMAKE_TO_PATH=System"
|
||||
call refreshenv
|
||||
|
||||
) else (
|
||||
echo Rust is already installed.
|
||||
)
|
||||
|
||||
echo Building for Windows x64...
|
||||
cargo build --release --target x86_64-pc-windows-msvc || (
|
||||
echo Cargo build failed.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Generating CHECKSUM.txt...
|
||||
powershell -Command ^
|
||||
"Get-FileHash .\target\x86_64-pc-windows-msvc\release\%PROJECT_NAME%.exe -Algorithm SHA256 | Out-File .\target\x86_64-pc-windows-msvc\release\CHECKSUM.txt"
|
||||
|
||||
if not exist "target\release" mkdir "target\release"
|
||||
copy /Y "target\x86_64-pc-windows-msvc\release\%PROJECT_NAME%.exe" "target\release\" >nul
|
||||
copy /Y "target\x86_64-pc-windows-msvc\release\CHECKSUM.txt" "target\release\CHECKSUM-windows-x64.txt" >nul
|
||||
|
||||
cd target\release
|
||||
echo Creating archive: %PROJECT_NAME%-windows-x64.zip
|
||||
if exist "%PROJECT_NAME%-windows-x64.zip" del /f /q "%PROJECT_NAME%-windows-x64.zip"
|
||||
powershell -Command "Compress-Archive -Path '%PROJECT_NAME%.exe','CHECKSUM-windows-x64.txt' -DestinationPath '%PROJECT_NAME%-windows-x64.zip' -Force"
|
||||
|
||||
if exist "%PROJECT_NAME%-windows-x64.zip" (
|
||||
REM -- append the ZIP’s SHA-256 to the existing checksum file ----
|
||||
certutil -hashfile "%PROJECT_NAME%-windows-x64.zip" SHA256 >> "CHECKSUM-windows-x64.txt"
|
||||
echo Created: %PROJECT_NAME%-windows-x64.zip
|
||||
) else (
|
||||
echo ERROR: Archive not created.
|
||||
)
|
||||
|
||||
echo Archives in target\release:
|
||||
dir /b *.zip 2>nul || echo None found.
|
||||
|
||||
endlocal
|
||||
exit /b 0
|
||||
7
data/default/ignore.conf
Normal file
7
data/default/ignore.conf
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# This file lists gitignore-style patterns: https://git-scm.com/docs/gitignore
|
||||
#
|
||||
# These patterns control which paths Kingfisher will scan.
|
||||
|
||||
**/objects/pack/pack-*.pack
|
||||
**/objects/pack/pack-*.idx
|
||||
**/packed-refs
|
||||
56
data/default/monitor_kf_mem.sh
Normal file
56
data/default/monitor_kf_mem.sh
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Name of the application (used for pid lookup, log and plot filenames)
|
||||
APP_NAME="kingfisher"
|
||||
|
||||
# Remove old log (if it exists) so we start fresh
|
||||
LOG_FILE="${APP_NAME}_mem.log"
|
||||
PLOT_FILE="${APP_NAME}_rss.png"
|
||||
[ -f "$LOG_FILE" ] && rm "$LOG_FILE"
|
||||
[ -f "$PLOT_FILE" ] && rm "$PLOT_FILE"
|
||||
|
||||
# Find the main PID
|
||||
main_pid=$(pidof "$APP_NAME")
|
||||
if [[ -z "$main_pid" ]]; then
|
||||
echo "Error: no running process named '$APP_NAME' found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine page size in KiB
|
||||
page_kb=$(( $(getconf PAGESIZE) / 1024 ))
|
||||
|
||||
# Monitor loop: sum VmRSS across parent + direct children
|
||||
while [[ -d /proc/$main_pid ]]; do
|
||||
ts=$(date +%s)
|
||||
pids="$main_pid $(pgrep -P $main_pid)"
|
||||
|
||||
total_kb=0
|
||||
for pid in $pids; do
|
||||
if rss_kb=$(awk '/VmRSS/ {print $2}' /proc/$pid/status 2>/dev/null); then
|
||||
total_kb=$(( total_kb + rss_kb ))
|
||||
fi
|
||||
done
|
||||
|
||||
# Convert KiB → MiB (2 decimal places) and log
|
||||
total_mb=$(awk "BEGIN {printf \"%.2f\", $total_kb/1024}")
|
||||
echo "$ts $total_mb" >> "$LOG_FILE"
|
||||
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# Once monitoring ends, generate the plot
|
||||
gnuplot -persist <<EOF
|
||||
set terminal pngcairo size 1280,720
|
||||
set output "$PLOT_FILE"
|
||||
set xdata time
|
||||
set timefmt "%s"
|
||||
set format x "%H:%M:%S"
|
||||
set xlabel "Time"
|
||||
set ylabel "RSS (MiB)"
|
||||
set title "${APP_NAME^} Memory Usage"
|
||||
set grid
|
||||
plot "$LOG_FILE" using 1:2 with lines linewidth 2 title "RSS"
|
||||
EOF
|
||||
|
||||
echo "Memory log written to $LOG_FILE"
|
||||
echo "Plot generated as $PLOT_FILE"
|
||||
2
data/default/rule_cleanup/.gitignore
vendored
Normal file
2
data/default/rule_cleanup/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.venv
|
||||
.venv/*
|
||||
108
data/default/rule_cleanup/main.py
Normal file
108
data/default/rule_cleanup/main.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.9"
|
||||
# dependencies = ["PyYAML>=6.0", "idna>=3.7"]
|
||||
# ///
|
||||
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import idna
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ModuleNotFoundError as exc:
|
||||
raise SystemExit(
|
||||
"PyYAML isn’t installed.\n"
|
||||
"Run the script with `uv run …` or add PyYAML to the dependency list."
|
||||
) from exc
|
||||
|
||||
|
||||
RULES_DIR = Path(os.path.expanduser("../../data/rules"))
|
||||
URL_KEY_RE = re.compile(r"url$", re.IGNORECASE) # keys literally named “url”
|
||||
TEMPLATE_RE = re.compile(r"\{\{.*?\}\}") # strip Liquid placeholders
|
||||
DOMAIN_RE = re.compile(r"^(?:[a-z][a-z0-9+\-.]*://)?([^/]+)", re.I)
|
||||
|
||||
|
||||
def find_yaml_files(root: Path):
|
||||
yield from root.rglob("*.yml")
|
||||
yield from root.rglob("*.yaml")
|
||||
|
||||
|
||||
def extract_domains(obj):
|
||||
"""Recursively yield every domain appearing in any 'url' key."""
|
||||
if isinstance(obj, dict):
|
||||
for k, v in obj.items():
|
||||
if URL_KEY_RE.fullmatch(str(k)) and isinstance(v, str):
|
||||
cleaned = TEMPLATE_RE.sub("", v).strip()
|
||||
parsed = urlparse(cleaned).netloc
|
||||
if not parsed:
|
||||
m = DOMAIN_RE.match(cleaned)
|
||||
if not m: # value wasn’t a URL/host – ignore
|
||||
continue
|
||||
parsed = m.group(1)
|
||||
|
||||
domain = (
|
||||
parsed.split("@")[-1] # drop any creds
|
||||
.split(":")[0] # drop port
|
||||
.lstrip(".")
|
||||
.rstrip(".")
|
||||
.lower()
|
||||
)
|
||||
if domain and "{{" not in domain: # ignore Liquid tokens
|
||||
yield domain
|
||||
else:
|
||||
yield from extract_domains(v)
|
||||
elif isinstance(obj, list):
|
||||
for item in obj:
|
||||
yield from extract_domains(item)
|
||||
|
||||
|
||||
def domain_active(domain: str) -> bool:
|
||||
"""Return True iff *domain* resolves via DNS."""
|
||||
if not domain:
|
||||
return False
|
||||
try:
|
||||
ascii_domain = idna.encode(domain).decode() # puny-encode if needed
|
||||
socket.gethostbyname(ascii_domain)
|
||||
return True
|
||||
except (socket.gaierror, UnicodeError, idna.IDNAError):
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
# list of (yaml_path, [dead_domain, …])
|
||||
inactive_files: list[tuple[Path, list[str]]] = []
|
||||
|
||||
for yml_path in find_yaml_files(RULES_DIR):
|
||||
try:
|
||||
docs = yaml.safe_load_all(yml_path.read_text())
|
||||
except yaml.YAMLError as e:
|
||||
print(f"⚠️ Skipping {yml_path} (YAML error: {e})")
|
||||
continue
|
||||
|
||||
domains: set[str] = set()
|
||||
for doc in docs:
|
||||
if doc is not None:
|
||||
domains.update(extract_domains(doc))
|
||||
|
||||
if not domains:
|
||||
continue
|
||||
|
||||
dead = sorted({d for d in domains if not domain_active(d)})
|
||||
if dead:
|
||||
inactive_files.append((yml_path, dead))
|
||||
|
||||
if inactive_files:
|
||||
print("YAML files with at least one non-resolving domain:")
|
||||
for path, dead in inactive_files:
|
||||
print(f" - {path}: {', '.join(dead)}")
|
||||
else:
|
||||
print("✅ All domains resolve.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
31
data/rules/adafruitio.yml
Normal file
31
data/rules/adafruitio.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
rules:
|
||||
- name: Adafruit IO Key
|
||||
id: kingfisher.adafruitio.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
aio_
|
||||
[a-zA-Z0-9]{28}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- ADAFRUIT_AIO_KEY = "aio_giXk31KzM05IVxHRwJwtpNGClUE5"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
url: https://io.adafruit.com/api/v2/kingfishermdb/feeds/
|
||||
headers:
|
||||
X-AIO-Key: "{{ TOKEN }}"
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"username":"kingfishermdb"'
|
||||
71
data/rules/adobe.yml
Normal file
71
data/rules/adobe.yml
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
rules:
|
||||
- name: Adobe Stock API Key
|
||||
id: kingfisher.adobe.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
adobe
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[A-F0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- adobeKey = 1a2b3c4d5e6f7890abcdef1234567890
|
||||
references:
|
||||
- https://developer.adobe.com/stock/docs/getting-started/03-api-authentication/
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
x-api-key: '{{ TOKEN }}'
|
||||
x-product: '{{ PRODUCTID }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"error_code":"403003"'
|
||||
negative: true
|
||||
url: https://stock.adobe.io/Rest/Media/1/Search/Files?locale=en_US&search_parameters%5Bwords%5D=cats%20in%20costume
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.adobe.2"
|
||||
variable: PRODUCTID
|
||||
- name: Adobe IO Product ID
|
||||
id: kingfisher.adobe.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
adobe
|
||||
(?:.|[\n\r]){0,64}?
|
||||
(
|
||||
[a-z0-9]{12}
|
||||
)
|
||||
\b
|
||||
min_entropy: 2.0
|
||||
visible: false
|
||||
examples:
|
||||
- adobeProduct = lV9ASPsd2P3d
|
||||
- name: Adobe OAuth Client Secret
|
||||
id: kingfisher.adobe.3
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
p8e-[A-Z0-9-]{32}
|
||||
)
|
||||
(?:[^A-Z0-9-]|$)
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- |
|
||||
{
|
||||
"client_credentials": {
|
||||
"client_id": "a65b0146769d433a835f36660881db50",
|
||||
"client_secret": "p8e-ibndcvsmAp9ZgPBZ606FSlYIZVlsZ-g5"
|
||||
},
|
||||
30
data/rules/age.yml
Normal file
30
data/rules/age.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
rules:
|
||||
- name: Age Recipient (X25519 public key)
|
||||
id: kingfisher.age.1
|
||||
pattern: '\b(age1[0-9a-z]{58})\b'
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'age1zvkyg2lqzraa2lnjvqej32nkuu0ues2s82hzrye869xeexvn73equnujwj'
|
||||
references:
|
||||
- https://age-encryption.org
|
||||
- https://htmlpreview.github.io/?https://github.com/FiloSottile/age/blob/main/doc/age.1.html
|
||||
- https://github.com/C2SP/C2SP/blob/8b6a842e0360d35111c46be2a8019b2276295914/age.md#the-x25519-recipient-type
|
||||
|
||||
- name: Age Identity (X22519 secret key)
|
||||
id: kingfisher.age.2
|
||||
pattern: '\b(AGE-SECRET-KEY-1[0-9A-Z]{58})\b'
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
# created: 2022-09-26T21:55:47-05:00
|
||||
# public key: age1epzmwwzw8n09slh0c7z1z52x43nnga7lkksx3qrh07tqz5v7lcys45428t
|
||||
this is the 'AGE-SECRET-KEY-1HJCRJVK7EE3A5N8CRP8YSEUGZKNW90Y5UR2RGYAS8L279LFP6LCQU5ADNR'
|
||||
- 'AGE-SECRET-KEY-1HJCRJVK7EE3A5N8CRP8YSEUGZKNW90Y5UR2RGYAS8L279LFP6LCQUEGAEX'
|
||||
references:
|
||||
- https://age-encryption.org
|
||||
- https://htmlpreview.github.io/?https://github.com/FiloSottile/age/blob/main/doc/age.1.html
|
||||
- https://github.com/C2SP/C2SP/blob/8b6a842e0360d35111c46be2a8019b2276295914/age.md#the-x25519-recipient-type
|
||||
categories:
|
||||
- secret
|
||||
37
data/rules/airbrake.yml
Normal file
37
data/rules/airbrake.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
rules:
|
||||
- name: Airbrake User Key
|
||||
id: kingfisher.airbrake.1
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
\b
|
||||
airbrake
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(
|
||||
[a-zA-Z0-9-]{40}
|
||||
)
|
||||
\b
|
||||
min_entropy: 4.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- airbrake secretKey= a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0
|
||||
- "airbrakeToken: 'a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0'"
|
||||
references:
|
||||
- https://docs.airbrake.io/docs/devops-tools/api/#list-projects-v4
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- words:
|
||||
- '"id"'
|
||||
type: WordMatch
|
||||
url: https://api.airbrake.io/api/v4/projects?key={{ TOKEN }}
|
||||
63
data/rules/airtable.yml
Normal file
63
data/rules/airtable.yml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
rules:
|
||||
- name: Airtable Personal Access Token
|
||||
id: kingfisher.airtable.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
pat
|
||||
[a-z0-9]{14}
|
||||
\.
|
||||
[a-z0-9]{62,66}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- airtable_pat = patXfsZBLOgxbJqSi.fd6210cfb2e0b2049bd8e7fa0e69f26a3a704412af6fcbd93a097c42507fc893
|
||||
categories:
|
||||
- api
|
||||
- secret
|
||||
references:
|
||||
- https://airtable.com/developers/web/api/authentication
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.airtable.com/v0/meta/whoami
|
||||
- name: Airtable OAuth Token
|
||||
id: kingfisher.airtable.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
[a-zA-Z0-9]+\.v1\.[a-zA-Z0-9_-]+\.[a-f0-9]+
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- example.v1.XYZ123_ABC.abcdef123456
|
||||
references:
|
||||
- https://airtable.com/developers/web/api/oauth-reference
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.airtable.com/v0/meta/whoami
|
||||
36
data/rules/aiven.yml
Normal file
36
data/rules/aiven.yml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
rules:
|
||||
- name: Aiven API Key
|
||||
id: kingfisher.aiven.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
aiven
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9/+=]{372}
|
||||
)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- FLASK_APP_AIVEN_API_KEY = "MOLXG502hGM9DsUSyvKVf2cx8zXEdBesHZLSqXnMj4agm9jLx4gpC9R+z26CX4tKgrIpjvR9dgorE/DzVxxH79Pd+mspIHgxkf7fL4eLxuFvl4RrvX9CWS7nMnfB9uDiM80AtGykzHm8KKr76I7UY8Az/i3x2OG5gFhH0+2AT0Qr75T1JbNF0IiPSjI3MQ0A1+k1b2DW2dwdNnYKEewrNjhVHre8sYLzMUE5Y+FIs8OFdpAm4YNUb283iVJjEcxT8AtMhmOrziMkmWn0haxjhT2qdxgnafGJidF0Dl/NIN+4o1WokQSyhHH1glhNV5wZcG4Po/KP3aPSRnrFE0+GZ6322TrWo1btS5mv+FKkS6gKq0zEfA=="
|
||||
references:
|
||||
- https://aiven.io/docs/tools/api
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: aivenv1 {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- words:
|
||||
- '"project_membership"'
|
||||
type: WordMatch
|
||||
url: https://api.aiven.io/v1/project
|
||||
52
data/rules/algolia.yml
Normal file
52
data/rules/algolia.yml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
rules:
|
||||
- name: Algolia Admin API Key
|
||||
id: kingfisher.algolia.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
algolia
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- algolia_api_key = "ij1mut5oe606wlrf5z4u8u31264z3gag"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
X-Algolia-API-Key: '{{ TOKEN }}'
|
||||
X-Algolia-Application-Id: '{{ APPID }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://{{ APPID }}-dsn.algolia.net/1/keys/{{ TOKEN }}
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.algolia.2"
|
||||
variable: APPID
|
||||
|
||||
- name: Algolia Application ID
|
||||
id: kingfisher.algolia.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
algolia
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
[A-Z0-9]{10}
|
||||
)
|
||||
\b
|
||||
min_entropy: 2.0
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- algolia_app_id = "WRB8YLFW7Y"
|
||||
35
data/rules/alibaba.yml
Normal file
35
data/rules/alibaba.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
rules:
|
||||
- name: Alibaba Access Key ID
|
||||
id: kingfisher.alibabacloud.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
LTAI[a-z0-9]{17,21}
|
||||
)
|
||||
\b
|
||||
min_entropy: 4.0
|
||||
confidence: medium
|
||||
examples:
|
||||
- LTAI8x2NiGqfyJGx7eLDhp12
|
||||
- LTAI5GqyJGhp12ad31L5hpix
|
||||
- name: Alibaba Access Key Secret
|
||||
id: kingfisher.alibabacloud.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
alibaba
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{30}
|
||||
)
|
||||
\b
|
||||
min_entropy: 4.2
|
||||
confidence: medium
|
||||
examples:
|
||||
- alibaba_secret = 7jkWdTjKLnSlGddwPR5gBn65PHcZG6
|
||||
- alibaba-token = aJHKLnSlGddwPR5g7jkWdTBn65PHc5
|
||||
|
||||
51
data/rules/anthropic.yml
Normal file
51
data/rules/anthropic.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
rules:
|
||||
- name: Anthropic API Key
|
||||
id: kingfisher.anthropic.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
sk-ant-api
|
||||
\d{2,4}
|
||||
-
|
||||
[\w\-]{93}
|
||||
AA
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- sk-ant-api668-Clm512odot9WDD7itfUU9R880nefA1EtYZDbpE-C9b0XQEWpqFKf9DQUo03vOfXl16oSmyar1CLF1SzV3YzpZJ6bahcpLAA
|
||||
categories:
|
||||
- api
|
||||
- secret
|
||||
references:
|
||||
- https://docs.anthropic.com/claude/reference/authentication
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
body: |
|
||||
{
|
||||
"model": "claude-3-haiku-20240307",
|
||||
"max_tokens": 1024,
|
||||
"messages": [
|
||||
{"role": "user", "content": "respond only with 'success'"}
|
||||
]
|
||||
}
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
anthropic-version: "2023-06-01"
|
||||
x-api-key: '{{ TOKEN }}'
|
||||
method: POST
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"type":"invalid_request_error"'
|
||||
url: https://api.anthropic.com/v1/messages
|
||||
41
data/rules/anypoint.yml
Normal file
41
data/rules/anypoint.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
rules:
|
||||
- name: Anypoint API Key
|
||||
id: kingfisher.anypoint.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
anypoint
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[0-9a-z]{8}
|
||||
-
|
||||
[0-9a-z]{4}
|
||||
-
|
||||
[0-9a-z]{4}
|
||||
-
|
||||
[0-9a-z]{4}
|
||||
-
|
||||
[0-9a-z]{12}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- anypoint_key = "33333333-cccc-dddd-eeee-444444444444"
|
||||
references:
|
||||
- https://www.postman.com/salesforce-developers/salesforce-developers/documentation/oj0opxa/mulesoft-anypoint-platform-apis
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://anypoint.mulesoft.com/accounts/api/me
|
||||
57
data/rules/artifactory.yml
Normal file
57
data/rules/artifactory.yml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
rules:
|
||||
- name: Artifactory Access Token
|
||||
id: kingfisher.artifactory.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
AKC[a-zA-Z0-9]{64,74}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
export HOMEBREW_ARTIFACTORY_API_TOKEN=AKCp8igrDNFerC357m4422e4tmu7xB983QLPxJhKFcSMfoux2RFvp8rc4jC8t9ncdmYCMFD8W
|
||||
export HOMEBREW_ARTIFACTORY_API_USER=kashorn
|
||||
- 'jfrog rt dl --url=http://localhost:8071/artifactory --apikey=AKCp2WXX7SDvcsmny528sSDnaB3zACkNQoRcD8D1WmxhMV9gk6Wp8mVWC8bh38kJQbXagUT8Z generic-local/hello.txt'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
X-JFrog-Art-Api: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: JsonValid
|
||||
url: https://{{ JFROGURL }}/artifactory/api/repositories
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.artifactory.2"
|
||||
variable: JFROGURL
|
||||
|
||||
- name: Artifactory JFrog URL
|
||||
id: kingfisher.artifactory.2
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
[a-z0-9]
|
||||
(?:
|
||||
[a-z0-9\-]{0,61}
|
||||
[a-z0-9]
|
||||
)?
|
||||
\.jfrog\.io
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- mycompany.jfrog.io
|
||||
- my-company-name.jfrog.io
|
||||
- a.jfrog.io
|
||||
89
data/rules/asana.yml
Normal file
89
data/rules/asana.yml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
rules:
|
||||
- name: Asana Client ID
|
||||
id: kingfisher.asana.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
asana
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[0-9]{16}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- asana_key = "8195513847023883"
|
||||
- "ASANA_API_TOKEN: 1234567890123456"
|
||||
references:
|
||||
- https://developers.asana.com/docs/authentication
|
||||
|
||||
- name: Asana Client Secret
|
||||
id: kingfisher.asana.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
asana
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{30,40}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- "asana :'20c2F0d03201af478ca1aBE9515A1A4FEfb'"
|
||||
- ASANA_PAT = 1234567890abcdef1234567890abcdef12
|
||||
|
||||
- name: Asana OAuth / Personal Access Token
|
||||
id: kingfisher.asana.3
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
asana
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
[01]{1,}
|
||||
\/
|
||||
[0-9a-f]{16,32}
|
||||
(?:
|
||||
:
|
||||
[a-z0-9]{32,64}
|
||||
)?
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- asana_pat = 1/1248440223456784:d3d7e52e5c4a5d4c9bc424d2d882324d
|
||||
- asana token = 0/d6f1e29e5b4b4d8c9bb419b2d882154d
|
||||
categories:
|
||||
- api
|
||||
- key
|
||||
- asana
|
||||
references:
|
||||
- https://developers.asana.com/docs/personal-access-token#example
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- 'data:'
|
||||
- email
|
||||
- name
|
||||
url: https://app.asana.com/api/1.0/users/me
|
||||
41
data/rules/atlassian.yml
Normal file
41
data/rules/atlassian.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
rules:
|
||||
- name: Atlassian API token
|
||||
id: kingfisher.atlassian.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
atlassian
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{24}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- Atlassian_key = "DjayBenyJrtpvydFCzAphcqc"
|
||||
- "ATLASSIAN_API_TOKEN:'abcdef1234567890abcdef12'"
|
||||
references:
|
||||
- https://developer.atlassian.com/cloud/admin/organization/rest/api-group-orgs/#api-v1-orgs-get
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
url: https://api.atlassian.com/me
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
Accept: "application/json"
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- "Unauthorized"
|
||||
negative: true
|
||||
87
data/rules/auth0.yml
Normal file
87
data/rules/auth0.yml
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
rules:
|
||||
- name: Auth0 Client ID
|
||||
id: kingfisher.auth0.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
auth0
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9_-]{32,60}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
visible: false
|
||||
examples:
|
||||
- auth0_client_id = 'dBpXUZS34Rhg6WUKTLLj3E9yWGY6IJgV'
|
||||
- 'auth0ClientId: ''Hf5sS5kLmNm6Lmvoc1BwXryX49bPD4L0'''
|
||||
- AUTH0_CLIENT_ID='abcdef1234567890abcdef1234567890abcdef12'
|
||||
references:
|
||||
- https://auth0.com/docs/get-started/applications/application-settings
|
||||
- name: Auth0 Client Secret
|
||||
id: kingfisher.auth0.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
auth0
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9_-]{64,}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'auth0_client_secret: ''v7qhPJv1fDRoUwBjktpWgS8LPVr-hXWOI8tFnRtWrU3jNYZf1hW3hfJjA8rEER9D'''
|
||||
- auth0ClientSecret = 'YOUR_VERY_LONG_AND_SECURE_CLIENT_SECRET_GOES_HERE_12345678900000'
|
||||
- AUTH0_CLIENT_SECRET='abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
url: https://{{ DOMAIN }}/oauth/token
|
||||
headers:
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
body: |
|
||||
grant_type=authorization_code&client_id={{ CLIENT_ID }}&client_secret={{ CLIENT_SECRET }}&code=AUTHORIZATION_CODE&redirect_uri=undefined
|
||||
method: POST
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- "access_denied"
|
||||
- "invalid_grant"
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.auth0.1
|
||||
variable: CLIENTID
|
||||
- rule_id: "kingfisher.auth0.3"
|
||||
variable: DOMAIN
|
||||
- name: Auth0 Domain
|
||||
id: kingfisher.auth0.3
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
[a-z0-9]
|
||||
[a-z0-9._-]*
|
||||
auth0\.com
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- https://mycompany.us.auth0.com/
|
||||
- 'auth0Domain: ''example-org.auth0.com'''
|
||||
- AUTH0_DOMAIN=myapp.eu.auth0.com
|
||||
categories:
|
||||
- domain
|
||||
- auth0
|
||||
74
data/rules/aws.yml
Normal file
74
data/rules/aws.yml
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
rules:
|
||||
- name: AWS Access Key ID
|
||||
id: kingfisher.aws.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
(?:AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)
|
||||
[0-9A-Z]{16}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.2
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- ASIAOZW6VBVAZFJHJLQA
|
||||
|
||||
- name: AWS Secret Access Key
|
||||
id: kingfisher.aws.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?:
|
||||
\b
|
||||
(?:AWS|AMAZON|AMZN|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[A-Za-z0-9/+=]{40}
|
||||
)
|
||||
\b
|
||||
|
|
||||
\b(?:AWS|AMAZON|AMZN|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)
|
||||
(?:.|[\n\r]){0,96}?
|
||||
(?:SECRET|PRIVATE|ACCESS)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?:KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[A-Za-z0-9/+=]{40}
|
||||
)
|
||||
\b
|
||||
)
|
||||
min_entropy: 4.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- foo.backup.archive.aws.secretkey=sBmHlDFrNcsz35N+LRjwlUxF8/wypT4tiJCQ0wP4
|
||||
- '"awsSecretKey":"3lyTWqHMt5UySny2drdPYheRTEzrNux8Cn5JWFHL"'
|
||||
- '"\"awsSecretKey\":\"3lyTWqHMt5UySny2drdPYheRTEzrNux8Cn5JWFHL\"," +'
|
||||
- |
|
||||
"Whiteboard" : {
|
||||
"type" : "aws-s3",
|
||||
"config" : {
|
||||
"accessKeyId" : "AKIAIVOURJN3SXRRLZFQ",
|
||||
"region" : "us-east-1",
|
||||
"secretAccessKey" : "3lyTWqHMt5UySny2drdPYheRTEzrNux8Cn5JWFHL"
|
||||
},
|
||||
validation:
|
||||
type: AWS
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.aws.1
|
||||
variable: AKID
|
||||
|
||||
- name: AWS Session Token
|
||||
id: kingfisher.aws.4
|
||||
pattern: '(?i)(?:aws.?session|aws.?session.?token|aws.?token)["''`]?\s{0,30}(?::|=>|=)\s{0,30}["''`]?([a-z0-9/+=]{16,200})[^a-z0-9/+=]'
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
export AWS_ACCESS_KEY_ID="I08BCX2ACV45ED1DOC9J"
|
||||
export AWS_SECRET_ACCESS_KEY="0qk+o7XctJMmG6ydO8537c9+TofLJU1K0PiVBXSg"
|
||||
export AWS_SESSION_TOKEN="eyJhbGciOiJIUzUxMi53InR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJJMDhCQ1gySkpWNDVFRDFET0M5SiIsImFjciI6Ij53LCJhdWQiOiJhY2NvdW50IiwiYXV0aF90aW1lIjowLCJhenAiOiJtaW5pbyIsImVtYWlsIjoiYWlkYW4uY29wZUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImV4cCI6MTU4MDUwMDIzOCwiZmFtaWx5X25hbWUiOiJDb3BlIiwiZ2l2ZW5fbmFtZSI6IkFpZGFuIENvcGUiLCJpYXQiOjE1ODA0OTk5MzgsImlzcyI6Imh0dHBzOi8vYXV0aHN0Zy5wb3BkYXRhLmJjLmNhL2F1dGgvcmVhbG1zL3NhbXBsZSIsImp0aSI6IjU5ZTM5ODAxLWQxMmUtNDVhYS04NmQzLWVhMmNmZDU0NmE2MiIsIm1pbmlvX3BvbGljeSI6ImRhdGFzZXRfMV9ybyIsIm5hbWUiOiJBaWRhbiBDb3BlIENvcGUiLCJuYmYiOjAsInByZWZlcnJlZF91c2VybmFtZSI6ImFjb3BlLTk5LXQwNSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2Vzc2lvbl9zdGF0ZSI6IjcxYjczZWJjLThlMzMtNGMyMi04NmE2LWI0MzhhNDM4ZmI2MiIsInN1YiI6IjVkOTBlOTgzLTA1NDItNDYyYS1hZWIwLWYxZWVmNjcwYzdlNSIsInR5cCI6IkJlYXJlciJ9.J-a9PORJToz7MUrnPQlOywcqtVMNkXy53Gedp_V4PW-Gbf1_BAMjwuw_X7fKRd6hkNfEn43CKKju7muzi_d1Ig"
|
||||
87
data/rules/azure.yml
Normal file
87
data/rules/azure.yml
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
rules:
|
||||
- name: Azure Connection String
|
||||
id: kingfisher.azure.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?: AccountName | SharedAccessKeyName | SharedSecretIssuer) \s*=\s* ([^;]{1,80}) \s*;\s*
|
||||
.{0,10}\s*
|
||||
(?: AccountKey | SharedAccessKey | SharedSecretValue) \s*=\s* ([^;]{1,100})
|
||||
(?: ;|$ )
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
# Azure Storage Connection String
|
||||
AzureWebJobsStorage=DefaultEndpointsProtocol=https;AccountName=hanatour9833;AccountKey=6jqh42QQjWWBwoPGGR/Jr0PZjhBMZVbHm/gkhEfHvOj8aV6+oI8ed6ZAAwB5a6993WqyQDiuJJB0QpseJwqYxw==;EndpointSuffix=core.windows.net
|
||||
- |
|
||||
DefaultEndpointsProtocol=http;AccountName=testacc1;
|
||||
AccountKey=1gy3lpE7Du1j5ljKiupgKzywSw2isjsa69sfsdfsdsgfsgfdgfdgfd/YThisv/OVVLfIOv9kQ==;
|
||||
BlobEndpoint=http://127.0.0.1:8440/testacc1;
|
||||
TableEndpoint=http://127.0.0.1:8440/testacc1;
|
||||
QueueEndpoint=http://127.0.0.1:8440/testacc1;
|
||||
- |
|
||||
"IOTHUB_CONNECTION_STRING": {
|
||||
"value": "HostName=d1-vi-ioth521.azure-devices.net;SharedAccessKeyName=registryReadWrite;SharedAccessKey=S8ii67l3Gd1Ba69az78iP9UksewzhjvUfh1DIuDs30w="
|
||||
}
|
||||
- |
|
||||
"AZURE_STORAGE_CONNECTION_STRING": {
|
||||
"value": "DefaultEndpointsProtocol=https;AccountName=d1biblobstor521;AccountKey=NjEwGHd9+piK+iCi2C2XURWPmeDDjif9UKN1HAszYptL4iQ+yD7/dgjLMZc3VOpURsa53aJ4HZfbVWzL429C5g==;EndpointSuffix=core.windows.net"
|
||||
}
|
||||
negative_examples:
|
||||
- 'InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=ai.contoso.com;'
|
||||
- 'InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://custom.com:111/;LiveEndpoint=https://custom.com:222/;ProfilerEndpoint=https://custom.com:333/;SnapshotEndpoint=https://custom.com:444/;'
|
||||
references:
|
||||
- https://azure.microsoft.com/en-us/blog/windows-azure-web-sites-how-application-strings-and-connection-strings-work/
|
||||
- https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string
|
||||
- https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-sas#best-practices-when-using-sas
|
||||
categories:
|
||||
- api
|
||||
- fuzzy
|
||||
- secret
|
||||
|
||||
- name: Azure App Configuration Connection String
|
||||
id: kingfisher.azure.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(https://[a-zA-Z0-9-]+\.azconfig\.io);
|
||||
Id=(.{4}-.{2}-.{2}:[a-zA-Z0-9+/]{18,22});
|
||||
Secret=([a-zA-Z0-9+/]{36,50}=)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'Endpoint=https://foo-nonprod-appconfig.azconfig.io;Id=ABCD-E6-s0:tl6ABcdefGHi7kLMno/p;Secret=abCD1EF+GHIJxLMnOPqRSa53VWX05zaBCdE/fg9hi4k='
|
||||
- 'https://foo-nonprod-appconfig.azconfig.io;Id=ABCD-E6-s0:tl6ABcdefGHi7kLMno/p;Secret=abCD1EF+GHIJxLMnOA53ST8uVWX05zaBCdE/fg9hi4k='
|
||||
- 'Endpoint=https://appconfig-test01.azconfig.io;Id=09pv-l0-s0:opFCQMC6+9485xJgN5Ws;Secret=GcoEA53t7GLRNJ910M46IrbHO/Vg0tt4HujRdsaCoTY='
|
||||
- ' private static string appConfigurationConnectionString = "Endpoint=https://appcs-fg-pwc.azconfig.io;Id=pi5x-l9-s0:SZLlhHA53Nz2MpAl04cU;Secret=CQ+mlfQqkzfZv4XA53gigJ/seeXMKwNsqW/rM3wmtuE=";'
|
||||
negative_examples:
|
||||
- |
|
||||
text:
|
||||
az appconfig feature delete --connection-string Endpoint=https://contoso.azconfig.io;Id=xxx;Secret=xxx --feature color --label MyLabel
|
||||
references:
|
||||
- https://docs.microsoft.com/en-us/azure/azure-app-configuration/
|
||||
- https://docs.microsoft.com/en-us/azure/azure-app-configuration/howto-best-practices
|
||||
- https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/appconfiguration/azure-appconfiguration/azure/appconfiguration/_utils.py
|
||||
categories:
|
||||
- api
|
||||
- fuzzy
|
||||
- secret
|
||||
|
||||
- name: Azure Personal Access Token
|
||||
id: kingfisher.azure.3
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i: ADO_PAT | pat_token | personal_?access_?token | \$token )
|
||||
\s* = \s*
|
||||
["']
|
||||
([a-z0-9]{52})
|
||||
["']
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
var personalAccessToken = "zpczok4kqgnw5prpfy3ehiylbqvgbjfkdiqkejsxqamy7qbkep7q"; // Provide a value or retrieve it from configuration
|
||||
- |
|
||||
$token = "58oo4mvqr2tpw7b4w3loeckwfu5o6nw3sihfckvlwoxgqimlddza"
|
||||
- |
|
||||
if __name__ == "__main__":
|
||||
ado_pat = "iyfmob6xjrfmit67anxbot64umfx2clwx7dz5ynxi4q2z3uqegvq"
|
||||
32
data/rules/azuredevops.yml
Normal file
32
data/rules/azuredevops.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
rules:
|
||||
- name: Azure DevOps Personal Access Token
|
||||
id: kingfisher.azure.devops.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
azure
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(
|
||||
[a-z0-9]{75}AZDO[a-z0-9]{5}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3
|
||||
confidence: medium
|
||||
examples:
|
||||
- azure devops pat = FBdFol081crwkIHWJH2yiqDDyrFjVSi7HWl22hN2hTYfsB8NlGDpJQQJ77BAACAAAAAAAAAAAAASAZDOBucT
|
||||
references:
|
||||
- https://learn.microsoft.com/en-us/rest/api/azure/devops/profile/profiles/get?view=azure-devops-rest-7.1&tabs=HTTP
|
||||
- https://learn.microsoft.com/en-us/azure/devops/release-notes/2024/general/sprint-241-update
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: 'Basic {{ ":" | append: TOKEN | b64enc }}'
|
||||
method: GET
|
||||
url: https://app.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1-preview.1
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
58
data/rules/azureopenai.yml
Normal file
58
data/rules/azureopenai.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
rules:
|
||||
- name: Azure OpenAI API Key
|
||||
id: kingfisher.azureopenai.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
azure
|
||||
(?:.|[\n\r]){0,8}?
|
||||
(?:openai)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,8}?
|
||||
(
|
||||
[a-f0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- azure_openai_key=1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d
|
||||
- azure_openai_secret=abcdef0123456789abcdef0123456789
|
||||
references:
|
||||
- https://learn.microsoft.com/en-us/rest/api/azureopenai/models/list?view=rest-azureopenai-2024-10-21&tabs=HTTP
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: 'https://{{ AZUREHOST }}/openai/models?api-version=2024-10-21'
|
||||
headers:
|
||||
Api-Key: '{{ TOKEN }}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"object":'
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.azureopenai.host.1
|
||||
variable: AZUREHOST
|
||||
- name: Azure OpenAI Host
|
||||
id: kingfisher.azureopenai.host.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
[a-z0-9-]+
|
||||
\.openai\.azure\.com
|
||||
)
|
||||
\b
|
||||
min_entropy: 2.0
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- my-instance.openai.azure.com
|
||||
- mycompany-east-us.openai.azure.com
|
||||
55
data/rules/azuresearchquery.yml
Normal file
55
data/rules/azuresearchquery.yml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
rules:
|
||||
- name: Azure Search Query Key
|
||||
id: kingfisher.azuresearch.key.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
azure
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(
|
||||
[0-9a-zA-Z]{52}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- azure_search_key = XK8TnSRDXsoxiFYiH6Ix2aBC6jvWozd9Ida1yNjWgFHSjeDlblDK
|
||||
references:
|
||||
- https://learn.microsoft.com/en-us/rest/api/searchservice/search-documents
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: '{{ AZUREURL }}/docs?search=%2a&$top=0&api-version=2024-07-01'
|
||||
headers:
|
||||
api-key: '{{ TOKEN }}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.azuresearch.url.1
|
||||
variable: AZUREURL
|
||||
- name: Azure Search URL
|
||||
id: kingfisher.azuresearch.url.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
azure
|
||||
(?:.|[\n\r]){0,32}?
|
||||
https:\/\/
|
||||
(
|
||||
[0-9a-z]{5,40}
|
||||
\.search\.windows\.net
|
||||
\/indexes\/
|
||||
[0-9a-z]{5,40}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.0
|
||||
confidence: medium
|
||||
examples:
|
||||
- azure_search_url=https://myservice.search.windows.net/indexes/myindex
|
||||
42
data/rules/azurestorage.yml
Normal file
42
data/rules/azurestorage.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
rules:
|
||||
- name: Azure Storage Account Name
|
||||
id: kingfisher.azurestorage.name.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?:
|
||||
(?i:
|
||||
(?:Account|Storage)
|
||||
(?:[._-]Account)?
|
||||
[._-]?Name
|
||||
)
|
||||
(?:.|[\n\r]){0,20}?
|
||||
([a-z0-9]{3,24})
|
||||
|
|
||||
([a-z0-9]{3,24})
|
||||
(?i:\.blob\.core\.windows\.net)
|
||||
)\b
|
||||
min_entropy: 2.5
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- storage_name=mystorageaccount123
|
||||
- mystorageaccount.blob.core.windows.net
|
||||
|
||||
- name: Azure Storage Account Key
|
||||
id: kingfisher.azurestorage.key.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i:(?:Access|Account|Storage)[_.-]?Key)
|
||||
(?:.|[\n\r]){0,25}?
|
||||
(
|
||||
[a-zA-Z0-9+\\/-]{86,88}={0,2}
|
||||
)
|
||||
min_entropy: 4.0
|
||||
confidence: medium
|
||||
examples:
|
||||
- AccountKey=Xy9aB8cD7eF6gH5iJ4kL3mN2oP1qR0sT9uV8wX7yZ6aB5cD4eF3gH2iJ1kL0mN9oP8qR7sT6uV5wX4yZ3aB2cD1eF0gH9iJ8kL7mN6oP5q==\
|
||||
validation:
|
||||
type: AzureStorage
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.azurestorage.name.1
|
||||
variable: AZURENAME
|
||||
36
data/rules/baremetrics.yml
Normal file
36
data/rules/baremetrics.yml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
rules:
|
||||
- name: Baremetrics API Key
|
||||
id: kingfisher.baremetrics.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
baremetrics
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9_-]{25}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
references:
|
||||
- https://developers.baremetrics.com/reference/sources
|
||||
examples:
|
||||
- baremetrics_api_key = "12345abcdef67890abcdef123"
|
||||
- "BAREMETRICS_API_KEY:'abcde12345fghij67890klmno'"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.baremetrics.com/v1/sources
|
||||
32
data/rules/beamer.yml
Normal file
32
data/rules/beamer.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
rules:
|
||||
- name: Beamer API token
|
||||
id: kingfisher.beamer.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
beamer
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
b_[a-zA-Z0-9=_\\/\\\-+]{44}
|
||||
)
|
||||
min_entropy: 3.0
|
||||
confidence: medium
|
||||
examples:
|
||||
- beamer = b_ByDfulghxvvmHbArJSFfQhxemJPQHOwplxuydlKEEbfe
|
||||
- "BEAMER_key = 'b_ByDfulghxvvmHbArJSFfQhxemJPQHOwplxuydlKEEbfe'"
|
||||
references:
|
||||
- https://getbeamer-api.pages.dev/
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Beamer-Api-Key: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"name":'
|
||||
url: https://api.getbeamer.com/v0/ping
|
||||
65
data/rules/bitbucket.yml
Normal file
65
data/rules/bitbucket.yml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
rules:
|
||||
- name: Bitbucket Client ID
|
||||
id: kingfisher.bitbucket.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
bitbucket
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?:client|id)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
([a-z0-9]{30,40})
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- bitbucket_id=byl2nhrv34zaclukjhvomlvjabkujf
|
||||
- bitbucket.client.ID=abcdefghij1234567890klmnopqrst
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Basic {{ TOKEN | b64enc }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.bitbucket.org/2.0/user
|
||||
|
||||
- name: Bitbucket Secret
|
||||
id: kingfisher.bitbucket.3
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
bitbucket
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9+_\-+]{44}
|
||||
)
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- bitbucket_key=HedmnK9h6KD_eh9KK8FlI9ahUc8WfaNZ4gulbrtN2ouV
|
||||
- bitbucket_secret=kd8j2h4jf9s8mf6l4k9j2h4jf9s8mf6l4k9j2h4jf9s8mf6l
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.bitbucket.org/2.0/user
|
||||
75
data/rules/blynk.yml
Normal file
75
data/rules/blynk.yml
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
rules:
|
||||
- name: Blynk Device Access Token
|
||||
id: kingfisher.blynk.1
|
||||
pattern: |
|
||||
(?x)
|
||||
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/external/api/[a-zA-Z0-9/]*\?token=
|
||||
([a-zA-Z0-9_\-]{32})
|
||||
&
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- curl "https://blynk.cloud/external/api/get?token=Rps15JICmtRVbFyS_95houlLbm6xIQ2L&V1"
|
||||
- curl "https://fra1.blynk.cloud/external/api/get?token=Rps15JICmtRVbFyS_95houlLbm6xIQ2L&V1"
|
||||
- curl "https://lon1.blynk.cloud/external/api/get?token=Rps15JICmtRVbFyS_95houlLbm6xIQ2L&V1"
|
||||
- curl "https://blynk.cloud/external/api/update/property?token=Rps15JICmtRVbFyS_95houlLbm6xIQ2L&pin=v1&isDisabled=true"
|
||||
- name: Blynk Organization Access Token
|
||||
id: kingfisher.blynk.2
|
||||
pattern: |
|
||||
(?x)
|
||||
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/api/[a-zA-Z0-9_\-\s/\\]*
|
||||
-H\s*"Authorization:\s*Bearer\s*
|
||||
([a-zA-Z0-9_\-]{40})
|
||||
"
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'curl https://fra1.blynk.cloud/api/organization/profile -H "Authorization: Bearer eIdWHQqRfFmvP5LDDh-IGxPUzi7I27HthzCPAVmS"'
|
||||
- |
|
||||
curl https://fra1.blynk.cloud/api/organization/profile \
|
||||
-H "Authorization: Bearer eIdWHQqRfFmvP5LDDh-IGxPUzi7I27HthzCPAVmS"
|
||||
- name: Blynk Organization Access Token
|
||||
id: kingfisher.blynk.3
|
||||
pattern: |
|
||||
(?x)
|
||||
-H\s*"Authorization:\s*Bearer\s*
|
||||
([a-zA-Z0-9_\-]{40})
|
||||
"[\s\\]*https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/api
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'curl -H "Authorization: Bearer eIdWHQqRfFmvP5LDDh-IGxPUzi7I27HthzCPAVmS" https://fra1.blynk.cloud/api/organization/profile'
|
||||
- |
|
||||
curl -H "Authorization: Bearer eIdWHQqRfFmvP5LDDh-IGxPUzi7I27HthzCPAVmS" \
|
||||
https://fra1.blynk.cloud/api/organization/profile
|
||||
- name: Blynk Organization Client Credentials
|
||||
id: kingfisher.blynk.8
|
||||
pattern: |
|
||||
(?x)
|
||||
https://(?:fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/oauth2/[a-zA-Z0-9_\-\s/\\?=&]*
|
||||
(oa2-client-id_[a-zA-Z0-9_\-]{32})
|
||||
(?: : | &client_secret= )
|
||||
([a-zA-Z0-9_\-]{40})
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'curl -X POST https://fra1.blynk.cloud/oauth2/token?grant_type=client_credentials -u oa2-client-id_zmNtW-D0Toqpz4AZnBLCIlklBrz9ynU-:5uC5Y4Mcvdl5rB56rBmxnvB4DZgiIpcyTPbOoEWp'
|
||||
- |
|
||||
curl -X POST https://fra1.blynk.cloud/oauth2/token?grant_type=client_credentials \
|
||||
-u oa2-client-id_zmNtW-D0Toqpz4AZnBLCIlklBrz9ynU-:5uC5Y4Mcvdl5rB56rBmxnvB4DZgiIpcyTPbOoEWp
|
||||
- 'curl -X POST https://fra1.blynk.cloud/oauth2/token?grant_type=client_credentials&client_id=oa2-client-id_zmNtW-D0Toqpz4AZnBLCIlklBrz9ynU-&client_secret=5uC5Y4Mcvdl5rB56rBmxnvB4DZgiIpcyTPbOoEWp'
|
||||
- name: Blynk Organization Client Credentials
|
||||
id: kingfisher.blynk.9
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(oa2-client-id_[a-zA-Z0-9_\-]{32})
|
||||
:([a-zA-Z0-9_\-]{40})
|
||||
[\s\\]*https://(fra1\.|lon1\.|ny3\.|sgp1\.|blr1\.)*blynk\.cloud/oauth2
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'curl -X POST -u oa2-client-id_zmNtW-D0Toqpz4AZnBLCIlklBrz9ynU-:5uC5Y4Mcvdl5rB56rBmxnvB4DZgiIpcyTPbOoEWp https://fra1.blynk.cloud/oauth2/token?grant_type=client_credentials'
|
||||
- |
|
||||
curl -X POST -u oa2-client-id_zmNtW-D0Toqpz4AZnBLCIlklBrz9ynU-:5uC5Y4Mcvdl5rB56rBmxnvB4DZgiIpcyTPbOoEWp \
|
||||
https://fra1.blynk.cloud/oauth2/token?grant_type=client_credentials
|
||||
84
data/rules/circleci.yml
Normal file
84
data/rules/circleci.yml
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
rules:
|
||||
- name: CircleCI API Personal Access Token
|
||||
id: kingfisher.circleci.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
CCIPAT_
|
||||
[a-z0-9]{4}
|
||||
[a-z]{5}
|
||||
[a-z0-9]{3}
|
||||
[0-9]{3}
|
||||
[a-z]{2}
|
||||
[A-Z]{2}
|
||||
[0-9]{1}
|
||||
[a-z]{1}
|
||||
[a-z0-9]{1}
|
||||
[0-9]{1}
|
||||
[a-z]{1}
|
||||
_
|
||||
[a-z0-9]{40}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- CircleCI_PAT = "CCIPAT_lZyPAuThWn2G908ssDT0g33e_t7qh0r5hrvsqzmuraqzduq6qco5onxgrtcn7y2z4"
|
||||
- |
|
||||
export CIRCLECI_TOKEN=CCIPAT_lZyPAuThWn2G908ssDT0g33e_t7qh0r5hrvsqzmuraqzduq6qco5onxgrtcn7y2z4
|
||||
references:
|
||||
- https://circleci.com/docs/api-developers-guide/#using-the-api-securely-wtih-curl
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Accept: application/json
|
||||
Circle-Token: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"id"'
|
||||
url: https://circleci.com/api/v2/me
|
||||
- name: CircleCI API Project Token
|
||||
id: kingfisher.circleci.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
circleci
|
||||
(?:.|[\n\r]){0,64}?
|
||||
(
|
||||
[a-f0-9]{40}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- circleci_project_secret = 'Ca61263Bf9A4DcEECd00EdAAacb4eaEe74e8682f'
|
||||
- "CIRCLECI_API_TOKEN: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
|
||||
references:
|
||||
- https://circleci.com/docs/api/v1/index.html#projects
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Circle-Token: '{{ TOKEN }}'
|
||||
Accept: application/json
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"vcs_url"'
|
||||
url: https://circleci.com/api/v1.1/projects
|
||||
78
data/rules/cloudflare.yml
Normal file
78
data/rules/cloudflare.yml
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
rules:
|
||||
- name: Cloudflare API Token
|
||||
id: kingfisher.cloudflare.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
cloudflare
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9_-]{38,42}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- cloudflareAPIKey = A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0
|
||||
- |
|
||||
CLOUDFLARE_API_TOKEN: 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0'
|
||||
cloudflare_key="B1C2D3E4F5G6H7I8J9K0L1M2N3O4P5Q6R7S8T9U0V1"
|
||||
references:
|
||||
- https://developers.cloudflare.com/api/resources/user/subresources/tokens/methods/verify/
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.cloudflare.com/client/v4/user/tokens/verify
|
||||
|
||||
- name: Cloudflare CA Key
|
||||
id: kingfisher.cloudflare.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(?:cloudflare|x-auth-user-service-key)
|
||||
(?:.|[\n\r]){0,64}?
|
||||
(
|
||||
v1\.0-[a-z0-9._-]{160,}
|
||||
)
|
||||
["'`]?
|
||||
\b
|
||||
min_entropy: 4.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'X-Auth-User-Service-Key = v1.0-e26de050e02ddeaeef6de8d5ee267df5e78f68666ddd0ee76f22d26a0d20756f-eda77de60e8e76077e162727656787de2005d25e2f6e502e2d067657ed65722eade065275001a0f6f6e521e5e1fd76a6e8d7e2d6da8a2ee01e66e061e22570e2-07f2ede0aed78e82e8d2e620aaef8656d81e762266d7d226a205de7e18e2256a'
|
||||
- |
|
||||
cloudflare_service_key: "v1.0-e26de050e02ddeaeef6de8d5ee267df5e78f68666ddd0ee76f22d26a0d20756f-eda77de60e8e76077e162727656787de2005d25e2f6e502e2d067657ed65722eade065275001a0f6f6e521e5e1fd76a6e8d7e2d6da8a2ee01e66e061e22570e2-07f2ede0aed78e82e8d2e620aaef8656d81e762266d7d226a205de7e18e2256a"
|
||||
references:
|
||||
- https://developers.cloudflare.com/api/keys/
|
||||
- https://developers.cloudflare.com/fundamentals/api/get-started/keys/
|
||||
categories:
|
||||
- api
|
||||
- secret
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
X-Auth-User-Service-Key: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.cloudflare.com/client/v4/certificates?per_page=1
|
||||
39
data/rules/cloudsight.yml
Normal file
39
data/rules/cloudsight.yml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
rules:
|
||||
- name: CloudSight API Key
|
||||
id: kingfisher.cloudsight.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
cloudsight
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{20,24}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- cloudsight api_key = 2d0d3432369b6b53e7621f
|
||||
- |
|
||||
CLOUDSIGHT_API_KEY: 'a1b2c3d4e5f6g7h8i9j0k1l2'
|
||||
cloudsight_secret="3m4n5o6p7q8r9s0t1u2v3w4x5"
|
||||
references:
|
||||
- https://cloudsight.docs.apiary.io/#introduction/authentication
|
||||
- https://cloudsight.ai/api
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: CloudSight {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.cloudsightapi.com/image_requests
|
||||
33
data/rules/codacy.yml
Normal file
33
data/rules/codacy.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
rules:
|
||||
- name: Codacy API Key
|
||||
id: kingfisher.codacy.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
codacy
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[0-9A-Za-z]{20,24}
|
||||
)
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- codacySECRET=a1b2c3d4e5f6g7h8i9j0
|
||||
- codacyACCESS_KEY k1l2m3n4o5p6q7r8s9t0
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Accept: application/json
|
||||
api-token: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://app.codacy.com/api/v3/user/organizations
|
||||
40
data/rules/codeclimate.yml
Normal file
40
data/rules/codeclimate.yml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
rules:
|
||||
- name: CodeClimate Reporter ID
|
||||
id: kingfisher.codeclimate.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?: CODECLIMATE| CC_TEST_REPORTER_ID)
|
||||
(?:.|[\n\r]){0,64}?
|
||||
(
|
||||
[a-f0-9]{64}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- ' - RAILS_ENV=test CODECLIMATE_REPO_TOKEN=d37a8b9e09642cb73cfcf4e1284815fc3d6a55a7714110187ac59856ae4ab5ad'
|
||||
- |
|
||||
- uses: paambaati/codeclimate-action@v2.2.4
|
||||
env:
|
||||
CC_TEST_REPORTER_ID: 945dfb58a832d233a3caeb84e3e6d3be212e8c7abcb48117fce63b9adcb43647
|
||||
- CODECLIMATE_API_TOKEN=d37a8b9e09642cb73cfcf4e1284815fc3d6a55a7714110187ac59856ae4ab5ad
|
||||
- CODECLIMATE_API_TOKEN="d37a8b9e09642cb73cfcf4e1284815fc3d6a55a7714110187ac59856ae4ab5ad"
|
||||
references:
|
||||
- https://developer.codeclimate.com/#overview
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: 'https://api.codeclimate.com/v1/orgs'
|
||||
headers:
|
||||
Accept: 'application/vnd.api+json'
|
||||
Authorization: 'Token token={{ TOKEN }}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"id":'
|
||||
55
data/rules/confluent.yml
Normal file
55
data/rules/confluent.yml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
rules:
|
||||
- name: Confluent Client ID
|
||||
id: kingfisher.confluent.1
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
\b(?:confluent|ccloud|cpdev|kafka)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-zA-Z0-9]{16}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3
|
||||
confidence: medium
|
||||
visible: false
|
||||
examples:
|
||||
- confluent client_id=ABCD1234EFGH5678
|
||||
- kafka_client=WXYZ9876MNOP5432
|
||||
references:
|
||||
- https://docs.confluent.io/cloud/current/access-management/authenticate/api-keys.html
|
||||
- name: Confluent API Secret
|
||||
id: kingfisher.confluent.2
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
(?:confluent|ccloud|cpdev|kafka)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-zA-Z0-9\+/]{64}
|
||||
)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- confluent secret=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ab
|
||||
- kafka_token=ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyzABCD
|
||||
references:
|
||||
- https://docs.confluent.io/cloud/current/api.html#tag/API-Keys-(iamv2)/operation/getIamV2ApiKey
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: 'Basic {{ CLIENTID | append: ":" | append: TOKEN | b64enc }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.confluent.cloud/iam/v2/api-keys/{{ CLIENTID }}
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.confluent.1"
|
||||
variable: CLIENTID
|
||||
11
data/rules/crates.io.yml
Normal file
11
data/rules/crates.io.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
rules:
|
||||
- name: crates.io API Key
|
||||
id: kingfisher.cratesio.1
|
||||
pattern: '\b(cio[a-zA-Z0-9]{32})\b'
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'Bearer: ciotgp8BGZBlX192iExSQPm0SrUlBunG8zd'
|
||||
references:
|
||||
- https://crates.io/data-access
|
||||
- https://github.com/rust-lang/crates.io/blob/master/src/util/token.rs
|
||||
26
data/rules/credentials.yml
Normal file
26
data/rules/credentials.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
rules:
|
||||
- name: Credentials in a URL
|
||||
id: kingfisher.credentials.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
https?:\/\/
|
||||
(
|
||||
[a-z0-9._~-]+
|
||||
)
|
||||
:
|
||||
(
|
||||
[a-z0-9._~-]+
|
||||
)
|
||||
@
|
||||
(
|
||||
[a-z0-9.-]+
|
||||
)
|
||||
(
|
||||
\/
|
||||
[a-z0-9\/._~-]*
|
||||
)?
|
||||
min_entropy: 3.0
|
||||
confidence: low
|
||||
examples:
|
||||
- https://eaRIWNkE:qyOIhJiM@j2LYY414Q5cCYD
|
||||
83
data/rules/databricks.yml
Normal file
83
data/rules/databricks.yml
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
rules:
|
||||
- name: Databricks API token
|
||||
id: kingfisher.databricks.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
(
|
||||
dapi
|
||||
[a-f0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- "DATABRICKS_TOKEN: 'dapicd295a7be286969133e18a58e4afe7bd-3'"
|
||||
- "dapif21ee53d2b3648c2a1ed38953312a203"
|
||||
categories:
|
||||
- api
|
||||
- secret
|
||||
references:
|
||||
- https://docs.databricks.com/dev-tools/api/latest/authentication.html
|
||||
|
||||
- name: Databricks API Token
|
||||
id: kingfisher.databricks.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
dapi[0-9a-f]{32}
|
||||
)
|
||||
(-\d)?
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dapiabcd1234abcd1234abcd1234abcd1234
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://{{ DOMAIN }}/api/2.0/clusters/list
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.databricks.2"
|
||||
variable: DOMAIN
|
||||
|
||||
- name: Databricks Domain
|
||||
id: kingfisher.databricks.3
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
[a-z0-9-]+
|
||||
(?:
|
||||
\.[a-z0-9\-]+
|
||||
)*
|
||||
\.
|
||||
(
|
||||
cloud\.databricks\.com |
|
||||
gcp\.databricks\.com |
|
||||
azurewebsites\.net
|
||||
)
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- company-name.cloud.databricks.com
|
||||
categories:
|
||||
- cloud
|
||||
- infrastructure
|
||||
references:
|
||||
- https://docs.databricks.com/workspace/workspace-details.html
|
||||
- https://docs.gcp.databricks.com/workspace/workspace-details.html
|
||||
- https://docs.microsoft.com/en-us/azure/databricks/scenarios/what-is-azure-databricks
|
||||
64
data/rules/datadog.yml
Normal file
64
data/rules/datadog.yml
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
rules:
|
||||
- name: Datadog API Key
|
||||
id: kingfisher.datadog.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(?:datadog|dd-|dd_)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dd-apikey-dd52c29224affe29d163c6bf99e5c34f
|
||||
- datadog-secrettoken-0024a29224affe29d173c0bf99e5a89d
|
||||
references:
|
||||
- https://docs.datadoghq.com/account_management/api-app-keys/
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Accept: application/json
|
||||
DD-API-KEY: '{{ TOKEN }}'
|
||||
DD-APPLICATION-KEY: '{{ APPKEY }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.datadoghq.com/api/v2/current_user
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.datadog.2
|
||||
variable: APPKEY
|
||||
|
||||
- name: Datadog Application Secret
|
||||
id: kingfisher.datadog.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(?:
|
||||
dd[_-]?\w{0,8}[_-]?(?:key|secret) |
|
||||
datadog |
|
||||
dog
|
||||
)
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{40}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dd_secret_key-3c0c3965368a6b10f7640dbda46abfdca981c2d3
|
||||
- datadog_token = BzHpkcs7LujMb3Q1vLRRjbpBNxxYV0ousumYoKJS
|
||||
references:
|
||||
- https://docs.datadoghq.com/account_management/api-app-keys/
|
||||
23
data/rules/dependency_track.yml
Normal file
23
data/rules/dependency_track.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
rules:
|
||||
- name: Dependency-Track API Key
|
||||
id: kingfisher.dtrack.1
|
||||
pattern: '\b(odt_[A-Za-z0-9]{32,255})\b'
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'odt_KTJlDq2AGGGlqG4riKdT7p980AW8RlU5'
|
||||
- 'odt_ABCDDq2AGxGlrF4ribBT7p98AOM9TlU8'
|
||||
- 'odt_FHxhQGh77JAHHIYpZ818UQ0aYjXIdMIxxgeR'
|
||||
# validation:
|
||||
# type: Http
|
||||
# content:
|
||||
# request:
|
||||
# headers:
|
||||
# Authorization: "Bearer {{ TOKEN }}"
|
||||
# method: GET
|
||||
# response_matcher:
|
||||
# - report_response: true
|
||||
# - status:
|
||||
# - 200
|
||||
# type: StatusMatch
|
||||
# url: https://dependencytrack.example.com/api/v1/token/verify
|
||||
67
data/rules/digitalocean.yml
Normal file
67
data/rules/digitalocean.yml
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
rules:
|
||||
- name: DigitalOcean API Key
|
||||
id: kingfisher.digitalocean.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
(?:dop|doo)_v1_
|
||||
[a-f0-9]{64}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dop_v1_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
|
||||
- 'token = "dop_v1_ef0e04edc13918192246e0c90f0735c7f4db7a5a036a857e48d6cc98f1c9576b"'
|
||||
categories:
|
||||
- api
|
||||
- secret
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.digitalocean.com/v2/projects?per_page=1
|
||||
|
||||
- name: DigitalOcean Refresh Token
|
||||
id: kingfisher.digitalocean.2
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
dor_v1_
|
||||
[a-f0-9]{64}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dor_v1_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
|
||||
- ' "refresh_token": "dor_v1_d6ce5b93104521c47be0b580e9296454ef4a319b02b5513469f0ec71d99af2e2",'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://cloud.digitalocean.com/v1/oauth/token
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
body: |
|
||||
{
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": "{{ TOKEN }}"
|
||||
}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: JsonValid
|
||||
77
data/rules/discord.yml
Normal file
77
data/rules/discord.yml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
rules:
|
||||
- name: Discord Webhook URL
|
||||
id: kingfisher.discord.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
(
|
||||
https://discord\.com/api/webhooks/
|
||||
\d{18}
|
||||
)/
|
||||
(
|
||||
[0-9a-z_\-]{68}
|
||||
)
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- https://discord.com/api/webhooks/123456789012345678/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdef
|
||||
categories:
|
||||
- api
|
||||
- webhook
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: '{{ TOKEN }}'
|
||||
- name: Discord Bot Token
|
||||
id: kingfisher.discord.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
[MNO][A-Za-z0-9_-]{23}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'apikey: MC0dBvKTjD0rY0cV8i37CjBf.uLHQPq.Nb1Ok1mEhe-3iTdrGOuegj29yQR'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bot {{ BOTID }}
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://discord.com/api/v8/users/@me
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.discord.3"
|
||||
variable: BOTID
|
||||
- name: Discord Bot ID
|
||||
id: kingfisher.discord.3
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(?:discord|botid|bot_id)
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
\d{17,19}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- discord = 12345678901234567
|
||||
- 'bot_id: "123456789012345678"'
|
||||
17
data/rules/django.yml
Normal file
17
data/rules/django.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
rules:
|
||||
- name: Django Secret Key
|
||||
id: kingfisher.django.1
|
||||
pattern: |
|
||||
(?x)
|
||||
[DJANGO]\w{0,8}SECRET_KEY
|
||||
.{1,16}?
|
||||
\b
|
||||
(
|
||||
[a-zA-Z0-9*!$@\#&_%^-]{45,55}
|
||||
)
|
||||
\b
|
||||
min_entropy: 4.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- os.environ.get('DJANGO_SECRET_KEY','wwf*2#86t64!fgh6yav$aoeuo@u2o@fy&*gg76q!&%6x_wbduad')
|
||||
- DJANGO_SECRET_KEY = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z"
|
||||
32
data/rules/dockerhub.yml
Normal file
32
data/rules/dockerhub.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
rules:
|
||||
- name: Docker Hub Personal Access Token
|
||||
id: kingfisher.dockerhub.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
dckr_pat_[a-zA-Z0-9_-]{27}
|
||||
)
|
||||
(?: $ | [^a-zA-Z0-9_-] )
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- docker login -u gemesa -p dckr_pat_hc8VxYclixyTr2rDFsa2rqzkP3Y
|
||||
- docker login -u gemesa -p dckr_pat_tkzBYxjNNC3R_Yg6jd_O-G8FbrJ
|
||||
- docker login -u gemesa -p dckr_pat_1q8yKET1VDJTpfCwseUDzT8vFh-
|
||||
references:
|
||||
- https://docs.docker.com/reference/api/hub/latest/#tag/access-tokens/paths/~1v2~1access-tokens~1%7Buuid%7D/get
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://hub.docker.com/v2/access-tokens?page_size=1
|
||||
179
data/rules/doppler.yml
Normal file
179
data/rules/doppler.yml
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
rules:
|
||||
- name: Doppler CLI Token
|
||||
id: kingfisher.doppler.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(dp\.ct\.[a-zA-Z0-9]{40,44})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dp.ct.bAqhcVzrhy5cRHkOlNTc0Ve6w5NUDCpcutm8vGE9myi
|
||||
references:
|
||||
- https://docs.doppler.com/reference/api
|
||||
- https://docs.doppler.com/reference/auth-token-formats
|
||||
- https://docs.doppler.com/reference/auth-me
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.doppler.com/v3/projects
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
- name: Doppler Personal Token
|
||||
id: kingfisher.doppler.2
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(dp\.pt\.[a-zA-Z0-9]{40,44})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dp.pt.bAqhcVzrhy5cRHkOlNTc0Ve6w5NUDCpcutm8vGE9myi
|
||||
references:
|
||||
- https://docs.doppler.com/reference/api
|
||||
- https://docs.doppler.com/reference/auth-token-formats
|
||||
- https://docs.doppler.com/reference/auth-me
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.doppler.com/v3/projects
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler Service Token
|
||||
id: kingfisher.doppler.3
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(dp\.st\.(?:[a-z0-9\-_]{2,35}\.)?[a-zA-Z0-9]{40,44})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dp.st.dev.bAqhcVzrhy5cRHkOlNTc0Ve6w5NUDCpcutm8vGE9myi
|
||||
references:
|
||||
- https://docs.doppler.com/reference/api
|
||||
- https://docs.doppler.com/reference/auth-token-formats
|
||||
- https://docs.doppler.com/reference/auth-me
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.doppler.com/v3/me
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler Service Account Token
|
||||
id: kingfisher.doppler.4
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(dp\.sa\.[a-zA-Z0-9]{40,44})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dp.sa.bAqhcVzrhy5cRHkOlNTc0Ve6w5NUDCpcutm8vGE9myi
|
||||
references:
|
||||
- https://docs.doppler.com/reference/api
|
||||
- https://docs.doppler.com/reference/auth-token-formats
|
||||
- https://docs.doppler.com/reference/auth-me
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.doppler.com/v3/me
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler SCIM Token
|
||||
id: kingfisher.doppler.5
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(dp\.scim\.[a-zA-Z0-9]{40,44})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dp.scim.bAqhcVzrhy5cRHkOlNTc0Ve6w5NUDCpcutm8vGE9myi
|
||||
references:
|
||||
- https://docs.doppler.com/reference/api
|
||||
- https://docs.doppler.com/reference/auth-token-formats
|
||||
- https://docs.doppler.com/reference/auth-me
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.doppler.com/v3/scim/v2/ServiceProviderConfig
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
|
||||
- name: Doppler Audit Token
|
||||
id: kingfisher.doppler.6
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(dp\.audit\.[a-zA-Z0-9]{40,44})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dp.audit.bAqhcVzrhy5cRHkOlNTc0Ve6w5NUDCpcutm8vGE9myi
|
||||
references:
|
||||
- https://docs.doppler.com/reference/api
|
||||
- https://docs.doppler.com/reference/auth-token-formats
|
||||
- https://docs.doppler.com/reference/auth-me
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.doppler.com/v3/auditlogs
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
33
data/rules/dynatrace.yml
Normal file
33
data/rules/dynatrace.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
rules:
|
||||
- name: Dynatrace Token
|
||||
id: kingfisher.dynatrace.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
dt0[a-z][0-9]{2}
|
||||
\.
|
||||
[A-Z0-9]{24}
|
||||
\.
|
||||
[A-Z0-9]{64}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- dt0c01.ST2EY72KQINMH574WMNVI7YN.G3DFPBEJYMODIDAEX454M7YWBUVEFOWKPRVMWFASS64NFH52PX6BNDVFFM572RZM
|
||||
references:
|
||||
- https://docs.dynatrace.com/docs/discover-dynatrace/references/dynatrace-api/basics/dynatrace-api-authentication
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.dynatrace.com/v2/release-notes
|
||||
headers:
|
||||
Authorization: "Api-Token {{ TOKEN }}"
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: JsonValid
|
||||
33
data/rules/easypost.yml
Normal file
33
data/rules/easypost.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
rules:
|
||||
- name: EasyPost API token
|
||||
id: kingfisher.easypost.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
EZ[AT]K
|
||||
[a-zA-Z0-9]{54}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- '"EZTKXxNbJDeDLDyrXuIgHd3cr1YmP7MFqY9cHAPYMOXhUN8nJ671JKaGME"'
|
||||
- EZAK1234abcd5678efgh9012ijkl3456mnop7890qrst1234uvwx5678yz
|
||||
categories:
|
||||
- api
|
||||
- secret
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: 'Basic {{ TOKEN | append: ":" | b64enc }}'
|
||||
Content-Type: application/json
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.easypost.com/v2/shipments?page_size=5
|
||||
81
data/rules/facebook.yml
Normal file
81
data/rules/facebook.yml
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
rules:
|
||||
- name: Facebook App ID
|
||||
id: kingfisher.facebook.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(?:facebook|fb)
|
||||
(?:.|[\n\r]){0,8}?
|
||||
(?:APP|APPLICATION)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
\d{15}
|
||||
)
|
||||
\b
|
||||
min_entropy: 2.0
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- '"facebook String appId = "294790898041575"; String appSecret = "ce3f9f0362bbe5ab01dfc8ee565e4372"'
|
||||
- 'fb_app_id: 123456789012345'
|
||||
- 'FACEBOOK_APPLICATION_ID=123456789012345'
|
||||
|
||||
- name: Facebook Secret Key
|
||||
id: kingfisher.facebook.2
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
\b (?: facebook | fb )
|
||||
.?
|
||||
(?: api | app | application | client | consumer | customer | secret | key )
|
||||
.?
|
||||
(?: key | oauth | sec | secret )?
|
||||
.{0,2} \s{0,20} .{0,2} \s{0,20} .{0,2}
|
||||
\b ([a-z0-9]{32}) \b
|
||||
examples:
|
||||
- ' # config.facebook.key = "34cebc81c056a21bc66e212f947d73ec"'
|
||||
- " var fbApiKey = '0278fc1adf6dc1d82a156f306ce2c5cc';"
|
||||
- ' fbApiKey: "171e84fd57f430fc59afa8fad3dbda2a",'
|
||||
- '"facebook appSecret = "ce3f9f0362bbe5ab01dfc8ee565e4372"'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: >-
|
||||
https://graph.facebook.com/v19.0/oauth/access_token
|
||||
?client_id={{ APIID }}
|
||||
&client_secret={{ TOKEN }}
|
||||
&grant_type=client_credentials
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.facebook.1
|
||||
variable: APIID
|
||||
|
||||
- name: Facebook Access Token
|
||||
id: kingfisher.facebook.3
|
||||
pattern: |
|
||||
(?x)
|
||||
(?:
|
||||
\b
|
||||
(?:facebook|fb\b)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:access_token|access[\s-]token)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
)?
|
||||
\b
|
||||
(EAACEdEose0cBA[a-zA-Z0-9]{20,})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- "url = 'https://graph.facebook.com/me/friends?access_token=EAACEdEose0cBAD5XZCz5JXYvqyeJzcSvFZC42toHiWyfjhcZCMZBZCpE3uRJnEBsrhUEMRK1wWs6SsdiDCaCI1mYwyoNuMix2XZCpvsKbZB9TumtZBlcLeIpl4pa931Ce9rTinEAhtyVVZAAZAX4NmfpBUqWtzCRC0fX5GZBn7ZC28mPKAZDZD'"
|
||||
- 'fb_access_token: "EAACEdEose0cBAMZD123456789abcdefghijklmnopqrstuvwxyz"'
|
||||
- 'FACEBOOK_ACCESS_TOKEN=EAACEdEose0cBAZAQW123456789abcdefghijklmnopqrstuvwxyzASDFGHJKL'
|
||||
32
data/rules/fastly.yml
Normal file
32
data/rules/fastly.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
rules:
|
||||
- name: Fastly API token
|
||||
id: kingfisher.fastly.1
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
\b
|
||||
fastly
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
([a-z0-9_-]{32})
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'Fastly token: fgsb3ef237afd6c1b9d91f81cdba64f3'
|
||||
references:
|
||||
- https://developer.fastly.com/reference/api/#authentication
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Fastly-Key: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.fastly.com/current_user
|
||||
58
data/rules/figma.yml
Normal file
58
data/rules/figma.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
rules:
|
||||
- name: Figma Personal Access Token
|
||||
id: kingfisher.figma.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
figd_[A-Za-z0-9_-]{38,42}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- figma pat = figd_rh1234567890123456789012345678901234abcd
|
||||
- "figma access token: figd_1234567890123456789012345678901234abcdef"
|
||||
references:
|
||||
- https://www.figma.com/developers/api#users
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
X-Figma-Token: '{{ TOKEN }}'
|
||||
method: GET
|
||||
url: https://api.figma.com/v1/me
|
||||
|
||||
- name: Figma Personal Access Header Token
|
||||
id: kingfisher.figma.2
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
figma
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[0-9A-F]{4}
|
||||
-[0-9A-F]{8}
|
||||
(?:-[0-9A-F]{4}){3}
|
||||
-[0-9A-F]{12}
|
||||
)
|
||||
\b
|
||||
examples:
|
||||
- "--header='X-Figma-Token: 1394-0ca7a5be-8e22-40ee-8c40-778d41ab2313'"
|
||||
references:
|
||||
- https://www.figma.com/developers/api#users
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
X-Figma-Token: '{{ TOKEN }}'
|
||||
method: GET
|
||||
url: https://api.figma.com/v1/me
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- "Invalid token"
|
||||
negative: true
|
||||
39
data/rules/fileio.yml
Normal file
39
data/rules/fileio.yml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
rules:
|
||||
- name: FileIO Secret Key
|
||||
id: kingfisher.fileio.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
fileio
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
[A-Z0-9]{16}
|
||||
(?:\.[A-Z0-9]{7}){2}
|
||||
\.[A-Z0-9]{8}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- fileio SECRETKEY = Z9Y8X7W6V5U4T3S2R1Q0.P9O8N7M6L5K4J3H2G1F
|
||||
- fileio.PRIVATE.TOKEN = F0E1D2C3B4A596877869.5E4D3C2B1A0Z9Y8X7W6V
|
||||
- fileio_key = M8N6B4V2C0X9Z7L5K3J1.H2G4F6D8S0A9P7O5I3U1
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://file.io/api/v2/account
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: HeaderMatch
|
||||
header: content-type
|
||||
expected: ["application/json"]
|
||||
- type: JsonValid
|
||||
65
data/rules/finicity.yml
Normal file
65
data/rules/finicity.yml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
rules:
|
||||
- name: Finicity API token
|
||||
id: kingfisher.finicity.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
finicity
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-f0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- finicity_api_token = "5600675afe3e1a0fa76d5b99b30ca7a8"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.finicity.com/aggregation/v2/transactions
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
x-finicity-app-key: '{{ TOKEN }}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"transactions":'
|
||||
|
||||
- name: Finicity client secret
|
||||
id: kingfisher.finicity.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
finicity
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{20}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- finicity-clientsecret = 'geruj4LFyu0NBeBOnQvo'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://api.finicity.com/aggregation/v2/customers
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
x-finicity-secret: '{{ TOKEN }}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
30
data/rules/frame.io.yml
Normal file
30
data/rules/frame.io.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
rules:
|
||||
- name: Frame.io API token
|
||||
id: kingfisher.frame.io.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
fio-u-(?:[A-Z0-9_-]{16}){4}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- fio-u-TaWoPIBovaGCbBkUtGPKWS0D3cu254VA33IFCCrtwl8J2Dtq2pMJ9MvNHmNoL2XX
|
||||
- ffio-u-TaWoPIBovaGCbBkUtGPKWS0D3cu254VA33IFCCrtwl8J2Dtq2pMJ9MvNHmNoL2XX
|
||||
references:
|
||||
- https://developer.frame.io/api/reference/operation/getMe/
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.frame.io/v2/me
|
||||
61
data/rules/gcp.yml
Normal file
61
data/rules/gcp.yml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
rules:
|
||||
- name: GCP API Token
|
||||
id: kingfisher.gcp.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?m)
|
||||
(?i)
|
||||
(?s)
|
||||
(
|
||||
\{[^{}]*
|
||||
\"auth_provider_x509_cert_url\":
|
||||
.{0,512}?
|
||||
}
|
||||
)
|
||||
|
|
||||
\{
|
||||
(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*
|
||||
"auth_provider_x509_cert_url":\s*".+?"
|
||||
(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*
|
||||
\}
|
||||
min_entropy: 4.5
|
||||
confidence: high
|
||||
examples:
|
||||
- '{"auth_provider_x509_cert_url":DFD5dC97C711CE1E078FD0A5CBCE1fE895D44abb47faeD9280db6137a35EDdfBd173Dbf57B1e54c10dD9B8E36bdE4816EedEF6c55D9ca4FC14dEf71c9bNd6a5aF9c46EC6BdfCD4f7fEeb2B9E96A8CfBD9a965bbaDAACe0d09ffe19Fc5BF3B6c9636A9b35d0cFcfC0ECb118edf8E2dEae3f1D3C36F5Da9DBe562f3E2deb9D20f4ndDC106bdEdBeaB5629362eE6d0C1b90e84A6C281C14c2ade40c3AF17ACeEfe0e998d5f3BaadbB7D479ab3cEbc8Be263Dc07dF2}'
|
||||
- |-
|
||||
{
|
||||
"admin": {
|
||||
"credential": {
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"client_email": "firebase-example@firebase-example.iam.gserviceaccount.com",
|
||||
"client_id": "218284793146200123456",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-example-a1mrc%40firebase-example.iam.gserviceaccount.com",
|
||||
"database_url": "https://example.firebaseio.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
validation:
|
||||
type: GCP
|
||||
- name: GCP Private Key ID
|
||||
id: kingfisher.gcp.3
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
gcp
|
||||
(?:
|
||||
.{0,20}?
|
||||
(?:key|secret)
|
||||
)
|
||||
.{0,12}
|
||||
[=:]
|
||||
\s{0,8}
|
||||
["']?
|
||||
([0-9a-z]{35,40})
|
||||
["']?
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- gcp_secret = ANzaSy0c3475372a7b10f7740dbda47abfdca42
|
||||
196
data/rules/generic.yml
Normal file
196
data/rules/generic.yml
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
rules:
|
||||
- name: Generic Secret
|
||||
id: kingfisher.generic.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
secret
|
||||
.{0,20}
|
||||
\b
|
||||
([0-9a-z]{32,64})
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: low
|
||||
examples:
|
||||
- ' private static String CLIENT_SECRET = "6fb1cff7690db9ac066cadbbde8e3c078efdabcf";'
|
||||
- name: Generic API Key
|
||||
id: kingfisher.generic.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?: api_key | apikey | access_key | accesskey )
|
||||
(?:.|[\n\r]){0,8}?
|
||||
\b
|
||||
([0-9a-z][0-9a-z\-._/+]{30,62}[0-9a-z])
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: low
|
||||
examples:
|
||||
- 'API_KEY = "951bc382db9abad29c68634761dd6e19"'
|
||||
- "buildConfigField 'String' , 'API_KEY' , '\"951bc382db9cfee29c68634761dd6e19\"'\tAPI_KEY\t"
|
||||
- name: Generic Username and Password
|
||||
id: kingfisher.generic.3
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?: username | user)
|
||||
\b
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?: password | pass )
|
||||
(?:.|[\n\r]){0,16}?
|
||||
["'] ([^"']{5,30}) ["']
|
||||
min_entropy: 3.3
|
||||
confidence: low
|
||||
examples:
|
||||
- |
|
||||
credential = UsernamePasswordCredential(
|
||||
client_id='da34859b-2ae4-48c3-bfe0-1b28b7cf2eed',
|
||||
username='donjuandemarco',
|
||||
password='1qay@WXS????',
|
||||
tenant_id='bc877b20-f135-4c13-a266-8ed26b8f0f4b')
|
||||
- |
|
||||
hostname = '10.11.12.13'
|
||||
username = 'donjuandemarco@example.com'
|
||||
password = '`123QWERasdf'
|
||||
- |
|
||||
hostname = '10.11.12.13'
|
||||
USERNAME = 'donjuandemarco@example.com'
|
||||
# some comment
|
||||
# some other comment
|
||||
PASS = '`123QWERasdf'
|
||||
- |
|
||||
user = 'abuser' # some comment
|
||||
password = 'abuser123456' # some other comment
|
||||
- |
|
||||
user = 'Aladdin'
|
||||
password = 'open sesame'
|
||||
- name: Generic Username and Password
|
||||
id: kingfisher.generic.4
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?: username | user)
|
||||
\b
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?: password | pass )
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(\S{5,30})
|
||||
(?: \s | $ )
|
||||
min_entropy: 3.3
|
||||
confidence: low
|
||||
examples:
|
||||
- |
|
||||
user = Aladdin
|
||||
password = open_sesame
|
||||
- |
|
||||
user = Aladdin
|
||||
// some comment
|
||||
// some other comment
|
||||
password = open_sesame
|
||||
- ":authn_dbd_params => 'host=db_host port=3306 user=apache password=###### dbname=apache_auth',"
|
||||
- name: Generic Password
|
||||
id: kingfisher.generic.5
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
password
|
||||
\b
|
||||
(?:.|[\n\r]){0,16}?
|
||||
["']
|
||||
([^$<%@.,\s'"(){}&/\#\-][^\s'"(){}/]{4,}) (?# password )
|
||||
["']
|
||||
min_entropy: 3.3
|
||||
confidence: low
|
||||
categories: [fuzzy, generic, secret]
|
||||
examples:
|
||||
- |
|
||||
password = "super$ecret"
|
||||
- |
|
||||
password="super$ecret"
|
||||
- |
|
||||
String usernamePassword = "application:" + appKey + ":" + appSecret;
|
||||
- |
|
||||
my_password: "super$ecret"
|
||||
- |
|
||||
"password": "super$ecret",
|
||||
- |
|
||||
my_password := "super$ecret"
|
||||
- |
|
||||
password => "super$ecret"
|
||||
- |
|
||||
"ApplicationServicesConnection" : {
|
||||
"ServiceAddress" : "https://services-dev.examples.com",
|
||||
"AdminPassword" : "thisismypassword"
|
||||
}
|
||||
- |
|
||||
private const string DevFolkoosComPfxPassword = "thisismypassword";
|
||||
- |
|
||||
"password": "YOURPASSWROD"
|
||||
- |
|
||||
create_random_name('sfrp-cli-cert2', 24),
|
||||
'cluster_name': self.create_random_name('sfrp-cli-', 24),
|
||||
'vm_password': "Pass123!@#",
|
||||
'policy_path': os.path.join(TEST_DIR, 'policy.json')
|
||||
})
|
||||
- name: Weak Password Pattern
|
||||
id: kingfisher.weak_password.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
blink\d{3,6}
|
||||
|correcthorsebatterystaple\d{0,6}
|
||||
|letmein\d{1,6}
|
||||
|newpass\d{1,6}
|
||||
|p@ssw0rd\d{0,6}
|
||||
|pa55word\d{0,6}
|
||||
|pass4now\d{0,6}
|
||||
|password\d{1,6}
|
||||
|qwer\d{4,6}
|
||||
|qwerty\d{3,6}
|
||||
|trustno\d{1,6}
|
||||
)
|
||||
\b
|
||||
min_entropy: 1.0
|
||||
confidence: low
|
||||
examples:
|
||||
- password123
|
||||
- blink5678
|
||||
- letmein42
|
||||
- p@ssw0rd99
|
||||
- qwerty456
|
||||
- name: Generic Username and Password
|
||||
id: kingfisher.generic.8
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?: db_user | db_USERNAME | db_name)
|
||||
\b
|
||||
(?:.|[\n\r]){0,8}?
|
||||
["'] ([^"']{5,40}) ["']
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(
|
||||
db_password | db_pass
|
||||
\b
|
||||
(?:.|[\n\r]){0,16}?
|
||||
["'] [^"']{5,40}
|
||||
) ["']
|
||||
min_entropy: 3.3
|
||||
confidence: low
|
||||
examples:
|
||||
- |
|
||||
credential = UsernamePasswordCredential(
|
||||
client_id='da34859b-2ae4-48c3-bfe0-1b28b7cf2eed',
|
||||
username='donjuandemarco',
|
||||
password='1qay@WXS????',
|
||||
tenant_id='bc877b20-f135-4c13-a266-8ed26b8f0f4b')
|
||||
- |
|
||||
hostname = '10.11.12.13'
|
||||
username = 'donjuandemarco@example.com'
|
||||
password = '`123QWERasdf'
|
||||
- |
|
||||
hostname = '10.11.12.13'
|
||||
USERNAME = 'donjuandemarco@example.com'
|
||||
# some comment
|
||||
# some other comment
|
||||
PASS = '`123QWERasdf'
|
||||
- |
|
||||
user = 'abuser' # some comment
|
||||
password = 'abuser123456' # some other comment
|
||||
- |
|
||||
user = 'Aladdin'
|
||||
password = 'open sesame'
|
||||
205
data/rules/github.yml
Normal file
205
data/rules/github.yml
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
rules:
|
||||
- name: GitHub Personal Access Token
|
||||
id: kingfisher.github.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
(?: # for token prefixes
|
||||
ghp| # Personal Access Token
|
||||
gho| # OAuth Token
|
||||
ghu| # GitHub App User-to-Server Token
|
||||
ghs| # GitHub App Server-to-Server Token
|
||||
ghr| # Refresh Token
|
||||
github_pat # Alternative format for Personal Access Token
|
||||
)_
|
||||
(?: # for token body
|
||||
[a-z0-9_]{35,235} # 35 to 235 lowercase alphanumeric characters or underscores
|
||||
)
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- "GITHUB_KEY=ghp_XIxB7KMNdAr3zqWtQqhE94qglHqOzn1D1stg"
|
||||
- "let g:gh_token='ghp_4U3LSowpDx8XvYE7A8GH56oxU5aWnY2mzIbV'"
|
||||
- |
|
||||
## git developer settings
|
||||
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
|
||||
- "oauth_token: gho_fq75OMU7UVbS9pTZmoCCzJT6TM5d1w099FgG"
|
||||
- "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"
|
||||
references:
|
||||
- https://docs.github.com/en/rest/users?apiVersion=2022-11-28
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://api.github.com/graphql
|
||||
headers:
|
||||
Authorization: token {{ TOKEN }}
|
||||
Accept: application/vnd.github+json
|
||||
Content-Type: application/json
|
||||
body: |
|
||||
{
|
||||
"query": "{ viewer { login } }"
|
||||
}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"login"'
|
||||
- name: GitHub OAuth Access Token
|
||||
id: kingfisher.github.2
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
gho_
|
||||
[a-zA-Z0-9]{36}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- ' "url": "git+https://FelipeMestre:gho_psT9pqNFsehnc4se0ZzzR0HBxapxZD35hNHi@github.com/gontarz/PW_2021_Website-FelipeMestre.git"'
|
||||
- ' oauth_token: gho_fq75OMU7UVbS9pTZmoCCzJT6TM5d1w099FgG'
|
||||
references:
|
||||
- https://docs.github.com/en/rest/users?apiVersion=2022-11-28
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://api.github.com/graphql
|
||||
headers:
|
||||
Authorization: token {{ TOKEN }}
|
||||
Accept: application/vnd.github+json
|
||||
Content-Type: application/json
|
||||
body: |
|
||||
{
|
||||
"query": "{ viewer { login } }"
|
||||
}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"login"'
|
||||
- name: GitHub App Token
|
||||
id: kingfisher.github.3
|
||||
pattern: '\b((?:ghu|ghs)_[a-zA-Z0-9]{36})\b'
|
||||
examples:
|
||||
- ' "token": "ghu_16C7e42F292c69C2E7C10c838347Ae178B4a",'
|
||||
- |
|
||||
Example usage:
|
||||
git clone http://ghs_RguXIkihJjwHAP6eXEYxaPNvywurTr5IOAbg@github.com/username/repo.git
|
||||
references:
|
||||
- https://docs.github.com/en/rest/users?apiVersion=2022-11-28
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://api.github.com/graphql
|
||||
headers:
|
||||
Authorization: token {{ TOKEN }}
|
||||
Accept: application/vnd.github+json
|
||||
Content-Type: application/json
|
||||
body: |
|
||||
{
|
||||
"query": "{ viewer { login } }"
|
||||
}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"login"'
|
||||
- name: GitHub Refresh Token
|
||||
id: kingfisher.github.4
|
||||
pattern: '\b(ghr_[a-zA-Z0-9]{76})\b'
|
||||
examples:
|
||||
- ' "refresh_token": "ghr_1B4a2e77838347a7E420ce178F2E7c6912E169246c3CE1ccbF66C46812d16D5B1A9Dc86A1498",'
|
||||
references:
|
||||
- https://docs.github.com/en/rest/users?apiVersion=2022-11-28
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://api.github.com/graphql
|
||||
headers:
|
||||
Authorization: token {{ TOKEN }}
|
||||
Accept: application/vnd.github+json
|
||||
Content-Type: application/json
|
||||
body: |
|
||||
{
|
||||
"query": "{ viewer { login } }"
|
||||
}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"login"'
|
||||
- name: GitHub Client ID
|
||||
id: kingfisher.github.5
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
(?:github)
|
||||
.?
|
||||
(?: api | app | application | client | consumer | customer )?
|
||||
.?
|
||||
(?: id | identifier | key )
|
||||
.{0,2} \s{0,20} .{0,2} \s{0,20} .{0,2}
|
||||
\b ([a-z0-9]{20}) \b
|
||||
examples:
|
||||
- |
|
||||
GITHUB_CLIENT_ID=ac58d6da7d7a84c039b7
|
||||
GITHUB_SECRET=37d02377a3e9d849e18704c3ec883f9c5787d857
|
||||
- name: GitHub Secret Key
|
||||
id: kingfisher.github.6
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
github
|
||||
.?
|
||||
(?: api | app | application | client | consumer | customer | secret | key )
|
||||
.?
|
||||
(?: key | oauth | sec | secret )?
|
||||
.{0,2} \s{0,20} .{0,2} \s{0,20} .{0,2}
|
||||
\b ([a-z0-9]{40}) \b
|
||||
examples:
|
||||
- |
|
||||
GITHUB_CLIENT_ID=ac58d6da7d7a84c039b7
|
||||
GITHUB_SECRET=37d02377a3e9d849e18704c3ec883f9c5787d857
|
||||
- name: GitHub Personal Access Token (fine-grained permissions)
|
||||
id: kingfisher.github.7
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(github_pat_[0-9a-zA-Z_]{82})
|
||||
\b
|
||||
examples:
|
||||
- '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'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://api.github.com/graphql
|
||||
headers:
|
||||
Authorization: token {{ TOKEN }}
|
||||
Accept: application/vnd.github+json
|
||||
Content-Type: application/json
|
||||
body: |
|
||||
{
|
||||
"query": "{ viewer { login } }"
|
||||
}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"login"'
|
||||
89
data/rules/gitlab.yml
Normal file
89
data/rules/gitlab.yml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
rules:
|
||||
- name: GitLab Private Token
|
||||
id: kingfisher.gitlab.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
glpat-
|
||||
[0-9a-zA-Z_-]{20}
|
||||
)
|
||||
(?:\b|$)
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- glpat-kSaPeOD_-T0JxMi3p28B
|
||||
- |
|
||||
docker build -t tweedledee \
|
||||
-f Dockerfile \
|
||||
--build-arg 'GO_REPO_TOKEN=glpat-tFrjFXD7soVU2fqxuDMh' \
|
||||
references:
|
||||
- https://docs.gitlab.com/api/users/#get-your-user-status
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
PRIVATE-TOKEN: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"message"'
|
||||
url: https://gitlab.com/api/v4/user/status
|
||||
|
||||
- name: GitLab Runner Registration Token
|
||||
id: kingfisher.gitlab.2
|
||||
pattern: '\b(GR1348941[0-9a-zA-Z_-]{20})(?:\b|$)'
|
||||
examples:
|
||||
- |
|
||||
sudo gitlab-runner register \
|
||||
--non-interactive \
|
||||
--url "https://gitlab.com/" \
|
||||
--registration-token "GR1348941_iAgdMy7a3NhZaa5oNoH" \
|
||||
--executor "docker" \
|
||||
--docker-image ubuntu:latest \
|
||||
--description "docker-runner" \
|
||||
--tag-list "docker, CICD, App" \
|
||||
--run-untagged="true" \
|
||||
--locked="false" \
|
||||
--access-level="not_protected"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
headers:
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Accept: application/json
|
||||
body: token={{ TOKEN }}
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200, 201]
|
||||
url: https://gitlab.com/api/v4/runners/verify
|
||||
|
||||
- name: GitLab Pipeline Trigger Token
|
||||
id: kingfisher.gitlab.3
|
||||
pattern: '\b(glptt-[0-9a-f]{40})\b'
|
||||
examples:
|
||||
- |
|
||||
curl \
|
||||
-X POST \
|
||||
--fail \
|
||||
--no-progress-meter \
|
||||
-F token=glptt-0d66598d696a02da33fb65e2a041f607c68ea50d \
|
||||
-F ref=main
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
PRIVATE-TOKEN: '{{ TOKEN }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
url: https://gitlab.com/api/v4/ci/pipeline_triggers/{{ TOKEN }}
|
||||
40
data/rules/gocardless.yml
Normal file
40
data/rules/gocardless.yml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
rules:
|
||||
- name: GoCardless API Token
|
||||
id: kingfisher.gocardless.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
gocardless
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
live_
|
||||
[A-Za-z0-9=_-]{16}
|
||||
(?:[A-Za-z0-9=_-]{8}){3}
|
||||
[A-Za-z0-9=_-]{0,2}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'gocardless_token = "live_8uq9fsUA28SqKT=CTsQxgKrqB6_7QV5tA39I8y5H'
|
||||
- GOCARDLESS_LIVE_KEY = "live_cpo0k9jbnb2djeaq=tga45ua_bnhev5ivv294a6cs"
|
||||
categories:
|
||||
- api
|
||||
- payment
|
||||
- identifier
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Accept: application/json
|
||||
GoCardless-Version: "2015-07-06"
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.gocardless.com/customers?limit=1
|
||||
96
data/rules/google.yml
Normal file
96
data/rules/google.yml
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
rules:
|
||||
- name: Google Client ID
|
||||
id: kingfisher.google.1
|
||||
pattern: '(?i)\b([0-9]+-[a-z0-9_]{32})\.apps\.googleusercontent\.com'
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
visible: false
|
||||
examples:
|
||||
- " 'clientID' : '231545488769-4d1mcev9vifvlncrern52id2pqqf5u5l.apps.googleusercontent.com',"
|
||||
- " //$google_client_id = '244082345999-o6m8f1pmb1e76tjfj9v7b96j31e53ps5.apps.googleusercontent.com';"
|
||||
- " GOOGLE_OAUTH2_CLIENT_ID = '607830223128-4qgthc7ofdqce232dk690t5jgkm1ce33.apps.googleusercontent.com'"
|
||||
- ' $cordovaOauth.google("653512027492-5u9blotr1521fa0lo1172nhv4pmqgttq.apps.googleusercontent.com", ["email"]).then(function(result) {'
|
||||
|
||||
- name: Google OAuth Client Secret
|
||||
id: kingfisher.google.2
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(GOCSPX-[a-zA-Z0-9_-]{28})
|
||||
(?:[^a-zA-Z0-9_-] | $)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'const CLIENTSECRET = "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"'
|
||||
|
||||
- name: Google OAuth Client Secret
|
||||
id: kingfisher.google.3
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
client.?secret .{0,10}
|
||||
\b
|
||||
([a-z0-9_-]{24})
|
||||
(?: [^a-z0-9_-] |$)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- '"client_secret":"aaaaaaaaaaaaaaaaaaaaaaa-"'
|
||||
- " //$google_client_secret = 'fnhqAakzWrX-mtFQ4PRdMoy0';"
|
||||
- " 'clientSecret' : 'Ufvuj-d6alhwGKvvLh_8Nq0K'"
|
||||
|
||||
- name: Google OAuth Access Token
|
||||
id: kingfisher.google.4
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(ya29\.[0-9A-Za-z_-]{20,1024})
|
||||
(?: [^0-9A-Za-z_-]|$)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
const setupCredentials = () => {
|
||||
const { encryptedData, iv } = encrypt({
|
||||
expiry_date: 1642441058842,
|
||||
access_token:
|
||||
'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzCu947fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod',
|
||||
// This token is linked to a test Google account (typebot.test.user@gmail.com)
|
||||
refresh_token:
|
||||
'1//039xWRt8YaYa3CgYIARAAGAMSNwF-L9Iru9FyuTrDSa7lkSceggPho83kJt2J29Ga91EhT1C6XV1vmo6bQS9puL_R2t8FIwR3gek',
|
||||
})
|
||||
- |
|
||||
-- Clear login if it's a new connection.
|
||||
--propertyTable.access_token = 'ya29.Ci_UA7aEsvT6-oVI8f96kvB6i8oO13WgdZUviLaCVtpEPYZqhQcQycR-u2X9xtmYGA'
|
||||
|
||||
- name: Google OAuth Credentials
|
||||
id: kingfisher.google.6
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
([0-9]+-[a-z0-9_]{32}\.apps\.googleusercontent\.com)
|
||||
(?:
|
||||
(?s).{0,40}
|
||||
)
|
||||
\b
|
||||
(?:
|
||||
(GOCSPX-[a-zA-Z0-9_-]{28})
|
||||
|
|
||||
(?:
|
||||
(?i) client.?secret .{0,10} \b ([a-zA-Z0-9_-]{24})
|
||||
)
|
||||
)
|
||||
(?:[^a-zA-Z0-9_-] | $)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
const CLIENT_ID = '304167046824-45h8no7j0s38akv998nivvb7i17ckqeh.apps.googleusercontent.com';
|
||||
const CLIENT_SECRET = '1QcFpNjHoAf4_XczYwhYicTl';
|
||||
- |
|
||||
public static GAPIS_CREDENTIALS = {
|
||||
// 1. Generate credentials: https://console.cloud.google.com/apis/
|
||||
// 2. Create OAuth page and set spreadsheets and drive.metadata.readonly scopes
|
||||
client_id: '132261435625-69ubohrvppjr9hcc5t9uighsb7j2cqhv.apps.googleusercontent.com',
|
||||
client_secret: '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',
|
||||
redirect_uri: `http://localhost:${Config.OAUTH_HTTP_PORT}/oauth2callback`
|
||||
};
|
||||
32
data/rules/googleoauth2.yml
Normal file
32
data/rules/googleoauth2.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
rules:
|
||||
- name: Google OAuth2 Access Token
|
||||
id: kingfisher.google.oauth2.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
ya29\.(?i:[a-z0-9_-]{30,})
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- "ya29.A0ARrdaM9Ra8K7R9AcxA1PpIMLVQ021H0TL0PRh2s_HH0_tn5gCSSf"
|
||||
- "ya29.Cj0KCQjwgLr5BRC3ARIsAKEd0AR9_Fg5fjV5pVXZlFYobyfz7Bb9SyOv"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
Accept: application/json
|
||||
method: GET
|
||||
url: https://www.googleapis.com/oauth2/v3/userinfo
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"email":'
|
||||
25
data/rules/jwt.yml
Normal file
25
data/rules/jwt.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
rules:
|
||||
- name: JSON Web Token (base64url-encoded)
|
||||
id: kingfisher.jwt.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
ey[a-zA-Z0-9_-]{12,} (?# header )
|
||||
\.
|
||||
ey[a-zA-Z0-9_-]{12,} (?# payload )
|
||||
\.
|
||||
[a-zA-Z0-9_-]{12,} (?# signature )
|
||||
)
|
||||
(?:[^a-zA-Z0-9_-]|$) (?# this instead of a \b anchor because that doesn't play nicely with `-` )
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmEmMjLiuyu5CSpyHI'
|
||||
- 'NUCLEAR_SERVICES_ANON_KEY=eyJhbGciOiJIUzI1NiIsEnR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFqcnVqc2lzY2Nzdnl2am5xdG5xIiwicm9sZSI6ImEub24iLCJpYXQiOjE2NTY1OTY0NjEsImV4cCI6MTk3MjE3MjQ2MX0.WQWcwBAQFNE259f2o8ruFln_UMLTFEnEaUD7KHrs9Aw'
|
||||
references:
|
||||
- https://en.wikipedia.org/wiki/JSON_Web_Token
|
||||
- https://datatracker.ietf.org/doc/html/rfc7519
|
||||
- https://en.wikipedia.org/wiki/Base64#URL_applications
|
||||
- https://datatracker.ietf.org/doc/html/rfc4648
|
||||
- https://developer.okta.com/blog/2018/06/20/what-happens-if-your-jwt-is-stolen
|
||||
96
data/rules/mongodb.yml
Normal file
96
data/rules/mongodb.yml
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
rules:
|
||||
- name: MongoDB API Private Key
|
||||
id: kingfisher.mongodb.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
(?:
|
||||
(?:\b|_|-|\.)
|
||||
(?:mongodb|atlas)
|
||||
(?:\b|_|-|\.)
|
||||
)
|
||||
.{0,1000}?
|
||||
(?:private|priv|secret|auth|pass|key)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(
|
||||
[a-fA-F0-9]{8}
|
||||
-
|
||||
[a-fA-F0-9]{4}
|
||||
-
|
||||
[a-fA-F0-9]{4}
|
||||
-
|
||||
[a-fA-F0-9]{4}
|
||||
-
|
||||
[a-fA-F0-9]{12}
|
||||
)
|
||||
min_entropy: 3.7
|
||||
examples:
|
||||
- ATLAS_PRIVATE_KEY=4b18315e-6b7d-4337-b449-5d38f5a189ec
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Accept: application/vnd.atlas.2023-02-01+json
|
||||
Content-Type: application/json
|
||||
method: GET
|
||||
digest: '{{ PUBKEY | append: ":" | append: TOKEN }}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"orgId":'
|
||||
- '"id":'
|
||||
url: https://cloud.mongodb.com/api/atlas/v2/groups
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.mongodb.2"
|
||||
variable: PUBKEY
|
||||
|
||||
- name: MongoDB API PUBLIC Key
|
||||
id: kingfisher.mongodb.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?:
|
||||
(?:\b|_|-|\.)
|
||||
(?:mongodb|atlas)
|
||||
(?:\b|_|-|\.)
|
||||
)
|
||||
(?:public|pub|user|id)
|
||||
(?:.|[\n\r]){0,4}?
|
||||
(
|
||||
[a-zA-Z]+
|
||||
)
|
||||
(?:$|[^a-zA-Z0-9/+=-])
|
||||
min_entropy: 2.0
|
||||
confidence: medium
|
||||
visible: false
|
||||
examples:
|
||||
- 'mongodb-public: qj4Zrh8e6A'
|
||||
- name: MongoDB URI Connection String
|
||||
id: kingfisher.mongodb.3
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
mongodb(?:\+srv)?://[\S]{3,50}:(?:[\S]{3,88})@[-.%\w/:]+
|
||||
)
|
||||
\b
|
||||
min_entropy: 3
|
||||
examples:
|
||||
- client = mongoc_client_new ("mongodb+srv://someuser:hunter2@my-atlas-rd941.mongodb.net/test?retryWrites=true&w=majority");
|
||||
- "mongodb+srv://user:passw0rd@cluster0.something.mongodb.net/"
|
||||
- "mongodb://mongoadmin:contoso@something.foo.mongodb.net/myFirstDatabase"
|
||||
- name: MongoDB Atlas Service Account Token
|
||||
id: kingfisher.mongodb.4
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
mdb_sa_sk_[0-9a-zA-Z_-]{6}[0-9a-zA-Z]{34}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- mdb_sa_sk_BdIX_jLzut2WTgglKzKvSgWMDDj5hEoTqdwOyLOL
|
||||
7
data/rules/mysql.yaml
Normal file
7
data/rules/mysql.yaml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
rules:
|
||||
- name: MySQL URI with Credentials
|
||||
id: kingfisher.mysql.1
|
||||
pattern: (?xi)\bmysql:\/\/[a-z0-9]+:([a-z0-9!@\#$%^&*()_+{}|:<>?=\\-]+)@[a-z0-9.]+:[0-9]+\/[a-z0-9]+\b
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- CONNECTION_URI="mysql://nimda:m42p!o@2wd@google.com:5434/elephant"
|
||||
73
data/rules/odbc.yml
Normal file
73
data/rules/odbc.yml
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
rules:
|
||||
- name: Credentials in ODBC Connection String
|
||||
id: kingfisher.odbc.1
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
(?: User | User\ Id | UserId | Uid) \s*=\s* ([^\s;]{3,100}) \s* ;
|
||||
[\ \t]* .{0,10} [\ \t]*
|
||||
(?: Password | Pwd) \s*=\s* ([^\t\ ;]{3,100}) \s* (?: [;] | $)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- |
|
||||
//Database Info
|
||||
$host = "localhost";
|
||||
$database = "NHOHVA";
|
||||
$user = "mg1021"; $password = "goodspec";
|
||||
- |
|
||||
//Database Info
|
||||
$host = "localhost";
|
||||
$database = "NHOHVA";
|
||||
$user = "mg1021"; $password = goodspec;
|
||||
- 'Server=host;Port=5432;User Id=username;Password=secret;Database=databasename;'
|
||||
- 'Server=host;Port=5432;SomeOtherKey=SomeOtherValue;User Id=username;Password=secret;Database=databasename;'
|
||||
- 'Data Source=190.190.200.100,1433;Network Library=DBMSSOCN;Initial Catalog=myDataBase;User ID=myUsername;Password=myPassword;'
|
||||
- 'Data Source=190.190.200.100,1433;Network_library=DBMSSOCN;Initial Catalog=myDataBase;User ID=myUsername;Password=myPassword;'
|
||||
- 'Provider=SQLNCLI;Server=myServerName,myPortNumber;Database=myDataBase;Uid=myUsername;Pwd=myPassword;'
|
||||
- |
|
||||
adoConn.Open("Provider=SQLOLEDB.1;User ID=specialbill_user; " & "Password =specialbill_user;Initial Catalog=SpecialBill_PROD;Data Source=uszdba01;")
|
||||
- |
|
||||
"driver={SQL Server};server=(#{datastore['DBHOST']});database=#{datastore['DBNAME']};uid=#{datastore['DBUID']};pwd=#{datastore['DBPASSWORD']}"
|
||||
negative_examples:
|
||||
- 'def login(self, user = "", password = "", domain = ""):'
|
||||
- |
|
||||
if datastore['VERBOSE']
|
||||
text = ''
|
||||
text << "User=#{username}, "
|
||||
text << "Password=#{password}, "
|
||||
text << "Domain=#{domain}, "
|
||||
text << "Full Name=#{full_name}, "
|
||||
text << "E-mail=#{e_mail}"
|
||||
print_good(text)
|
||||
- |
|
||||
if (len < ulen + wlen + 2)
|
||||
break;
|
||||
user = (char *) (p + 1);
|
||||
pwd = (char *) (p + ulen + 2);
|
||||
p += ulen + wlen + 2;
|
||||
- |
|
||||
/* Set default values */
|
||||
server = xmalloc(sizeof(*server));
|
||||
server->user = "anonymous";
|
||||
server->password = "busybox@";
|
||||
- |
|
||||
System.out.println("Here we go...");
|
||||
String url = "jdbc:msf:sql://127.0.0.1:8080/sample";
|
||||
String userid = "userid";
|
||||
String password = "password";
|
||||
- |
|
||||
char *domain = NULL;
|
||||
char *user = NULL;
|
||||
char *password = NULL;
|
||||
- |
|
||||
<?php
|
||||
\$user = \$_POST["username"];
|
||||
\$pwd = \$_POST["password"];
|
||||
\$otherdata = \$_POST["otherdata"];
|
||||
?>
|
||||
references:
|
||||
- https://docs.aws.amazon.com/redshift/latest/mgmt/configure-odbc-connection.html
|
||||
- https://docs.microsoft.com/en-us/azure/data-explorer/kusto/api/connection-strings/kusto
|
||||
- https://docs.microsoft.com/en-us/azure/mariadb/howto-connection-string
|
||||
- https://docs.microsoft.com/en-us/azure/mysql/single-server/how-to-connection-string
|
||||
- https://www.connectionstrings.com/
|
||||
55
data/rules/okta.yml
Normal file
55
data/rules/okta.yml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
rules:
|
||||
- name: Okta API Token
|
||||
id: kingfisher.okta.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?s)
|
||||
(?:okta|ssws)
|
||||
(?:.|[\n\r]){0,64}?
|
||||
\b
|
||||
(
|
||||
00[a-z0-9_-]{39}[a-z0-9_]
|
||||
)
|
||||
min_entropy: 3.3
|
||||
examples:
|
||||
- okta_api_token=00hqNORUpnTcdFWA5WEM4YwOkw6RXeFw21lFDRKmY1
|
||||
- 'okta_api_token = 00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
- 'OKTA_API_KEY = "00-aaaaaaaaaaaaa-aaaaaaaaaaaaaaaaaaaaaaaaa"'
|
||||
- 'okta_secret: 00QCjAl4MlV-WPXM-ABCDEFGHIJKL-0HmjFx-vbGua'
|
||||
- 'Authorization: SSWS 00QCjAl4MlV-WPXM-ABCDEFGHIJKL-0HmjFx-vbGua'
|
||||
- |
|
||||
variable "corp_okta_api_token" {
|
||||
default = "004EWTpRQT_HJtG_nL-agxacgzYHjxPcF99kJsFzWg"
|
||||
}
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Accept: application/json
|
||||
Authorization: SSWS {{ TOKEN }}
|
||||
Content-Type: application/json
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- activated
|
||||
url: https://{{ DOMAIN }}/api/v1/users/me
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.okta.2"
|
||||
variable: DOMAIN
|
||||
- name: Okta Domain
|
||||
id: kingfisher.okta.2
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(
|
||||
[a-z0-9-]{1,40}\.okta(?:preview|-emea)?\.com
|
||||
)
|
||||
\b
|
||||
min_entropy: 3
|
||||
visible: false
|
||||
examples:
|
||||
- company-name.okta.com
|
||||
30
data/rules/openai.yml
Normal file
30
data/rules/openai.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
rules:
|
||||
- name: OpenAI API Key
|
||||
id: kingfisher.openai.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
sk-[a-zA-Z0-9]{48}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- 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
|
||||
- curl https://api.openai.com/v1/images/generations -H 'Content-Type application/json' -H "Authorization Bearer sk-mxIt5s1tyfCJyIKHwrqOT4BlbkFJT3VVmv6VdSwB7XXIq1TO"
|
||||
references:
|
||||
- https://help.openai.com/en/articles/9132009-how-can-i-view-the-users-or-organizations-associated-with-an-api-key
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://api.openai.com/v1/me
|
||||
68
data/rules/pem.yml
Normal file
68
data/rules/pem.yml
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
rules:
|
||||
- name: PEM-Encoded Private Key
|
||||
id: kingfisher.pem.1
|
||||
pattern: |
|
||||
(?x)
|
||||
-----BEGIN\ .{0,20}\ ?PRIVATE\ KEY\ ?.{0,20}-----
|
||||
\s*
|
||||
( (?: [a-zA-Z0-9+/=\s"',] | \\r | \\n ) {50,} )
|
||||
\s*
|
||||
-----END\ .{0,20}\ ?PRIVATE\ KEY\ ?.{0,20}-----
|
||||
min_entropy: 4.5
|
||||
confidence: high
|
||||
prevalidated: false
|
||||
examples:
|
||||
- |
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn
|
||||
NhAAAAAwEAAQAAAIEAtDSHFO5tfN+jYMJuiNvBaplkSI3eFqKMLOvXyVu+dmSEic6xyKWQ
|
||||
qjQiFpXogArvAq2tBxWOq7F+a6rNhDKdICD2amRwDHqKD1bzXVSZ5c1XnpCFsBiQaEyX2i
|
||||
qyjScnntFHIpTCVHNxILDxsStocj64YS0C7hfCGVhft/Ts/O0AAAIQJOKnUyTip1MAAAAH
|
||||
c3NoLXJzYQAAAIEAtDSHFO5tfN+jYMJuiNvBaplkSI3eFqKMLOvXyVu+dmSEic6xyKWQqj
|
||||
QiFpXogArvAq2tBxWOq7F+a6rNhDKdICD2amRwDHqKD1bzXVSZ5c1XnpCFsBiQaEyX2iqy
|
||||
jScnntFHIpTCVHNxILDxsStocj6Cf99C7hfCGVhft/Ts/O0AAAADAQABAAAAgBcaTN8gGi
|
||||
VSPo3fH3CoS8mw1KyAk6JvQG1Z5xZHjsl65YsNVrmUkFFh0aT3nxEbVb0QKwineN0GKmD/
|
||||
Ss3R91a573gzli7TJPFCHhhBbE7FRC4KQMTc1/UANwFYQVcfZ4n9IVHr3jiWToSY3XbC66
|
||||
Zcd0sg+d+YRjIxUktuNFHBAAAAQQCOOKbSUJAWzcTDbxImwDCAfBMlEeMAnJrwobL/zxbT
|
||||
GhKdnqnomoreFdYL8vOcOlwZG0hUKIA6AM1GsMzp6aCwAAAAQQDmAABpOQnkDy8v8kTDhP
|
||||
dW3lAqRGOU4WRWj7WystQv/VjuJpceekhOyhNJBuNHDKZ3IT1agAZHIhhL+webE2S1AAAA
|
||||
QQDIk4H1agCohlHUg50PcyKzE/zZ85Gw0ErTmgqIIGd4B1AqUtjwVe1qFoqHuZPtq2cbVF
|
||||
1HTHh6GX//J6rKWVJZAAAAGWJsYXJzZW5AYnJhZGZvcmRzLW1icC5sYW4B
|
||||
-----END RSA PRIVATE KEY-----
|
||||
- |
|
||||
"-----BEGIN RSA PRIVATE KEY-----" +
|
||||
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn" +
|
||||
"NhAAAAAwEAAQAAAIEAtDSHFO5tfN+jYMJuiNvBaplkSI3eFqKMLOvXyVu+dmSEic6xyKWQ" +
|
||||
"qjQiFpXogArvAq2tBxWOq7F+a6rNhDKdICD2amRwDHqKD1bzXVSZ5c1XnpCFsBiQaEyX2i" +
|
||||
"qyjScnntFHIpTCVHNxILDxsStocj6Cf99C7hfCGVhft/Ts/O0AAAIQJOKnUyTip1MAAAAH" +
|
||||
"c3NoLXJzYQAAAIEAtDSHFO5tfN+jYMJuiNvBaplkSI3eFqKMLOvXyVu+dmSEic6xyKWQqj" +
|
||||
"QiFpXogArvAq2tBxWOq7F+a6rNhDKdICD2amRwDHqKD1bzXVSZ5c1XnpCFsBiQaEyX2iqy" +
|
||||
"jScnntFHIpTCVHNxILDxsStocj6Cf99C7hfCGVhft/Ts/O0AAAADAQABAAAAgBcaTN8gGi" +
|
||||
"VSPo3fH3CoS8mw1KyAk6JvQG1Z5xZHjsl65YsNVrmUkFFh0aT3nxEbVb0QKwineN0GKmD/" +
|
||||
"Ss3R91a573gzli7TJPFCHhhBbE7FRC4KQMTc1/UANwFYQVcfZ4n9IVHr3jiWToSY3XbC66" +
|
||||
"Zcd0sg+d+YRjIxUktuNFHBAAAAQQCOOKbSUJAWzcTDbxImwDCAfBMlEeMAnJrwobL/zxbT
|
||||
GhKdnqnomoreFdYL8vOcOlwZG0hUKIA6AM1GsMzp6aCwAAAAQQDmAABpOQnkDy8v8kTDhP
|
||||
dW3lAqRGOU4WRWj7WystQv/VjuJpceekhOyhNJBuNHDKZ3IT1agAZHIhhL+webE2S1AAAA
|
||||
QQDIk4H1agCohlHUg50PcyKzE/zZ85Gw0ErTmgqIIGd4B1AqUtjwVe1qFoqHuZPtq2cbVF
|
||||
1HTHh6GX//J6rKWVJZAAAAGWJsYXJzZW5AYnJhZGZvcmRzLW1icC5sYW4B
|
||||
-----END RSA PRIVATE KEY-----
|
||||
- |
|
||||
"-----BEGIN RSA PRIVATE KEY-----\r\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn\r\nNhAAAAAwEAAQAAAIEAtDSHFO5tfN+jYMJuiNvBaplkSI3eFqKMLOvXyVu+dmSEic6xyKWQ\r\nqjQiFpXogArvAq2tBxWOq7F+a6rNhDKdICD2amRwDHqKD1bzXVSZ5c1XnpCFsBiQaEyX2i\r\nqyjScnntFHIpTCVHNxILDxsStocj6Cf99C7hfCGVhft/Ts/O0AAAIQJOKnUyTip1MAAAAH\r\nc3NoLXJzYQAAAIEAtDSHFO5tfN+jYMJuiNvBaplkSI3eFqKMLOvXyVu+dmSEic6xyKWQqj\r\nQiFpXogArvAq2tBxWOq7F+a6rNhDKdICD2amRwDHqKD1bzXVSZ5c1XnpCFsBiQaEyX2iqy\r\njScnntFHIpTCVHNxILDxsStocj6Cf99C7hfCGVhft/Ts/O0AAAADAQABAAAAgBcaTN8gGi\r\nVSPo3fH3CoS8mw1KyAk6JvQG1Z5xZHjsl65YsNVrmUkFFh0aT3nxEbVb0QKwineN0GKmD/\r\nSs3R91a573gzli7TJPFCHhhBbE7FRC4KQMTc1/UANwFYQVcfZ4n9IVHr3jiWToSY3XbC66\r\nZcd0sg+d+YRjIxUktuNFHBAAAAQQCOOKbSUJAWzcTDbxImwDCAfBMlEeMAnJrwobL/zxbT\r\nGhKdnqnomoreFdYL8vOcOlwZG0hUKIA6AM1GsMzp6aCwAAAAQQDmAABpOQnkDy8v8kTDhP\r\ndW3lAqRGOU4WRWj7WystQv/VjuJpceekhOyhNJBuNHDKZ3IT1agAZHIhhL+webE2S1AAAA\r\nQQDIk4H1agCohlHUg50PcyKzE/zZ85Gw0ErTmgqIIGd4B1AqUtjwVe1qFoqHuZPtq2cbVF\r\n1HTHh6GX//J6rKWVJZAAAAGWJsYXJzZW5AYnJhZGZvcmRzLW1icC5sYW4B\r\n-----END RSA PRIVATE KEY-----"
|
||||
- name: Base64-PEM-Encoded Private Key
|
||||
id: kingfisher.pem.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(
|
||||
(?: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0t (?# prefix of base64 encoding of `-----BEGIN RSA PRIVATE KEY-----` )
|
||||
| LS0tLS1CRUdJTiBEU0EgUFJJVkFURSBLRVktLS0t (?# prefix of base64 encoding of `-----BEGIN DSA PRIVATE KEY-----` )
|
||||
| LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0t (?# prefix of base64 encoding of `-----BEGIN EC PRIVATE KEY-----` )
|
||||
)
|
||||
[a-zA-Z0-9+/=]{50,}
|
||||
)
|
||||
(?: [^a-zA-Z0-9+/=] | $ )
|
||||
min_entropy: 4.5
|
||||
confidence: high
|
||||
prevalidated: false
|
||||
examples:
|
||||
- 'PRIVATE_KEY_B64=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBb3kxWFh1VkFRcHFIYlFFMDVta2hyTmcvMTI0Ri8ySzlPYW5pelpUWlVVaEswOFU4CkxhaC9SbVVsWHFRMDEvU255aktGOWZqUDhFcU1OZ1dpamUzYmVwL3RPOVpTMEFUMi9PVlJXeS9TOG52RDQ5WTMKenMxMktSbERhR2lZc0RsYUZrbHJkeDQ4RWhRVmdHN3hmWE1jaC9OejJzc2FEby9kRkNBOW80TkZZQWUzM2UveApWNVo1UHNkWkl6dkNZQVlCNDRoUEtpN3JXRE1IbFdzM1kvVkVtQXMzSzVNK2QvL3QzRHB4WnBEbWJERGdYa2w2CjZUdDh3VXloUVZ3MkZpMStobTF1T2QwYjFkaW9aNko2OXNTT2JOZXpSR3YxYjdZaFltT0JKL1JBbHN5ZHoxTmgKVXpXT1lYV0Z1OGJrOU9JM3lQMEc0TE84QjhtbWRldE1RVVoyelFJREFRQUJBb0lCQUN2ckhUUHVVZ0JiSlE0QwpvQ0ZQdEgrWDZIN3NIdk1ndVR0VzdUTlYxN1BYMkVQdE53ZzI3S0tld0pNYmNSbWF3THBjSk5BU09xMDY4MGZxCjlsaHE1NEsybnB4WFVBeXErV3NSc1hid2hUODhibm5aQTBaRzZJR2hTaEpFN0t1cGxBU2htQ29FV2ppbmJTNFgKTGlvTW5HWSs4VFMzSzNrMTRWUDBaWUtuNXprMERHZnFBMEo0VTRXSmxUeGwrTWZxd0pJOTlrcTdHbFVlZkdncQpuK3Q1d2NrV3BPbTd5TUJjZTlTSXlmTm54bnU3TkZYQm50VTN5RGxSUThWUWZmNEtRMzJCaWNiYlJWemR1TThNCnNxMU5CZWNzL0EzUXRvdG1nWUc4d094ZXpNS3Iyays2QzB2NmlFc0h5T0lmR25GWktSZDJFd0dnWlo3aytURHUKUUYrcjd1VUNnWUVBMkRqNUJoYmpybDFRNTZya3BhTGFvVldRV1Y5YUYzUUJtNlNZM2VQYmlvY2JNR2k1ak1ESQpkSjdJVXlLYUljK3BNV1RQYlBmVUd2WmNENlczZDFBNUNUSnFuWHVuVlY3czRqaWJ6WDZUbjhNM3IrMHZTZnNZCmdPMHBtRFpndlNqaVZTRUNBQTZFOFUxQ1lFZU5KUDFDOW12cGJVNzJRTEpndWp3M3JMb2oyYmNDZ1lFQXdUSXYKOUNSeWNOQXRBbDcvUHdWZGh5eXRvVHBSRnZDSU1HSVk5SjMxZ3lva0ZlaFQvWjQ4WkF6anl6ZTBSUXYzdGUxTQoveVJMQkVETGkwbEtrZFVXckVkaVR3dm1KdkpwMDZ0OEdCbERsK25ycXVLWTFxVThDbTR5cis4QzZtRThkVnZrClNINXBhRXptOERFTE1wSjhGVTZFYnhmZHZjRzZmSGx6dnVnZmc1c0NnWUFFQ1BRa3QvS2h3MTRLSkxkRm5BZG0KY1ZsVFFhTkZ3c1Z3NlI1dExaNWdOR3MrZVFYVmFaZVVEWTZCZHFqWHJxOWltNVgvVzVTYXVEUTVtb2NVOCt0TQpqNk5Mc3c0SldzOGkzWm1TdVNUNkcwT0R4ZkpXK0JlWitGTUpZeUpsQlVsTCsyUzFLWkF6akpTTGhXcE40V2dKCmZ6UUk5U3RGUTg3b1NzMWpMTW9VZXdLQmdGOE9CMlFURHErTTdhaE4vejROc0wvU2JyZDJEdkcvZFBLQlFaQVIKcS90V0g1MGJ5ejlzdkgvcGk2YXdDS1UwUnpPZXh4UjkwZDhNMWxqNHZaVFZDQ3ZKajRnZTdhVlovbEdqL1JHSwpWS1NJOW1nRXgzaE1vaWJybzByR3lXTnlaaUhFRGFUUmRhRll2UU9PemRpYkZDd1RqcnR1UGE2Z2c5VzhtQU5sCkNDUmpBb0dBSTRIbnpyV3kzaU5kR2xqVnh4bW1DN1V0c0MvajJBUEZpcHc0ZHJ0U2NsMDFRZzF5WkowbDNBTk4KOU5lTmVSUUFzN3pFTng2T1B1SzlxYy83T1ROMTJKaHdoUTIzdXZwNjZjV0krdTRjcVpOZTJyZVFVVWVmM3psbQpMcXRmOU50VHp5M3pjMGZQcGoxQnBlRmxHSG9SVDhjVHpBWjFTeGwyZWChazlqS2RVeDQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0t'
|
||||
- ' "privateKey": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBbUhKOEJHdTFYZUZ4aENVQXBrNHNSTVI4RnRTdGtyMEx0OWtWTGNSUjRFWitiOWhHCmR0blJpOFhqV3d5MU5zMHliMkJMdHBpVHZKSFVKTUphWXluZ2ZkZnZhcWhocm1yYm5vV0pLQkxmeUxwTXFNS1EKQ3RialFxbnVrQURJUWVQd2ZGeTNpVHkxd1JkRC9zTUs1U0VtV0Fxb0pZQk50eTFZZzA2UzVkYVlPM2xjY3hrYQpQWjRjcm9McWF6Ny9tU3dDVTR5VWRSb3h4WVF4VG1MZXg5M2tqU09TTmdpK0FXc0lCbjV3UHI0VHNuVHFSeWpIClN2aEdMdk9YREpRYWZRdk56WjFSL1FYMzlOQk9xOEVKZW5pWXdaUm9uNVcvNVhMYW94MFFyUGhrY1BES3A5SVUKeHpJakUwWlNmMStUK1FFbTQ3TkFtSnhvZjFhdGRFVzZDTCtheHdJREFRQUJBb0lCQUQ3enI4REhsWnFSK1NWZgpmbGd1bWRzLzVCb3Rjd3ZRWXlGbFZIaVV4RmEvNVlCY0tDVDJKN0QzWTc1NmplNTJaK2hVTkkvUGk5cG53ZG40CkpBa2xCdDRRcUg0NzBES05UK216TFFOT1gvanM3YkVXdnhLcTBDZjhNbFptN0V0QlRGS2VtdS9pRVJBT2duYVcKcGs0ZUZVNXdBQ1dVU1FObWgxR1p4ZEdCZjFXM1VjUnQxcFRvOEtQTDluZm4vSGJiRFNsQkNVL3VIcWd2TSt2cApmTE03bzRIVDZ1K1ZzU00rWGZqeDhpeE5ZRHdoalNuKzQyZm13d1d3ZzJISHUrdUozZ1pUSWQwRUI1VW9hdUNjCjZUTlVtcEJscjU5UGFmVkZRWUY1S3VxaHJXKzVQaWpHcHBZcXg4Ynl6aFpOQzkwZnl5V0NXcXg2eGFZVm5OdzgKNkJmUXM2a0NnWUVBeVlyRVg1NU1RTzJnWDY2TGwxaGJDMzNzWk1OZzloVG1SK1doSTFjNksvbFZ1TFoyL0RPdwpsYTZ6eHdBU204Z0ZyVUFYbUljV2h2b3FwWGVzNWZzOVZKeDlNT0ZVYVBrckRPQllnY1laMUR6VVNVOHc3SSttCnlyV3hRUkRNajhvSGpRbHVpM0s2MzZucm5RajhxOGkvQ2dranVPcHJGZnliMzVEMFlDdjVXZzBDZ1lFQXdhT3cKRWFhN0l1MjFGa08vbmFjdVhjSnBhNkVlUTNqZFNlNlRQaXZ6bVVXU0haeGJuUy9XSnJaRjQwSExzUWxOZHl0ZgpNTTBKZFU0VmMyR0NVc1pMYjdQSmJwdVRqRERSSHJXV1pCMnhiemF0K3A3N2RzNWlOcXFRcTZ6M0syUVh4Y3ZTCis5am5VZXpDU2Y0N1R1OWNTTW96V3hTMW82b1BPSFdHVFRvdHR5TUNnWUFQdWc1Y3o4TnZoWnR3Ry9TMG1LWnkKSFI5bk5YL0pkQlFNSkRVUXh1dTVKcm16c2psU3NNM2t3RDh6RmlSZGw1d3B5c2lNbEc0RGxsM2hqNWNrVXhpVQpFNm9KT0d3WHpPbTVGWUNTajl6UUhQY0x5V3d0NlgvQWJiRXBQS0JaMEJBS3gyT2k2ZzcvQ1FsanRhSFIzZFphCmVDQWJlOTlqVmRUcit5bTJuM2ZUdVFLQmdBMm5TZ25rbEx0Z3dXMEJkK2hZMm1jWUJ6RGttbXF0Z2dUdGdvcFcKdFFWd3AxM1pJWWlTeituSTNtS295QUVDbytpc01Ua1NyQUVPY1dyQ1RGc2p5anZsRkdYdEtGa3hNLzJUVmpoVwo4NlRnMlNHYnhpVlpaZ2x1dTJhdmVub2Z3NkZadnRXdE5KcE5OR0hkUURkUG4xVXVsTEp1WW1SWTRGdmR4WXQ2CmQ3QzdBb0dBRUsvalFiZ0l3OXFLQUNOZ0JySnB1cU5Ham9JajFoQTRlb29DMXp1bFEyZUpnZ2J5OTBpSDg2VzEKM0xyOVZMVFkyc2JKTzlqekZVR0lOL01BOEhYQTE1a2grZHRibkRsdFRFZGNnenBCRzhCQUZRQ3hQWnBGWHhtZgpDUmhXN1l6RW1IeWJ4R0toR3NOK2M3NUhKTHZFSWwrRTh6eitXRk9xT240dkJXU1ZwSnc9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==",'
|
||||
36
data/rules/postgres.yml
Normal file
36
data/rules/postgres.yml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
rules:
|
||||
- name: Postgres URL with hardcoded password
|
||||
id: kingfisher.postgres.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
(?:
|
||||
postgres
|
||||
(?:ql)?
|
||||
| postgis
|
||||
)
|
||||
:\/\/
|
||||
(?:
|
||||
[\w]+
|
||||
)
|
||||
:
|
||||
(?:
|
||||
[^\@]+
|
||||
)
|
||||
@
|
||||
(?:
|
||||
[^:\/]+
|
||||
)
|
||||
:
|
||||
(?:
|
||||
\d+
|
||||
)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- CONNECTION_URI="postgres://postgres:s2Tf2k@rLMy@google.com:5434/elephant"
|
||||
- Connection URI= postgresql://nimda:vg498hwegw1udp6s@db-postgresql-nyc1-64297-do-user-1243723-0.db.ondigitalocean.com:25060/defaultdb?sslmode=require
|
||||
- CONNECTION_URI="postgres://postgres:s2Tf2k@rLMy@google.com:5434/elephant"
|
||||
- CONNECTION_URI="postgis://postgres:s2Tf2k@rLMy@google.com:5434/elephant"
|
||||
- CONNECTION_URI="postgis://postgres:s2Tf2k@rLMy@google.com:5434/elephant"
|
||||
validation:
|
||||
type: Postgres
|
||||
104
data/rules/privkey.yml
Normal file
104
data/rules/privkey.yml
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
rules:
|
||||
- name: Contains encrypted RSA private key
|
||||
id: kingfisher.privkey.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?msi)
|
||||
(
|
||||
-----BEGIN\s
|
||||
(?:RSA)\s
|
||||
PRIVATE\sKEY
|
||||
(\sBLOCK)?
|
||||
-----
|
||||
)
|
||||
[\r\n]
|
||||
Proc-Type:.*?ENCRYPTED
|
||||
[\r\n]
|
||||
DEK-INFO.*?
|
||||
[\r\n][\r\n]
|
||||
[a-z0-9/+=\r\n\\n]{32,}?
|
||||
-----END\s
|
||||
(?:RSA)\s
|
||||
PRIVATE\sKEY
|
||||
(\sBLOCK)?
|
||||
-----
|
||||
min_entropy: 4.5
|
||||
confidence: high
|
||||
prevalidated: false
|
||||
examples:
|
||||
- |-
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,1F77CE6d2Bb6B18537633Ec3aD093b9C
|
||||
|
||||
kyx3
|
||||
wbfxsauty36i7t
|
||||
i9z9jp/m7wiptdmxpx2zh
|
||||
wehs0oz1abuede+tq4ama6kjrywq0
|
||||
xw2mbgz1fmxw+8ub4rf2wkm/qlvnopukbitesk2+
|
||||
b55+04h565guxlu+8u4+mynsoqgahx0p5jg4h1vo0lpw/ru1qe60p4nz7635xeel868y72bsysa5/90qkhnt1bkxcqyqkmw3
|
||||
949mmahkh/cd9+f+x9wprv4mtq02ks1pzpb6zz6t
|
||||
+ril
|
||||
frnc129xvp11ndqbyjqlg3jf9ovlb1qula84ftj8m
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
- name: Contains Private Key
|
||||
id: kingfisher.privkey.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?ims)
|
||||
(
|
||||
-----BEGIN\s
|
||||
(?:
|
||||
RSA |
|
||||
PGP |
|
||||
DSA |
|
||||
OPENSSH |
|
||||
ENCRYPTED |
|
||||
EC
|
||||
)?
|
||||
\s{0,1}
|
||||
PRIVATE\sKEY
|
||||
(\sBLOCK)?
|
||||
-----
|
||||
[a-z0-9 /+=\r\n\\n]{32,}?
|
||||
-----END\s
|
||||
(?:
|
||||
RSA |
|
||||
PGP |
|
||||
DSA |
|
||||
OPENSSH |
|
||||
ENCRYPTED
|
||||
)?
|
||||
\s{0,1}
|
||||
PRIVATE\sKEY
|
||||
(\sBLOCK)?
|
||||
-----
|
||||
)
|
||||
min_entropy: 4.5
|
||||
confidence: high
|
||||
prevalidated: false
|
||||
examples:
|
||||
- |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
34fNwLAJYISQf4pT4wgMrzN1p3kdMYLTEYXw12k+SlKL=QLKQj=SP=hOpCG5Cdj/Hkk0ARyOgoZbJycYA
|
||||
8D9r9K2m6N1ZaUT96UrFqjlL9nAqmZ+13D82H1CYLKy0NOAY3XBLzLk46HZd8na2
|
||||
-----END PRIVATE KEY-----
|
||||
- |
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
34fNwLAJYISQf4pT4wgMrzN1p3kdMYLTEYXw12k+SlKL=QLKQj=SP=hOpCG5Cdj/Hkk0ARyOgoZbJycYA
|
||||
8D9r9K2m6N1ZaUT96UrFqjlL9nAqmZ+13D82H1CYLKy0NOAY3XBLzLk46HZd8na2
|
||||
-----END RSA PRIVATE KEY-----
|
||||
- |-
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
FPaBqPQWx0nxTJbSrPavSaNtmAyAH6etkjZs0/In
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
- |-
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7a7kN8LymUu8Z
|
||||
8D9r9K2m6N1ZaUT96UrFqjlL9nAqmZ+13D82H1CYLKy0NOAY3XBLzLk46HZd8na2
|
||||
-----END PRIVATE KEY-----
|
||||
- |-
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY BLOCK-----
|
||||
V75NeIrlsI80Gf0aTS2RZQvEcUQ3n6XwFnOvB/O5rRv3HGqvptc3P3n0bxfEg5KA
|
||||
-----END ENCRYPTED PRIVATE KEY BLOCK-----
|
||||
27
data/rules/psexec.yml
Normal file
27
data/rules/psexec.yml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
rules:
|
||||
- name: Credentials in PsExec
|
||||
id: kingfisher.psexec.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
psexec .{0,100}
|
||||
-u \s* (\S+) \s+ (?# username )
|
||||
-p \s* (\S+) (?# password )
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
categories: [fuzzy, secret]
|
||||
examples:
|
||||
- 'cmd.exe /C PSEXEC \\10.0.94.120 -u Administrator -p dev_admin CMD /C ECHO'
|
||||
- 'PSEXEC.EXE \\LocalComputerIPAddress -u DOMAIN\my-user -p mypass CMD'
|
||||
- 'psExec \\OAIJCTDU8024272 -u User -p $Password -i -d calc.exe'
|
||||
- |
|
||||
:: satmodel2
|
||||
%RUNTIMEDIR%\PsExec.exe \\satmodel2 -u SATMODEL2\MTCPB -p %nothing% -i 2 -c -f %TEMP%\psexec_helper.bat %RUNTIMEDIR% .\JavaOnly_runNode2.cmd
|
||||
%RUNTIMEDIR%\pslist.exe \\satmodel2 java
|
||||
if %ERRORLEVEL% NEQ 0 goto done
|
||||
- |
|
||||
ASSEMBLE THE BATCH FILE TO COPY THE FILE ACROSS THE DOMAIN
|
||||
start PsExec.exe /accepteula @C:\share$\comps1.txt -u DOMAIN\ADMINISTRATOR -p PASSWORD cmd /c COPY "\PRIMARY DOMAIN CONTROLLER\share$\fx166.exe" "C:\windows\temp\"
|
||||
SAVE IT AS "COPY.BAT"
|
||||
- 'system("psexec \\\\192.168.3.77 -u Administrator -p braksha shutdown -r -f -t 0");'
|
||||
references:
|
||||
- https://learn.microsoft.com/en-us/sysinternals/downloads/psexec
|
||||
64
data/rules/pypi.yml
Normal file
64
data/rules/pypi.yml
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
rules:
|
||||
- name: PyPI Upload Token
|
||||
id: kingfisher.pypi.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
pypi-AgEIcHlwaS5vcmc[a-zA-Z0-9_-]{50,}
|
||||
)
|
||||
(?:[^a-zA-Z0-9_-]|$)
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- '# password = pypi-AgEIcHlwaS5vcmcABCD1234efgh5678ijklmnopqrst9098UVWXYZabcd1234EFGHIJKL'
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: POST
|
||||
url: https://upload.pypi.org/legacy/
|
||||
response_is_html: true
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- "isn't allowed to upload to project"
|
||||
headers:
|
||||
Authorization: 'Basic {{ "__token__:" | append: TOKEN | b64enc }}'
|
||||
multipart:
|
||||
parts:
|
||||
- name: name
|
||||
type: text
|
||||
content: "my-package"
|
||||
- name: version
|
||||
type: text
|
||||
content: "0.0.1"
|
||||
- name: filetype
|
||||
type: text
|
||||
content: "sdist"
|
||||
- name: metadata_version
|
||||
type: text
|
||||
content: "2.1"
|
||||
- name: summary
|
||||
type: text
|
||||
content: "A simple example package"
|
||||
- name: home_page
|
||||
type: text
|
||||
content: "https://github.com/yourusername/my_package"
|
||||
- name: sha256_digest
|
||||
type: text
|
||||
content: "0447379dd46c4ca8b8992bda56d07b358d015efb9300e6e16f224f4536e71d64"
|
||||
- name: md5_digest
|
||||
type: text
|
||||
content: "9b4036ab91a71124ab9f1d32a518e2bb"
|
||||
- name: :action
|
||||
type: text
|
||||
content: "file_upload"
|
||||
- name: protocol_version
|
||||
type: text
|
||||
content: "1"
|
||||
- name: content
|
||||
type: file
|
||||
content: "path/to/my_package-0.0.1.tar.gz"
|
||||
content_type: "application/octet-stream"
|
||||
40
data/rules/rabbitmq.yml
Normal file
40
data/rules/rabbitmq.yml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
rules:
|
||||
- name: RabbitMQ Credential
|
||||
id: kingfisher.rabbitmq.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
(?:
|
||||
amqps?
|
||||
)
|
||||
:\/\/
|
||||
[\S]{3,50}
|
||||
:
|
||||
(
|
||||
[\S]{3,50}
|
||||
)
|
||||
@
|
||||
[-.%\w\/:]+
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- amqp://user:password@rabbitmq.example.com/queue
|
||||
- amqps://admin:3eCa3P@192.168.1.10:5671/vhost
|
||||
# validation:
|
||||
# type: Http
|
||||
# content:
|
||||
# request:
|
||||
# url: '{{ URL }}'
|
||||
# headers:
|
||||
# Custom-header: '{{ TOKEN }}'
|
||||
# method: GET
|
||||
# response_matcher:
|
||||
# - report_response: true
|
||||
# - status:
|
||||
# - 200
|
||||
# type: StatusMatch
|
||||
# - report_response: true
|
||||
# - type: WordMatch
|
||||
# words:
|
||||
# - '"connected":true'
|
||||
53
data/rules/react.yml
Normal file
53
data/rules/react.yml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
rules:
|
||||
- name: React App Username
|
||||
id: kingfisher.reactapp.1
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
\b
|
||||
REACT_APP (?: _[A-Z0-9]+)* _USER (?: NAME)? (?# variable name )
|
||||
\s* = \s*
|
||||
['"]?
|
||||
( [^\s'"$]{3,} ) (?# value )
|
||||
(?: [\s'"$] | $ )
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- '# REACT_APP_GUEST_USERNAME=guest'
|
||||
- '# REACT_APP_USER=postgres'
|
||||
- 'REACT_APP_AUTH_USER=postgres'
|
||||
- 'REACT_APP_AUTH_USERNAME=bowie'
|
||||
- ' REACT_APP_AUTH_USERNAME=bowie # some comment'
|
||||
- 'REACT_APP_MAILER_USERNAME=smtp_username # Enter your SMTP email username'
|
||||
negative_examples:
|
||||
- 'REACT_APP_FRONTEND_LOGIN_FORGOT_USERNAME=$REACT_APP_MATRIX_BASE_URL/classroom/#/forgot_username'
|
||||
categories: [fuzzy, identifier]
|
||||
references:
|
||||
- https://create-react-app.dev/docs/adding-custom-environment-variables/
|
||||
- https://stackoverflow.com/questions/48699820/how-do-i-hide-an-api-key-in-create-react-app
|
||||
|
||||
- name: React App Password
|
||||
id: kingfisher.reactapp.2
|
||||
pattern: |
|
||||
(?x)(?i)
|
||||
\b
|
||||
REACT_APP (?: _[A-Z0-9]+)* _PASS (?: WORD)? (?# variable name )
|
||||
\s* = \s*
|
||||
['"]?
|
||||
( [^\s'"$]{6,} ) (?# value )
|
||||
(?: [\s'"$] | $ )
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- '# REACT_APP_GUEST_PASSWORD=mycoin!1'
|
||||
- '# REACT_APP_PASS=whiteduke'
|
||||
- 'REACT_APP_AUTH_PASS=whiteduke'
|
||||
- 'REACT_APP_AUTH_PASSWORD=whiteduke'
|
||||
- ' REACT_APP_AUTH_PASSWORD=whiteduke # some comment'
|
||||
- 'REACT_APP_MAILER_PASSWORD=smtp_password # Enter your SMTP email password'
|
||||
negative_examples:
|
||||
- ' const password = process.env.REACT_APP_FIREBASE_DEV_PASSWORD || "not-set"'
|
||||
- 'REACT_APP_FRONTEND_LOGIN_FORGOT_PASSWORD=$REACT_APP_MATRIX_BASE_URL/classroom/#/forgot_password'
|
||||
categories: [fuzzy, secret]
|
||||
references:
|
||||
- https://create-react-app.dev/docs/adding-custom-environment-variables/
|
||||
- https://stackoverflow.com/questions/48699820/how-do-i-hide-an-api-key-in-create-react-app
|
||||
35
data/rules/recaptcha.yml
Normal file
35
data/rules/recaptcha.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
rules:
|
||||
- name: reCAPTCHA API Key
|
||||
id: kingfisher.recaptcha.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
recaptcha
|
||||
(?:.|[\n\r]){0,16}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
6l[c-f][a-z0-9_-].{36}
|
||||
)
|
||||
min_entropy: 3
|
||||
confidence: medium
|
||||
examples:
|
||||
- recaptcha apikey = 6Lcr--w-BBBBBw-w-w----w-w-www-www--ww-w-
|
||||
- recaptcha_secret = 6Lcw--w-AAAAAw-w-w----w-w-www-www--ww-w-
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
body: |
|
||||
secret={{ TOKEN }}
|
||||
response=test
|
||||
method: POST
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"success": true'
|
||||
url: https://www.google.com/recaptcha/api/siteverify
|
||||
117
data/rules/slack.yml
Normal file
117
data/rules/slack.yml
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
rules:
|
||||
- name: Slack App Token
|
||||
id: kingfisher.slack.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
(?:
|
||||
.{0,24}[=:]
|
||||
\s{0,8}
|
||||
)?
|
||||
(
|
||||
xapp-
|
||||
[0-9]{1,3}-
|
||||
[0-9a-z]{10,15}-
|
||||
[0-9a-z]{10,15}-
|
||||
[0-9a-z]{10,66}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- xapp-1-A05V64V7F2B-5062360157732-9f01726eebe77df2c096a65e95acdd02107b2c1e92ca341cff27ca271b7251b4
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Content-Type: application/json; charset=utf-8
|
||||
method: POST
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"ok":true'
|
||||
url: https://slack.com/api/auth.test
|
||||
|
||||
- name: Slack Token
|
||||
id: kingfisher.slack.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
xox[pbarose]
|
||||
[-0-9]{0,3}-
|
||||
[0-9a-z]{6,15}-
|
||||
[0-9a-z]{6,15}-
|
||||
[-0-9a-z]{6,66}
|
||||
)\b
|
||||
|
|
||||
(
|
||||
xoxe\.xox[bparose]-
|
||||
\d-
|
||||
[A-Z0-9]{155,170}
|
||||
)\b
|
||||
|
|
||||
(
|
||||
xoxe-\d-
|
||||
[A-Z0-9]{140,150}
|
||||
)\b
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- xoxb-853BAAEE-1B2eDb6A4c75-01bB6Da1CE3E98f6fED5AeC07Dc3E94C
|
||||
- xoxe.xoxp-1-Mi0yLTIxNTE3NzA2MDYtNTEyOTQ2NTEzMDYxMy03MTkwNzY4NDc5ODc3LTcxOTM1NTk2ODMxODctZGE3NWQ0NmZiNTk3MTU1ZDA4ZTQwZTYxOWY2Njc3YTgzNzNhNDkxMzg4ZWZiYzljMjRkOWZhODgxYzA0NGI2ZQ
|
||||
- xoxe-1-My0xLTIxNTE3NzA2MDYtNzE5MDc2ODQ3OTg3Ny03MjE2NDY0MzYxNDcyLTliOWNhYzZhODU3ODc3NjAxZGM4ODg2ZWRkMmE2MTc3ZDA3ODY4ZmUzZDg2NzEyZTU0Zjk2ZTYzMWMxZTVmOTA
|
||||
references:
|
||||
- https://api.slack.com/methods/auth.test
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: Bearer {{ TOKEN }}
|
||||
Content-Type: application/json; charset=utf-8
|
||||
method: POST
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"ok":true'
|
||||
url: https://slack.com/api/auth.test
|
||||
|
||||
- name: Slack Webhook
|
||||
id: kingfisher.slack.4
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
https://hooks\.slack\.com/services/
|
||||
T[a-z0-9_-]{8,12}/ # Team ID
|
||||
B[a-z0-9_-]{8,12}/ # Bot ID
|
||||
[a-z0-9_-]{20,30} # Webhook token
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
examples:
|
||||
- https://hooks.slack.com/services/TY40v9sZ9/BxIqhIXIi/NGUyXK6nK7HMAqd0ASzXluoV
|
||||
- https://hooks.slack.com/services/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
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
method: POST
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- ok
|
||||
- invalid_payload
|
||||
- type: WordMatch
|
||||
words:
|
||||
- "invalid_token"
|
||||
negative: true
|
||||
url: '{{ TOKEN }}'
|
||||
31
data/rules/supabasetoken.yml
Normal file
31
data/rules/supabasetoken.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
rules:
|
||||
- name: Supabase API Key
|
||||
id: kingfisher.supabase.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
sbp_
|
||||
(
|
||||
[a-z0-9_-]{40}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- sbp_abcd1234efgh5678ijkl9012mnop3456qrst7890
|
||||
- sbp_1234567890abcdefghij1234567890klmnopqrst
|
||||
references:
|
||||
- https://supabase.com/docs/reference/api/v1-get-an-organization
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
method: GET
|
||||
url: https://api.supabase.com/v1/organizations
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
31
data/rules/tailscale.yml
Normal file
31
data/rules/tailscale.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
rules:
|
||||
- name: Tailscale API Key
|
||||
id: kingfisher.tailscale.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
tskey-[a-z]+-[A-Za-z0-9_-]{20,24}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.0
|
||||
confidence: medium
|
||||
examples:
|
||||
- tskey-secret-12345678-abcd
|
||||
- tskey-api-abcdefg-123456789
|
||||
references:
|
||||
- https://tailscale.com/kb/1215/oauth-clients
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.tailscale.com/api/v2/tailnet/-/devices
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
Accept: application/json
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
34
data/rules/travisci.yml
Normal file
34
data/rules/travisci.yml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
rules:
|
||||
- name: Travis CI Token
|
||||
id: kingfisher.travisci.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
travis
|
||||
(?:.|[\\n\r]){0,16}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9-_]{22}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.0
|
||||
confidence: medium
|
||||
examples:
|
||||
- "travis_token splendid21RANDOMCONTENT_token"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.travis-ci.com/repos?limit=1
|
||||
headers:
|
||||
Authorization: token {{ TOKEN }}
|
||||
Accept: application/vnd.travis-ci.3+json
|
||||
Travis-API-Version: "3"
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
58
data/rules/twilio.yml
Normal file
58
data/rules/twilio.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
rules:
|
||||
- name: Twilio API ID
|
||||
id: kingfisher.twilio.1
|
||||
pattern: |
|
||||
(?x)
|
||||
\b
|
||||
(
|
||||
(?:SK|AC)[a-fA-F0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
examples:
|
||||
- |
|
||||
const twilioAccountSid = 'AC712594f590c0d8ace55c04858f7398f9' // Your Account SID from www.twilio.com/console
|
||||
const twilioApiKeySID = 'SK9b4cc552783500ace5414a1ed3e9fd1a'
|
||||
const twilioApiKeySecret = 'l6LUelKF2BUtMLace5oShZSmRppadYqI'
|
||||
- |
|
||||
// https://www.twilio.com/console/video/dev-tools/api-keys
|
||||
'API' => env('TWILIO_API','SK6e84981d07ace5c9df33e1ab043a2fb2'),
|
||||
'API_KEY' => env('TWILIO_API_KEY', 'wbTs1SUt6Aace5eKeNCxuYvJa6PhaRd0')
|
||||
- name: Twilio API Key
|
||||
id: kingfisher.twilio.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
twilio
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.0
|
||||
examples:
|
||||
- Twilio_key=Cd2Bd1dE1201aE2DFFEcfeBafCc3c31D
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
Accept: '*/*'
|
||||
Authorization: "Basic {{ TWILIOID | append: ':' | append: TOKEN | b64enc }}"
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- match_all_words: true
|
||||
type: WordMatch
|
||||
words:
|
||||
- '"first_page_uri":'
|
||||
- '"accounts":'
|
||||
url: https://api.twilio.com/2010-04-01/Accounts.json
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.twilio.1"
|
||||
variable: TWILIOID
|
||||
31
data/rules/uri.yml
Normal file
31
data/rules/uri.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
rules:
|
||||
- name: URI with Username and Secret
|
||||
id: kingfisher.uri.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
(https?)://
|
||||
[a-z][a-z0-9+\-.]*
|
||||
:
|
||||
[a-z0-9\-._~%!$&'()*,;=]+
|
||||
@
|
||||
(?:[a-z0-9\-._~%]+|\[[a-f0-9:.]+\]|\[v[a-f0-9][a-z0-9\-._~%!$&'()*,;=:]+\])
|
||||
(:?[0-9]+)?
|
||||
(?:/[a-z0-9\-._~%!$&'()*,;=:@]*)* # Match path
|
||||
/?
|
||||
(?:\?[a-z0-9\-._~%!$&'()*,;=:@/?]*)?
|
||||
(?:\#[a-z0-9\-._~%!$&'()*,;=:@/?]*)?
|
||||
min_entropy: 4.0
|
||||
confidence: medium
|
||||
examples:
|
||||
- https://username:secret@example.com/path
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: '{{ TOKEN }}'
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
type: StatusMatch
|
||||
status:
|
||||
- 200
|
||||
65
docs/COMPARISON.md
Normal file
65
docs/COMPARISON.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
### Runtime Comparison (seconds)
|
||||
*Lower runtimes are better.*
|
||||
| Repository | Kingfisher Runtime | TruffleHog Runtime | GitLeaks Runtime | detect-secrets Runtime |
|
||||
|------------|--------------------|--------------------|------------------|------------------------|
|
||||
| croc | 2.64 | 10.36 | 3.10 | 0.16 |
|
||||
| rails | 8.75 | 24.19 | 24.24 | 0.48 |
|
||||
| ruby | 22.93 | 132.68 | 61.37 | 0.79 |
|
||||
| gitlab | 135.41 | 325.93 | 350.84 | 5.04 |
|
||||
| django | 6.91 | 227.63 | 59.50 | 0.61 |
|
||||
| lucene | 15.62 | 89.11 | 76.24 | 0.66 |
|
||||
| mongodb | 25.37 | 174.93 | 175.80 | 2.74 |
|
||||
| linux | 205.19 | 597.51 | 548.96 | 5.49 |
|
||||
| typescript | 64.99 | 183.04 | 232.34 | 4.23 |
|
||||
|
||||
### Validated/Verified Findings Comparison
|
||||
|
||||
Note: For GitLeaks and detect-secrets, validated/verified counts are not available.
|
||||
|
||||
| Repository | Kingfisher Validated | TruffleHog Verified | GitLeaks Verified | detect-secrets Verified |
|
||||
|------------|----------------------|---------------------|-------------------|-------------------------|
|
||||
| croc | 0 | 0 | 0 | 0 |
|
||||
| rails | 0 | 0 | 0 | 0 |
|
||||
| ruby | 0 | 0 | 0 | 0 |
|
||||
| gitlab | 6 | 6 | 0 | 0 |
|
||||
| django | 0 | 0 | 0 | 0 |
|
||||
| lucene | 0 | 0 | 0 | 0 |
|
||||
| mongodb | 0 | 0 | 0 | 0 |
|
||||
| linux | 0 | 0 | 0 | 0 |
|
||||
| typescript | 0 | 0 | 0 | 0 |
|
||||
|
||||
### Network Requests Comparison
|
||||
*'Network Requests' shows the total number of HTTP calls made during a scan. Since Gitleaks and detect‑secrets don’t validate secrets, they never make any network requests.*
|
||||
|
||||
| Repository | Kingfisher Network Requests | TruffleHog Network Requests | GitLeaks Network Requests | detect-secrets Network Requests |
|
||||
|------------|-----------------------------|-----------------------------|---------------------------|----------------------------------|
|
||||
| croc | 0 | 17 | 0 | 0 |
|
||||
| rails | 1 | 25 | 0 | 0 |
|
||||
| ruby | 3 | 33 | 0 | 0 |
|
||||
| gitlab | 17 | 15624 | 0 | 0 |
|
||||
| django | 0 | 66 | 0 | 0 |
|
||||
| lucene | 0 | 116 | 0 | 0 |
|
||||
| mongodb | 1 | 191 | 0 | 0 |
|
||||
| linux | 0 | 287 | 0 | 0 |
|
||||
| typescript | 0 | 10 | 0 | 0 |
|
||||
### QuickChart.io Visualizations
|
||||
|
||||
#### Runtime Chart
|
||||
*Lower runtimes are better*
|
||||
|
||||

|
||||
|
||||
|
||||
#### Validated/Verified Findings Chart
|
||||
*Validated/Verified counts are reported where available*
|
||||
|
||||

|
||||
|
||||
|
||||
*Lower runtimes are better. Validated/Verified counts are reported where available. 'Network Requests' indicates the number of HTTP requests made during scanning.*
|
||||
|
||||
OS: darwin
|
||||
Architecture: arm64
|
||||
CPU Cores: 16
|
||||
RAM: 48.00 GB
|
||||
|
||||
74
docs/FINGERPRINT.md
Normal file
74
docs/FINGERPRINT.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
## Finding Fingerprints
|
||||
|
||||
Every reported finding carries a **64-bit fingerprint** that acts as a stable, privacy-safe ID.
|
||||
It lets the scanner **deduplicate** repeated hits of the *same logical issue* while still treating different locations as distinct.
|
||||
|
||||
```bash
|
||||
🔓 AWS SECRET ACCESS KEY => [KINGFISHER.AWS.2]
|
||||
|Finding.......: 4HKmwiS1GzI[...]2TF6zYz7
|
||||
|Fingerprint...: 14085685380484734428
|
||||
|Confidence....: medium
|
||||
|Entropy.......: 5.12
|
||||
[...]
|
||||
|
||||
```
|
||||
---
|
||||
|
||||
### How the *reported* fingerprint is calculated
|
||||
|
||||
1. **Finding Bytes** – the matched finding pattern
|
||||
|
||||
2. **Origin label** – one of
|
||||
*`"git"`*, *`"file"`*, *`"ext"`*, identifying whether the hit came from a Git
|
||||
history, a plain on-disk file, or an extended source.
|
||||
|
||||
3. **Byte offsets** – `offset_start` and `offset_end`, the exact byte range of
|
||||
the match inside the blob/file (little-endian `u64` each).
|
||||
|
||||
Those four fields are concatenated:
|
||||
|
||||
```bash
|
||||
< finding_bytes> + <origin_label> + <offset_start> + <offset_end>
|
||||
```
|
||||
|
||||
The resulting buffer is hashed with **XXH3-64**, producing a single unsigned-64 value:
|
||||
|
||||
```bash
|
||||
finding-bytes + origin + start-offset + end-offset -> XXH3-64 -> finding_fingerprint
|
||||
```
|
||||
|
||||
|
||||
This fingerprint is what you see reported in the finding output.
|
||||
|
||||
---
|
||||
|
||||
### Why the rule’s SHA-1 is used (and not the secret)
|
||||
|
||||
The fingerprint is a [XXH3-64](https://github.com/Cyan4973/xxHash) hash of the following components concatenated together:
|
||||
|
||||
* The content of the matched secret.
|
||||
* A coarse-grained origin label (`git`, `file`, or `ext`).
|
||||
* The start and end byte-offsets of the match.
|
||||
|
||||
This content-aware approach provides several benefits:
|
||||
|
||||
| Reason | Benefit |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Accurate Secret Tracking** | If a key is rotated (e.g., from `AKIA…AAA` to `AKIA…BBB`), the new key correctly receives a new fingerprint. This allows for precise tracking of a secret's lifecycle. |
|
||||
| **Location Uniqueness** | Because byte offsets are part of the hash, two identical secrets found on different lines will have separate fingerprints. |
|
||||
| **Privacy-Safe by Design** | The fingerprint is a one-way hash, not the raw secret itself. This prevents sensitive credential data from being exposed in reports and logs. |
|
||||
| **Light-weight Origin** | Using a coarse origin label (`git`, `file`, etc.) avoids fingerprint churn across commits while still separating findings from different types of scans. |
|
||||
|
||||
This method ensures that every unique secret is tracked precisely, providing a clear and accurate picture of sensitive data exposure.
|
||||
|
||||
---
|
||||
### Controlling deduplication
|
||||
|
||||
By default the CLI **deduplicates** findings that share the same fingerprint, so you see only one entry even if the secret appears in multiple commits.
|
||||
|
||||
|
||||
If you want to see **every individual occurrence**, run with `--no-dedup`:
|
||||
|
||||
```bash
|
||||
kingfisher scan /path/to/repo --no-dedup
|
||||
```
|
||||
35
docs/PARSING.md
Normal file
35
docs/PARSING.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Kingfisher Source Code Parsing
|
||||
Kingfisher leverages tree‐sitter as an extra layer of analysis when scanning source files written in supported programming languages. In practice, after its initial regex‐based scan (powered by Vectorscan), Kingfisher checks if the file’s language is known.
|
||||
|
||||
If so, it creates a Checker (see below) that uses tree‐sitter to parse the file and run language‐specific queries. This additional pass refines the detection by capturing more structured patterns—such as secret-like tokens—that might be obscured or spread over code constructs.
|
||||
|
||||
### How It’s Called
|
||||
|
||||
In the scanning phase (in the Matcher’s implementation), Kingfisher does the following:
|
||||
- **Language Detection:** When processing a blob, if a language string is provided (e.g. inferred from file metadata or extension), the code calls a helper (via a function like `get_language_and_queries`) to retrieve the corresponding tree‐sitter language and a set of queries.
|
||||
- **Checker Creation:** With these values, a `Checker` struct is instantiated. This struct holds both the target language (as defined in its `Language` enum) and a map of tree‐sitter queries to run.
|
||||
- **Parsing and Querying:** The Checker’s key method (e.g. `check` or indirectly via `modify_regex`) retrieves a thread‐local tree‐sitter parser (to avoid recreating the parser on every call), sets the appropriate language, and parses the source code into a syntax tree. It then executes the queries over that tree, extracting ranges and texts of interest that might represent secrets.
|
||||
*(See the implementation details in the parser module – for example, the `modify_regex` function in the Checker, and the conditional tree‐sitter call in Matcher::scan_blob)*
|
||||
|
||||
### Supported Languages
|
||||
|
||||
The design supports many common source code languages. The Language enum (defined in the parser module) includes variants for:
|
||||
- **Scripting:** Bash, Python, Ruby, PHP
|
||||
- **Compiled languages:** C, C++, C#, Rust, Java
|
||||
- **Web-related languages:** CSS, HTML, JavaScript, TypeScript, YAML, Toml
|
||||
- **Others:** Go, and even a generic “Regex” mode
|
||||
|
||||
Each variant maps to its corresponding tree‐sitter language through the `get_ts_language()` method.
|
||||
|
||||
### When Tree‐sitter Is Not Called
|
||||
|
||||
Tree‐sitter won’t be invoked in certain cases:
|
||||
- **No Language Identified:** If the file isn’t recognized as belonging to one of the supported languages or no language hint is provided, the Checker isn’t even constructed.
|
||||
- **Non-source Files:** Binary files or files that aren’t expected to contain code (or aren’t extracted from archives) bypass tree‐sitter parsing.
|
||||
- **Fallback on Errors:** If tree‐sitter parsing fails (e.g. due to malformed code or other errors), Kingfisher will fall back on its regex/Vectorscan matches without the additional tree‐sitter insights.
|
||||
|
||||
### Summary
|
||||
|
||||
In essence, Kingfisher’s use of tree‐sitter is conditional and complementary. It is called only when the scanned file is a source code file written in a supported language, and its role is to enrich the scanning results by leveraging the syntax tree and language-specific queries. When files are non-source, binary, or if no language is provided, tree‐sitter is not invoked, and Kingfisher relies solely on its regex-based detection.
|
||||
|
||||
This layered approach helps improve the accuracy of secret detection while maintaining high performance.
|
||||
309
docs/RULES.md
Normal file
309
docs/RULES.md
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
# Writing Custom Rules for Kingfisher
|
||||
|
||||
A _rule_ in Kingfisher is a YAML document that describes how to detect and (optionally) validate secrets in your codebase. With custom rules you can:
|
||||
|
||||
- **Extend** Kingfisher without touching Rust code
|
||||
- **Tune** sensitivity via entropy and confidence
|
||||
- **Plug in** live checks against external services
|
||||
|
||||
This document explains how to write custom rules for Kingfisher using a YAML-based rule system. The rules define regular expressions to detect secrets in source code and other textual data, and they can include validation steps to confirm the secret's authenticity. By using a rules-based system, Kingfisher is highly extensible—new rules can be added or existing ones modified without changing the core code.
|
||||
|
||||
## 1. Rule Schema
|
||||
|
||||
Each rule file defines one or more entries under a top‑level `rules:` list. Every entry supports the following fields:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- name: # (string) Human-friendly rule name
|
||||
id: # (string) Unique identifier (e.g. kingfisher.aws.1)
|
||||
|
||||
pattern: | # (multi-line regex) Detection pattern
|
||||
(?x)(?i)
|
||||
aws
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b([A-Za-z0-9/+=]{40})\b
|
||||
|
||||
min_entropy: 3.5 # (float) Minimum Shannon entropy
|
||||
confidence: medium # (enum: low | medium | high)
|
||||
|
||||
examples: # (list) strings that must match
|
||||
- AWS_SECRET="AKIA…"
|
||||
|
||||
references: # (optional list) context URLs
|
||||
- https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html
|
||||
|
||||
visible: true # (bool) hide helper matches when false
|
||||
|
||||
depends_on_rule: # (optional) capture chaining
|
||||
- rule_id: kingfisher.aws.id
|
||||
variable: AKID # referenced as {{ AKID }}
|
||||
|
||||
validation: # (optional) live validation
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://api.example.com/v1/check
|
||||
headers:
|
||||
X-Secret: "{{ TOKEN }}"
|
||||
X-Id: "{{ AKID }}"
|
||||
response_is_html: true # by default, validation responses containing HTML or considered invalid. Set to `true` if you expect HTML returned from a validation response
|
||||
response_matcher:
|
||||
- report_response: true # always include raw payload
|
||||
- type: StatusMatch
|
||||
status: [200] # positive check
|
||||
- type: StatusMatch
|
||||
status: [401,403]
|
||||
negative: true # negative check → must NOT match
|
||||
- type: HeaderMatch
|
||||
header: content-type
|
||||
expected: ["application/json"]
|
||||
- type: JsonValid
|
||||
```
|
||||
|
||||
| Field | What it does |
|
||||
| ----------------- | -------------------------------------------------------------------- |
|
||||
| name | Friendly name shown in reports |
|
||||
| id | Unique text ID (namespace.v#) used internally |
|
||||
| pattern | Regex used to spot secrets (free‑spacing & flags allowed) |
|
||||
| min_entropy | Threshold to guard against low‑complexity false positives |
|
||||
| confidence | Suggests severity: low → high |
|
||||
| examples | Good matches; used for testing |
|
||||
| visible | false to hide non‑secret captures (e.g. IDs) |
|
||||
| depends_on_rule | Chain rules: use captures from one rule in another’s validation |
|
||||
| validation | Configure HTTP, AWS, GCP, etc. checks to verify live validity |
|
||||
|
||||
|
||||
*responser_matcher* variants. Multiple can be used
|
||||
| Variant | Required keys | Behavior |
|
||||
|-----------------|-------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
|
||||
| **StatusMatch** | `status` (list\<int>)<br>`negative` (bool, default `false`) | Pass when codes match (or don’t match if `negative`). |
|
||||
| **WordMatch** | `words` (list\<string>)<br>`match_all_words` (bool)<br>`negative` (bool) | Word/substring checks in body. |
|
||||
| **HeaderMatch** | `header` (string)<br>`expected` (list\<string>)<br>`match_all_values` (bool) | Header value assertions. |
|
||||
| **JsonValid** | – | Pass only if body parses as JSON. Use when response is expected as JSON data |
|
||||
| **XmlValid** | – | Pass only if body parses as well-formed XML. Use when response is expected as XML data |
|
||||
| **ReportResponse** | `report_response` (bool) | Include raw payload in finding for debugging. |
|
||||
|
||||
|
||||
### How depends_on_rule Works
|
||||
|
||||
- **Dependency Declaration:**
|
||||
In your YAML rule definition, you add a `depends_on_rule` section. Here you specify:
|
||||
- **rule_id:** The identifier of the rule whose output is required.
|
||||
- **variable:** The name (typically in uppercase) that will be used to reference the captured value from the dependency rule.
|
||||
|
||||
- **Chaining Captures:**
|
||||
When Kingfisher scans a file, it processes rules in a specific order. If a rule has a dependency, the engine first checks whether the dependent rule has already matched on the same input (or blob). If it did, the captured value (for example, an access key ID) is made available to the dependent rule.
|
||||
|
||||
- **Using the Captured Value:**
|
||||
This captured value can then be used during the validation phase. For instance, if you have a rule for an Algolia Admin API Key that depends on an Algolia Application ID (captured as `APPID`), the validation logic can incorporate the `APPID` value to confirm that the secret matches the expected pattern or format for that specific account.
|
||||
|
||||
### Use depends_on_rule to require one rule before another runs:
|
||||
|
||||
```yaml
|
||||
depends_on_rule:
|
||||
- rule_id: kingfisher.algolia.app_id # must match first
|
||||
variable: APPID # captured as {{ APPID }}
|
||||
```
|
||||
|
||||
- **Capture flow**: First rule captures `APPID` → second rule injects `{{ APPID }}` into validation HTTP request or pattern
|
||||
- **Visible control:** set `visible: false` on the supporting rule so it doesn’t clutter your report for non-secret matches
|
||||
## Algolia Example
|
||||
|
||||
Consider this example rule for an Algolia Application ID and Admin Key combination. To validate that this is an active credential, both must be matched:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- name: Algolia Admin API Key
|
||||
id: kingfisher.algolia.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
algolia
|
||||
(?:.|[\n\r]){0,32}?
|
||||
\b
|
||||
(
|
||||
[a-z0-9]{32}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
confidence: medium
|
||||
examples:
|
||||
- algolia_api_key = "ij1mut5oe606wlrf5z4u8u31264z3gag"
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
headers:
|
||||
X-Algolia-API-Key: '{{ TOKEN }}'
|
||||
X-Algolia-Application-Id: '{{ APPID }}'
|
||||
method: GET
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
url: https://{{ APPID }}-dsn.algolia.net/1/keys
|
||||
depends_on_rule:
|
||||
- rule_id: "kingfisher.algolia.2"
|
||||
variable: APPID
|
||||
|
||||
- name: Algolia Application ID
|
||||
id: kingfisher.algolia.2
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
algolia
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
[A-Z0-9]{10}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.5
|
||||
visible: false
|
||||
confidence: medium
|
||||
examples:
|
||||
- algolia_app_id = "WRB8YLFW7Y"
|
||||
|
||||
```
|
||||
|
||||
### How It Works:
|
||||
|
||||
* Algolia Application ID Rule (kingfisher.algolia.2):
|
||||
|
||||
This rule scans for an Algolia Application ID—a 10-character alphanumeric string. It is marked with visible: false so that even if it matches, the finding is not directly reported. Its primary role is to provide a supporting value for other rules rather than to be flagged as a secret by itself.
|
||||
|
||||
* Algolia Admin API Key Rule (kingfisher.algolia.1):
|
||||
This rule detects the Algolia Admin API Key using a regex pattern. It includes a depends_on_rule property that specifies a dependency on the Algolia Application ID rule.
|
||||
|
||||
* The dependency declares that the rule requires the output of the Algolia Application ID rule, and the captured value is assigned to the variable APPID.
|
||||
* In the validation section, this captured `APPID` is used dynamically in the HTTP request (for example, in the header `X-Algolia-Application-Id` and in the URL).
|
||||
|
||||
The dependency mechanism (depends_on_rule) ensures that:
|
||||
|
||||
* Non-secret data (like an application ID) is captured without cluttering the scan report (thanks to visible: false).
|
||||
* The secret (the API key) is validated in context, with the necessary supporting information automatically injected.
|
||||
* Rules remain modular and extensible; you can update the dependent rule or its pattern independently, and the change will automatically be reflected where the value is used.
|
||||
|
||||
## The `visible: false` Property
|
||||
|
||||
The `visible: false` property tells Kingfisher to hide the finding from the final scan report. This is particularly useful for rules that capture data not meant to be reported as a secret, but rather to serve as supporting context for another rule.
|
||||
|
||||
For example, a rule might match a username, an email address, an AWS Access Key ID, or an Application ID. While these pieces of information are captured during scanning, they are not secrets on their own. Instead, they are used by other rules—via the `depends_on_rule` mechanism—to validate an associated secret. By marking such rules as `visible: false`, you prevent these non-secret findings from cluttering your report, yet their values remain available for dependent rules.
|
||||
|
||||
`visible: false` helps keep the scan output focused on actual secrets while still capturing important contextual data needed for comprehensive validation.
|
||||
|
||||
|
||||
## Writing Custom Rules
|
||||
|
||||
When writing custom rules, consider the following best practices:
|
||||
|
||||
1. **Multi-line Regex:** Write your regex patterns over multiple lines for clarity. Use the `(?x)` flag to enable free-spacing mode.
|
||||
2. **Optimize for Performance:** Structure your regex to minimize backtracking. Use non-capturing groups where possible and keep the pattern as concise as possible.
|
||||
3. **Validation Integration:** Define a `validation` section if you want to verify the detected secret. You can use Liquid templating to insert dynamic values—use the unnamed capture as `TOKEN` and any named captures in uppercase.
|
||||
4. **Test with Examples:** Always include examples that should match and, optionally, negative examples to ensure your rule behaves as expected.
|
||||
|
||||
## Examples
|
||||
|
||||
Below are some examples to guide you in writing custom rules
|
||||
|
||||
### Anthropic API Key
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- name: Anthropic API Key
|
||||
id: kingfisher.anthropic.1
|
||||
pattern: |
|
||||
(?x)
|
||||
(?i)
|
||||
\b
|
||||
(
|
||||
sk-ant-api
|
||||
\d{2,4}
|
||||
-
|
||||
[\w\-]{93}
|
||||
AA
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- sk-ant-api668-Clm512odot9WDD7itfUU9R880nefA1EtYZDbpE-C9b0XQEWpqFKf9DQUo03vOfXl16oSmyar1CLF1SzV3YzpZJ6bahcpLAA
|
||||
categories:
|
||||
- api
|
||||
- secret
|
||||
references:
|
||||
- https://docs.anthropic.com/claude/reference/authentication
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
body: |
|
||||
{
|
||||
"model": "claude-3-haiku-20240307",
|
||||
"max_tokens": 1024,
|
||||
"messages": [
|
||||
{"role": "user", "content": "respond only with 'success'"}
|
||||
]
|
||||
}
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
anthropic-version: "2023-06-01"
|
||||
x-api-key: '{{ TOKEN }}'
|
||||
method: POST
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- status:
|
||||
- 200
|
||||
type: StatusMatch
|
||||
- report_response: true
|
||||
- type: WordMatch
|
||||
words:
|
||||
- '"type":"invalid_request_error"'
|
||||
url: https://api.anthropic.com/v1/messages
|
||||
```
|
||||
|
||||
### FileIO Secret Key
|
||||
```yaml
|
||||
rules:
|
||||
- name: FileIO Secret Key
|
||||
id: kingfisher.fileio.1
|
||||
pattern: |
|
||||
(?xi)
|
||||
\b
|
||||
fileio
|
||||
(?:.|[\n\r]){0,32}?
|
||||
(?:SECRET|PRIVATE|ACCESS|KEY|TOKEN)
|
||||
(?:.|[\n\r]){0,16}?
|
||||
\b
|
||||
(
|
||||
[A-Z0-9]{16}
|
||||
(?:\.[A-Z0-9]{7}){2}
|
||||
\.[A-Z0-9]{8}
|
||||
)
|
||||
\b
|
||||
min_entropy: 3.3
|
||||
confidence: medium
|
||||
examples:
|
||||
- fileio SECRETKEY = Z9Y8X7W6V5U4T3S2R1Q0.P9O8N7M6L5K4J3H2G1F
|
||||
- fileio.PRIVATE.TOKEN = F0E1D2C3B4A596877869.5E4D3C2B1A0Z9Y8X7W6V
|
||||
- fileio_key = M8N6B4V2C0X9Z7L5K3J1.H2G4F6D8S0A9P7O5I3U1
|
||||
validation:
|
||||
type: Http
|
||||
content:
|
||||
request:
|
||||
method: GET
|
||||
url: https://file.io/api/v2/account
|
||||
headers:
|
||||
Authorization: "Bearer {{ TOKEN }}"
|
||||
response_matcher:
|
||||
- report_response: true
|
||||
- type: StatusMatch
|
||||
status: [200]
|
||||
- type: HeaderMatch
|
||||
header: content-type
|
||||
expected: ["application/json"]
|
||||
- type: JsonValid
|
||||
|
||||
```
|
||||
462
docs/benchmark/.gitignore
vendored
Normal file
462
docs/benchmark/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/visualstudio,macos,go
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio,macos,go
|
||||
|
||||
*.html
|
||||
### Go ###
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
### VisualStudio ###
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
### VisualStudio Patch ###
|
||||
# Additional files built by Visual Studio
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudio,macos,go
|
||||
35
docs/benchmark/README.md
Normal file
35
docs/benchmark/README.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Benchmark
|
||||
|
||||
A Go-based benchmarking tool that clones a set of repositories, runs security scanning tools (Kingfisher, Trufflehog, Gitleaks, and Detect-Secrets), and reports execution times, scan results, and network request counts. An intercepting HTTP proxy (without TLS decryption) counts network requests made by each tool.
|
||||
|
||||
## Features
|
||||
|
||||
- **Repository Cloning:** Clones a predefined list of repositories.
|
||||
- **Tool Execution:** Runs external scanning tools and collects timing and result metrics.
|
||||
- **Network Request Counting:** Uses an HTTP proxy (listening on 127.0.0.1:9191) to count each network request made.
|
||||
- **Multi-Format Reporting:** Outputs results as a plain text table (default), Markdown table (`-markdown`), or interactive HTML report (`-html`).
|
||||
- **Custom Base Directory:** Use a custom directory via the `-basedir` flag; defaults to `os.TempDir()/benchmark` if not specified.
|
||||
- **HTML Report:** When using `-html`, writes to a timestamped HTML file, uses DataTables for sorting/searching, and automatically opens it in your default browser.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Go:** [Install Go](https://golang.org/doc/install)
|
||||
- **Git:** Ensure `git` is installed and in your PATH.
|
||||
- **Scanning Tools:** The following tools must be installed and available in your PATH:
|
||||
- `kingfisher`
|
||||
- `trufflehog`
|
||||
- `gitleaks`
|
||||
- `detect-secrets`
|
||||
|
||||
## Installation
|
||||
|
||||
Clone the repository and build the program:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yourusername/benchmark-proxy-scanner.git
|
||||
cd benchmark-proxy-scanner
|
||||
go build -o benchmark
|
||||
|
||||
## Usage
|
||||
|
||||
./
|
||||
3
docs/benchmark/go.mod
Normal file
3
docs/benchmark/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module kfbmark
|
||||
|
||||
go 1.24.0
|
||||
536
docs/benchmark/main.go
Normal file
536
docs/benchmark/main.go
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
// main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Global counter for intercepted HTTP requests.
|
||||
netReqCount int64
|
||||
// BASE_DIR will be set from a flag or default to os.TempDir()/benchmark.
|
||||
BASE_DIR string
|
||||
)
|
||||
|
||||
// Repo list: name and git URL.
|
||||
var repos = []struct {
|
||||
name string
|
||||
url string
|
||||
}{
|
||||
{"croc", "https://github.com/schollz/croc.git"},
|
||||
{"rails", "https://github.com/rails/rails.git"},
|
||||
{"ruby", "https://github.com/ruby/ruby.git"},
|
||||
{"gitlab", "https://gitlab.com/gitlab-org/gitlab.git"},
|
||||
{"django", "https://github.com/django/django.git"},
|
||||
{"lucene", "https://github.com/apache/lucene.git"},
|
||||
{"mongodb", "https://github.com/mongodb/mongo.git"},
|
||||
{"linux", "https://github.com/torvalds/linux.git"},
|
||||
{"typescript", "https://github.com/microsoft/TypeScript.git"},
|
||||
}
|
||||
|
||||
// RepoResult holds timing, tool outputs, and network request counts.
|
||||
type RepoResult struct {
|
||||
Repo string
|
||||
// Runtimes in seconds.
|
||||
KFTime time.Duration
|
||||
KFFindings int
|
||||
KFValidated int
|
||||
KFNetReq int64
|
||||
THTime time.Duration
|
||||
THFindings int
|
||||
THVerified int
|
||||
THNetReq int64
|
||||
GLTime time.Duration
|
||||
GLFindings int
|
||||
GLNetReq int64
|
||||
DSTime time.Duration
|
||||
DSFindings int
|
||||
DSNetReq int64
|
||||
}
|
||||
|
||||
// --- Proxy Implementation ---
|
||||
|
||||
// proxyHandler counts each request then forwards it.
|
||||
type proxyHandler struct{}
|
||||
|
||||
func (p *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
atomic.AddInt64(&netReqCount, 1)
|
||||
if req.Method == "CONNECT" {
|
||||
handleTunneling(w, req)
|
||||
} else {
|
||||
handleHTTP(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func handleHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
proxy := httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {},
|
||||
}
|
||||
proxy.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
func handleTunneling(w http.ResponseWriter, req *http.Request) {
|
||||
destConn, err := net.Dial("tcp", req.Host)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 503)
|
||||
return
|
||||
}
|
||||
hijacker, ok := w.(interface {
|
||||
Hijack() (net.Conn, *bufio.ReadWriter, error)
|
||||
})
|
||||
if !ok {
|
||||
http.Error(w, "Hijacking not supported", 500)
|
||||
return
|
||||
}
|
||||
clientConn, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 503)
|
||||
return
|
||||
}
|
||||
_, err = clientConn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n"))
|
||||
if err != nil {
|
||||
clientConn.Close()
|
||||
destConn.Close()
|
||||
return
|
||||
}
|
||||
go transfer(destConn, clientConn)
|
||||
go transfer(clientConn, destConn)
|
||||
}
|
||||
|
||||
func transfer(dst io.WriteCloser, src io.ReadCloser) {
|
||||
defer dst.Close()
|
||||
defer src.Close()
|
||||
io.Copy(dst, src)
|
||||
}
|
||||
|
||||
func runProxy() {
|
||||
server := &http.Server{
|
||||
Addr: "127.0.0.1:9191",
|
||||
Handler: &proxyHandler{},
|
||||
}
|
||||
log.Printf("Starting proxy on %s", server.Addr)
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
log.Fatalf("Proxy error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Helper Functions ---
|
||||
|
||||
func getSystemInfo() string {
|
||||
osInfo := runtime.GOOS
|
||||
arch := runtime.GOARCH
|
||||
cpuCount := runtime.NumCPU()
|
||||
memInfo := "N/A"
|
||||
if osInfo == "darwin" {
|
||||
out, err := exec.Command("sysctl", "-n", "hw.memsize").Output()
|
||||
if err == nil {
|
||||
if memBytes, err := strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64); err == nil {
|
||||
memInfo = fmt.Sprintf("%.2f GB", float64(memBytes)/(1024*1024*1024))
|
||||
}
|
||||
}
|
||||
} else if osInfo == "linux" {
|
||||
data, err := ioutil.ReadFile("/proc/meminfo")
|
||||
if err == nil {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(data))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "MemTotal:") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 {
|
||||
if memKb, err := strconv.ParseInt(parts[1], 10, 64); err == nil {
|
||||
memInfo = fmt.Sprintf("%.2f GB", float64(memKb)/(1024*1024))
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("OS: %s\nArchitecture: %s\nCPU Cores: %d\nRAM: %s\n", osInfo, arch, cpuCount, memInfo)
|
||||
}
|
||||
|
||||
func cloneRepo(repoName, repoURL string) error {
|
||||
dest := filepath.Join(BASE_DIR, repoName)
|
||||
if _, err := os.Stat(dest); os.IsNotExist(err) {
|
||||
fmt.Printf("Cloning %s...\n", repoName)
|
||||
cmd := exec.Command("git", "clone", repoURL, dest)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
fmt.Printf("Repo '%s' exists, skipping clone.\n", repoName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCommand(cmdArgs []string, cwd string, ignoreErrors bool, combineStderr bool) (time.Duration, int, string, error) {
|
||||
atomic.StoreInt64(&netReqCount, 0)
|
||||
start := time.Now()
|
||||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||
cmd.Dir = cwd
|
||||
cmd.Env = append(os.Environ(), "HTTP_PROXY=127.0.0.1:9191", "HTTPS_PROXY=127.0.0.1:9191")
|
||||
var output []byte
|
||||
var err error
|
||||
if combineStderr {
|
||||
output, err = cmd.CombinedOutput()
|
||||
} else {
|
||||
output, err = cmd.Output()
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
exitCode := 0
|
||||
if err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
exitCode = exitError.ExitCode()
|
||||
} else {
|
||||
if !ignoreErrors {
|
||||
return elapsed, exitCode, "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "[TIME] Command: %s took %.2fs\n", strings.Join(cmdArgs, " "), elapsed.Seconds())
|
||||
return elapsed, exitCode, string(output), nil
|
||||
}
|
||||
|
||||
func parseKingfisherOutput(output string) (int, int) {
|
||||
total, validated := 0, 0
|
||||
scanner := bufio.NewScanner(strings.NewReader(output))
|
||||
for scanner.Scan() {
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(scanner.Bytes(), &data); err == nil {
|
||||
if v, ok := data["findings"].(float64); ok {
|
||||
total = int(v)
|
||||
}
|
||||
if v, ok := data["successful_validations"].(float64); ok {
|
||||
validated = int(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return total, validated
|
||||
}
|
||||
|
||||
func parseTrufflehogOutput(output string) (int, int) {
|
||||
total, verified := 0, 0
|
||||
scanner := bufio.NewScanner(strings.NewReader(output))
|
||||
for scanner.Scan() {
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(scanner.Bytes(), &data); err == nil {
|
||||
if u, ok := data["unverified_secrets"].(float64); ok {
|
||||
total += int(u)
|
||||
}
|
||||
if v, ok := data["verified_secrets"].(float64); ok {
|
||||
verified = int(v)
|
||||
total += int(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return total, verified
|
||||
}
|
||||
|
||||
func parseGitleaksOutput(reportPath string) int {
|
||||
data, err := ioutil.ReadFile(reportPath)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
var arr []interface{}
|
||||
if err := json.Unmarshal(data, &arr); err == nil {
|
||||
return len(arr)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func parseDetectSecretsOutput(output string) int {
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(output), &data); err != nil {
|
||||
return 0
|
||||
}
|
||||
results := data["results"]
|
||||
sum := 0
|
||||
if m, ok := results.(map[string]interface{}); ok {
|
||||
for _, v := range m {
|
||||
if arr, ok := v.([]interface{}); ok {
|
||||
sum += len(arr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func formatDuration(d time.Duration) string {
|
||||
secs := int(d.Seconds() + 0.5)
|
||||
m := secs / 60
|
||||
s := secs % 60
|
||||
if m > 0 {
|
||||
return fmt.Sprintf("%.2f (%dm %ds)", d.Seconds(), m, s)
|
||||
}
|
||||
return fmt.Sprintf("%.2f (%ds)", d.Seconds(), s)
|
||||
}
|
||||
|
||||
// --- Output Functions for Markdown Tables ---
|
||||
|
||||
func printRuntimeTable(results []RepoResult) {
|
||||
fmt.Println("### Runtime Comparison (seconds)")
|
||||
fmt.Println()
|
||||
fmt.Println("| Repository | Kingfisher Runtime | TruffleHog Runtime | GitLeaks Runtime | detect-secrets Runtime |")
|
||||
fmt.Println("|------------|--------------------|--------------------|------------------|------------------------|")
|
||||
for _, r := range results {
|
||||
fmt.Printf("| %s | %.2f | %.2f | %.2f | %.2f |\n",
|
||||
r.Repo,
|
||||
r.KFTime.Seconds(),
|
||||
r.THTime.Seconds(),
|
||||
r.GLTime.Seconds(),
|
||||
r.DSTime.Seconds())
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func printFindingsTable(results []RepoResult) {
|
||||
fmt.Println("### Validated/Verified Findings Comparison")
|
||||
fmt.Println()
|
||||
fmt.Println("Note: For GitLeaks and detect-secrets, validated/verified counts are not available.")
|
||||
fmt.Println()
|
||||
fmt.Println("| Repository | Kingfisher Validated | TruffleHog Verified | GitLeaks Verified | detect-secrets Verified |")
|
||||
fmt.Println("|------------|----------------------|---------------------|-------------------|-------------------------|")
|
||||
for _, r := range results {
|
||||
fmt.Printf("| %s | %d | %d | %d | %d |\n",
|
||||
r.Repo,
|
||||
r.KFValidated,
|
||||
r.THVerified,
|
||||
0,
|
||||
0)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func printNetworkTable(results []RepoResult) {
|
||||
fmt.Println("### Network Requests Comparison")
|
||||
fmt.Println()
|
||||
fmt.Println("| Repository | Kingfisher Network Requests | TruffleHog Network Requests | GitLeaks Network Requests | detect-secrets Network Requests |")
|
||||
fmt.Println("|------------|-----------------------------|-----------------------------|---------------------------|----------------------------------|")
|
||||
for _, r := range results {
|
||||
fmt.Printf("| %s | %d | %d | %d | %d |\n",
|
||||
r.Repo,
|
||||
r.KFNetReq,
|
||||
r.THNetReq,
|
||||
r.GLNetReq,
|
||||
r.DSNetReq)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// --- QuickChart.io Integration ---
|
||||
// This function builds chart URLs using QuickChart.io and prints Markdown image links.
|
||||
func printQuickChartLinks(results []RepoResult) {
|
||||
labels := []string{}
|
||||
for _, r := range results {
|
||||
labels = append(labels, r.Repo)
|
||||
}
|
||||
|
||||
chartURL := func(title string, datasetMap map[string][]float64) string {
|
||||
type dataset struct {
|
||||
Label string `json:"label"`
|
||||
Data []float64 `json:"data"`
|
||||
}
|
||||
data := struct {
|
||||
Type string `json:"type"`
|
||||
Data struct {
|
||||
Labels []string `json:"labels"`
|
||||
Datasets []dataset `json:"datasets"`
|
||||
} `json:"data"`
|
||||
Options map[string]interface{} `json:"options"`
|
||||
}{
|
||||
Type: "bar",
|
||||
}
|
||||
data.Data.Labels = labels
|
||||
for label, vals := range datasetMap {
|
||||
data.Data.Datasets = append(data.Data.Datasets, dataset{Label: label, Data: vals})
|
||||
}
|
||||
data.Options = map[string]interface{}{
|
||||
"title": map[string]string{"display": "true", "text": title},
|
||||
"scales": map[string]interface{}{
|
||||
"yAxes": []map[string]interface{}{
|
||||
{"ticks": map[string]interface{}{"beginAtZero": true}},
|
||||
},
|
||||
},
|
||||
}
|
||||
jsonBytes, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
log.Printf("Error marshaling JSON: %v", err)
|
||||
}
|
||||
// URL-encode the JSON configuration.
|
||||
return "https://quickchart.io/chart?c=" + url.QueryEscape(string(jsonBytes))
|
||||
}
|
||||
|
||||
// Build datasets.
|
||||
var (
|
||||
kfTimes, thTimes, glTimes, dsTimes []float64
|
||||
kfValids, thVerifs, glZero, dsZero []float64
|
||||
kfReqs, thReqs, glReqs, dsReqs []float64
|
||||
)
|
||||
for _, r := range results {
|
||||
kfTimes = append(kfTimes, r.KFTime.Seconds())
|
||||
thTimes = append(thTimes, r.THTime.Seconds())
|
||||
glTimes = append(glTimes, r.GLTime.Seconds())
|
||||
dsTimes = append(dsTimes, r.DSTime.Seconds())
|
||||
|
||||
kfValids = append(kfValids, float64(r.KFValidated))
|
||||
thVerifs = append(thVerifs, float64(r.THVerified))
|
||||
glZero = append(glZero, 0)
|
||||
dsZero = append(dsZero, 0)
|
||||
|
||||
kfReqs = append(kfReqs, float64(r.KFNetReq))
|
||||
thReqs = append(thReqs, float64(r.THNetReq))
|
||||
glReqs = append(glReqs, float64(r.GLNetReq))
|
||||
dsReqs = append(dsReqs, float64(r.DSNetReq))
|
||||
}
|
||||
|
||||
fmt.Println("### QuickChart.io Visualizations")
|
||||
fmt.Println()
|
||||
fmt.Println("#### Runtime Chart")
|
||||
fmt.Println("", map[string][]float64{
|
||||
"Kingfisher": kfTimes,
|
||||
"TruffleHog": thTimes,
|
||||
"GitLeaks": glTimes,
|
||||
"detect-secrets": dsTimes,
|
||||
}) + ")")
|
||||
fmt.Println()
|
||||
fmt.Println("#### Validated/Verified Findings Chart")
|
||||
fmt.Println(" + ")")
|
||||
fmt.Println()
|
||||
fmt.Println("#### Network Requests Chart")
|
||||
fmt.Println(" + ")")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Parse command-line flags.
|
||||
baseDirFlag := flag.String("basedir", "", "Directory to clone repos (default: os.TempDir()/benchmark)")
|
||||
markdownFlag := flag.Bool("markdown", true, "Output in Markdown format")
|
||||
flag.Parse()
|
||||
|
||||
// Set BASE_DIR.
|
||||
if *baseDirFlag == "" {
|
||||
BASE_DIR = filepath.Join(os.TempDir(), "benchmark")
|
||||
} else {
|
||||
BASE_DIR = *baseDirFlag
|
||||
}
|
||||
// Ensure BASE_DIR exists.
|
||||
os.MkdirAll(BASE_DIR, 0755)
|
||||
|
||||
// Start the proxy in a goroutine.
|
||||
go runProxy()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
fmt.Println(getSystemInfo())
|
||||
|
||||
var results []RepoResult
|
||||
|
||||
// Process each repository.
|
||||
for _, r := range repos {
|
||||
if err := cloneRepo(r.name, r.url); err != nil {
|
||||
log.Fatalf("Error cloning %s: %v", r.name, err)
|
||||
}
|
||||
repoPath := filepath.Join(BASE_DIR, r.name)
|
||||
var res RepoResult
|
||||
res.Repo = r.name
|
||||
|
||||
// Kingfisher.
|
||||
fmt.Printf("[Kingfisher] Scanning %s...\n", repoPath)
|
||||
kfArgs := []string{"kingfisher", "scan", repoPath, "--no-dedup", "--format", "json"}
|
||||
kfTime, _, kfOut, err := runCommand(kfArgs, ".", false, false)
|
||||
if err != nil {
|
||||
log.Printf("Error running kingfisher: %v", err)
|
||||
}
|
||||
res.KFTime = kfTime
|
||||
res.KFFindings, res.KFValidated = parseKingfisherOutput(kfOut)
|
||||
res.KFNetReq = atomic.LoadInt64(&netReqCount)
|
||||
|
||||
// TruffleHog.
|
||||
fmt.Printf("\n[TruffleHog] Scanning %s...\n", repoPath)
|
||||
fileURI := "file://" + repoPath
|
||||
thArgs := []string{"trufflehog", "git", fileURI, "--json"}
|
||||
thTime, _, thOut, err := runCommand(thArgs, ".", false, true)
|
||||
if err != nil {
|
||||
log.Printf("Error running trufflehog: %v", err)
|
||||
}
|
||||
res.THTime = thTime
|
||||
res.THFindings, res.THVerified = parseTrufflehogOutput(thOut)
|
||||
res.THNetReq = atomic.LoadInt64(&netReqCount)
|
||||
|
||||
// GitLeaks.
|
||||
fmt.Printf("\n[GitLeaks] Scanning %s...\n", repoPath)
|
||||
glReport := filepath.Join(BASE_DIR, fmt.Sprintf("gl-%s.json", r.name))
|
||||
glArgs := []string{"gitleaks", "git", "-v", repoPath, "--report-path", glReport}
|
||||
glTime, _, _, _ := runCommand(glArgs, ".", true, false)
|
||||
res.GLTime = glTime
|
||||
res.GLFindings = parseGitleaksOutput(glReport)
|
||||
res.GLNetReq = atomic.LoadInt64(&netReqCount)
|
||||
|
||||
// detect-secrets.
|
||||
fmt.Printf("\n[detect-secrets] Scanning %s...\n", repoPath)
|
||||
dsArgs := []string{"detect-secrets", "scan", repoPath}
|
||||
dsTime, _, dsOut, err := runCommand(dsArgs, ".", false, false)
|
||||
if err != nil {
|
||||
log.Printf("Error running detect-secrets: %v", err)
|
||||
}
|
||||
res.DSTime = dsTime
|
||||
res.DSFindings = parseDetectSecretsOutput(dsOut)
|
||||
res.DSNetReq = atomic.LoadInt64(&netReqCount)
|
||||
|
||||
results = append(results, res)
|
||||
}
|
||||
|
||||
// --- Output Report in Markdown ---
|
||||
if *markdownFlag {
|
||||
// Print separate summary tables.
|
||||
printRuntimeTable(results)
|
||||
printFindingsTable(results)
|
||||
printNetworkTable(results)
|
||||
// Print QuickChart.io image links.
|
||||
printQuickChartLinks(results)
|
||||
} else {
|
||||
// Fallback to a text table.
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "Repository\tKingfisher Runtime\tKingfisher Findings\tKingfisher Validated\tKingfisher Network Requests\tTruffleHog Runtime\tTruffleHog Findings\tTruffleHog Verified\tTruffleHog Network Requests\tGitLeaks Runtime\tGitLeaks Findings\tGitLeaks Network Requests\tdetect-secrets Runtime\tdetect-secrets Findings\tdetect-secrets Network Requests")
|
||||
fmt.Fprintln(w, "----------\t------------------\t---------------------\t----------------------\t-------------------------\t------------------\t---------------------\t---------------------\t--------------------------\t----------------\t-------------------\t---------------------------\t---------------------\t-------------------------\t--------------------------")
|
||||
for _, r := range results {
|
||||
fmt.Fprintf(w, "%s\t%s\t%d\t%d\t%d\t%s\t%d\t%d\t%d\t%s\t%d\t%d\t%s\t%d\t%d\n",
|
||||
r.Repo,
|
||||
formatDuration(r.KFTime), r.KFFindings, r.KFValidated, r.KFNetReq,
|
||||
formatDuration(r.THTime), r.THFindings, r.THVerified, r.THNetReq,
|
||||
formatDuration(r.GLTime), r.GLFindings, r.GLNetReq,
|
||||
formatDuration(r.DSTime), r.DSFindings, r.DSNetReq)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
fmt.Println("\n*Lower runtimes are better. Validated/Verified counts are reported where available. 'Network Requests' indicates the number of HTTP requests made during scanning.*")
|
||||
fmt.Println(getSystemInfo())
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue