Skip to content
Open
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
976e24d
feat: add secret-scan and trufflehog-scan actions
KlausNie Mar 16, 2026
612921a
fix: install trufflehog binary instead of Docker action
KlausNie Mar 16, 2026
c2c8aae
fix: install trufflehog to RUNNER_TEMP/bin (writable on k8s runner)
KlausNie Mar 16, 2026
c13e39d
fix: revert to official trufflesecurity/trufflehog Docker action
KlausNie Mar 16, 2026
5593f20
fix: write safe.directory into container HOME gitconfig
KlausNie Mar 16, 2026
1f9ed41
fix: run trufflehog Docker image with explicit workspace mount
KlausNie Mar 16, 2026
fbbc8f1
fix: use github scanner mode — no volume mount needed
KlausNie Mar 16, 2026
987f839
Fix TruffleHog scan to use github scanner mode with PR number
KlausNie Mar 16, 2026
80a2935
Switch TruffleHog to git subcommand with remote clone
KlausNie Mar 16, 2026
4882532
Temporarily remove --only-verified for TruffleHog pattern-match test
KlausNie Mar 16, 2026
0c4dafb
Add --no-verification to catch all pattern matches
KlausNie Mar 16, 2026
4abb9b0
Add trace logging to debug TruffleHog detection
KlausNie Mar 17, 2026
4724e7f
Restore --only-verified after testing
KlausNie Mar 17, 2026
a16f552
Emit inline PR annotations for TruffleHog findings
KlausNie Mar 17, 2026
a2f8750
Temporarily tee JSON output for debugging
KlausNie Mar 17, 2026
40505ad
Remove debug tee, capture TruffleHog output to tempfile only
KlausNie Mar 17, 2026
1347ae3
Fix JSON key lookup to handle both camelCase and capitalized variants
KlausNie Mar 17, 2026
bf102ca
Use recursive search to find file/line in TruffleHog JSON output
KlausNie Mar 17, 2026
3b18604
Debug: print SourceMetadata structure
KlausNie Mar 17, 2026
abe6b9b
Remove debug print
KlausNie Mar 17, 2026
b24d2bb
Mount workspace into TruffleHog container instead of pulling via token
KlausNie Mar 17, 2026
093b539
Switch TruffleHog to use trufflesecurity/trufflehog@main action
KlausNie Mar 17, 2026
7c0dbe4
Remove --fail from extra_args, the action already passes it
KlausNie Mar 17, 2026
1cd2a58
Revert TruffleHog to remote clone approach
KlausNie Mar 17, 2026
03e0bf3
Switch TruffleHog from Docker to downloaded binary
KlausNie Mar 17, 2026
0009cf4
Add artifact signature verification to TruffleHog download
KlausNie Mar 17, 2026
c105642
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