diff --git a/.github/workflows/reusable-trufflehog.yml b/.github/workflows/reusable-trufflehog.yml index 6ac6a54..d149cdc 100644 --- a/.github/workflows/reusable-trufflehog.yml +++ b/.github/workflows/reusable-trufflehog.yml @@ -230,6 +230,15 @@ jobs: echo "${FINDINGS}" echo '' echo "${ACTION_TEXT}" + echo '' + echo '---' + echo '' + echo '**Ignoring False Positives:**' + echo 'To mark a false positive, add `# trufflehog:ignore` as an inline comment on the same line as the detected secret:' + echo '```' + echo 'my_fake_secret = "AKIAIOSFODNN7EXAMPLE" # trufflehog:ignore' + echo '```' + echo 'This works for files that support line numbers (most source files). After adding the comment, push your changes and the scan will re-run.' echo 'EOF' } >> "$GITHUB_OUTPUT" fi diff --git a/.github/workflows/self-zizmor.yaml b/.github/workflows/self-zizmor.yaml index ab5ca56..b46f34b 100644 --- a/.github/workflows/self-zizmor.yaml +++ b/.github/workflows/self-zizmor.yaml @@ -32,7 +32,7 @@ jobs: fi echo "found-files=${FOUND_FILES}" >> $GITHUB_OUTPUT zizmor: - name: Run zizmor from current branch (self test) + name: Run zizmor permissions: actions: read @@ -45,10 +45,14 @@ jobs: - zizmor-check if: ${{ needs.zizmor-check.outputs.found-files == 'true' }} - uses: grafana/shared-workflows/.github/workflows/reusable-zizmor.yml@835ea6d866784d0db603612bfbb354d152b62f5c + # Reusable workflow: zizmor + grafana-bench. Use ref with caller repo name for service/suite (fixes service=security). + uses: grafana/shared-workflows/.github/workflows/reusable-zizmor.yml@main with: runs-on: ${{ !github.event.repository.private && 'ubuntu-latest' || 'ubuntu-arm64-small' }} fail-severity: high min-severity: high min-confidence: low extra-args: --offline + send-bench-metrics: true + # Grafana Bench: pass PROMETHEUS_URL (and optionally PROMETHEUS_USER, PROMETHEUS_PASSWORD) for metrics; GAR access for Docker image. + secrets: inherit diff --git a/.github/workflows/test-trufflehog-ignore.yml b/.github/workflows/test-trufflehog-ignore.yml new file mode 100644 index 0000000..420a955 --- /dev/null +++ b/.github/workflows/test-trufflehog-ignore.yml @@ -0,0 +1,14 @@ +name: Test TruffleHog Ignore + +on: + pull_request: + push: + branches: + - add-trufflehog-ignore-docs + +jobs: + test-trufflehog: + uses: ./.github/workflows/reusable-trufflehog.yml + with: + fail-on-verified: "false" + fail-on-unverified: "false" diff --git a/test-trufflehog-ignore.py b/test-trufflehog-ignore.py new file mode 100644 index 0000000..06283cd --- /dev/null +++ b/test-trufflehog-ignore.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +""" +Test file for TruffleHog ignore functionality. + +This file contains fake secrets to test the # trufflehog:ignore comment feature. +""" + +# This should be detected (no ignore comment) +fake_aws_key = "AKIAIOSFODNN7EXAMPLE" + +# This should be ignored (has trufflehog:ignore comment) +fake_github_token = "ghp_1234567890abcdefghijklmnopqrstuvwxyz" # trufflehog:ignore + +# Another test case - this should be detected +test_secret = "sk_test_1234567890abcdefghijklmnopqrstuvwxyz" + +# This should be ignored +another_fake = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" # trufflehog:ignore diff --git a/trufflehog/GRAFANA_SETUP.md b/trufflehog/GRAFANA_SETUP.md new file mode 100644 index 0000000..a97a3e0 --- /dev/null +++ b/trufflehog/GRAFANA_SETUP.md @@ -0,0 +1,235 @@ +# Grafana Setup Guide for TruffleHog Integration + +**Zizmor + Grafana Bench (see metrics in one go):** Use the [grafana-bench-stack](../../grafana-bench-stack/README.md) in this repo: `cd grafana-bench-stack && docker compose up -d`, then set `PROMETHEUS_PUSHGATEWAY_URL` and open the **Zizmor (Grafana Bench)** dashboard in Grafana. + +## Quick Setup Options + +### Option 1: Grafana Cloud (Recommended for Testing - Fastest) + +1. **Sign up for Grafana Cloud** (free tier available) + - Go to https://grafana.com/auth/sign-up/create-user + - Create a free account (no credit card required for free tier) + +2. **Get your Loki credentials:** + - Go to https://grafana.com/orgs/YOUR_ORG/connections/loki + - Click "Send logs" → "Push logs" + - Copy: + - **LOKI_URL**: `https://logs-prod-XXX.grafana.net/loki/api/v1/push` + - **LOKI_USERNAME**: Your Grafana Cloud username (usually an ID like `123456`) + - **LOKI_PASSWORD**: Your API token (create one at https://grafana.com/orgs/YOUR_ORG/api-keys) + +3. **Get your Prometheus Pushgateway:** + - Go to https://grafana.com/orgs/YOUR_ORG/connections/prometheus + - Click "Send metrics" → "Remote write" + - You'll need to set up a Prometheus instance or use a remote write endpoint + - **Alternative**: Use a standalone Prometheus Pushgateway (see Option 2) + +### Option 2: Local Docker Setup (For Testing) + +Run these commands to set up local instances: + +```bash +# Start Loki +docker run -d --name loki -p 3100:3100 grafana/loki:latest + +# Start Prometheus Pushgateway +docker run -d --name pushgateway -p 9091:9091 prom/pushgateway:latest + +# Start Grafana (optional, for visualization) +docker run -d --name grafana -p 3000:3000 grafana/grafana:latest +``` + +Then use: +- **LOKI_URL**: `http://localhost:3100` (or your server IP) +- **PROMETHEUS_PUSHGATEWAY_URL**: `http://localhost:9091` (or your server IP) + +**Note**: For GitHub Actions to reach these, you'll need to expose them publicly (use ngrok, or deploy to a cloud server). + +### Option 3: Use Existing Grafana Instance + +If you already have Grafana/Loki/Prometheus: +- **LOKI_URL**: Your Loki instance URL (e.g., `https://loki.yourcompany.com`) +- **LOKI_USERNAME**: Your Loki username (if auth required) +- **LOKI_PASSWORD**: Your Loki password/token +- **PROMETHEUS_PUSHGATEWAY_URL**: Your Pushgateway URL (e.g., `https://pushgateway.yourcompany.com`) + +## Adding Secrets to GitHub + +### For Testing (Repository Secrets - Quickest) + +1. Go to your repository: `https://github.com/YOUR_ORG/security-github-actions` +2. Click **Settings** → **Secrets and variables** → **Actions** +3. Click **New repository secret** +4. Add these secrets: + + - **Name**: `LOKI_URL` + **Value**: Your Loki URL (e.g., `https://logs-prod-XXX.grafana.net/loki/api/v1/push`) + + - **Name**: `LOKI_USERNAME` + **Value**: Your Loki username (Grafana Cloud user ID) + + - **Name**: `LOKI_PASSWORD` + **Value**: Your Loki API token/password + + - **Name**: `PROMETHEUS_PUSHGATEWAY_URL` + **Value**: Your Pushgateway URL (e.g., `http://localhost:9091` or your cloud URL) + +### For Production (Organization Secrets - Recommended) + +1. Go to your organization: `https://github.com/organizations/YOUR_ORG/settings/secrets/actions` +2. Click **New organization secret** +3. Add the same secrets as above + +## Setting Up Grafana Dashboards + +### 1. Add Data Sources + +#### Add Loki Data Source: +1. In Grafana, go to **Configuration** → **Data sources** +2. Click **Add data source** → Select **Loki** +3. Enter your Loki URL +4. If using Grafana Cloud, use the credentials from step 2 above +5. Click **Save & test** + +#### Add Prometheus Data Source: +1. Go to **Configuration** → **Data sources** +2. Click **Add data source** → Select **Prometheus** +3. Enter your Prometheus URL (the one scraping the Pushgateway) +4. Click **Save & test** + +### 2. Create Dashboard + +1. Go to **Dashboards** → **New** → **New dashboard** +2. Click **Add visualization** + +#### Panel 1: Total Secrets Over Time (Prometheus) +- **Data source**: Prometheus +- **Query**: + ```promql + sum(trufflehog_secrets_total) by (repository) + ``` +- **Visualization**: Time series +- **Title**: "Total Secrets Found by Repository" + +#### Panel 2: Verified vs Unverified (Prometheus) +- **Data source**: Prometheus +- **Query A**: + ```promql + sum(trufflehog_secrets_verified) by (repository) + ``` +- **Query B**: + ```promql + sum(trufflehog_secrets_unverified) by (repository) + ``` +- **Visualization**: Time series +- **Title**: "Verified vs Unverified Secrets" + +#### Panel 3: Secrets by Detector Type (Prometheus) +- **Data source**: Prometheus +- **Query**: + ```promql + sum(trufflehog_secrets_by_detector_*) by (detector) + ``` +- **Visualization**: Bar chart or Pie chart +- **Title**: "Secrets by Detector Type" + +#### Panel 4: Recent Findings Table (Loki) +- **Data source**: Loki +- **Query**: + ```logql + {job="trufflehog"} + ``` +- **Visualization**: Table or Logs +- **Title**: "Recent Secret Findings" + +#### Panel 5: Findings Count Over Time (Loki) +- **Data source**: Loki +- **Query**: + ```logql + count_over_time({job="trufflehog"}[1h]) + ``` +- **Visualization**: Time series +- **Title**: "Findings Count (Last Hour)" + +### 3. Useful Loki Queries for Filtering + +**Verified secrets only:** +```logql +{job="trufflehog", verified="true"} +``` + +**By specific repository:** +```logql +{job="trufflehog", repository="your-repo-name"} +``` + +**By detector type:** +```logql +{job="trufflehog", detector="aws"} +``` + +**Combined filter:** +```logql +{job="trufflehog", verified="true", repository="your-repo-name"} +``` + +## Testing the Integration + +1. **Trigger a workflow** that uses the TruffleHog reusable workflow +2. **Check workflow logs** for: + - "✅ Successfully sent X findings to Loki" + - "✅ Successfully sent X metrics to Prometheus" +3. **Check Grafana**: + - Loki: Query `{job="trufflehog"}` - you should see log entries + - Prometheus: Query `trufflehog_secrets_total` - you should see metrics + +## Troubleshooting + +### No data appearing in Grafana + +1. **Check GitHub Actions logs:** + - Look for errors in the "Send findings to Loki" or "Send metrics to Prometheus" steps + - Verify secrets are set correctly + +2. **Verify URLs are accessible:** + - Test Loki URL: `curl -u USERNAME:PASSWORD https://your-loki-url/ready` + - Test Pushgateway: `curl http://your-pushgateway-url/metrics` + +3. **Check Prometheus is scraping Pushgateway:** + - If using Prometheus, ensure it's configured to scrape the Pushgateway + - Check Prometheus targets page + +4. **Verify data format:** + - Check Loki logs: Query `{job="trufflehog"}` should return entries + - Check Prometheus metrics: Query `trufflehog_secrets_total` should return values + +### Common Issues + +- **401 Unauthorized**: Check LOKI_USERNAME and LOKI_PASSWORD are correct +- **Connection refused**: Verify URLs are correct and accessible from GitHub Actions runners +- **No metrics in Prometheus**: Ensure Prometheus is scraping the Pushgateway endpoint + +## Quick Test Script + +You can test the scripts locally: + +```bash +# Set environment variables +export LOKI_URL="https://your-loki-url" +export LOKI_USERNAME="your-username" +export LOKI_PASSWORD="your-password" +export REPOSITORY="test/repo" +export COMMIT_SHA="abc123" +export BRANCH="main" +export TRUFFLEHOG_RESULTS_FILE="results.json" + +# Create a test results.json +echo '[{"DetectorName": "AWS", "Verified": true, "SourceMetadata": {"Data": {"Filesystem": {"file": "test.txt", "line": 1}}}, "Redacted": "test"}]' > results.json + +# Test Loki script +python trufflehog/send-to-loki.py + +# Test Prometheus script +export PROMETHEUS_PUSHGATEWAY_URL="http://your-pushgateway-url" +python trufflehog/send-to-prometheus.py +``` diff --git a/trufflehog/workflow-example-with-grafana-bench.yml b/trufflehog/workflow-example-with-grafana-bench.yml new file mode 100644 index 0000000..3ba2b8e --- /dev/null +++ b/trufflehog/workflow-example-with-grafana-bench.yml @@ -0,0 +1,181 @@ +# Example: TruffleHog Workflow with Grafana-Bench Integration +# +# This is a reference implementation showing how to add grafana-bench +# to your existing TruffleHog workflow for enhanced Grafana visualization +# +# Copy the relevant sections into your .github/workflows/reusable-trufflehog.yml + +# Add these steps after line 201 (after "Upload scan results" step) + + # ==================================================================== + # OPTION 1: Keep Python for Loki, Add Grafana-Bench for Prometheus + # ==================================================================== + + - name: Setup Python for Loki + if: ${{ !cancelled() && secrets.LOKI_URL != '' }} + uses: actions/setup-python@0b6a380b5a7827e48e69b2e0e596c5c8c2b0e0b0 # v5.1.0 + with: + python-version: '3.x' + + - name: Install Python dependencies + if: ${{ !cancelled() && secrets.LOKI_URL != '' }} + run: pip install requests + + - name: Send findings to Loki + if: ${{ !cancelled() && secrets.LOKI_URL != '' }} + continue-on-error: true + env: + LOKI_URL: ${{ secrets.LOKI_URL }} + LOKI_USERNAME: ${{ secrets.LOKI_USERNAME }} + LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }} + REPOSITORY: ${{ github.repository }} + COMMIT_SHA: ${{ github.sha }} + BRANCH: ${{ github.ref_name }} + TRUFFLEHOG_RESULTS_FILE: results.json + run: python trufflehog/send-to-loki.py + + # NEW: Install grafana-bench + - name: Setup Grafana Bench + if: ${{ !cancelled() && secrets.PROMETHEUS_PUSHGATEWAY_URL != '' }} + run: | + VERSION="v1.0.0" + ARCH=$(uname -m) + + # Map architecture names + if [[ "$ARCH" == "x86_64" ]]; then + ARCH="amd64" + elif [[ "$ARCH" == "aarch64" ]]; then + ARCH="arm64" + fi + + # Download and install + BINARY_URL="https://github.com/grafana/grafana-bench/releases/download/${VERSION}/grafana-bench_${VERSION#v}_linux_${ARCH}.tar.gz" + echo "Downloading grafana-bench from: ${BINARY_URL}" + + curl -sSfL "${BINARY_URL}" | tar -xz -C /tmp + sudo mv /tmp/grafana-bench /usr/local/bin/grafana-bench + sudo chmod +x /usr/local/bin/grafana-bench + + # Verify installation + grafana-bench --version + + # NEW: Process findings with grafana-bench + - name: Send metrics to Prometheus via Grafana Bench + if: ${{ !cancelled() && secrets.PROMETHEUS_PUSHGATEWAY_URL != '' }} + continue-on-error: true + env: + PROMETHEUS_PUSHGATEWAY_URL: ${{ secrets.PROMETHEUS_PUSHGATEWAY_URL }} + GITHUB_REF_NAME: ${{ github.ref_name }} + run: | + echo "Processing TruffleHog findings with Grafana Bench..." + + grafana-bench test \ + --service ${{ format('grafana-{0}', github.event.repository.name) }} \ + --service-version ${{ github.sha }} \ + --test-runner trufflehog \ + --suite-name ${{ github.repository }}/trufflehog \ + --suite-path . \ + --trufflehog-json-file results.json \ + --prometheus-metrics \ + --prometheus-url "${PROMETHEUS_PUSHGATEWAY_URL}" \ + --run-stage ci \ + --log-level info + + echo "✅ Metrics sent to Prometheus Pushgateway" + + # Keep existing failure policy check + - name: Check failure policy + env: + FAIL_ON_VERIFIED: ${{ inputs.fail-on-verified }} + FAIL_ON_UNVERIFIED: ${{ inputs.fail-on-unverified }} + VERIFIED_COUNT: ${{ steps.scan.outputs.verified }} + UNVERIFIED_COUNT: ${{ steps.scan.outputs.unverified }} + run: | + SHOULD_FAIL=false + if [[ "${FAIL_ON_VERIFIED}" == "true" && "${VERIFIED_COUNT}" != "0" ]]; then + SHOULD_FAIL=true + fi + if [[ "${FAIL_ON_UNVERIFIED}" == "true" && "${UNVERIFIED_COUNT}" != "0" ]]; then + SHOULD_FAIL=true + fi + + if [[ "${SHOULD_FAIL}" == "true" ]]; then + echo "Workflow failed due to secrets found (verified: ${VERIFIED_COUNT}, unverified: ${UNVERIFIED_COUNT})" + exit 1 + else + echo "No action needed - secrets within configured thresholds" + fi + + +# ==================================================================== +# ALTERNATIVE: Use Grafana-Bench Action (Cleaner) +# ==================================================================== + + - name: Setup Python for Loki + if: ${{ !cancelled() && secrets.LOKI_URL != '' }} + uses: actions/setup-python@0b6a380b5a7827e48e69b2e0e596c5c8c2b0e0b0 # v5.1.0 + with: + python-version: '3.x' + + - name: Install Python dependencies + if: ${{ !cancelled() && secrets.LOKI_URL != '' }} + run: pip install requests + + - name: Send findings to Loki + if: ${{ !cancelled() && secrets.LOKI_URL != '' }} + continue-on-error: true + env: + LOKI_URL: ${{ secrets.LOKI_URL }} + LOKI_USERNAME: ${{ secrets.LOKI_USERNAME }} + LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }} + REPOSITORY: ${{ github.repository }} + COMMIT_SHA: ${{ github.sha }} + BRANCH: ${{ github.ref_name }} + TRUFFLEHOG_RESULTS_FILE: results.json + run: python trufflehog/send-to-loki.py + + # Alternative: Use official grafana-bench action + - name: Setup Grafana Bench + if: ${{ !cancelled() && secrets.PROMETHEUS_PUSHGATEWAY_URL != '' }} + uses: grafana/grafana-bench/.github/actions/setup-grafana-bench@main + with: + version: 'v1.0.0' + + - name: Process with Grafana Bench + if: ${{ !cancelled() && secrets.PROMETHEUS_PUSHGATEWAY_URL != '' }} + continue-on-error: true + env: + PROMETHEUS_PUSHGATEWAY_URL: ${{ secrets.PROMETHEUS_PUSHGATEWAY_URL }} + run: | + grafana-bench test \ + --service ${{ format('grafana-{0}', github.event.repository.name) }} \ + --service-version ${{ github.sha }} \ + --test-runner trufflehog \ + --suite-name ${{ github.repository }}/trufflehog \ + --suite-path . \ + --trufflehog-json-file results.json \ + --prometheus-metrics \ + --prometheus-url "${PROMETHEUS_PUSHGATEWAY_URL}" \ + --run-stage ci + + - name: Check failure policy + env: + FAIL_ON_VERIFIED: ${{ inputs.fail-on-verified }} + FAIL_ON_UNVERIFIED: ${{ inputs.fail-on-unverified }} + VERIFIED_COUNT: ${{ steps.scan.outputs.verified }} + UNVERIFIED_COUNT: ${{ steps.scan.outputs.unverified }} + run: | + SHOULD_FAIL=false + if [[ "${FAIL_ON_VERIFIED}" == "true" && "${VERIFIED_COUNT}" != "0" ]]; then + SHOULD_FAIL=true + fi + if [[ "${FAIL_ON_UNVERIFIED}" == "true" && "${UNVERIFIED_COUNT}" != "0" ]]; then + SHOULD_FAIL=true + fi + + if [[ "${SHOULD_FAIL}" == "true" ]]; then + echo "Workflow failed due to secrets found (verified: ${VERIFIED_COUNT}, unverified: ${UNVERIFIED_COUNT})" + exit 1 + else + echo "No action needed - secrets within configured thresholds" + fi