nuclei-dast

Installs and runs ProjectDiscovery Nuclei template-based HTTP scanning: selects templates via -t {path} and -tags/-severity filters, controls request rate with -rl, emits JSONL output via -j for cross-tool finding aggregation, authors custom YAML matchers for app-specific checks, and gates CI on severity thresholds. Use when the team runs Nuclei alongside ZAP for template-driven DAST coverage, needs fuzzing-style probes beyond ZAP passive scan, or wants to operationalize community CVE templates in a pipeline.

/plugin install qa-security-scanning@testland-qa

Overview

Per docs.projectdiscovery.io/tools/nuclei/overview (opens in new window):

"Nuclei is a fast and customisable vulnerability scanner powered by simple YAML-based templates."

Each template is a YAML file defining a request plus matchers that decide whether the response is a finding. The community library ships 6,500+ templates for CVEs, misconfigurations, exposed panels, and default credentials; custom templates add app-specific checks. Run it alongside ZAP - ZAP crawls for broad coverage, Nuclei adds deep template-driven CVE checks - and feed both into cross-tool triage.

When to use

  • The repo needs CVE-specific or misconfiguration checks beyond ZAP passive scan.
  • A CI pipeline should gate on critical/high template matches without active spider coverage.
  • The team maintains custom YAML templates for proprietary endpoints.
  • Nuclei output must be unified with ZAP findings via cross-tool aggregation.

Step 1 - Install

Per docs.projectdiscovery.io/tools/nuclei/install (opens in new window):

Go (recommended for CI):

go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

Requires the latest Go toolchain version.

Homebrew (macOS/Linux):

brew install nuclei

Docker:

docker pull projectdiscovery/nuclei:latest

Precompiled binary: download from github.com/projectdiscovery/nuclei/releases (opens in new window) and place on PATH.

After install, update the default template library:

nuclei -update-templates

Step 2 - First scan

Per docs.projectdiscovery.io/tools/nuclei/running (opens in new window):

nuclei -u https://staging.example.com -j -o nuclei-report.jsonl

-j writes JSONL output; -o names the file. Each line is one finding, ready for cross-tool aggregation. The default template set is applied; DOS-capable templates are excluded from the default run (see Step 6).

Step 3 - Common flags

Per nuclei-running (opens in new window). The flags used in most scans:

FlagDefaultUse
-u <url>(required)Single target URL or host
-t <path>community templatesTemplate file or directory
-tags <tag,...>-Run only templates with matching tags
-severity <level,...>-Filter by info, low, medium, high, critical
-rl <int>150Max requests per second
-j / -jsonloffJSONL output (feeds triager)
-o <file>stdoutOutput file path
-validateoffSyntax-check templates without running

Full flag reference (target lists, exclusions, concurrency, timeouts, SARIF, debug): references/flags.md.

Per nuclei-running (opens in new window), -rl caps request rate regardless of the -c and -bs concurrency settings.

Step 4 - Severity filtering and CI gating

Run only high and critical templates for a fast CI gate:

nuclei -u https://staging.example.com \
  -severity high,critical \
  -rl 50 \
  -j -o nuclei-critical.jsonl

Then count findings and gate:

count=$(wc -l < nuclei-critical.jsonl)
if [ "$count" -gt 0 ]; then
  echo "FAIL: $count critical/high findings" >&2
  exit 1
fi

For a softer gate (fail on critical only, warn on high):

nuclei -u https://staging.example.com -severity critical -j -o nuclei-crit.jsonl
nuclei -u https://staging.example.com -severity high    -j -o nuclei-high.jsonl

Run both, fail CI on any line in nuclei-crit.jsonl, log nuclei-high.jsonl as advisory.

Step 5 - Template selection

Per docs.projectdiscovery.io/templates/introduction (opens in new window) and docs.projectdiscovery.io/templates/structure (opens in new window):

Community templates are organized by tag. Common tags include cve, rce, sqli, xss, misconfig, exposure, default-login.

Run all CVE templates:

nuclei -u https://staging.example.com -tags cve -j -o nuclei-cves.jsonl

Run a specific template file:

nuclei -u https://staging.example.com -t nuclei-templates/cves/2024/CVE-2024-1234.yaml

Run all templates in a local directory:

nuclei -u https://staging.example.com -t ./custom-templates/ -j -o nuclei-custom.jsonl

List templates that would run without executing:

nuclei -tl -tags misconfig -severity high,critical

Step 6 - Custom YAML templates

Per template-struct (opens in new window), every template requires: a unique id, an info block, and at least one protocol request with matchers.

Minimal HTTP template:

id: custom-debug-endpoint

info:
  name: Debug Endpoint Exposed
  author: your-team
  severity: high
  description: "Detects an exposed /debug endpoint returning sensitive runtime info."
  tags: exposure,custom

http:
  - method: GET
    path:
      - "{{BaseURL}}/debug"
    matchers-condition: and
    matchers:
      - type: status
        status:
          - 200
      - type: word
        words:
          - "goroutine"
          - "heap"
        condition: or

Matcher types per template-intro (opens in new window):

TypeMatches against
wordResponse body or headers contain literal string(s)
regexResponse body matches a regular expression
statusHTTP response status code
dslBoolean expression using Nuclei DSL functions

Validate before running:

nuclei -t ./custom-templates/custom-debug-endpoint.yaml -validate

Step 7 - JSONL output for cross-tool triage

Each JSONL line represents one match. Key fields:

{
  "template-id": "cve-2024-1234",
  "info": { "name": "...", "severity": "high", "tags": ["cve"] },
  "host": "https://staging.example.com",
  "matched-at": "https://staging.example.com/vulnerable-path",
  "timestamp": "2026-06-04T12:00:00Z"
}

Feed the JSONL to cross-tool aggregation:

nuclei -u https://staging.example.com -j -o nuclei-report.jsonl
# aggregate nuclei-report.jsonl alongside zap-report.json

For SARIF output (GitHub Code Scanning):

nuclei -u https://staging.example.com -se nuclei.sarif

Step 8 - Rate limiting and responsible use

Per nuclei-running (opens in new window), the default rate is 150 req/s. Nuclei generates several thousand requests when the full template set runs against a single target. Per the Nuclei FAQ (opens in new window):

"After detecting a security issue we always recommend that you validate it a second time before reporting it." Use -debug to confirm the matcher fired against the expected response content.

DOS-capable templates are tagged and excluded from default scans. To run them explicitly (staging only, never production):

nuclei -u https://staging.example.com -tags dos -rl 5

Default-safe limits for shared infrastructure:

nuclei -u https://staging.example.com -rl 30 -c 10 -bs 10

Only scan targets you are authorized to test. Nuclei is an active probe tool; running it against a target without authorization may violate computer abuse laws.

Step 9 - CI integration

Install Nuclei, update templates, run a severity-gated scan, then fail the job on any finding:

go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
nuclei -update-templates
nuclei -u "$STAGING_URL" -severity high,critical -rl 50 -j -o nuclei-report.jsonl
[ "$(wc -l < nuclei-report.jsonl)" -eq 0 ]

Full GitHub Actions workflow (checkout, artifact upload, SARIF upload to Code Scanning): references/ci-integration.md.

Anti-patterns

Anti-patternWhy it failsFix
Run all templates against productionActive probes may trigger WAF blocks, corrupt data, or run DOS-tagged templatesStaging only; use -etags dos on shared envs
Skip -j / -jsonl outputFindings are stdout-only; can't feed the finding-triage stepAlways pass -j -o <file> (Step 7)
Skip -rl in CI on shared infraDefault 150 req/s spikes load on shared stagingSet -rl 30 or lower (Step 8)
Run -validate only, skip a real scanSyntax passes but matchers may produce no outputAlways run a real scan against a known-vulnerable target to confirm match
Suppress findings without a tracked justificationInvisible risk debtUse an IGNORE list with date + ticket, same pattern as ZAP config TSV (see zap-baseline Step 6)
Treat info severity as noiseinfo templates surface exposed panels, paths, and tech stack - useful for reconnaissance hardeningRoute info findings to finding triage, not direct suppression

Limitations

  • Nuclei is active: every template sends at least one HTTP request. It is not safe to run the full template set against production.
  • Template coverage is only as current as the last nuclei -update-templates; pin template versions in CI to avoid scan variance between runs.
  • Matchers are per-template; a misconfigured custom template silently returns no findings. Always -validate and smoke-test against a known-vulnerable endpoint before trusting zero-finding output.
  • Nuclei does not spider the target; it probes specific paths defined in templates. Use alongside ZAP for crawl-based coverage.
  • -rl controls outbound rate but not the total request count; large template sets against a slow target may run for tens of minutes.

References

Reference

Nuclei CI integration

View source (opens in a new tab)

Full GitHub Actions workflow for nuclei-dast Step 9. Runs a severity-gated scan on staging, fails the job on any finding, and uploads the report as an artifact.

jobs:
  nuclei-dast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Install Nuclei
        run: go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

      - name: Update templates
        run: nuclei -update-templates

      - name: Run Nuclei scan
        run: |
          nuclei \
            -u ${{ vars.STAGING_URL }} \
            -severity high,critical \
            -rl 50 \
            -j -o nuclei-report.jsonl

      - name: Gate on findings
        run: |
          count=$(wc -l < nuclei-report.jsonl 2>/dev/null || echo 0)
          echo "Nuclei findings: $count"
          [ "$count" -eq 0 ]

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: nuclei-report
          path: nuclei-report.jsonl

SARIF upload to Code Scanning

Use -se nuclei.sarif and the github/codeql-action/upload-sarif action to surface findings inline in pull request diffs.

Reference

Nuclei flag reference

View source (opens in a new tab)

Full CLI flag reference for nuclei-dast Step 3. Source: https://docs.projectdiscovery.io/tools/nuclei/running

FlagDefaultUse
-u <url>(required)Single target URL or host
-l <file>-File of targets, one per line
-t <path>community templatesTemplate file or directory
-tags <tag,...>-Run only templates with matching tags
-severity <level,...>-Filter by info, low, medium, high, critical
-etags <tag,...>-Exclude templates by tag
-exclude-severity <level,...>-Skip severity levels
-rl <int>150Max requests per second
-c <int>25Parallel templates to execute
-bs <int>25Hosts processed per template
-timeout <int>10Request timeout in seconds
-retries <int>1Retries on failure
-j / -jsonloffJSONL output (feeds triager)
-o <file>stdoutOutput file path
-se <file>-SARIF export (GitHub Code Scanning)
-statsoffShow live scan statistics
-validateoffSyntax-check templates without running
-debugoffPrint all requests and responses

-rl caps request rate regardless of the -c and -bs concurrency settings: the request rate cannot exceed the value set by -rl.