docs(07-06): add CI/CD integration guide
- Pre-commit hook install/force/uninstall lifecycle - GitHub Actions workflow example with SARIF upload - External scanner import walkthrough (trufflehog, gitleaks) - Exit-code table for CI gating
This commit is contained in:
161
docs/CI-CD.md
Normal file
161
docs/CI-CD.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# KeyHunter CI/CD Integration
|
||||
|
||||
KeyHunter is designed to plug into developer workflows at three points: as a local
|
||||
**pre-commit hook** that blocks leaks before they land in git history, as a **GitHub
|
||||
Actions** step that uploads SARIF findings into the repository's Code Scanning tab,
|
||||
and as an **import sink** that consolidates results from other scanners (TruffleHog,
|
||||
Gitleaks) into one normalized database. This guide shows how to wire up each one.
|
||||
|
||||
## Pre-commit Hook
|
||||
|
||||
The pre-commit hook scans only files that are staged for commit
|
||||
(`git diff --cached --name-only --diff-filter=ACMR`), so it adds minimal latency to
|
||||
local development while still catching fresh leaks before they leave the workstation.
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
keyhunter hook install
|
||||
```
|
||||
|
||||
This writes an executable shell script to `.git/hooks/pre-commit` in the current
|
||||
repository. The script shells out to the `keyhunter` binary on your `PATH` and exits
|
||||
non-zero if any findings are produced — which aborts the commit.
|
||||
|
||||
The command refuses to overwrite an existing `pre-commit` hook unless you pass
|
||||
`--force`:
|
||||
|
||||
```bash
|
||||
keyhunter hook install --force
|
||||
```
|
||||
|
||||
With `--force`, the existing hook is backed up to
|
||||
`.git/hooks/pre-commit.bak.<unix-timestamp>` before the new one is written. You can
|
||||
restore it manually at any time.
|
||||
|
||||
### Bypass a Single Commit
|
||||
|
||||
If you need to commit in an emergency and want to skip the hook for one commit only,
|
||||
use git's built-in bypass flag:
|
||||
|
||||
```bash
|
||||
git commit --no-verify -m "hotfix: bypass scan"
|
||||
```
|
||||
|
||||
No KeyHunter state is changed — the next commit will be scanned normally.
|
||||
|
||||
### Uninstall
|
||||
|
||||
```bash
|
||||
keyhunter hook uninstall
|
||||
```
|
||||
|
||||
This removes `.git/hooks/pre-commit`. Any `.bak.<timestamp>` files created by
|
||||
`--force` are left in place so you can restore previous hooks manually.
|
||||
|
||||
## GitHub Actions (SARIF upload to Code Scanning)
|
||||
|
||||
KeyHunter emits SARIF 2.1.0 output (`--output sarif`), which GitHub's Code Scanning
|
||||
service ingests directly. Once uploaded, findings appear in the repository's
|
||||
**Security** tab, can be triaged, and can block pull requests via branch protection
|
||||
rules.
|
||||
|
||||
Save the following as `.github/workflows/keyhunter.yml`:
|
||||
|
||||
```yaml
|
||||
name: KeyHunter
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install KeyHunter
|
||||
run: |
|
||||
curl -sSL https://github.com/salvacybersec/keyhunter/releases/latest/download/keyhunter_linux_amd64.tar.gz | tar -xz
|
||||
sudo mv keyhunter /usr/local/bin/
|
||||
- name: Scan repository
|
||||
run: keyhunter scan . --output sarif > keyhunter.sarif
|
||||
continue-on-error: true
|
||||
- name: Upload SARIF to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: keyhunter.sarif
|
||||
category: keyhunter
|
||||
```
|
||||
|
||||
### Why `continue-on-error: true`?
|
||||
|
||||
KeyHunter exits with code `1` when it finds keys (see **Exit Codes** below). Without
|
||||
`continue-on-error: true`, GitHub Actions would mark the scan step as failed and skip
|
||||
the upload step — meaning the findings would never reach the Security tab. Letting
|
||||
the scan "fail" gracefully and then uploading the SARIF file gives you both the
|
||||
annotated findings in Code Scanning **and** the option to enforce blocking via branch
|
||||
protection rules on the Code Scanning check itself.
|
||||
|
||||
### Why `security-events: write`?
|
||||
|
||||
GitHub requires the `security-events: write` permission on the job for
|
||||
`github/codeql-action/upload-sarif@v3` to post results into Code Scanning. The
|
||||
default `GITHUB_TOKEN` has this permission available but it must be explicitly
|
||||
requested at the job level when using fine-grained workflow permissions. `fetch-depth: 0`
|
||||
ensures the full history is available so findings can be attributed to specific
|
||||
commits.
|
||||
|
||||
## Importing External Scanner Output
|
||||
|
||||
KeyHunter's `import` subcommand consolidates findings from other scanners into its
|
||||
SQLite database. This is useful when you run multiple tools in CI and want a single
|
||||
place to triage, verify, and track leaks over time.
|
||||
|
||||
### TruffleHog (JSON)
|
||||
|
||||
```bash
|
||||
trufflehog filesystem . --json > trufflehog.json
|
||||
keyhunter import --format=trufflehog trufflehog.json
|
||||
```
|
||||
|
||||
### Gitleaks (JSON)
|
||||
|
||||
```bash
|
||||
gitleaks detect -f json -r gitleaks.json
|
||||
keyhunter import --format=gitleaks gitleaks.json
|
||||
```
|
||||
|
||||
### Gitleaks (CSV)
|
||||
|
||||
```bash
|
||||
gitleaks detect -f csv -r gitleaks.csv
|
||||
keyhunter import --format=gitleaks-csv gitleaks.csv
|
||||
```
|
||||
|
||||
### Idempotency
|
||||
|
||||
Every imported finding is hashed on `(provider, masked_key, source)` before insert.
|
||||
Re-running the same import — for example, because a later CI step needs to consume
|
||||
the results — will not create duplicate rows. The command prints a summary of the
|
||||
form `Imported N findings (M new, K duplicates)` so you can confirm dedup worked.
|
||||
|
||||
## Exit Codes
|
||||
|
||||
Both `keyhunter scan` and `keyhunter import` follow the same exit-code convention so
|
||||
they compose predictably with CI runners and shell scripts:
|
||||
|
||||
| Code | Meaning | Typical CI usage |
|
||||
| ---- | ------------------------------------------ | --------------------------------------------- |
|
||||
| `0` | Clean run — no findings | Step passes, pipeline continues |
|
||||
| `1` | Findings present | Mark the job as failed / upload SARIF anyway |
|
||||
| `2` | Runtime error (bad flags, I/O, parse fail) | Pipeline should stop; investigate logs |
|
||||
|
||||
For GitHub Actions SARIF uploads, use `continue-on-error: true` on the scan step so
|
||||
that exit code `1` still lets the upload step run. For simple gating (e.g. a Makefile
|
||||
target), `keyhunter scan . && echo clean || echo findings` distinguishes the three
|
||||
states.
|
||||
Reference in New Issue
Block a user