CVE-2026-41646 Overview
CVE-2026-41646 is an access control vulnerability [CWE-284] in Nuclei, the YAML-based vulnerability scanner from ProjectDiscovery. The flaw exists in the JavaScript protocol runtime and affects versions 3.0.0 through versions before 3.8.0. JavaScript templates can read local .js and .json files through the require() function, bypassing the allow-local-file-access restriction that Nuclei enforces by default.
An attacker who supplies a malicious template can read sensitive files from the host running the scanner. The vulnerability requires local execution context and user interaction to load the crafted template. ProjectDiscovery patched the issue in version 3.8.0.
Critical Impact
Malicious Nuclei templates can read arbitrary .js and .json files on the scanning host, exposing configuration data, credentials, and source code despite default file access protections.
Affected Products
- ProjectDiscovery Nuclei version 3.0.0 and later
- ProjectDiscovery Nuclei versions prior to 3.8.0
- Nuclei Go module distributions with JavaScript protocol support
Discovery Timeline
- 2026-05-08 - CVE-2026-41646 published to the National Vulnerability Database (NVD)
- 2026-05-08 - Last updated in NVD database
Technical Details for CVE-2026-41646
Vulnerability Analysis
Nuclei supports JavaScript-based templates executed by an embedded Goja runtime. The runtime exposes a require() function to load native modules and helper scripts. Nuclei enforces an allow-local-file-access flag that gates whether templates can read files from the local filesystem.
The require() implementation did not consult this flag when resolving module paths. A template author could call require() against arbitrary .js or .json paths and receive the file contents back in the runtime. This permitted indirect file disclosure even when operators explicitly disabled local file access.
Impact is limited to confidentiality of files matching the JavaScript module loader's accepted extensions. Integrity and availability of the scanning host are not directly affected.
Root Cause
The shared require.Registry instance in pkg/js/compiler/pool.go was initialized once at startup without binding it to the per-execution file access policy. Because the registry persisted across runs and ignored the protocol state, no enforcement layer rejected paths that pointed outside the permitted directories. The fix introduces filepath validation through a new filepathutil helper inside pkg/protocols/common/protocolstate/file.go.
Attack Vector
Exploitation requires an operator to execute a malicious template under their Nuclei installation. The attacker delivers the template through a public template repository, a pull request to a community catalog, or social engineering. Once loaded, the template calls require() with a target path and exfiltrates the returned content using Nuclei's existing HTTP or DNS protocols.
// Patch excerpt: pkg/js/compiler/pool.go
// Removes the package-level shared registry that ignored file access policy
var (
- r *require.Registry
lazyRegistryInit = sync.OnceFunc(func() {
- r = new(require.Registry) // this can be shared by multiple runtimes
// autoregister console node module with default printer it uses gologger backend
require.RegisterNativeModule(console.ModuleName, console.RequireWithPrinter(goconsole.NewGoConsolePrinter()))
})
Source: ProjectDiscovery Nuclei commit 6f2ade6
// Patch excerpt: pkg/protocols/common/protocolstate/file.go
// Adds filepathutil for proper path validation against allow-local-file-access
package protocolstate
import (
- "strings"
-
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
+ filepathutil "github.com/projectdiscovery/nuclei/v3/pkg/utils/filepath"
"github.com/projectdiscovery/utils/errkit"
fileutil "github.com/projectdiscovery/utils/file"
mapsutil "github.com/projectdiscovery/utils/maps"
Source: ProjectDiscovery Nuclei commit 6f2ade6
Detection Methods for CVE-2026-41646
Indicators of Compromise
- Nuclei templates containing require() calls referencing absolute paths or paths outside the template directory
- Untrusted YAML templates with embedded JavaScript blocks loading .js or .json files unrelated to scan logic
- Outbound HTTP or DNS requests from Nuclei processes containing base64 or hex-encoded payloads matching local file contents
Detection Strategies
- Audit all template sources and pin scanner installations to Nuclei 3.8.0 or later before execution
- Static analysis of community templates for suspicious require() arguments referencing system paths such as /etc, ~/.aws, or ~/.ssh
- Process monitoring on hosts that run Nuclei to detect file reads outside the template workspace by the nuclei binary
Monitoring Recommendations
- Log Nuclei command-line invocations and capture the -t template flag and -allow-local-file-access state for review
- Forward host telemetry from CI/CD runners and security tooling hosts that execute Nuclei into a centralized data lake for correlation
- Alert on Nuclei processes reading files matching credential patterns or configuration extensions outside expected paths
How to Mitigate CVE-2026-41646
Immediate Actions Required
- Upgrade Nuclei to version 3.8.0 or later on all hosts, build agents, and container images
- Inventory and remove untrusted custom or third-party templates from local template directories
- Restrict execution of Nuclei to dedicated service accounts with minimum filesystem privileges
Patch Information
ProjectDiscovery published the fix in pull request #7332 and commit 6f2ade6a9b427c284c15a43445f9c7f055e60e5d. Coordination details are available in GitHub Security Advisory GHSA-29rg-wmcw-hpf4. Upgrade to Nuclei 3.8.0 to apply the corrected require() handler that honors allow-local-file-access.
Workarounds
- Run Nuclei inside an ephemeral container or chroot that exposes only template files and scan inputs
- Execute scans under an unprivileged user with no access to credential stores, SSH keys, or cloud configuration files
- Disable use of JavaScript protocol templates from external sources until upgrades complete
# Verify and pin Nuclei to the patched release
nuclei -version
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@v3.8.0
# Run scans with reduced filesystem exposure
docker run --rm \
--read-only \
--user 1000:1000 \
-v "$(pwd)/templates:/templates:ro" \
projectdiscovery/nuclei:v3.8.0 \
-t /templates -u https://target.example.com
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


