Flow icon
Commands icon
Script icon
Log icon
If icon
Pause icon
Set icon
Fail icon

Nuclei Post-Deploy Security Gate with Sign-Off and Accepted-Risk Baseline

Scan each staging deploy with Nuclei, ignore accepted risks via a KV baseline, and pause the release for security sign-off on new findings.

Categories
Infrastructure

Dynamic scanners find what code review misses: a debug endpoint left exposed, missing security headers, a default login, a known CVE in a component. Run once, though, a scanner buries the team in the same findings every time, and nobody owns the decision to ship anyway. This blueprint runs ProjectDiscovery Nuclei against every staging deploy, separates new findings from the ones already accepted, and turns new findings into an explicit security sign-off with Kestra's Pause task: accept and the release continues with the baseline updated, reject (or let it time out) and the release fails.

How it works

  1. after_staging_deploy (io.kestra.plugin.core.trigger.Flow) starts the gate when deploy_staging succeeds. It ships disabled: true.
  2. scan (io.kestra.plugin.scripts.shell.Commands) runs the official projectdiscovery/nuclei:v3.11.1 image with the chosen tags, rate-limited, writing one JSON line per finding. The exit code is captured so findings.jsonl is always kept.
  3. compare_with_baseline (io.kestra.plugin.scripts.python.Script) fingerprints every finding (template, matcher, matched URL), reads the accepted fingerprints from nuclei_baseline_<app> with kv(), and counts the new findings and the new ones at the block_on severities.
  4. log_findings logs the totals and each new finding, most severe first.
  5. gate (io.kestra.plugin.core.flow.If): with no new blocking findings it logs a clear result. Otherwise security_signoff (io.kestra.plugin.core.flow.Pause) waits up to 24 hours with two resume inputs, accept and reason. Accepting runs accept_into_baseline (io.kestra.plugin.core.kv.Set) so the same findings never block again; rejecting runs block_release (io.kestra.plugin.core.execution.Fail). An unanswered pause fails the release (deny by default).

What you get

  • A security scan on every staging deploy with no manual step.
  • Only new findings block; accepted risks are remembered per application in the KV store.
  • A recorded human decision (who resumed, accept or reject, and why) in the execution history for every release that needed one.
  • The raw findings.jsonl and the new-finding count as flow outputs.

Who it's for

  • Application security and platform teams that own the release gates.
  • Engineering teams that want security checks between staging and production without blocking on known, accepted risks.

Why orchestrate this with Kestra

A scanner in a CI job either fails every build or gets ignored. Kestra links the scan to the real deploy through a Flow trigger, keeps the accepted-risk state in the KV store, and uses Pause with resume inputs for a real, auditable approval step with a timeout, so the release waits for a decision instead of a log nobody reads.

Prerequisites

  • A staging or preview environment you own and are authorized to scan, reachable from the worker.
  • A Kestra worker that can run containers (Docker or Kubernetes task runner) and pull projectdiscovery/nuclei and python:3.12-slim. Nuclei downloads its community templates on the first run.

Secrets

  • None for a public staging URL. For an authenticated app, add a header with -H in scan from a secret, for example a bearer token kept as STAGING_SCAN_TOKEN.

Quick start

  1. Set target_url, app and the tags you want, and keep block_on at critical,high,medium to start.
  2. Execute the flow. If new blocking findings appear, open the paused execution, review log_findings and resume with accept true or false and a reason.
  3. Execute again: accepted findings no longer block, only new ones do.
  4. Point after_staging_deploy at your deploy flow and enable it.

How to extend

  • Notify the security channel from the then branch before the pause, with a link to the execution.
  • Run your own custom templates next to the community ones for app-specific checks.
  • Expire accepted fingerprints after 90 days by storing a timestamp with each entry.
  • Open a ticket for every accepted finding with the issue-tracker plugins.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.