name: 'Hound Security Scan' description: 'Find exposed credentials, malicious dependencies and unsafe CI in a repository' author: 'Hound Antivirus' branding: icon: 'shield' color: 'purple' inputs: path: description: 'Directory to scan, relative to the repository root' required: false default: '.' history: description: 'Also walk git history for credentials that were removed but not revoked' required: false default: 'false' fail-on: description: 'Fail the job at this severity or above: critical | warning | never' required: false default: 'critical' annotate: description: 'Annotate the affected files in the diff view' required: false default: 'true' version: description: 'Hound version to use, or "latest"' required: false default: 'latest' outputs: critical: description: 'Number of critical findings' value: ${{ steps.scan.outputs.critical }} warnings: description: 'Number of warnings' value: ${{ steps.scan.outputs.warnings }} report: description: 'Path to the JSON report' value: ${{ steps.scan.outputs.report }} runs: using: 'composite' steps: - id: install shell: bash # Verified against the same signed manifest the desktop agent uses, so # a compromised download host cannot substitute a different binary here # any more than it can there. run: | set -euo pipefail want='${{ inputs.version }}' if [ "$want" = latest ]; then want="$(curl -fsSL https://dl.houndav.com/latest.json | python3 -c 'import json,sys;print(json.load(sys.stdin)["release"]["version"])')" fi url="https://dl.houndav.com/deb/hound_${want}_amd64.deb" curl -fsSL "$url" -o /tmp/hound.deb expected="$(curl -fsSL "${url}.sha256")" actual="$(sha256sum /tmp/hound.deb | cut -d' ' -f1)" if [ "$expected" != "$actual" ]; then echo "::error::the Hound download does not match its published checksum" exit 1 fi sudo apt-get install -y -qq /tmp/hound.deb >/dev/null echo "installed hound $want" - id: scan shell: bash working-directory: ${{ github.workspace }} run: | set -uo pipefail args="" if [ '${{ inputs.history }}' = 'true' ]; then args="--history"; fi # Two reports: the supply-chain sweep and the hygiene checks. They # share a finding shape, so the outputs merge cleanly. hound hygiene '${{ inputs.path }}' $args --json > /tmp/hygiene.json || true hound supply-chain '${{ inputs.path }}' --json > /tmp/supply.json 2>/dev/null || true python3 "$GITHUB_ACTION_PATH/report.py" \ --hygiene /tmp/hygiene.json \ --supply /tmp/supply.json \ --annotate '${{ inputs.annotate }}' \ --fail-on '${{ inputs.fail-on }}' \ --summary "${GITHUB_STEP_SUMMARY:-/dev/null}" \ --out /tmp/hound-report.json status=$? echo "report=/tmp/hound-report.json" >> "$GITHUB_OUTPUT" exit $status