Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
82a33f6
feat: add secret-scan and trufflehog-scan actions
KlausNie Mar 16, 2026
b832fdd
fix: install trufflehog binary instead of Docker action
KlausNie Mar 16, 2026
61cdc6a
fix: install trufflehog to RUNNER_TEMP/bin (writable on k8s runner)
KlausNie Mar 16, 2026
f55a326
fix: revert to official trufflesecurity/trufflehog Docker action
KlausNie Mar 16, 2026
f190f1d
fix: write safe.directory into container HOME gitconfig
KlausNie Mar 16, 2026
964e425
fix: run trufflehog Docker image with explicit workspace mount
KlausNie Mar 16, 2026
9d9c0c7
fix: use github scanner mode — no volume mount needed
KlausNie Mar 16, 2026
80b4622
Fix TruffleHog scan to use github scanner mode with PR number
KlausNie Mar 16, 2026
df152a8
Switch TruffleHog to git subcommand with remote clone
KlausNie Mar 16, 2026
d32f9a7
Temporarily remove --only-verified for TruffleHog pattern-match test
KlausNie Mar 16, 2026
d248778
Add --no-verification to catch all pattern matches
KlausNie Mar 16, 2026
2d418d3
Add trace logging to debug TruffleHog detection
KlausNie Mar 17, 2026
cf89b6e
Restore --only-verified after testing
KlausNie Mar 17, 2026
8a5fce0
Emit inline PR annotations for TruffleHog findings
KlausNie Mar 17, 2026
86c7b75
Temporarily tee JSON output for debugging
KlausNie Mar 17, 2026
0bbcaaa
Remove debug tee, capture TruffleHog output to tempfile only
KlausNie Mar 17, 2026
b14d132
Fix JSON key lookup to handle both camelCase and capitalized variants
KlausNie Mar 17, 2026
5fb3179
Use recursive search to find file/line in TruffleHog JSON output
KlausNie Mar 17, 2026
019b1d2
Debug: print SourceMetadata structure
KlausNie Mar 17, 2026
398e863
Remove debug print
KlausNie Mar 17, 2026
29d15ce
Mount workspace into TruffleHog container instead of pulling via token
KlausNie Mar 17, 2026
755ce80
Switch TruffleHog to use trufflesecurity/trufflehog@main action
KlausNie Mar 17, 2026
89da3a0
Remove --fail from extra_args, the action already passes it
KlausNie Mar 17, 2026
3706ae8
Revert TruffleHog to remote clone approach
KlausNie Mar 17, 2026
9184769
Switch TruffleHog from Docker to downloaded binary
KlausNie Mar 17, 2026
c7a1350
Add artifact signature verification to TruffleHog download
KlausNie Mar 17, 2026
1d1ddcd
Fix tarball filename for sha256sum verification
KlausNie Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions secret-scan/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: 'Secret Scan'
description: 'Scans source files for secrets using max/secret-scan.'
inputs:
include-path:
description: 'Path to include in the scan.'
required: false
default: ''
exclude-path:
description: 'Path to exclude from the scan.'
required: false
default: ''
runs:
using: "composite"
steps:
- uses: max/secret-scan@master
with:
include_path: ${{ inputs.include-path }}
exclude_path: ${{ inputs.exclude-path }}
32 changes: 32 additions & 0 deletions trufflehog-scan/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: 'TruffleHog Scan'
description: 'Scans for verified secrets using TruffleHog OSS.'
inputs:
version:
description: 'TruffleHog version to download (e.g. "3.88.1"). Defaults to the latest release.'
required: false
default: ''
base:
description: 'Base branch to compare against. Defaults to the PR base or repository default branch.'
required: false
default: ''
exclude-paths:
description: 'Path to a file listing paths to exclude from the scan.'
required: false
default: ''
include-paths:
description: 'Path to a file listing paths to include in the scan.'
required: false
default: ''
runs:
using: "composite"
steps:
- name: Run TruffleHog
shell: bash
env:
INPUT_VERSION: ${{ inputs.version }}
INPUT_BASE: ${{ inputs.base }}
INPUT_EXCLUDE_PATHS: ${{ inputs.exclude-paths }}
INPUT_INCLUDE_PATHS: ${{ inputs.include-paths }}
GITHUB_BASE_REF: ${{ github.base_ref }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: bash "$GITHUB_ACTION_PATH/scripts/trufflehog_scan.sh"
123 changes: 123 additions & 0 deletions trufflehog-scan/scripts/trufflehog_scan.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/usr/bin/env bash
set -euo pipefail

# ── Resolve base commit ──────────────────────────────────────────────────────
BASE="${INPUT_BASE:-}"
if [[ -z "$BASE" ]]; then
REF="${GITHUB_BASE_REF:-$DEFAULT_BRANCH}"
BASE="$(git rev-parse "origin/$REF")"
fi

HEAD_SHA="$(git rev-parse HEAD)"

TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

# ── Resolve TruffleHog version ───────────────────────────────────────────────
VERSION="${INPUT_VERSION:-}"
if [[ -z "$VERSION" ]]; then
VERSION=$(curl -sSf "https://api.github.com/repos/trufflesecurity/trufflehog/releases/latest" \
| python3 -c "import sys, json; print(json.load(sys.stdin)['tag_name'].lstrip('v'))")
fi

ARCH=$(uname -m)
[[ "$ARCH" == "x86_64" ]] && ARCH="amd64"
[[ "$ARCH" == "aarch64" ]] && ARCH="arm64"

# ── Download cosign (pinned, SHA256-verified) ─────────────────────────────────
# Pinned to cosign v3.0.5 — update both values together when bumping.
COSIGN_VERSION="3.0.5"
declare -A COSIGN_SHA256=(
[amd64]="db15cc99e6e4837daabab023742aaddc3841ce57f193d11b7c3e06c8003642b2"
[arm64]="d098f3168ae4b3aa70b4ca78947329b953272b487727d1722cb3cb098a1a20ab"
)
COSIGN_BIN="$TMPDIR/cosign"

curl -sSfL \
"https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-linux-${ARCH}" \
-o "$COSIGN_BIN"

echo "${COSIGN_SHA256[$ARCH]} $COSIGN_BIN" | sha256sum -c
chmod +x "$COSIGN_BIN"

# ── Download TruffleHog artifacts ────────────────────────────────────────────
RELEASE_BASE="https://github.com/trufflesecurity/trufflehog/releases/download/v${VERSION}"
TARBALL_NAME="trufflehog_${VERSION}_linux_${ARCH}.tar.gz"

curl -sSfL "${RELEASE_BASE}/${TARBALL_NAME}" -o "$TMPDIR/${TARBALL_NAME}"
curl -sSfL "${RELEASE_BASE}/trufflehog_${VERSION}_checksums.txt" -o "$TMPDIR/checksums.txt"
curl -sSfL "${RELEASE_BASE}/trufflehog_${VERSION}_checksums.txt.pem" -o "$TMPDIR/checksums.txt.pem"
curl -sSfL "${RELEASE_BASE}/trufflehog_${VERSION}_checksums.txt.sig" -o "$TMPDIR/checksums.txt.sig"

# ── Verify cosign signature on checksums file ─────────────────────────────────
"$COSIGN_BIN" verify-blob \
--certificate "$TMPDIR/checksums.txt.pem" \
--signature "$TMPDIR/checksums.txt.sig" \
--certificate-identity-regexp "https://github.com/trufflesecurity/trufflehog/" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
"$TMPDIR/checksums.txt"

# ── Verify tarball SHA256 against checksums ───────────────────────────────────
(cd "$TMPDIR" && grep "${TARBALL_NAME}" checksums.txt | sha256sum -c)

# ── Extract binary ────────────────────────────────────────────────────────────
tar -xz -C "$TMPDIR" -f "$TMPDIR/${TARBALL_NAME}"
TRUFFLEHOG="$TMPDIR/trufflehog"
chmod +x "$TRUFFLEHOG"

# ── Build scan args ───────────────────────────────────────────────────────────
ARGS="--only-verified --no-update --json"
[[ -n "${INPUT_EXCLUDE_PATHS:-}" ]] && ARGS="$ARGS --exclude-paths=$INPUT_EXCLUDE_PATHS"
[[ -n "${INPUT_INCLUDE_PATHS:-}" ]] && ARGS="$ARGS --include-paths=$INPUT_INCLUDE_PATHS"

# ── Run scan ──────────────────────────────────────────────────────────────────
OUTFILE="$TMPDIR/findings.json"

"$TRUFFLEHOG" git \
"file://$GITHUB_WORKSPACE" \
--since-commit="$BASE" \
--branch="$HEAD_SHA" \
$ARGS > "$OUTFILE" || true

# ── Emit annotations ──────────────────────────────────────────────────────────
python3 - "$OUTFILE" << 'PYEOF'
import sys, json

def find_git_metadata(node):
if isinstance(node, dict):
if 'file' in node or 'File' in node:
return node
for v in node.values():
result = find_git_metadata(v)
if result:
return result
return {}

count = 0
with open(sys.argv[1]) as f:
for line in f:
line = line.strip()
if not line:
continue
try:
d = json.loads(line)
except json.JSONDecodeError:
continue
git = find_git_metadata(d.get('SourceMetadata', {}))
file_path = git.get('file', git.get('File', ''))
line_num = git.get('line', git.get('Line', 1)) or 1
detector = d.get('DetectorName', 'Unknown')
verified = 'verified' if d.get('Verified', False) else 'unverified'
msg = f"TruffleHog [{detector}]: {verified} secret detected"
if file_path:
print(f"::error file={file_path},line={line_num}::{msg}")
else:
print(f"::error::{msg}")
count += 1

if count > 0:
print(f"TruffleHog found {count} secret(s).")
sys.exit(1)
else:
print("No secrets detected.")
PYEOF