Keys of different types can format to the same string (`1` and `1.0`), and
keeping either value would depend on map iteration order. `ToJSONValue` and
`ToJSONMap` now return an error on such a collision, which both
`MarshalJSON` methods propagate.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Template YAML decodes nested maps as `map[interface{}]interface{}`, which
the JSON encoder rejects, so a result whose `info.metadata` (or javascript
args carried in `meta`) held a map was dropped from `-j`/`-jsonl` output.
`model.Info` and `output.ResultEvent` now encode their metadata with
string-keyed maps.
Closes#7971
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add -max-request-body-inflight (default 256 MB, 0 disables it).
executeRequest acquires its body size from a shared budget before it
dumps and sends, and releases it when it returns, so concurrent
requests with multi-MB bodies queue instead of all holding their
copies at once. Bodies under 1 MB skip the budget, since the semaphore
is FIFO and a queued heavy request would stall them. Race requests
skip it too, because they must all reach the open gate together.
The HTTP timeout in ExecuteWithResults now starts once the budget
admits the request, so time spent queued does not time it out.
Since #7469 the output writer always masks Authorization, Proxy-Authorization,
Cookie, Set-Cookie and X-Api-Key, which leaves exported findings without the
data needed to reproduce them by hand. -no-redact turns the built-in list off;
keys given with -redact are still masked.
Templates that send multi-MB bodies (e.g. CVE-2026-109618 sends 1, 8 and
20 MB) kept 8-10 copies of each body alive per request. With 25 hosts in
flight this reached ~10 GB RSS and OOM-killed scan VMs.
- add pkg/utils/httpbody: a zero-copy request body that rewinds on EOF;
GetBody hands out new cursors over the same slice instead of cloning
- build raw, model and fuzzed request bodies with httpbody instead of
retryablehttp's ReusableReadCloser, which tees every read into a second
growing buffer and clones on each GetBody
- dump requests with http.Request.Write into a capped writer instead of
retryablehttp Dump/httputil.DumpRequestOut; output is unchanged for
bodies under the cap
- keep only the first 32 KiB of a body in request dumps (new
-rqss/-request-size-save flag, 0 keeps all) and leave bodies over 4 KiB
out of curl commands
- build the project cache key only when a project file is used, hashing
the body when the dump is truncated
- replay sends the full body when the dump was truncated
- slice the raw request body from the request instead of reading it out
- pass replacer values to fasttemplate as []byte so writes don't copy them
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add -pro, -payload-random-order to run a template's payload combinations
in a random order instead of the sequential order of the payload sets.
The iterator shuffles the combination indices [0,total) and decodes each
index back to a combination, so it visits every combination exactly once
for batteringram, pitchfork and clusterbomb, keeping pitchfork's
index-aligned pairs and the full clusterbomb product. Total() is
unchanged, so request estimates and progress stay correct. When the flag
is off the sequential path is used as before.
Adds -utc / -update-templates-channel (stable, latest). stable keeps the
current behaviour. latest installs the head of the nuclei-templates main
branch, so templates merged after the last release are available without
waiting for the next one.
The version for the latest channel is the last release plus the short
commit as semver build metadata (e.g. v10.5.0+3de629d). Build metadata is
ignored when comparing versions, so switching back to stable doesn't
downgrade, and only a newer release makes it outdated there. Since the
version check only knows about releases, the latest channel compares the
installed version against the current head to decide when to update.
The channel can also be set in the config file like the other options.
Closes#5608
Adds a CSV exporter under pkg/reporting/exporters/csv, wired into the
reporting module and exposed via -csv-export / -ce. It writes one
flattened row per result with a stable, spreadsheet/SIEM-friendly schema:
template-id, severity, host, matched-at, cve, cvss, timestamp.
Mirrors the existing exporter contract (New/Export/Close) and the flag
wiring pattern used by jsonl/pdf. All fields are written through
encoding/csv so adversarial values (commas/quotes/newlines in
attacker-influenced host/matched-at) cannot break out of their cell or
forge columns. Unit tests cover header+row, missing classification,
structural-injection escaping, and multi-CVE joining.
Signed-off-by: Devam Shah <[email protected]>
* refactor: adopt XDG storage and layered configs
Load XDG system configuration (and /etc/nuclei/config.yaml) before the
active user configuration, selected config, profile, and command line.
Preserve explicit command-line values, replace collections between
layers, and defer stateful option handling until the final values are
known. Keep callback flags and authentication setup limited to the CLI.
Move configuration loading, profile resolution, inline targets, and
inline secrets into internal/configuration. Keep NUCLEI_CONFIG_DIR as
a compatibility override for the user configuration directory.
Use platform directories for configuration, template data, persistent
state, and cache data. Discover default template roots through
XDG_DATA_HOME and XDG_DATA_DIRS without assigning ownership semantics
to the selected path.
Store template state in XDG_STATE_HOME and migrate the legacy
.templates-config.json file when needed. Persist the active template
path, keep custom provider templates below that root, and apply the
same installation, update, health-check, and reset behavior to every
root.
Keep .nuclei-ignore with the active template root. Derive its local
hash from the file and pass the remote hash through the update check
without persisting either value.
Move generated resume and crash-recovery files into persistent state
and use XDG_CACHE_HOME for the catalog index. Write template state and
cache snapshots atomically with private permissions.
Validate every reset target before deleting any path. Reject broad or
unsafe targets, preserve explicit resume paths, and remove generated
inline-secret directories on normal, interrupted, and fatal exits.
Isolate configuration, state, cache, and template paths in functional
and integration tests, and add coverage for layered precedence, state
migration, active-root lifecycle, reset safety, and private file modes.
Closes#6755
Signed-off-by: Dwi Siswanto <[email protected]>
* chore(cmd): use memProfile+ext instead
Signed-off-by: Dwi Siswanto <[email protected]>
* fix(lib): gate SDK version check
Signed-off-by: Dwi Siswanto <[email protected]>
---------
Signed-off-by: Dwi Siswanto <[email protected]>
* feat(filepath): add IsPathWithinAnyDirectory func
Signed-off-by: Dwi Siswanto <[email protected]>
* refactor: use path-aware filesystem containment checks
Several filesystem trust boundaries relied on
lexical prefix checks to decide whether a path
fell under an allowed directory. That let sibling
paths be treated as if they were children of
trusted directories.
Replace those checks with canonical path
containment checks for helper payload loading,
template archive extraction, custom template
metadata, template path classification, and
headless screenshot output validation.
Signed-off-by: Dwi Siswanto <[email protected]>
* adding more tests
* fixing tests
* adding test
---------
Signed-off-by: Dwi Siswanto <[email protected]>
Co-authored-by: Mzack9999 <[email protected]>
* fix(js): respect `allow-local-file-access` in `require`
The goja `require() `function used the default
host filesystem loader which let JavaScript
templates import any local files even when
`allow-local-file-access` was disabled.
Pooled runtimes kept `require()` state around so
a module loaded during a privileged execution
could remain cached for a later restricted one.
Rebuild the require registry per execution after
setting the execution context, and route file-
backed module loads to preserve native modules
while enforcing the same sandbox rules (as
`nuclei/fs`).
Signed-off-by: Dwi Siswanto <[email protected]>
* fix: cross-platform sandbox path checks
Replace lexical prefix checks in the template file
sandbox with a shared path containment helper that
canonicalizes both paths before comparing them to
prevent false rejections when the configured
templates directory and the resolved file path
differ only due to symlink expansion on macOS or
path normalization on Windows.
Apply the helper in `protocolstate.NormalizePath()`
and `Options.GetValidAbsPath()` so JS `require()`-
based module loads and helper file resolution use
the same rules.
Signed-off-by: Dwi Siswanto <[email protected]>
---------
Signed-off-by: Dwi Siswanto <[email protected]>
Resume files are runtime artifacts and SHOULD NOT
be presented as persistent config.
Update reset messaging to separate config vs cache
and explicitly include resume state under cache
cleanup. Also delete the cache dir during `-reset`
to keep behavior aligned with the new semantics.
Close#6792
Signed-off-by: Dwi Siswanto <[email protected]>