Repository: projectdiscovery/nuclei

Issue:
[BUG] Template context confusion when using the flow protocol while mass-scanning
### Is there an existing issue for this?

- [x] I have searched the existing issues.

### Current Behavior

I've written a simple nuclei template that (for the sake of this example) looks for Readme files in a WordPress' website plugins/themes locations. The steps are the following:
1. Issue an http request to the wesite's webroot and extract script/style/img imports using xpath
2. Clean the imports to only include the URL up to the plugin's or theme's root
3. For each theme/plugin root, issue an http request to look for the files `README.md` or `README.txt`

The template is as follows:
```yaml
id: wordpress_readme

info:
  name: WordPress readme detection
  author: nisay759
  severity: info
  tags: readme,wordpress,themes,plugins

flow: |
  // get imports from website's root
  http(1);

  let uniq = Dedupe();

  // get unique webroot of each import
  for (let path of iterate(template["imports"])) {
    let pathArray = path.split("/");
    let rootIdx = pathArray.indexOf("wp-content") + 3;
    let finalUrl = pathArray.slice(0, rootIdx).join("/");
    uniq.Add(finalUrl);
  }

  // for each import, look for readme
  for (let url of iterate(uniq.Values())) {
    set("target", url);
    http(2);
  }

http:
  # http(1) - Query web root and extract themes/plugins imports
  - method: GET
    redirects: true
    path:
      - "{{BaseURL}}"

    extractors:
      - type: xpath
        name: "imports"
        internal: true
        xpath:
          - "/html/*/script/@src[contains(.,'wp-content/plugins/')]"
          - "/html/*/script/@src[contains(.,'wp-content/themes/')]"
          - "/html/*/style/@src[contains(.,'wp-content/themes/')]"
          - "/html/*/img/@src[contains(.,'wp-content/themes/')]"

  # http(2) - Given a theme/plugin "target", look for Readme file
  - method: GET
    path:
      - "{{target}}/README.md"
      - "{{target}}/README.txt"

    stop-at-first-match: true
    matchers:
      - type: status
        status:
          - 200
```
The template runs as expected when running against a single host. I ran it against [https://wordcamp.org/](https://wordcamp.org/) since they have a public bug bounty program:

```
../nuclei -proxy http://127.0.0.1:8080 -t wordpress_readme.yaml -u https://wordcamp.org        
                                                                                                                          
                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.4

		projectdiscovery.io

[INF] Current nuclei version: v3.4.4 (unknown) - remove '-duc' flag to enable update checks
[INF] Current nuclei-templates version: v9.8.9 (unknown) - remove '-duc' flag to enable update checks
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 0
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 1
[wordpress_readme] [http] [info] https://central.wordcamp.org/wp-content/plugins/gutenberg/README.md
[INF] Scan completed in 3.601335048s. 1 matches found.
```

But when I run the template against a list of targets that contains the same previous host, I get no results. The list size seems to matter, and for a few hosts the template works just fine. Over 30 hosts, the results start becoming inconsistent.

For example, let's consider the following list that's comprised of Alexa's Top 29 hosts + our previous host, making it 30 targets:

```
https://google.com
https://youtube.com
https://facebook.com
https://baidu.com
https://wikipedia.org
https://qq.com
https://taobao.com
https://yahoo.com
https://tmall.com
https://amazon.com
https://twitter.com
https://sohu.com
https://live.com
https://jd.com
https://vk.com
https://instagram.com
https://sina.com.cn
https://weibo.com
https://reddit.com
https://login.tmall.com
https://360.cn
https://yandex.ru
https://linkedin.com
https://blogspot.com
https://netflix.com
https://twitch.tv
https://whatsapp.com
https://yahoo.co.jp
https://csdn.net
https://wordcamp.org
```
Running nuclei against this list yields no results :

```
../nuclei -proxy http://127.0.0.1:8080 -t wordpress_readme.yaml -l list.txt                         

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.4

		projectdiscovery.io

[INF] Current nuclei version: v3.4.4 (unknown) - remove '-duc' flag to enable update checks
[INF] Current nuclei-templates version: v9.8.9 (unknown) - remove '-duc' flag to enable update checks
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 0
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 30
[INF] Scan completed in 2.540299977s. No results found.
```

The position of the target in the list file doesn't seem to change the outcome.

I have noticed one curious behaviour though: Since https://wordcamp.org redirects to https://central.wordcamp.org, using the latter URL yields the expected result (maybe some internal alphabetical sorting of hosts ?).

I've digged through the documentation, and found the following mention in [https://docs.projectdiscovery.io/tools/nuclei/mass-scanning-cli](https://docs.projectdiscovery.io/tools/nuclei/mass-scanning-cli):

```
(Note: Nuclei by default reuses javascript runtimes to avoid overhead of creating new runtimes for each request)
```
Which made me think that maybe the `uniq` variable gets overwritten with an empty `Dedupe` instance when other hosts are scanned, since they all share the same runtime. With this assumption in mind, I edited the template as following:

```yaml
id: wordpress_readme

info:
  name: WordPress readme detection
  author: nisay759
  severity: info
  tags: readme,wordpress,themes,plugins

flow: |
  // get imports from website's root
  http(1);

  template["{{BaseURL}}_results"] = Dedupe();

  // get unique webroot of each import
  for (let path of iterate(template["imports"])) {
    let pathArray = path.split("/");
    let rootIdx = pathArray.indexOf("wp-content") + 3;
    let finalUrl = pathArray.slice(0, rootIdx).join("/");
    template["{{BaseURL}}_results"].Add(finalUrl);
  }

  // for each import, look for readme
  for (let url of iterate(template["{{BaseURL}}_results"].Values())) {
    set("target", url);
    http(2);
  }

http:
  # http(1) - Query web root and extract themes/plugins imports
  - method: GET
    redirects: true
    path:
      - "{{BaseURL}}"

    extractors:
      - type: xpath
        name: "imports"
        internal: true
        xpath:
          - "/html/*/script/@src[contains(.,'wp-content/plugins/')]"
          - "/html/*/script/@src[contains(.,'wp-content/themes/')]"
          - "/html/*/style/@src[contains(.,'wp-content/themes/')]"
          - "/html/*/img/@src[contains(.,'wp-content/themes/')]"

  # http(2) - Given a theme/plugin "target", look for Readme file
  - method: GET
    path:
      - "{{target}}/README.md"
      - "{{target}}/README.txt"

    stop-at-first-match: true
    matchers:
      - type: status
        status:
          - 200
```
In this updated version of the template, `let uniq = Dedupe()` becomes `template["{{BaseURL}}_results"] = Dedupe()`, so that each host's results are stored in a unique key of the `template` map.

With this change, the template seems to work as expected:

```
../nuclei -proxy http://127.0.0.1:8080 -t wordpress_readme_2.yaml -l list.txt                       

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.4

		projectdiscovery.io

[INF] Current nuclei version: v3.4.4 (unknown) - remove '-duc' flag to enable update checks
[INF] Current nuclei-templates version: v9.8.9 (unknown) - remove '-duc' flag to enable update checks
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 0
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 30
[wordpress_readme] [http] [info] https://central.wordcamp.org/wp-content/plugins/gutenberg/README.md
[INF] Scan completed in 13.148207118s. 1 matches found.
```
Notes:
- In the examples above, I'm running the template through a proxy (to inspect the requests that are issued by nuclei). The behavior is the same regardless of the type of proxy used, or whether a proxy is used at all. I omitted those cases for the sake of brevity.
- The issue arises when scanning in `template-spray` mode. The `host-spray` mode yields the expected results, but it's kind of counterproductive using this mode when running a single template.

### Expected Behavior

Variables defined whithin a `flow` block should be scoped to the current host being scanned.

If a fix is not possible, the documentation should be updated to mention this kind of edge-case.

### Steps To Reproduce

Steps to reproduce are described in the previous sections.

### Relevant log output

```shell

```

### Environment

```markdown
- OS: Arch Linux
- Nuclei: v3.4.4 (from GitHub releases)
- Nuclei's `config.yaml` content:


scan-strategy: "template-spray"
exclude-tags: []
disable-update-check: true
disable-unsigned-templates: false
header:
  - User-Agent: "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
```

### Anything else?

_No response_

Hints from issue comments:
@dwisiswant0: Very well-written issue. Thanks for providing that info! We'll look into it and investigate further.@ehsandeep: @dwisiswant0 the acutal error that might lead to root cause, this get displayed as warning while running with `-v` option.
```
 <- SyntaxError: Identifier 'uniq' has already been declared at <eval>:1:1(0)
```@ehsandeep: @nisay759 this is being fixed in https://github.com/projectdiscovery/nuclei/pull/6282@nisay759: @ehsandeep Thanks for the heads up.

I tried to test with the fix that was just merged into the `dev` branch, but I still have the same issue when running the template through a socks proxy. The problem seems to be fixed when running nuclei without a proxy, or when proxying through an http proxy (which in turn proxies to the same socks proxy).

Here's my build process. If I missed a step or did something wrong please let me know

1. Get the latest changes from the git repo and make sure I have the fix in the local `dev` branch
```
➜  nuclei git:(dev) git pull
Already up to date.

➜  nuclei git:(dev) git log HEAD^..HEAD
commit 2b729e40373cc81c13ee104844b3839bbe96c559 (HEAD -> dev, origin/dev, origin/HEAD)
Author: Tarun Koyalwar <45962551+tarunKoyalwar@users.noreply.github.com>
Date:   Mon Jun 30 15:13:00 2025 +0530

    fix context leak in flow (#6282)
    
    * fix context leak in flow
    
    * handle sizedwaitpool when not reused
```
2. Build nuclei using Dockerfile
```
docker buildx build . -t nuclei:test
```
3. Start a container with the new image
```
docker run --rm -it --entrypoint /bin/sh -v $PWD/list.txt:/root/list.txt -v $PWD/wordpress_readme.yaml:/root/wordpress_readme.yaml nuclei:test
/ # cd /root/
~ # ls
list.txt               wordpress_readme.yaml
```

Running the template directly shows no problem, as expected:

```
~ # nuclei -t wordpress_readme.yaml -l list.txt -v

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.5

		projectdiscovery.io

[VER] Started metrics server at localhost:9092
[INF] Current nuclei version: v3.4.5 (latest)
[INF] Current nuclei-templates version: v10.2.3 (latest)
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 105
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 30
[VER] [wordpress_readme] Sent HTTP request to https://twitch.tv
[VER] [wordpress_readme] Sent HTTP request to https://wikipedia.org
[VER] [wordpress_readme] Sent HTTP request to https://google.com
[VER] [wordpress_readme] Sent HTTP request to https://reddit.com
[VER] [wordpress_readme] Sent HTTP request to https://whatsapp.com
[VER] [wordpress_readme] Sent HTTP request to https://live.com
[VER] [wordpress_readme] Sent HTTP request to https://twitter.com
[VER] [wordpress_readme] Sent HTTP request to https://blogspot.com
[VER] [wordpress_readme] Sent HTTP request to https://instagram.com
[VER] [wordpress_readme] Sent HTTP request to https://vk.com
[VER] [wordpress_readme] Sent HTTP request to https://facebook.com
[VER] [wordpress_readme] Sent HTTP request to https://amazon.com
[VER] [wordpress_readme] Sent HTTP request to https://youtube.com
[VER] [wordpress_readme] Sent HTTP request to https://linkedin.com
[VER] [wordpress_readme] Sent HTTP request to https://wordcamp.org
[VER] [wordpress_readme] Sent HTTP request to https://login.tmall.com
[VER] [wordpress_readme] Sent HTTP request to https://netflix.com
[VER] [wordpress_readme] Sent HTTP request to https://yahoo.com
[VER] [wordpress_readme] Sent HTTP request to https://yandex.ru
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/gutenberg/README.md
[wordpress_readme] [http] [info] https://central.wordcamp.org/wp-content/plugins/gutenberg/README.md
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/tagregator/README.md
[VER] [wordpress_readme] Sent HTTP request to https://qq.com
[VER] [wordpress_readme] Sent HTTP request to https://baidu.com
[VER] [wordpress_readme] Sent HTTP request to https://jd.com
[VER] [wordpress_readme] Sent HTTP request to https://sohu.com
[VER] [wordpress_readme] Sent HTTP request to https://360.cn
[VER] [wordpress_readme] Sent HTTP request to https://taobao.com
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/tagregator/README.txt
[VER] [wordpress_readme] Sent HTTP request to https://yahoo.co.jp
[VER] [wordpress_readme] Sent HTTP request to https://tmall.com
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/jetpack/README.md
[VER] [wordpress_readme] Sent HTTP request to https://weibo.com
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/jetpack/README.txt
[VER] [wordpress_readme] Sent HTTP request to https://sina.com.cn
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/themes/wordcamp-central-2012/README.md
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/themes/wordcamp-central-2012/README.txt
[VER] [wordpress_readme] Sent HTTP request to https://csdn.net
[INF] Scan completed in 13.841904568s. 1 matches found.
```

Running the template through an http proxy seems to work fine too :

```
~ # nuclei -proxy http://172.17.0.1:18080 -t wordpress_readme.yaml -l list.txt -v

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.5

		projectdiscovery.io

[VER] Using http://172.17.0.1:18080 as proxy server
[VER] Started metrics server at localhost:9092
[INF] Current nuclei version: v3.4.5 (latest)
[INF] Current nuclei-templates version: v10.2.3 (latest)
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 105
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 30
[VER] [wordpress_readme] Sent HTTP request to https://reddit.com
[VER] [wordpress_readme] Sent HTTP request to https://twitch.tv
[VER] [wordpress_readme] Sent HTTP request to https://wikipedia.org
[VER] [wordpress_readme] Sent HTTP request to https://whatsapp.com
[VER] [wordpress_readme] Sent HTTP request to https://google.com
[VER] [wordpress_readme] Sent HTTP request to https://vk.com
[VER] [wordpress_readme] Sent HTTP request to https://twitter.com
[VER] [wordpress_readme] Sent HTTP request to https://linkedin.com
[VER] [wordpress_readme] Sent HTTP request to https://facebook.com
[VER] [wordpress_readme] Sent HTTP request to https://instagram.com
[VER] [wordpress_readme] Sent HTTP request to https://blogspot.com
[VER] [wordpress_readme] Sent HTTP request to https://jd.com
[VER] [wordpress_readme] Sent HTTP request to https://live.com
[VER] [wordpress_readme] Sent HTTP request to https://sohu.com
[VER] [wordpress_readme] Sent HTTP request to https://sina.com.cn
[VER] [wordpress_readme] Sent HTTP request to https://tmall.com
[VER] [wordpress_readme] Sent HTTP request to https://youtube.com
[VER] [wordpress_readme] Sent HTTP request to https://taobao.com
[VER] [wordpress_readme] Sent HTTP request to https://wordcamp.org
[VER] [wordpress_readme] Sent HTTP request to https://amazon.com
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/gutenberg/README.md
[VER] [wordpress_readme] Sent HTTP request to https://yandex.ru
[wordpress_readme] [http] [info] https://central.wordcamp.org/wp-content/plugins/gutenberg/README.md
[VER] [wordpress_readme] Sent HTTP request to https://yahoo.com
[VER] [wordpress_readme] Sent HTTP request to https://netflix.com
[VER] [wordpress_readme] Sent HTTP request to https://360.cn
[VER] [wordpress_readme] Sent HTTP request to https://login.tmall.com
[VER] [wordpress_readme] Sent HTTP request to https://weibo.com
[VER] [wordpress_readme] Sent HTTP request to https://baidu.com
[VER] [wordpress_readme] Sent HTTP request to https://qq.com
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/themes/wordcamp-central-2012/README.md
[VER] [wordpress_readme] Sent HTTP request to https://yahoo.co.jp
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/themes/wordcamp-central-2012/README.txt
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/tagregator/README.md
[VER] [wordpress_readme] Sent HTTP request to https://csdn.net
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/tagregator/README.txt
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/jetpack/README.md
[VER] [wordpress_readme] Sent HTTP request to https://central.wordcamp.org/wp-content/plugins/jetpack/README.txt
[INF] Scan completed in 4.900174417s. 1 matches found.
```

Runing the template through a socks5 proxy yields the same error :

```
~ # nuclei -proxy socks5://172.17.0.1:9090 -t wordpress_readme.yaml -l list.txt -v

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.5

		projectdiscovery.io

[VER] Using socks5://172.17.0.1:9090 as socket proxy server
[VER] Started metrics server at localhost:9092
[INF] Current nuclei version: v3.4.5 (latest)
[INF] Current nuclei-templates version: v10.2.3 (latest)
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 105
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 30
[VER] [wordpress_readme] Sent HTTP request to https://reddit.com
[VER] [wordpress_readme] Sent HTTP request to https://twitch.tv
[VER] [wordpress_readme] Sent HTTP request to https://wikipedia.org
[VER] [wordpress_readme] Sent HTTP request to https://whatsapp.com
[VER] [wordpress_readme] Sent HTTP request to https://twitter.com
[VER] [wordpress_readme] Sent HTTP request to https://google.com
[VER] [wordpress_readme] Sent HTTP request to https://instagram.com
[VER] [wordpress_readme] Sent HTTP request to https://facebook.com
[VER] [wordpress_readme] Sent HTTP request to https://vk.com
[VER] [wordpress_readme] Sent HTTP request to https://youtube.com
[VER] [wordpress_readme] Sent HTTP request to https://taobao.com
[VER] [wordpress_readme] Sent HTTP request to https://blogspot.com
[VER] [wordpress_readme] Sent HTTP request to https://amazon.com
[VER] [wordpress_readme] Sent HTTP request to https://tmall.com
[VER] [wordpress_readme] Sent HTTP request to https://linkedin.com
[VER] [wordpress_readme] Sent HTTP request to https://sohu.com
[VER] [wordpress_readme] Sent HTTP request to https://wordcamp.org
[VER] [wordpress_readme] Sent HTTP request to https://netflix.com
[VER] [wordpress_readme] Sent HTTP request to https://live.com
[VER] [wordpress_readme] Sent HTTP request to https://yahoo.com
[VER] [wordpress_readme] Sent HTTP request to https://sina.com.cn
[VER] [wordpress_readme] Sent HTTP request to https://yandex.ru
[VER] [wordpress_readme] Sent HTTP request to https://login.tmall.com
[VER] [wordpress_readme] Sent HTTP request to https://jd.com
[VER] [wordpress_readme] Sent HTTP request to https://weibo.com
[VER] [wordpress_readme] Sent HTTP request to https://360.cn
[VER] [wordpress_readme] Sent HTTP request to https://yahoo.co.jp
[VER] [wordpress_readme] Sent HTTP request to https://qq.com
[VER] [wordpress_readme] Sent HTTP request to https://baidu.com
[WRN] [wordpress_readme] Could not execute step on https://csdn.net: [:RUNTIME] failed to execute flow
// get imports from website's root
http(1);

let uniq = Dedupe();

// get unique webroot of each import
for (let path of iterate(template["imports"])) {
  let pathArray = path.split("/");
  let rootIdx = pathArray.indexOf("wp-content") + 3;
  let finalUrl = pathArray.slice(0, rootIdx).join("/");
  uniq.Add(finalUrl);
}

// for each import, look for readme
for (let url of iterate(uniq.Values())) {
  set("target", url);
  http(2);
}

 <- TypeError: Cannot read property 'imports' of undefined at <eval>:7:34(11)
[INF] Scan completed in 30.036850959s. No results found.
```

The socks proxy doesn't seem to be the issue, as confirmed with curl :

```
~ # apk add curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/community/x86_64/APKINDEX.tar.gz
(1/4) Installing c-ares (1.33.1-r0)
(2/4) Installing libpsl (0.21.5-r1)
(3/4) Installing libcurl (8.12.1-r0)
(4/4) Installing curl (8.12.1-r0)
Executing busybox-1.36.1-r28.trigger
OK: 631 MiB in 200 packages

~ # curl -x socks5://172.17.0.1:9090 ipinfo.io/ip
<redacted_ip_of_the_socks_server>
```@nisay759: Update: It's not the same issue, I think this is something different.

I redid the tests with the previous nuclei release (3.4.5), and the old error was :

```
<- SyntaxError: Identifier 'uniq' has already been declared at <eval>:1:1(0)
```

This new error (`<- TypeError: Cannot read property 'imports' of undefined at <eval>:7:34(11)`) is also present in 3.4.5 when running the template through a socks proxy.

@ehsandeep Do you think this is related to this issue, or should I open a separate one ?@ehsandeep: @nisay759 This error `<- TypeError: Cannot read property 'imports' of undefined at <eval>:7:34(11)` is likely expected when imports doesn’t hold any value, typically in cases where the site isn’t running WordPress. The error is verbose but should be harmless. Do you have any example hosts where imports has values from response but still triggers this error?@nisay759: > This error `<- TypeError: Cannot read property 'imports' of undefined at <eval>:7:34(11)` is likely expected when imports doesn’t hold any value, typically in cases where the site isn’t running WordPress.

In that case, running the template directly or through an http proxy should yield the exact same behavior as when using a socks proxy, which is not the case here.

Also, the error suggests that the variable `template` is `indefined`, and the error is thrown when trying to read the property `imports` in line 16 :

```javascript
  for (let path of iterate(template["imports"])) {
    // [...]
```

The error is not harmless: The 3rd example (with socks proxy) shows that no results were found (nuclei exits as soon as the error is encountered), whereas 1 result for the target https://wordcamp.org is expected, as shown in examples 1 and 2.

> Do you have any example hosts where imports has values from response but still triggers this error?

I couldn't reproduce the bug with a list of targets containing only wordpress instances. However, I found that the culprit is https://csdn.net :

```
./nuclei -proxy socks5://172.17.0.1:9090 -t wordpress_readme.yaml -u https://csdn.net -v                                                 

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.5

		projectdiscovery.io

[VER] Using socks5://172.17.0.1:9090 as socket proxy server
[VER] Started metrics server at localhost:9092
[INF] Current nuclei version: v3.4.5 (unknown) - remove '-duc' flag to enable update checks
[INF] Current nuclei-templates version: v9.8.9 (unknown) - remove '-duc' flag to enable update checks
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 0
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 1
[WRN] [wordpress_readme] Could not execute step on https://csdn.net: [:RUNTIME] failed to execute flow
// get imports from website's root
http(1);

let uniq = Dedupe();

// get unique webroot of each import
for (let path of iterate(template["imports"])) {
  let pathArray = path.split("/");
  let rootIdx = pathArray.indexOf("wp-content") + 3;
  let finalUrl = pathArray.slice(0, rootIdx).join("/");
  uniq.Add(finalUrl);
}

// for each import, look for readme
for (let url of iterate(uniq.Values())) {
  set("target", url);
  http(2);
}

 <- TypeError: Cannot read property 'imports' of undefined at <eval>:7:34(11)
[INF] Scan completed in 5.440468404s. No results found.
```

I'm still not sure what does this host have in particular that triggers this error, which is intriguing because it only shows when using a socks proxy.

For information, I managed to trigger the same error when specifying targets with closed ports, and the error is returned regardless of the proxy :

```
✗ ./nuclei -t wordpress_readme.yaml -u http://localhost:12345 -v            

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.4.5

		projectdiscovery.io

[VER] Started metrics server at localhost:9092
[INF] Current nuclei version: v3.4.5 (unknown) - remove '-duc' flag to enable update checks
[INF] Current nuclei-templates version: v9.8.9 (unknown) - remove '-duc' flag to enable update checks
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 0
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 1
[WRN] [wordpress_readme] Could not execute step on http://localhost:12345: [:RUNTIME] failed to execute flow
// get imports from website's root
http(1);

let uniq = Dedupe();

// get unique webroot of each import
for (let path of iterate(template["imports"])) {
  let pathArray = path.split("/");
  let rootIdx = pathArray.indexOf("wp-content") + 3;
  let finalUrl = pathArray.slice(0, rootIdx).join("/");
  uniq.Add(finalUrl);
}

// for each import, look for readme
for (let url of iterate(uniq.Values())) {
  set("target", url);
  http(2);
}

 <- TypeError: Cannot read property 'imports' of undefined at <eval>:7:34(11)
[INF] Scan completed in 1.395961ms. No results found.
```

I'm still unable to pinpoint the root cause. I'll do some more digging instead of polluting this thread, and I'll open another issue when I have more details.

Thanks for the assistance @ehsandeep !

Solution patch:
diff --git a/README_KR.md b/README_KR.md
index c59825cd..6181c866 100644
--- a/README_KR.md
+++ b/README_KR.md
@@ -341,7 +341,7 @@ Nuclei를 사용하면 자체 검사 모음으로 테스트 접근 방식을 사
 - 몇 분 안에 수천 개의 호스트를 처리할 수 있음.
 - 간단한 YAML DSL로 사용자 지정 테스트 접근 방식을 쉽게 자동화할 수 있음.
 
-버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](http://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다.](http://chaos.projectdiscovery.io).
+버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](http://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다](http://chaos.projectdiscovery.io).
 
 </td>
 </tr>
diff --git a/pkg/js/compiler/compiler.go b/pkg/js/compiler/compiler.go
index b13e7f9e..cd698e4a 100644
--- a/pkg/js/compiler/compiler.go
+++ b/pkg/js/compiler/compiler.go
@@ -156,9 +156,27 @@ func (c *Compiler) ExecuteWithOptions(program *goja.Program, args *ExecuteArgs,
 	return res, nil
 }
 
-// Wraps a script in a function and compiles it.
-func WrapScriptNCompile(script string, strict bool) (*goja.Program, error) {
-	if !stringsutil.ContainsAny(script, exportAsToken, exportToken) {
+// if the script uses export/ExportAS tokens then we can run it in IIFE mode
+// but if not we can't run it
+func CanRunAsIIFE(script string) bool {
+	return stringsutil.ContainsAny(script, exportAsToken, exportToken)
+}
+
+// SourceIIFEMode is a mode where the script is wrapped in a function and compiled.
+// This is used when the script is not exported or exported as a function.
+func SourceIIFEMode(script string, strict bool) (*goja.Program, error) {
+	val := fmt.Sprintf(`
+		(function() {
+			%s
+		})()
+	`, script)
+	return goja.Compile("", val, strict)
+}
+
+// SourceAutoMode is a mode where the script is wrapped in a function and compiled.
+// This is used when the script is exported or exported as a function.
+func SourceAutoMode(script string, strict bool) (*goja.Program, error) {
+	if !CanRunAsIIFE(script) {
 		// this will not be run in a pooled runtime
 		return goja.Compile("", script, strict)
 	}
diff --git a/pkg/protocols/code/code.go b/pkg/protocols/code/code.go
index b3344d08..1d687d23 100644
--- a/pkg/protocols/code/code.go
+++ b/pkg/protocols/code/code.go
@@ -130,7 +130,7 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
 
 	// compile pre-condition if any
 	if request.PreCondition != "" {
-		preConditionCompiled, err := compiler.WrapScriptNCompile(request.PreCondition, false)
+		preConditionCompiled, err := compiler.SourceAutoMode(request.PreCondition, false)
 		if err != nil {
 			return errorutil.NewWithTag(request.TemplateID, "could not compile pre-condition: %s", err)
 		}
diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go
index 344199fb..e8b9c0b3 100644
--- a/pkg/protocols/javascript/js.go
+++ b/pkg/protocols/javascript/js.go
@@ -215,7 +215,7 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
 		// proceed with whatever args we have
 		args.Args, _ = request.evaluateArgs(allVars, options, true)
 
-		initCompiled, err := compiler.WrapScriptNCompile(request.Init, false)
+		initCompiled, err := compiler.SourceAutoMode(request.Init, false)
 		if err != nil {
 			return errorutil.NewWithTag(request.TemplateID, "could not compile init code: %s", err)
 		}
@@ -236,7 +236,7 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
 
 	// compile pre-condition if any
 	if request.PreCondition != "" {
-		preConditionCompiled, err := compiler.WrapScriptNCompile(request.PreCondition, false)
+		preConditionCompiled, err := compiler.SourceAutoMode(request.PreCondition, false)
 		if err != nil {
 			return errorutil.NewWithTag(request.TemplateID, "could not compile pre-condition: %s", err)
 		}
@@ -245,7 +245,7 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
 
 	// compile actual source code
 	if request.Code != "" {
-		scriptCompiled, err := compiler.WrapScriptNCompile(request.Code, false)
+		scriptCompiled, err := compiler.SourceAutoMode(request.Code, false)
 		if err != nil {
 			return errorutil.NewWithTag(request.TemplateID, "could not compile javascript code: %s", err)
 		}
diff --git a/pkg/tmplexec/exec.go b/pkg/tmplexec/exec.go
index d0ed0933..c59b6c12 100644
--- a/pkg/tmplexec/exec.go
+++ b/pkg/tmplexec/exec.go
@@ -43,7 +43,7 @@ func NewTemplateExecuter(requests []protocols.Request, options *protocols.Execut
 		// we use a dummy input here because goal of flow executor at this point is to just check
 		// syntax and other things are correct before proceeding to actual execution
 		// during execution new instance of flow will be created as it is tightly coupled with lot of executor options
-		p, err := compiler.WrapScriptNCompile(options.Flow, false)
+		p, err := compiler.SourceAutoMode(options.Flow, false)
 		if err != nil {
 			return nil, fmt.Errorf("could not compile flow: %s", err)
 		}
diff --git a/pkg/tmplexec/flow/flow_executor.go b/pkg/tmplexec/flow/flow_executor.go
index 62112e0f..a8c55467 100644
--- a/pkg/tmplexec/flow/flow_executor.go
+++ b/pkg/tmplexec/flow/flow_executor.go
@@ -8,6 +8,7 @@ import (
 	"sync/atomic"
 
 	"github.com/dop251/goja"
+	"github.com/projectdiscovery/nuclei/v3/pkg/js/compiler"
 	"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
 	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
 	"github.com/projectdiscovery/nuclei/v3/pkg/scan"
@@ -195,7 +196,11 @@ func (f *FlowExecutor) ExecuteWithResults(ctx *scan.ScanContext) error {
 
 	// get a new runtime from pool
 	runtime := GetJSRuntime(f.options.Options)
-	defer PutJSRuntime(runtime) // put runtime back to pool
+	defer func() {
+		// whether to reuse or not depends on the whether script modifies
+		// global scope or not,
+		PutJSRuntime(runtime, compiler.CanRunAsIIFE(f.options.Flow))
+	}()
 	defer func() {
 		// remove set builtin
 		_ = runtime.GlobalObject().Delete("set")
diff --git a/pkg/tmplexec/flow/vm.go b/pkg/tmplexec/flow/vm.go
index f1f7dbb8..88033c25 100644
--- a/pkg/tmplexec/flow/vm.go
+++ b/pkg/tmplexec/flow/vm.go
@@ -35,7 +35,7 @@ func GetJSRuntime(opts *types.Options) *goja.Runtime {
 		if opts.JsConcurrency < 100 {
 			opts.JsConcurrency = 100
 		}
-		sizedgojapool, _ = sizedpool.New[*goja.Runtime](
+		sizedgojapool, _ = sizedpool.New(
 			sizedpool.WithPool[*goja.Runtime](gojapool),
 			sizedpool.WithSize[*goja.Runtime](int64(opts.JsConcurrency)),
 		)
@@ -45,8 +45,12 @@ func GetJSRuntime(opts *types.Options) *goja.Runtime {
 }
 
 // PutJSRuntime returns a JS runtime to pool
-func PutJSRuntime(runtime *goja.Runtime) {
-	sizedgojapool.Put(runtime)
+func PutJSRuntime(runtime *goja.Runtime, reuse bool) {
+	if reuse {
+		sizedgojapool.Put(runtime)
+	} else {
+		sizedgojapool.Put(gojapool.Get().(*goja.Runtime))
+	}
 }
 
 func registerBuiltins(runtime *goja.Runtime) {



Test patch:
diff --git a/pkg/js/compiler/compiler_test.go b/pkg/js/compiler/compiler_test.go
index 3a43a401..01acb0d0 100644
--- a/pkg/js/compiler/compiler_test.go
+++ b/pkg/js/compiler/compiler_test.go
@@ -21,7 +21,7 @@ func TestNewCompilerConsoleDebug(t *testing.T) {
 	})
 
 	compiler := New()
-	p, err := WrapScriptNCompile("console.log('hello world');", false)
+	p, err := SourceAutoMode("console.log('hello world');", false)
 	if err != nil {
 		t.Fatal(err)
 	}