Repository: projectdiscovery/nuclei

Issue:
[BUG] external-service-interaction false positive
### Is there an existing issue for this?

- [x] I have searched the existing issues.

### Current Behavior

Some test for vulnerable HOST header leads to false positive as during an external interaction test, Nuclei does NOT use the target server or related CDNs to generate the callback.

Instead, the template external-service-interaction.yaml makes Nuclei itself send the request to a Interactsh domain.

The template external-service-interaction.yaml does NOT test the target server's outbound connectivity.

Instead, it only tests:
- Interactsh domain is working
- Nuclei can resolve and interact with it
- DNS + HTTP interaction from Nuclei → Interactsh is successful

Because of that, the template can report:
- [info] external-service-interaction

But this does NOT mean:
- the target server made a request
- the target is vulnerable
- the target can reach your OOB server


### Expected Behavior

Need to check if external-service-interaction.yaml performs "DIG" first or similar query on resolving the unique d4cibapgtg4fjhr1ni8g8dpawtxxxxxxxx.oob.domain.com prior doing target interaction test. Which leads to false positive interaction results.

### Steps To Reproduce

1. Run nuclei like below flags
 
nuclei -t /home/kali/.local/nuclei-templates/http/miscellaneous/external-service-interaction.yaml -u http://x.x.x.x/ -debug -vv

### Relevant log output

```shell
nuclei -t /home/kali/.local/nuclei-templates/http/miscellaneous/external-service-interaction.yaml -u http://x.x.x.x/ -debug -vv

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

                projectdiscovery.io

[INF] Current nuclei version: v3.5.1 (latest)
[INF] Current nuclei-templates version: v10.3.1 (latest)
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 119
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 1
[external-service-interaction] External Service Interaction (@andreluna) [info]
[INF] Using Interactsh Server: oast.live
[INF] [external-service-interaction] Dumped HTTP request for http://x.x.x.x/

GET / HTTP/1.1
Host: d4c9uflndrldt865oubgmxxxxxxxxxxxx.oast.live
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0
Connection: close
Accept-Encoding: gzip

[DBG] [external-service-interaction] Dumped HTTP response http://x.x.x.x/

HTTP/1.1 404 Not Found
Connection: close
Content-Length: 196
Content-Type: text/html; charset=iso-8859-1
Date: Sat, 15 Nov 2025 15:41:51 GMT
Server: Apache

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
</body></html>
[d4c9uflndrldt865oubgmxxxxxxxxxxxx] Received DNS interaction from 1x.x.x.x at 2025-11-15 15:41:51
------------
DNS Request
------------

;; opcode: QUERY, status: NOERROR, id: 54053
;; flags:; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version 0; flags: do; udp: 1452

;; QUESTION SECTION:
;d4c9uflndrldt865oubgmxxxxxxxxxxxx.oast.live.   IN       A



------------
DNS Response
------------

;; opcode: QUERY, status: NOERROR, id: 54053
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2

;; QUESTION SECTION:
;d4c9uflndrldt865oubgmxxxxxxxxxxxx.oast.live.   IN       A

;; ANSWER SECTION:
d4c9uflndrldt865oubgmxxxxxxxxxxxx.oast.live.    3600    IN      A       x.x.x.x

;; AUTHORITY SECTION:
d4c9uflndrldt865oubgmxxxxxxxxxxxx.oast.live.    3600    IN      NS      ns1.oast.live.
d4c9uflndrldt865oubgmxxxxxxxxxxxx.oast.live.    3600    IN      NS      ns2.oast.live.

;; ADDITIONAL SECTION:
ns1.oast.live.  3600    IN      A       x.x.x.x
ns2.oast.live.  3600    IN      A       x.x.x.x


[external-service-interaction:word-1] [http] [info] http://x.x.x.x/
[INF] Scan completed in 6.292480252s. 1 matches found.
```

### Environment

```markdown
- OS: Kali Linux
- Nuclei: v3.5.1
- Go: go version go1.24.2 linux/amd64
```

### Anything else?

_No response_

Hints from issue comments:
@aphroph: I also tried hosting and using my own interactsh-server and client and yields to similar false positive output in using Nuclei.@dwisiswant0: I couldn’t reproduce it on my end; it always shows _0 matches found_ for me.

Any ideas on how I can make this reproducible locally?

**EDIT**:

Nvm! I’m able to reproduce it now.@dwisiswant0: Can you check if you can reproduce it with PR #6614 and lmk if it’s fixed on your side?

```console
$ git clone https://github.com/projectdiscovery/nuclei
$ cd nuclei/
$ gh pr checkout 6614
$ make build 2>/dev/null
$ ./bin/nuclei [...]
```@aphroph: Yes PR #6614 addressed the issue. It is not anymore showing false positive. Tried already with 2 different endpoints. Thank you for the quick fix.

```console
./bin/nuclei -t /home/kali/.local/nuclei-templates/http/miscellaneous/external-service-interaction.yaml -u http://x.x.x.x/ -debug -vv

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

                projectdiscovery.io

[INF] Current nuclei version: v3.5.1 (latest)
[INF] Current nuclei-templates version: v10.3.1 (latest)
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 119
[INF] Templates loaded for current scan: 1
[WRN] Loading 1 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 1
[external-service-interaction] External Service Interaction (@andreluna) [info]
[INF] Using Interactsh Server: oast.fun
[INF] [external-service-interaction] Dumped HTTP request for http://x.x.x.x/

GET / HTTP/1.1
Host: d4cp9utndrle18is73g0fxxxxxxxxcorrd.oast.fun
User-Agent: Mozilla/5.0 (SS; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
Connection: close
Accept-Encoding: gzip

[DBG] [external-service-interaction] Dumped HTTP response http://x.x.x.x/

HTTP/1.1 404 Not Found
Connection: close
Content-Length: 196
Content-Type: text/html; charset=iso-8859-1
Date: Sun, 16 Nov 2025 09:10:21 GMT
Server: Apache

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
</body></html>
[INF] Scan completed in 6.465537202s. No results found.
```

Solution patch:
diff --git a/README.md b/README.md
index a422e5da..ae4f3eab 100644
--- a/README.md
+++ b/README.md
@@ -140,7 +140,7 @@ TARGET:
    -u, -target string[]          target URLs/hosts to scan
    -l, -list string              path to file containing a list of target URLs/hosts to scan (one per line)
    -eh, -exclude-hosts string[]  hosts to exclude to scan from the input list (ip, cidr, hostname)
-   -resume string                resume scan using resume.cfg (clustering will be disabled)
+   -resume string                resume scan from and save to specified file (clustering will be disabled)
    -sa, -scan-all-ips            scan all the IP's associated with dns record
    -iv, -ip-version string[]     IP version to scan of hostname (4,6) - (default 4)
 
diff --git a/README_CN.md b/README_CN.md
index 5fb4dd14..0396b0ba 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -119,7 +119,7 @@ Nuclei是一款注重于可配置性、可扩展性和易用性的基于模板
 目标：
    -u, -target string[]                  指定扫描的目标URL/主机（多个目标则指定多个-u参数）
    -l, -list string                      指定包含要扫描的目标URL/主机列表的文件路径（一行一个）
-   -resume string                        使用指定的resume.cfg文件恢复扫描（将禁用请求聚类）
+   -resume string                        从指定文件恢复扫描并保存到指定文件（将禁用请求聚类）
    -sa, -scan-all-ips                    扫描由目标解析出来的所有IP（针对域名对应多个IP的情况）
    -iv, -ip-version string[]             要扫描的主机名的IP版本（4,6）-（默认为4）
 
diff --git a/README_ES.md b/README_ES.md
index ec7949ef..4432699d 100644
--- a/README_ES.md
+++ b/README_ES.md
@@ -118,7 +118,7 @@ TARGET:
    -u, -target string[]          URLs/hosts a escanear
    -l, -list string              ruta al archivo que contiene la lista de URLs/hosts a escanear (uno por línea)
    -eh, -exclude-hosts string[]  hosts a excluir para escanear de la lista de entrada (ip, cidr, hostname)
-   -resume string                reanudar el escaneo usando resume.cfg (la clusterización quedará inhabilitada)
+   -resume string                reanudar el escaneo desde y guardar en el archivo especificado (la clusterización quedará inhabilitada)
    -sa, -scan-all-ips            escanear todas las IP asociadas al registro dns
    -iv, -ip-version string[]     versión IP a escanear del nombre de host (4,6) - (por defecto 4)
 
diff --git a/README_ID.md b/README_ID.md
index 459352b4..db18e1db 100644
--- a/README_ID.md
+++ b/README_ID.md
@@ -98,7 +98,7 @@ Flags:
 TARGET:
    -u, -target string[]       target URLs/hosts to scan
    -l, -list string           path to file containing a list of target URLs/hosts to scan (one per line)
-   -resume string             resume scan using resume.cfg (clustering will be disabled)
+   -resume string             resume scan from and save to specified file (clustering will be disabled)
    -sa, -scan-all-ips         scan all the IP's associated with dns record
    -iv, -ip-version string[]  IP version to scan of hostname (4,6) - (default 4)
 
diff --git a/README_JP.md b/README_JP.md
index d80fb4df..33e5c282 100644
--- a/README_JP.md
+++ b/README_JP.md
@@ -113,7 +113,7 @@ Nucleiは、広範な設定可能性、大規模な拡張性、および使い
 ターゲット:
    -u, -target string[]          スキャンする対象のURL/ホスト
    -l, -list string              スキャンする対象のURL/ホストのリストが含まれているファイルへのパス（1行に1つ）
-   -resume string                resume.cfgを使用してスキャンを再開（クラスタリングは無効になります）
+   -resume string                指定されたファイルからスキャンを再開し、指定されたファイルに保存（クラスタリングは無効になります）
    -sa, -scan-all-ips            DNSレコードに関連付けられているすべてのIPをスキャン
    -iv, -ip-version string[]     ホスト名のスキャンするIPバージョン（4,6）-（デフォルトは4）
 
diff --git a/README_KR.md b/README_KR.md
index d0828564..2b137443 100644
--- a/README_KR.md
+++ b/README_KR.md
@@ -96,7 +96,7 @@ Nuclei는 빠르고, 템플릿 기반의 취약점 스캐너로
 TARGET:
    -u, -target string[]       스캔할 대상 URL/호스트
    -l, -list string           스캔할 대상 URL/호스트 목록이 있는 파일 경로 (한 줄에 하나씩)
-   -resume string             resume.cfg를 사용하여 스캔 재개 (클러스터링은 비활성화됨)
+   -resume string             지정된 파일에서 스캔을 재개하고 지정된 파일에 저장 (클러스터링은 비활성화됨)
    -sa, -scan-all-ips         dns 레코드와 관련된 모든 IP 스캔
    -iv, -ip-version string[]  스캔할 호스트의 IP 버전 (4,6) - (기본값 4)
 
diff --git a/README_PT-BR.md b/README_PT-BR.md
index e63a3d8a..64e4f4e3 100644
--- a/README_PT-BR.md
+++ b/README_PT-BR.md
@@ -118,7 +118,7 @@ TARGET:
    -u, -target string[]          URLs/hosts a serem escaneados
    -l, -list string              caminho do arquivo contendo a lista de URLs/hosts a serem escaneados (um por linha)
    -eh, -exclude-hosts string[]  hosts a serem excluídos do escaneamento na lista de entrada (ip, cidr, hostname)
-   -resume string                retomar o escaneamento usando resume.cfg (a clusterização será desabilitada)
+   -resume string                retomar o escaneamento a partir de e salvar no arquivo especificado (a clusterização será desabilitada)
    -sa, -scan-all-ips            escanear todos os IPs associados ao registro DNS
    -iv, -ip-version string[]     versão de IP a escanear do nome do host (4,6) - (padrão 4)
 
diff --git a/cmd/nuclei/main.go b/cmd/nuclei/main.go
index e2de5ff0..1a51c9f0 100644
--- a/cmd/nuclei/main.go
+++ b/cmd/nuclei/main.go
@@ -194,8 +194,11 @@ func main() {
 		})
 	}
 
-	// Setup graceful exits
+	// Setup filename for graceful exits
 	resumeFileName := types.DefaultResumeFilePath()
+	if options.Resume == "" {
+		resumeFileName = options.Resume
+	}
 	c := make(chan os.Signal, 1)
 	signal.Notify(c, os.Interrupt)
 	go func() {
@@ -255,7 +258,7 @@ on extensive configurability, massive extensibility and ease of use.`)
 		flagSet.StringSliceVarP(&options.Targets, "target", "u", nil, "target URLs/hosts to scan", goflags.CommaSeparatedStringSliceOptions),
 		flagSet.StringVarP(&options.TargetsFilePath, "list", "l", "", "path to file containing a list of target URLs/hosts to scan (one per line)"),
 		flagSet.StringSliceVarP(&options.ExcludeTargets, "exclude-hosts", "eh", nil, "hosts to exclude to scan from the input list (ip, cidr, hostname)", goflags.FileCommaSeparatedStringSliceOptions),
-		flagSet.StringVar(&options.Resume, "resume", "", "resume scan using resume.cfg (clustering will be disabled)"),
+		flagSet.StringVar(&options.Resume, "resume", "", "resume scan from and save to specified file (clustering will be disabled)"),
 		flagSet.BoolVarP(&options.ScanAllIPs, "scan-all-ips", "sa", false, "scan all the IP's associated with dns record"),
 		flagSet.StringSliceVarP(&options.IPVersion, "ip-version", "iv", nil, "IP version to scan of hostname (4,6) - (default 4)", goflags.CommaSeparatedStringSliceOptions),
 	)
diff --git a/go.mod b/go.mod
index c3a52735..88949450 100644
--- a/go.mod
+++ b/go.mod
@@ -22,7 +22,7 @@ require (
 	github.com/olekukonko/tablewriter v1.0.8
 	github.com/pkg/errors v0.9.1
 	github.com/projectdiscovery/clistats v0.1.1
-	github.com/projectdiscovery/fastdialer v0.4.15
+	github.com/projectdiscovery/fastdialer v0.4.16
 	github.com/projectdiscovery/hmap v0.0.95
 	github.com/projectdiscovery/interactsh v1.2.4
 	github.com/projectdiscovery/rawhttp v0.1.90
@@ -39,9 +39,9 @@ require (
 	github.com/valyala/fasttemplate v1.2.2
 	github.com/weppos/publicsuffix-go v0.50.0
 	go.uber.org/multierr v1.11.0
-	golang.org/x/net v0.46.0
+	golang.org/x/net v0.47.0
 	golang.org/x/oauth2 v0.30.0
-	golang.org/x/text v0.30.0
+	golang.org/x/text v0.31.0
 	gopkg.in/yaml.v2 v2.4.0
 )
 
@@ -96,21 +96,21 @@ require (
 	github.com/projectdiscovery/gcache v0.0.0-20241015120333-12546c6e3f4c
 	github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
 	github.com/projectdiscovery/goflags v0.1.74
-	github.com/projectdiscovery/gologger v1.1.59
+	github.com/projectdiscovery/gologger v1.1.60
 	github.com/projectdiscovery/gostruct v0.0.2
 	github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81
-	github.com/projectdiscovery/httpx v1.7.2-0.20250911192144-fc425deb041a
+	github.com/projectdiscovery/httpx v1.7.2
 	github.com/projectdiscovery/mapcidr v1.1.97
 	github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
-	github.com/projectdiscovery/networkpolicy v0.1.27
+	github.com/projectdiscovery/networkpolicy v0.1.28
 	github.com/projectdiscovery/ratelimit v0.0.82
 	github.com/projectdiscovery/rdap v0.9.0
 	github.com/projectdiscovery/sarif v0.0.1
 	github.com/projectdiscovery/tlsx v1.2.1
 	github.com/projectdiscovery/uncover v1.1.0
 	github.com/projectdiscovery/useragent v0.0.102
-	github.com/projectdiscovery/utils v0.6.1-0.20251030144701-ce5c4b44e1e6
-	github.com/projectdiscovery/wappalyzergo v0.2.54
+	github.com/projectdiscovery/utils v0.6.1
+	github.com/projectdiscovery/wappalyzergo v0.2.55
 	github.com/redis/go-redis/v9 v9.11.0
 	github.com/seh-msft/burpxml v1.0.1
 	github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
@@ -123,7 +123,7 @@ require (
 	github.com/zmap/zgrab2 v0.1.8
 	gitlab.com/gitlab-org/api/client-go v0.130.1
 	go.mongodb.org/mongo-driver v1.17.4
-	golang.org/x/term v0.36.0
+	golang.org/x/term v0.37.0
 	gopkg.in/yaml.v3 v3.0.1
 	moul.io/http2curl v1.0.0
 )
@@ -322,7 +322,7 @@ require (
 	github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
 	github.com/projectdiscovery/asnmap v1.1.1 // indirect
 	github.com/projectdiscovery/blackrock v0.0.1 // indirect
-	github.com/projectdiscovery/cdncheck v1.2.9 // indirect
+	github.com/projectdiscovery/cdncheck v1.2.10 // indirect
 	github.com/projectdiscovery/freeport v0.0.7 // indirect
 	github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb // indirect
 	github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
@@ -379,7 +379,7 @@ require (
 	go.opentelemetry.io/otel/trace v1.38.0 // indirect
 	go4.org v0.0.0-20230225012048-214862532bf5 // indirect
 	golang.org/x/arch v0.3.0 // indirect
-	golang.org/x/sync v0.17.0 // indirect
+	golang.org/x/sync v0.18.0 // indirect
 	gopkg.in/djherbis/times.v1 v1.3.0 // indirect
 	mellium.im/sasl v0.3.2 // indirect
 )
@@ -403,12 +403,12 @@ require (
 	go.etcd.io/bbolt v1.4.0 // indirect
 	go.uber.org/zap v1.27.0 // indirect
 	goftp.io/server/v2 v2.0.1 // indirect
-	golang.org/x/crypto v0.43.0 // indirect
+	golang.org/x/crypto v0.45.0 // indirect
 	golang.org/x/exp v0.0.0-20250911091902-df9299821621
-	golang.org/x/mod v0.28.0 // indirect
-	golang.org/x/sys v0.37.0 // indirect
+	golang.org/x/mod v0.29.0 // indirect
+	golang.org/x/sys v0.38.0 // indirect
 	golang.org/x/time v0.14.0 // indirect
-	golang.org/x/tools v0.37.0
+	golang.org/x/tools v0.38.0
 	google.golang.org/protobuf v1.36.6 // indirect
 	gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
 	gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect
@@ -421,4 +421,4 @@ require (
 )
 
 // https://go.dev/ref/mod#go-mod-file-retract
-retract v3.2.0 // retract due to broken js protocol issue
\ No newline at end of file
+retract v3.2.0 // retract due to broken js protocol issue
diff --git a/go.sum b/go.sum
index a1063e60..cde66ac1 100644
--- a/go.sum
+++ b/go.sum
@@ -817,14 +817,14 @@ github.com/projectdiscovery/asnmap v1.1.1 h1:ImJiKIaACOT7HPx4Pabb5dksolzaFYsD1kI
 github.com/projectdiscovery/asnmap v1.1.1/go.mod h1:QT7jt9nQanj+Ucjr9BqGr1Q2veCCKSAVyUzLXfEcQ60=
 github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ=
 github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
-github.com/projectdiscovery/cdncheck v1.2.9 h1:DsT+uZdGduJSsSrTbFRl1JDcsDHrPKi0v+/KziQnuTw=
-github.com/projectdiscovery/cdncheck v1.2.9/go.mod h1:ibL9HoZs2JYTEUBOZo4f+W+XEzQifFLOf4bpgFStgj4=
+github.com/projectdiscovery/cdncheck v1.2.10 h1:Ox86LS8RFjq6pYNTP3Eqdawlor/h+bnb7BTEKBpzFyM=
+github.com/projectdiscovery/cdncheck v1.2.10/go.mod h1:ibL9HoZs2JYTEUBOZo4f+W+XEzQifFLOf4bpgFStgj4=
 github.com/projectdiscovery/clistats v0.1.1 h1:8mwbdbwTU4aT88TJvwIzTpiNeow3XnAB72JIg66c8wE=
 github.com/projectdiscovery/clistats v0.1.1/go.mod h1:4LtTC9Oy//RiuT1+76MfTg8Hqs7FQp1JIGBM3nHK6a0=
 github.com/projectdiscovery/dsl v0.8.4 h1:p3rvzJae9BecOMufdYex3DX9zZeQNaXwVQe4kCEAOtE=
 github.com/projectdiscovery/dsl v0.8.4/go.mod h1:msE7dGAuHYRrKddEwB1yoQ5dHrzzyimQUjsGDsMDis8=
-github.com/projectdiscovery/fastdialer v0.4.15 h1:AHDgyydTdE5uUHGwzpvIDslY2AQn1kVq79gKEgFGAbE=
-github.com/projectdiscovery/fastdialer v0.4.15/go.mod h1:X0l4+KqOE/aIL00pyTnBj4pWQDPYnCGL7cwZsJu6SCQ=
+github.com/projectdiscovery/fastdialer v0.4.16 h1:rmCNr5N/9KTm0nSYjSuQ5j3aXmNIPf6HhJlAhN/7NRI=
+github.com/projectdiscovery/fastdialer v0.4.16/go.mod h1:X0l4+KqOE/aIL00pyTnBj4pWQDPYnCGL7cwZsJu6SCQ=
 github.com/projectdiscovery/fasttemplate v0.0.2 h1:h2cISk5xDhlJEinlBQS6RRx0vOlOirB2y3Yu4PJzpiA=
 github.com/projectdiscovery/fasttemplate v0.0.2/go.mod h1:XYWWVMxnItd+r0GbjA1GCsUopMw1/XusuQxdyAIHMCw=
 github.com/projectdiscovery/freeport v0.0.7 h1:Q6uXo/j8SaV/GlAHkEYQi8WQoPXyJWxyspx+aFmz9Qk=
@@ -835,16 +835,16 @@ github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb h1:rutG90
 github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb/go.mod h1:FLjF1DmZ+POoGEiIQdWuYVwS++C/GwpX8YaCsTSm1RY=
 github.com/projectdiscovery/goflags v0.1.74 h1:n85uTRj5qMosm0PFBfsvOL24I7TdWRcWq/1GynhXS7c=
 github.com/projectdiscovery/goflags v0.1.74/go.mod h1:UMc9/7dFz2oln+10tv6cy+7WZKTHf9UGhaNkF95emh4=
-github.com/projectdiscovery/gologger v1.1.59 h1:3XFidZHrUqtvL1CUbw7L1jtwiUmTZxT2CoQ0I/yiNh4=
-github.com/projectdiscovery/gologger v1.1.59/go.mod h1:8FJFKmo0N4ITIH3n1Jy4ze6ijr+mA3t78g+VpN8uBRU=
+github.com/projectdiscovery/gologger v1.1.60 h1:N2Zyu4WA2RgUeqSAdfhv/CLS4de8lDDc2+IdLKcAd5U=
+github.com/projectdiscovery/gologger v1.1.60/go.mod h1:8FJFKmo0N4ITIH3n1Jy4ze6ijr+mA3t78g+VpN8uBRU=
 github.com/projectdiscovery/gostruct v0.0.2 h1:s8gP8ApugGM4go1pA+sVlPDXaWqNP5BBDDSv7VEdG1M=
 github.com/projectdiscovery/gostruct v0.0.2/go.mod h1:H86peL4HKwMXcQQtEa6lmC8FuD9XFt6gkNR0B/Mu5PE=
 github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81 h1:yHh46pJovYbyiaHCV7oIDinFmy+Fyq36H1BowJgb0M0=
 github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81/go.mod h1:9lmGPBDGZVANzCGjQg+V32n8Y3Cgjo/4kT0E88lsVTI=
 github.com/projectdiscovery/hmap v0.0.95 h1:OO6MCySlK2xMzvJmsYUwdaI7YWv/U437OtsN0Ovw72k=
 github.com/projectdiscovery/hmap v0.0.95/go.mod h1:KiTRdGd/GzX7uaoFWPrPBxPf4X/uZ9HTQ9dQ8x7x1bo=
-github.com/projectdiscovery/httpx v1.7.2-0.20250911192144-fc425deb041a h1:5NBp4BegAQuT3QSnbBKt05LH1nOyEeFAXYh1+aE3Nlo=
-github.com/projectdiscovery/httpx v1.7.2-0.20250911192144-fc425deb041a/go.mod h1:SQl92RiEuBnv1QQ8aQLC3b1lfgGHttoqUV0cTTvlzxQ=
+github.com/projectdiscovery/httpx v1.7.2 h1:AfJ5wjhKOlywX+x+gPO4iPqgFEyoIJwvXsLpQQgs4+c=
+github.com/projectdiscovery/httpx v1.7.2/go.mod h1:hm0uTQGUTU1K0AQ1NQVfFrKfiS4u9Ynh8wArdXUXBS4=
 github.com/projectdiscovery/interactsh v1.2.4 h1:WUSj+fxbcV53J64oIAhbYzCKD1w/IyenyRBhkI5jiqI=
 github.com/projectdiscovery/interactsh v1.2.4/go.mod h1:E/IVNZ80/WKz8zTwGJWQygxIbhlRmuzZFsZwcGSZTdc=
 github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb h1:MGtI4oE12ruWv11ZlPXXd7hl/uAaQZrFvrIDYDeVMd8=
@@ -855,8 +855,8 @@ github.com/projectdiscovery/mapcidr v1.1.97 h1:7FkxNNVXp+m1rIu5Nv/2SrF9k4+LwP8Qu
 github.com/projectdiscovery/mapcidr v1.1.97/go.mod h1:9dgTJh1SP02gYZdpzMjm6vtYFkEHQHoTyaVNvaeJ7lA=
 github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw1pfT6bg35NiN7yd1XKtJap5Nk6lMwQ0RNi8=
 github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
-github.com/projectdiscovery/networkpolicy v0.1.27 h1:GsbvDIW3nPstAx8Beke6rtn95PhXnOcoXrnjcohn5Xk=
-github.com/projectdiscovery/networkpolicy v0.1.27/go.mod h1:/3XfgnxKNuxaTZc6wZ/Pq6fiKvK8N4OQyLmfcUeDk2E=
+github.com/projectdiscovery/networkpolicy v0.1.28 h1:Rwg8iZmM4n+CRWyUClthaSrTqDAW8zBI2HULRO1CF3k=
+github.com/projectdiscovery/networkpolicy v0.1.28/go.mod h1:/3XfgnxKNuxaTZc6wZ/Pq6fiKvK8N4OQyLmfcUeDk2E=
 github.com/projectdiscovery/ratelimit v0.0.82 h1:rtO5SQf5uQFu5zTahTaTcO06OxmG8EIF1qhdFPIyTak=
 github.com/projectdiscovery/ratelimit v0.0.82/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM=
 github.com/projectdiscovery/rawhttp v0.1.90 h1:LOSZ6PUH08tnKmWsIwvwv1Z/4zkiYKYOSZ6n+8RFKtw=
@@ -877,10 +877,10 @@ github.com/projectdiscovery/uncover v1.1.0 h1:UDp/qLZn78YZb6VPoOrfyP1vz+ojEx8VrT
 github.com/projectdiscovery/uncover v1.1.0/go.mod h1:2rXINmMe/lmVAt2jn9CpAOs9An57/JEeLZobY3Z9kUs=
 github.com/projectdiscovery/useragent v0.0.102 h1:Xfr8a7LQhIu0zeSz5gBxGCdyuqZbhkOMAEQUcEZXyBU=
 github.com/projectdiscovery/useragent v0.0.102/go.mod h1:DIfLRBKZ6dLhHRnMYkxdg6Jpu0kpE3pJlMG94dsIchY=
-github.com/projectdiscovery/utils v0.6.1-0.20251030144701-ce5c4b44e1e6 h1:nvszzYNHYnc8X+Dm68zMuYNNesZJp7QWfe8EEyL4azc=
-github.com/projectdiscovery/utils v0.6.1-0.20251030144701-ce5c4b44e1e6/go.mod h1:GOjhpPLmpMHcYJKI0vhjvjdczMQf3jWdUgYiBeKkwVk=
-github.com/projectdiscovery/wappalyzergo v0.2.54 h1:8w0qUb0dO9N5FN1y4M8pIzDNqLCj0MrITqV/1xp05Lw=
-github.com/projectdiscovery/wappalyzergo v0.2.54/go.mod h1:lwuDLdAqWDZ1IL8OQnoNQ0t17UP9AQSvVuFcDAm4FpQ=
+github.com/projectdiscovery/utils v0.6.1 h1:9bf3J2G4WJMULGm4Xq7+96+Uj4QpYID/tNnzberR6RE=
+github.com/projectdiscovery/utils v0.6.1/go.mod h1:j4Fb6PDir9PcTxLOL9cpSVDPVKtLTZwdVxxMAeG0JjA=
+github.com/projectdiscovery/wappalyzergo v0.2.55 h1:Yw8bSrEH/+prUrkb6c4NMwi5m2Sso28nrdy4vFxYn8I=
+github.com/projectdiscovery/wappalyzergo v0.2.55/go.mod h1:lwuDLdAqWDZ1IL8OQnoNQ0t17UP9AQSvVuFcDAm4FpQ=
 github.com/projectdiscovery/yamldoc-go v1.0.6 h1:GCEdIRlQjDux28xTXKszM7n3jlMf152d5nqVpVoetas=
 github.com/projectdiscovery/yamldoc-go v1.0.6/go.mod h1:R5lWrNzP+7Oyn77NDVPnBsxx2/FyQZBBkIAaSaCQFxw=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -1204,8 +1204,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
-golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
-golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1243,8 +1243,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
-golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
+golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
+golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1295,8 +1295,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
-golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
-golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1325,8 +1325,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
-golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
+golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1391,8 +1391,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
-golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1406,8 +1406,8 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
 golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
 golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
-golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
-golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
+golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
+golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1424,8 +1424,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
-golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
-golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1479,8 +1479,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
-golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
-golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
+golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
+golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/lib/config.go b/lib/config.go
index cdc56ce0..f624ef42 100644
--- a/lib/config.go
+++ b/lib/config.go
@@ -3,6 +3,7 @@ package nuclei
 import (
 	"context"
 	"errors"
+	"os"
 	"time"
 
 	"github.com/projectdiscovery/goflags"
@@ -559,3 +560,18 @@ func WithOptions(opts *pkgtypes.Options) NucleiSDKOptions {
 		return nil
 	}
 }
+
+// WithTemporaryDirectory allows setting a parent directory for SDK-managed temporary files.
+// A temporary directory will be created inside the provided directory and cleaned up on engine close.
+// If not set, a temporary directory will be automatically created in the system temp location.
+// The parent directory is assumed to exist.
+func WithTemporaryDirectory(parentDir string) NucleiSDKOptions {
+	return func(e *NucleiEngine) error {
+		tmpDir, err := os.MkdirTemp(parentDir, "nuclei-tmp-*")
+		if err != nil {
+			return err
+		}
+		e.tmpDir = tmpDir
+		return nil
+	}
+}
diff --git a/lib/sdk.go b/lib/sdk.go
index 3ed25217..99523c79 100644
--- a/lib/sdk.go
+++ b/lib/sdk.go
@@ -5,6 +5,7 @@ import (
 	"bytes"
 	"context"
 	"io"
+	"os"
 	"sync"
 
 	"github.com/projectdiscovery/gologger"
@@ -92,6 +93,9 @@ type NucleiEngine struct {
 
 	// Logger instance for the engine
 	Logger *gologger.Logger
+
+	// Temporary directory for SDK-managed template files
+	tmpDir string
 }
 
 // LoadAllTemplates loads all nuclei template based on given options
@@ -231,6 +235,9 @@ func (e *NucleiEngine) closeInternal() {
 	if e.httpxClient != nil {
 		_ = e.httpxClient.Close()
 	}
+	if e.tmpDir != "" {
+		_ = os.RemoveAll(e.tmpDir)
+	}
 }
 
 // Close all resources used by nuclei engine
diff --git a/lib/sdk_private.go b/lib/sdk_private.go
index d80a0fd0..ba394f02 100644
--- a/lib/sdk_private.go
+++ b/lib/sdk_private.go
@@ -3,6 +3,7 @@ package nuclei
 import (
 	"context"
 	"fmt"
+	"os"
 	"strings"
 	"sync"
 	"time"
@@ -170,20 +171,29 @@ func (e *NucleiEngine) init(ctx context.Context) error {
 		e.catalog = disk.NewCatalog(config.DefaultConfig.TemplatesDirectory)
 	}
 
+	if e.tmpDir == "" {
+		tmpDir, err := os.MkdirTemp("", "nuclei-tmp-*")
+		if err != nil {
+			return err
+		}
+		e.tmpDir = tmpDir
+	}
+
 	e.executerOpts = &protocols.ExecutorOptions{
-		Output:       e.customWriter,
-		Options:      e.opts,
-		Progress:     e.customProgress,
-		Catalog:      e.catalog,
-		IssuesClient: e.rc,
-		RateLimiter:  e.rateLimiter,
-		Interactsh:   e.interactshClient,
-		Colorizer:    aurora.NewAurora(true),
-		ResumeCfg:    types.NewResumeCfg(),
-		Browser:      e.browserInstance,
-		Parser:       e.parser,
-		InputHelper:  input.NewHelper(),
-		Logger:       e.opts.Logger,
+		Output:             e.customWriter,
+		Options:            e.opts,
+		Progress:           e.customProgress,
+		Catalog:            e.catalog,
+		IssuesClient:       e.rc,
+		RateLimiter:        e.rateLimiter,
+		Interactsh:         e.interactshClient,
+		Colorizer:          aurora.NewAurora(true),
+		ResumeCfg:          types.NewResumeCfg(),
+		Browser:            e.browserInstance,
+		Parser:             e.parser,
+		InputHelper:        input.NewHelper(),
+		TemporaryDirectory: e.tmpDir,
+		Logger:             e.opts.Logger,
 	}
 	if e.opts.ShouldUseHostError() && e.hostErrCache != nil {
 		e.executerOpts.HostErrorsCache = e.hostErrCache
diff --git a/pkg/protocols/common/interactsh/interactsh.go b/pkg/protocols/common/interactsh/interactsh.go
index 7cdf7c77..e92cfaec 100644
--- a/pkg/protocols/common/interactsh/interactsh.go
+++ b/pkg/protocols/common/interactsh/interactsh.go
@@ -455,3 +455,8 @@ func (c *Client) setHostname(hostname string) {
 
 	c.hostname = hostname
 }
+
+// GetHostname returns the configured interactsh server hostname.
+func (c *Client) GetHostname() string {
+	return c.getHostname()
+}
diff --git a/pkg/protocols/common/variables/variables.go b/pkg/protocols/common/variables/variables.go
index 0724c84c..fa5cc1db 100644
--- a/pkg/protocols/common/variables/variables.go
+++ b/pkg/protocols/common/variables/variables.go
@@ -3,10 +3,13 @@ package variables
 import (
 	"strings"
 
+	"github.com/Knetic/govaluate"
 	"github.com/invopop/jsonschema"
+	"github.com/projectdiscovery/nuclei/v3/pkg/operators/common/dsl"
 	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
 	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
 	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
+	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/marker"
 	protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
 	"github.com/projectdiscovery/nuclei/v3/pkg/types"
 	"github.com/projectdiscovery/nuclei/v3/pkg/utils"
@@ -17,7 +20,9 @@ import (
 // Variable is a key-value pair of strings that can be used
 // throughout template.
 type Variable struct {
-	LazyEval                        bool `yaml:"-" json:"-"` // LazyEval is used to evaluate variables lazily if it using any expression or global variables
+	// LazyEval is used to evaluate variables lazily if it using any expression
+	// or global variables.
+	LazyEval                        bool `yaml:"-" json:"-"`
 	utils.InsertionOrderedStringMap `yaml:"-" json:"-"`
 }
 
@@ -128,19 +133,67 @@ func evaluateVariableValue(expression string, values, processing map[string]inte
 // checkForLazyEval checks if the variables have any lazy evaluation i.e any dsl function
 // and sets the flag accordingly.
 func (variables *Variable) checkForLazyEval() bool {
+	var needsLazy bool
+
 	variables.ForEach(func(key string, value interface{}) {
+		if needsLazy {
+			return
+		}
+
 		for _, v := range protocolutils.KnownVariables {
 			if stringsutil.ContainsAny(types.ToString(value), v) {
-				variables.LazyEval = true
+				needsLazy = true
 				return
 			}
 		}
+
 		// this is a hotfix and not the best way to do it
 		// will be refactored once we move scan state to scanContext (see: https://github.com/projectdiscovery/nuclei/issues/4631)
 		if strings.Contains(types.ToString(value), "interactsh-url") {
-			variables.LazyEval = true
+			needsLazy = true
+			return
+		}
+
+		if hasUndefinedParams(types.ToString(value), variables) {
+			needsLazy = true
 			return
 		}
 	})
+
+	variables.LazyEval = needsLazy
+
 	return variables.LazyEval
 }
+
+// hasUndefinedParams checks if a variable value contains expressions that ref
+// parameters not defined in the current variable scope, indicating it needs
+// runtime context.
+func hasUndefinedParams(value string, variables *Variable) bool {
+	exprs := expressions.FindExpressions(value, marker.ParenthesisOpen, marker.ParenthesisClose, map[string]interface{}{})
+	if len(exprs) == 0 {
+		return false
+	}
+
+	definedVars := make(map[string]struct{})
+	variables.ForEach(func(key string, _ interface{}) {
+		definedVars[key] = struct{}{}
+	})
+
+	for _, expr := range exprs {
+		compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, dsl.HelperFunctions)
+		if err != nil {
+			// NOTE(dwisiswant0): here, it might need runtime context.
+			return true
+		}
+
+		vars := compiled.Vars()
+		for _, paramName := range vars {
+			// NOTE(dwisiswant0): also here, if it's not in our defined vars.
+			if _, exists := definedVars[paramName]; !exists {
+				return true
+			}
+		}
+	}
+
+	return false
+}
diff --git a/pkg/protocols/http/request.go b/pkg/protocols/http/request.go
index c538686e..4a97db7d 100644
--- a/pkg/protocols/http/request.go
+++ b/pkg/protocols/http/request.go
@@ -1137,6 +1137,15 @@ func (request *Request) addCNameIfAvailable(hostname string, outputEvent map[str
 		return
 	}
 
+	if request.options.Interactsh != nil {
+		interactshDomain := request.options.Interactsh.GetHostname()
+		if interactshDomain != "" {
+			if strings.EqualFold(hostname, interactshDomain) || strings.HasSuffix(hostname, "."+interactshDomain) {
+				return
+			}
+		}
+	}
+
 	data, err := request.dialer.GetDNSData(hostname)
 	if err == nil {
 		switch len(data.CNAME) {
diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go
index 8b872d84..2d927555 100644
--- a/pkg/protocols/javascript/js.go
+++ b/pkg/protocols/javascript/js.go
@@ -36,6 +36,7 @@ import (
 	"github.com/projectdiscovery/utils/errkit"
 	iputil "github.com/projectdiscovery/utils/ip"
 	mapsutil "github.com/projectdiscovery/utils/maps"
+	sliceutil "github.com/projectdiscovery/utils/slice"
 	syncutil "github.com/projectdiscovery/utils/sync"
 	urlutil "github.com/projectdiscovery/utils/url"
 )
@@ -133,8 +134,11 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
 	}
 
 	// "Port" is a special variable and it should not contains any dsl expressions
-	if strings.Contains(request.getPort(), "{{") {
-		return errkit.New("'Port' variable cannot contain any dsl expressions")
+	ports := request.getPorts()
+	for _, port := range ports {
+		if strings.Contains(port, "{{") {
+			return errkit.New("'Port' variable cannot contain any dsl expressions")
+		}
 	}
 
 	if request.Init != "" {
@@ -281,12 +285,28 @@ func (request *Request) GetID() string {
 
 // ExecuteWithResults executes the protocol requests and returns results instead of writing them.
 func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
+	// Get default port(s) if specified in template
+	ports := request.getPorts()
+
+	var errs []error
+
+	for _, port := range ports {
+		err := request.executeWithResults(port, target, dynamicValues, previous, callback)
+		if err != nil {
+			errs = append(errs, err)
+		}
+	}
 
+	return errkit.Join(errs...)
+}
+
+// executeWithResults executes the request
+func (request *Request) executeWithResults(port string, target *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
 	input := target.Clone()
 	// use network port updates input with new port requested in template file
 	// and it is ignored if input port is not standard http(s) ports like 80,8080,8081 etc
 	// idea is to reduce redundant dials to http ports
-	if err := input.UseNetworkPort(request.getPort(), request.getExcludePorts()); err != nil {
+	if err := input.UseNetworkPort(port, request.getExcludePorts()); err != nil {
 		gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
 	}
 
@@ -755,13 +775,21 @@ func (request *Request) Type() templateTypes.ProtocolType {
 	return templateTypes.JavascriptProtocol
 }
 
-func (request *Request) getPort() string {
+func (request *Request) getPorts() []string {
 	for k, v := range request.Args {
 		if strings.EqualFold(k, "Port") {
-			return types.ToString(v)
+			portStr := types.ToString(v)
+			ports := []string{}
+			for _, p := range strings.Split(portStr, ",") {
+				trimmed := strings.TrimSpace(p)
+				if trimmed != "" {
+					ports = append(ports, trimmed)
+				}
+			}
+			return sliceutil.Dedupe(ports)
 		}
 	}
-	return ""
+	return []string{}
 }
 
 func (request *Request) getExcludePorts() string {



Test patch:
diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go
index 6e99b7f8..c0b8c199 100644
--- a/cmd/integration-test/javascript.go
+++ b/cmd/integration-test/javascript.go
@@ -17,6 +17,7 @@ var jsTestcases = []TestCaseInfo{
 	{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNetHttps{}},
 	{Path: "protocols/javascript/oracle-auth-test.yaml", TestCase: &javascriptOracleAuthTest{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
 	{Path: "protocols/javascript/vnc-pass-brute.yaml", TestCase: &javascriptVncPassBrute{}},
+	{Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}},
 }
 
 var (
@@ -167,6 +168,17 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error {
 	return multierr.Combine(errs...)
 }
 
+type javascriptMultiPortsSSH struct{}
+
+func (j *javascriptMultiPortsSSH) Execute(filePath string) error {
+	// use scanme.sh as target to ensure we match on the 2nd default port 22
+	results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
+	if err != nil {
+		return err
+	}
+	return expectResultsCount(results, 1)
+}
+
 // purge any given resource if it is not nil
 func purge(resource *dockertest.Resource) {
 	if resource != nil && pool != nil {
diff --git a/cmd/nuclei/main_benchmark_test.go b/cmd/nuclei/main_benchmark_test.go
index 04e17bf9..26d7a196 100644
--- a/cmd/nuclei/main_benchmark_test.go
+++ b/cmd/nuclei/main_benchmark_test.go
@@ -1,9 +1,13 @@
 package main_test
 
 import (
+	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"os"
+	"runtime"
+	"runtime/pprof"
+	"strings"
 	"testing"
 	"time"
 
@@ -48,6 +52,31 @@ func TestMain(m *testing.M) {
 	os.Exit(exitCode)
 }
 
+// getUniqFilename generates a unique filename by appending .N if file exists
+// Similar to wget's behavior: file.cpu.prof, file.cpu.1.prof, file.cpu.2.prof, etc.
+func getUniqFilename(basePath string) string {
+	if _, err := os.Stat(basePath); os.IsNotExist(err) {
+		return basePath
+	}
+
+	lastDot := strings.LastIndex(basePath, ".")
+	var name, ext string
+	if lastDot != -1 {
+		name = basePath[:lastDot]
+		ext = basePath[lastDot:]
+	} else {
+		name = basePath
+		ext = ""
+	}
+
+	for i := 1; ; i++ {
+		newPath := fmt.Sprintf("%s.%d%s", name, i, ext)
+		if _, err := os.Stat(newPath); os.IsNotExist(err) {
+			return newPath
+		}
+	}
+}
+
 func getDefaultOptions() *types.Options {
 	return &types.Options{
 		RemoteTemplateDomainList:   []string{"cloud.projectdiscovery.io"},
@@ -106,24 +135,55 @@ func runEnumBenchmark(b *testing.B, options *types.Options) {
 	}
 	defer nucleiRunner.Close()
 
-	b.ResetTimer()
+	benchNameSlug := strings.ReplaceAll(b.Name(), "/", "-")
+
+	// Start CPU profiling
+	cpuProfileBase := fmt.Sprintf("%s.cpu.prof", benchNameSlug)
+	cpuProfilePath := getUniqFilename(cpuProfileBase)
+	cpuProfile, err := os.Create(cpuProfilePath)
+	if err != nil {
+		b.Fatalf("failed to create CPU profile: %s", err)
+	}
+	defer func() { _ = cpuProfile.Close() }()
+
+	if err := pprof.StartCPUProfile(cpuProfile); err != nil {
+		b.Fatalf("failed to start CPU profile: %s", err)
+	}
+	defer pprof.StopCPUProfile()
+
 	b.ReportAllocs()
 
-	for i := 0; i < b.N; i++ {
+	for b.Loop() {
 		if err := nucleiRunner.RunEnumeration(); err != nil {
 			b.Fatalf("%s failed: %s", b.Name(), err)
 		}
 	}
+
+	b.StopTimer()
+
+	// Write heap profile
+	heapProfileBase := fmt.Sprintf("%s.heap.prof", benchNameSlug)
+	heapProfilePath := getUniqFilename(heapProfileBase)
+	heapProfile, err := os.Create(heapProfilePath)
+	if err != nil {
+		b.Fatalf("failed to create heap profile: %s", err)
+	}
+	defer func() { _ = heapProfile.Close() }()
+
+	runtime.GC() // Force GC before heap profile
+	if err := pprof.WriteHeapProfile(heapProfile); err != nil {
+		b.Fatalf("failed to write heap profile: %s", err)
+	}
 }
 
 func BenchmarkRunEnumeration(b *testing.B) {
 	// Default case: run enumeration with default options == all nuclei-templates
-	// b.Run("Default", func(b *testing.B) {
-	// 	options := getDefaultOptions()
-	// 	options.Targets = []string{targetURL}
+	b.Run("Default", func(b *testing.B) {
+		options := getDefaultOptions()
+		options.Targets = []string{targetURL}
 
-	// 	runEnumBenchmark(b, options)
-	// })
+		runEnumBenchmark(b, options)
+	})
 
 	// Case: https://github.com/projectdiscovery/nuclei/pull/6258
 	b.Run("Multiproto", func(b *testing.B) {
diff --git a/integration_tests/protocols/javascript/multi-ports.yaml b/integration_tests/protocols/javascript/multi-ports.yaml
new file mode 100644
index 00000000..aa1c5fe2
--- /dev/null
+++ b/integration_tests/protocols/javascript/multi-ports.yaml
@@ -0,0 +1,28 @@
+id: multi-ports
+
+info:
+  name: Multi Ports - Detection
+  author: pdteam
+  severity: info
+  description: |
+    Multi Ports template for testing
+  metadata:
+    max-request: 1
+  tags: js,detect,multi-ports,enum,network
+
+javascript:
+  - pre-condition: |
+      isPortOpen(Host,Port);
+    code: |
+      var m = require("nuclei/ssh");
+      var c = m.SSHClient();
+      var response = c.ConnectSSHInfoMode(Host, Port);
+      Export(response);
+    args:
+      Host: "{{Host}}"
+      Port: "2222,22" # Port 22 should match
+
+    extractors:
+      - type: json
+        json:
+          - '.UserAuth'
\ No newline at end of file
diff --git a/pkg/protocols/common/variables/variables_test.go b/pkg/protocols/common/variables/variables_test.go
index 67aee33f..cbf560b4 100644
--- a/pkg/protocols/common/variables/variables_test.go
+++ b/pkg/protocols/common/variables/variables_test.go
@@ -4,6 +4,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/projectdiscovery/nuclei/v3/pkg/utils"
 	"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
 	"github.com/stretchr/testify/require"
 	"gopkg.in/yaml.v2"
@@ -41,3 +42,108 @@ a6: "123456"`
 	require.Equal(t, map[string]interface{}{"a2": "098f6bcd4621d373cade4e832627b4f6", "a3": "this_is_random_text", "a4": a4, "a5": "moc.elgoog", "a6": "123456"}, result, "could not get correct elements")
 
 }
+
+func TestCheckForLazyEval(t *testing.T) {
+	t.Run("undefined-parameters-in-expression", func(t *testing.T) {
+		// Variables with expressions that reference undefined parameters
+		// should be marked for lazy evaluation
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(2),
+		}
+		variables.Set("var1", "{{sha1(serial)}}")           // 'serial' is undefined
+		variables.Set("var2", "{{replace(user, '.', '')}}") // 'user' is undefined
+
+		result := variables.checkForLazyEval()
+		require.True(t, result, "should detect undefined parameters and set LazyEval=true")
+		require.True(t, variables.LazyEval, "LazyEval flag should be true")
+	})
+
+	t.Run("self-referencing-variables", func(t *testing.T) {
+		// Variables that reference other defined variables should NOT be lazy
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(2),
+		}
+		variables.Set("base", "example")
+		variables.Set("derived", "{{base}}_suffix") // 'base' is defined
+
+		result := variables.checkForLazyEval()
+		require.False(t, result, "should not set LazyEval for self-referencing defined variables")
+		require.False(t, variables.LazyEval, "LazyEval flag should be false")
+	})
+
+	t.Run("constant-expressions", func(t *testing.T) {
+		// Constant expressions without variables should NOT be lazy
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(2),
+		}
+		variables.Set("const1", "{{2+2}}")
+		variables.Set("const2", "{{rand_int(1, 100)}}")
+
+		result := variables.checkForLazyEval()
+		require.False(t, result, "should not set LazyEval for constant expressions")
+		require.False(t, variables.LazyEval, "LazyEval flag should be false")
+	})
+
+	t.Run("known-runtime-variables", func(t *testing.T) {
+		// Variables with known runtime variables (Host, BaseURL, etc.) should be lazy
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(1),
+		}
+		variables.Set("url", "{{BaseURL}}/api")
+
+		result := variables.checkForLazyEval()
+		require.True(t, result, "should detect known runtime variables")
+		require.True(t, variables.LazyEval, "LazyEval flag should be true")
+	})
+
+	t.Run("interactsh-url", func(t *testing.T) {
+		// Variables with interactsh-url should be lazy
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(1),
+		}
+		variables.Set("callback", "{{interactsh-url}}")
+
+		result := variables.checkForLazyEval()
+		require.True(t, result, "should detect interactsh-url")
+		require.True(t, variables.LazyEval, "LazyEval flag should be true")
+	})
+
+	t.Run("mixed-defined-and-undefined", func(t *testing.T) {
+		// Mix of defined and undefined parameters in actual expressions
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(3),
+		}
+		variables.Set("defined", "value")
+		variables.Set("uses_defined", "{{base64(defined)}}")           // OK - 'defined' exists
+		variables.Set("uses_undefined", "{{base64(undefined_param)}}") // NOT OK - 'undefined_param' doesn't exist
+
+		result := variables.checkForLazyEval()
+		require.True(t, result, "should detect undefined parameters even with some defined")
+		require.True(t, variables.LazyEval, "LazyEval flag should be true")
+	})
+
+	t.Run("plain-strings-no-expressions", func(t *testing.T) {
+		// Plain string values without expressions
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(2),
+		}
+		variables.Set("plain1", "simple value")
+		variables.Set("plain2", "another value")
+
+		result := variables.checkForLazyEval()
+		require.False(t, result, "should not set LazyEval for plain strings")
+		require.False(t, variables.LazyEval, "LazyEval flag should be false")
+	})
+
+	t.Run("complex-expression-with-undefined", func(t *testing.T) {
+		// Complex expression with multiple undefined parameters
+		variables := &Variable{
+			InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(1),
+		}
+		variables.Set("complex", "{{sha1(cert_serial + issuer)}}")
+
+		result := variables.checkForLazyEval()
+		require.True(t, result, "should detect undefined parameters in complex expressions")
+		require.True(t, variables.LazyEval, "LazyEval flag should be true")
+	})
+}