Skip to content

[BUG] SDK: .nuclei-ignore is read before the installer creates it, so the first engine runs with an empty deny-list #7704

Description

@G360-Niek

Is there an existing issue for this?

  • I have searched the existing issues.

Current Behavior

init() reads .nuclei-ignore before the installer writes it, in the same function.

The only read that feeds template filtering is lib/sdk_private.go:140, inside applyRequiredDefaults:

e.opts.ExcludeTags = append(e.opts.ExcludeTags, config.ReadIgnoreFile().Tags...)

applyRequiredDefaults has exactly one caller — init() line 199. Later in that same init(), the installer creates/updates the file:

line 349:  latestIgnoreHash, _ := installer.NucleiSDKVersionCheck()
line 379:  err = tm.UpdateIfOutdated()      // writes templates + .nuclei-ignore

init() spans 146–358 on v3.11.1 (read 188, writes 338/360), so the read always precedes the write. Consequence: on any host where .nuclei-ignore does not already exist, the first engine constructed reads nothing and runs with an empty ExcludeTags. It executes the templates the deny-list exists to block — dos, bruteforce, fuzz, local, txt-service.

ExcludeTags is frozen per engine and never refreshed, so the file appearing seconds later has no effect on that engine. Every subsequent engine re-reads the file, so it self-heals after the first — which is why it is easy to miss.

The blast radius is larger than "one engine" for SDK consumers that construct one engine per protocol group and reuse it across a scan: a single leaking engine can cover every HTTP template for that whole scan.

Expected Behavior

The deny-list should be in force for the first engine too. Either read the ignore file after the installer has had a chance to create/update it, or ensure it exists before reading. The installer already knows how to write it.

Steps To Reproduce

Controlled A/B on one host, same binary, same config, same targets — the only variable is whether .nuclei-ignore existed at engine construction.

  1. Start from a container/host with no nuclei config dir (fresh writable layer, or rm -rf ~/.config/nuclei ~/nuclei-templates), with updateTemplates enabled so the SDK installs templates itself.
  2. Run a scan that constructs a thread-safe engine and executes HTTP templates against a target that trips a fuzz-tagged template (we used waf-fuzz, tags: waf,fuzz,fuzzing,vuln).
  3. Observe: .nuclei-ignore is written during that first engine's init(), after the read.
  4. Run the same scan again, now that the file exists.

Timeline from our run:

12:12:23Z  container started (fresh writable layer, no nuclei config dir)
12:14:30Z  scan 1 starts; engine 1 init -> applyRequiredDefaults reads a MISSING .nuclei-ignore
12:15:12Z  same init, later: installer writes .nuclei-ignore
12:15:24Z  installer writes nuclei-templates
12:41:41Z  waf-fuzz (fuzz-tagged) fires and is persisted -- still engine 1
14:12:57Z  scan 2 starts, ignore file now present

Relevant log output

# scan 1 (.nuclei-ignore absent at engine construction)
fuzz-tagged templates detected = 8     across 4 hosts
total detections               = 13

# scan 2 (.nuclei-ignore present), same binary/config/targets
fuzz-tagged templates detected = 0
total detections               = 5
# all 4 of the hosts that tripped waf-fuzz in scan 1 still produced
# non-deny-listed detections in scan 2, so they were scanned, not skipped

Environment

  • OS: Linux (containerised deployment; also applies to any host without a pre-existing ignore file)
  • Nuclei: v3.11.1 used as a library; ordering also present on dev @ a34f810f
  • Go: go1.26.0

Anything else?

Environments that pre-provision .nuclei-ignore out-of-band (e.g. a shared volume populated before the process starts) never see this, which is why it can hide for a long time. It surfaces on any freshly provisioned host, and on containerised deployments it recurs on every redeploy that resets the filesystem — not just the first install.

Happy to open a PR. The smallest fix is to move the ignore-file merge in applyRequiredDefaults to after the installer runs, or to re-apply it once the installer has completed.

Related, same area, both already filed: #7695 (per-scan WithTemplateFilters clearing the deny-list, PR #7698) and #7696 (the files: section never applied in library mode, PR #7697). This one is independent of both — it is about when the file is read rather than what is read from it or what later overwrites it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions