Skip to content

Security scanning (Gitleaks + Trivy + DefectDojo)

Automated checks on every push and pull request in Woodpecker CI. Failed scans block merges.

What runs in CI

Step Tool Detects
gitleaks Gitleaks API keys, passwords, tokens in code and git history
trivy-fs Trivy HIGH/CRITICAL CVEs in dependencies, Dockerfile / compose misconfig
trivy-image Trivy Container image CVEs (full-stack apps on main only)
upload-defectdojo DefectDojo API Sends JSON reports to the dashboard
security-gate Fails pipeline if any scan found issues

Reading a red security-gate

The gate fails closed on two different problems, and its output says which:

Marker Message says Meaning What to do
.gitleaks_fail / .trivy_fail "found …" A real finding Fix it, or allowlist a false positive in scripts/gitleaks.toml (needs PR review)
.gitleaks_error / .trivy_error "SCANNER FAILURE, not a finding" The scanner never ran Restart the pipeline. Usually Trivy's vulnerability DB failing to download; it already retries 3×.

A scanner that could not run proves nothing, so it still blocks the deploy — but do not go looking for a CVE that was never reported. The two are told apart by whether the step produced a report at all, not by its exit code, since both cases exit non-zero. On a scanner error the DefectDojo upload is skipped as well: reimporting a placeholder report with close_old_findings=true would close every real finding from the last good scan.

Dashboard

Tool URL
DefectDojo https://security.rafaelgonzalezalbes.com

Centralized on infra VPS — pipelines on the apps Woodpecker agent (location: apps-vps) upload to the same security.* instance. Do not deploy DefectDojo on the apps VPS.

One Product per Forgejo repo, one CI engagement per product. Findings dedupe on reimport.

Bootstrap all repos:

bash scripts/bootstrap-all-defectdojo-engagements.sh
# or on VPS when API token fails:
bash scripts/wire-defectdojo-woodpecker-sql.sh

scripts/upload-defectdojo.sh resolves product_name + engagement_name from defectdojo_engagement_id before calling /api/v2/reimport-scan/ (DefectDojo 2.40 rejects engagement id alone with product_name parameter missing). It also sets close_old_findings=true so fixed issues (e.g. Trivy DS002) are mitigated on the next successful upload.

Setup (platform admin)

1. Deploy DefectDojo

# Generate passwords and encrypt (or: sops edit secrets/defectdojo.enc.env manually)
bash scripts/setup-defectdojo-secrets.sh
# Or encrypt template only: bash scripts/secrets-init.sh --encrypt-only

git add secrets/defectdojo.enc.env
git push   # Woodpecker deploys stack including defectdojo/

DNS: add A record security → VPS (85.215.32.166).

2. Bootstrap repo engagements

# local/.env: DEFECTDOJO_API_KEY=...
bash scripts/rollout-security-scanning.sh

Or per repo:

bash scripts/bootstrap-defectdojo-engagement.sh --owner alex --repo wearlab-main
bash scripts/bootstrap-defectdojo-engagement.sh --owner alex --repo wearlab-main

onboard-project.sh does this automatically for new projects.

3. Woodpecker secrets (per repo)

Secret Value
defectdojo_url https://security.rafaelgonzalezalbes.com
defectdojo_api_key DefectDojo API token
defectdojo_engagement_id From bootstrap script
security_webhook_url http://kuma-discord-bot.internal:8787/webhook/ci-security
security_webhook_secret Same as SECURITY_WEBHOOK_SECRET on the Discord bot

Wire all repos:

bash scripts/wire-defectdojo-woodpecker.sh

4. App repo files

Each repo needs:

  • Security steps in .woodpecker.yml (see templates/woodpecker-security.yml)
  • scripts/gitleaks.toml — allowlist for false positives
  • scripts/upload-defectdojo.sh — copied by onboard-project.sh
  • scripts/notify-discord-security.sh — posts to Discord #security when security-gate fails

Discord #security alerts

The Kuma Discord bot (uptime-kuma/discord-bot/) routes three alert types to #security:

Source Trigger Delivery
DefectDojo scan_added with new High/Critical findings HTTP POST /webhook/defectdojo
Woodpecker security-gate step fails notify-discord-security step → POST /webhook/ci-security
Uptime Kuma DefectDojo monitor down/up Socket.IO (monitor name matches DefectDojo\|Security)

Bot env (secrets/plain/kuma-discord-bot.env)

Variable Purpose
DISCORD_CHANNEL_SECURITY Discord channel ID for #security
SECURITY_WEBHOOK_SECRET Bearer token for inbound webhooks
WEBHOOK_PORT Default 8787 (internal devops network)

Generate webhook secret: openssl rand -hex 32

DefectDojo webhook (after bot deploy)

bash scripts/setup-defectdojo-discord-webhook.sh

Or manually: Administration → Notification Webhooks → URL http://kuma-discord-bot.internal:8787/webhook/defectdojo, events scan_added, header Authorization: Bearer <SECRET>.

Developer workflow

  1. Push branch → Woodpecker runs security steps before build/test
  2. If Gitleaks fails: remove the secret, rotate the credential, rewrite history if needed — see scripts/rotate-exposed-secrets.md in the infra-devops repo
  3. If Trivy fails: upgrade the vulnerable dependency or pin a patched version
  4. Open DefectDojo engagement for the repo to track remediation

False positives (allowlist)

Edit scripts/gitleaks.toml in the repo:

[allowlist]
paths = ['''(?i)fixtures/''']
regexes = ['''REPLACE_ME''']

Require PR review for allowlist changes — do not blanket-ignore *.env.

infra/devops specifics

  • secrets/*.enc.env (SOPS ciphertext) is allowlisted in scripts/gitleaks.toml
  • Never commit secrets/plain/, local/.env, or secrets/.age/

Verify

  1. Create a test branch with a fake secret → Gitleaks must fail CI
  2. Check DefectDojo → Product → CI engagement for the finding
  3. Remove secret → pipeline green