diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index 57ff74e671..599940d7a1 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
go-version: [1.20.x]
- os: [ubuntu-latest, windows-latest, macOS-13]
+ os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml
index c029803f31..79b154af8b 100644
--- a/.github/workflows/functional-test.yml
+++ b/.github/workflows/functional-test.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-latest, windows-latest, macOS-13]
+ os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go
uses: actions/setup-go@v4
diff --git a/SYNTAX-REFERENCE.md b/SYNTAX-REFERENCE.md
index 017e16349b..def307bae2 100755
--- a/SYNTAX-REFERENCE.md
+++ b/SYNTAX-REFERENCE.md
@@ -2745,6 +2745,19 @@ Fuzzing describes schema to fuzz headless requests
+
+
+cookie-reuse
bool
+
+
+
+
+CookieReuse is an optional setting that enables cookie reuse
+
+
+
+
+
diff --git a/integration_tests/generic/auth/certificate/assets/client.crt b/integration_tests/generic/auth/certificate/assets/client.crt
new file mode 100644
index 0000000000..4bbdee24b2
--- /dev/null
+++ b/integration_tests/generic/auth/certificate/assets/client.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfkCFFSLOinkkPWOfwuzMHF0B9EZIIghMA0GCSqGSIb3DQEBCwUAMEUx
+CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
+cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMwNjIxMDA0MzA2WhcNMjMwNzIxMDA0
+MzA2WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
+CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAp187MX/sPGlTl8WklyTSh8+kYURy2zWmp2FglMvo058NanCD
+rjYJvS2FNM2ZwRZH52qBr6rpqIsa8QDFjnlFJK5y4FNr104ZjDr1YXDfX5ftQeeX
+wCavFRDdH/s43kGyNjNeSH78EhqmC3z7LxCJT5k8rXRAyC02uBRqQTrRHfNuVvGI
+JZ59jxESzRJt89kWuYymkAj16LHffMvcq6HLwr/KG8IyrJJRj5KcDzYorFonTPe9
+rwBlAbU3LpQ4ZlEP1mQA2PdCg3t85pz3n+57Iw839bWrwbjfijXHY3yjbqfEAaqa
+md08nByTg8TZlt9UzjWgj0K1DXDMDDkJKqi39QIDAQABMA0GCSqGSIb3DQEBCwUA
+A4IBAQAH0MhznadrMFuY2ZYA69FbsvOygMctv8qZW1HrHS0X13IXeW+8uxfb5+gk
+yKFgXNMFueyd5PoN9vyC1t9AOBPnI56gaYm/MbDtwEqGo8C+9fjJasY23J90p54t
+G6vxcXwo33HVpWBeRBkVF/SePeCn+MKk0jd/JgJS0T0s1Ih6wkn84/83hDk4M2M2
+/yhc3wuNYdf/WB8QAfJAc2YpIfkMOoxGPTsxvREiZrPUyGiWa507hrHcQU0GV8qC
+KcnS7UUCT9TtJvQIKHwW68XjBudWpaILBj1TS8hOGseOJydJqbk8wyMTE6fgc1Ss
+KfTrfa0HOHIkAU/TfE22Zqfw4z70
+-----END CERTIFICATE-----
diff --git a/integration_tests/generic/auth/certificate/assets/client.key b/integration_tests/generic/auth/certificate/assets/client.key
new file mode 100644
index 0000000000..96a88abbc9
--- /dev/null
+++ b/integration_tests/generic/auth/certificate/assets/client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnXzsxf+w8aVOX
+xaSXJNKHz6RhRHLbNaanYWCUy+jTnw1qcIOuNgm9LYU0zZnBFkfnaoGvqumoixrx
+AMWOeUUkrnLgU2vXThmMOvVhcN9fl+1B55fAJq8VEN0f+zjeQbI2M15IfvwSGqYL
+fPsvEIlPmTytdEDILTa4FGpBOtEd825W8Yglnn2PERLNEm3z2Ra5jKaQCPXosd98
+y9yrocvCv8obwjKsklGPkpwPNiisWidM972vAGUBtTculDhmUQ/WZADY90KDe3zm
+nPef7nsjDzf1tavBuN+KNcdjfKNup8QBqpqZ3TycHJODxNmW31TONaCPQrUNcMwM
+OQkqqLf1AgMBAAECggEAEZiXdorGYUuJeElVFnbOk2ynEXrKwHURgkNgjgQqBCWS
+mYAet/ACchsZCAYdhgk7of62h6tmSUvmlzPHkUT6mfKlLSRYEBir4uxH4+ij8z7b
+uLQKZi8q9QIC3VviDKvHep9H6ENBaP3YOxj2p2oLpYysrmesb98hA5VR3m26knVb
+sfYwLdsrqQ5Tiuzm1Mdaca2HEYq0iXoZqjQHi31h33rw4HFBKcGX6w6PJOon+i2m
+eSSaCAJMYFkoS6NafJYwQDuwIwp+IyrYdI/vaAR6s2ufJ4doIjJC7YuPO5jpLpJc
+IBBl5e7pu4/rlwJCDARfBWuXFbjtoMAvmM1MojdNOQKBgQDYCIl97ef19LmeYfwk
+RW2xhQTVCWwwBBhSl06iB9SeFkzCCRb+AFjhVhcwe4xIWh9GXWZawZC2XSaoTXte
+hxcZRFbnpjcpZ6sYKiP4fB/GXGqYDUWovxu8gmXKrXtfwqJMXklfFE7WblGgK5gG
+l7OfbuczaRaIQHoIQYzfmeCwmwKBgQDGVhCSNFGiRaDG7k2VfXElaIyL7m7FlsXf
+EptolOeoGv5GvVr3CB5TvUqXN3haLZvUbBKRpWrDbP1n1i+77VICOLtq1qf6SEog
+1p2PAccGhXXvrL7LJLUr9Hk831D4fSX9TqVzdxwfMdGFepYoS4vm8fkGVCuaytDa
+fniJl8TarwKBgQCpTreCrAsY5bz7dcuIGamIcLmCxKm3T95IDDEiJ4ToiI2LnFga
+pOcDYtc1tf4RTiAoo1ZuVjk10vdS+7ZuNO1Tbg216rxchNTAUXZzbcPxT8hydiRb
+xbrVGFTybNe+CunrdBGIpH/M6hSqtL+mmwm5L8+eqQNxsSZyhf0D2LMRdQKBgD8J
+CXk+MZfOY1v2Tygs1zIZeVnb7M7VrYvJYSUq9jliYuBevDN5HBJnPfazhYe7qSQp
+OPmbRkRYNm2zEDa9JWxZVY+OK5MLOKwZKbhSy0uSTTpgf78WqpIOwB2NqDFhrRpF
+zaXV/FUZw0qV/HVQFWXQD+JoC/fFb/2RZoPsfX83AoGBAM4VPDUa1I16MuuW+FNZ
+cVENZK/qsXFMvm3xutezvMSgmCxVSnXy9GP8QbqkfMrDJd3v/HnwrC4ORTlU7rim
+AWSvC6CYO1c2RantleA46T90uWW8kP24TK1yWOrRGKuaQYvfokiiLVExq1nA1iSR
+/QPLg6vEoPMOLhB7BQBpsFkq
+-----END PRIVATE KEY-----
diff --git a/integration_tests/generic/auth/certificate/assets/server.crt b/integration_tests/generic/auth/certificate/assets/server.crt
new file mode 100644
index 0000000000..aa818acb4d
--- /dev/null
+++ b/integration_tests/generic/auth/certificate/assets/server.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfkCFHA1RpGfOY5p/vQmeMQ1oRFqH+CGMA0GCSqGSIb3DQEBCwUAMEUx
+CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
+cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMwNjIxMDA0MjQ2WhcNMjMwNzIxMDA0
+MjQ2WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
+CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA3VdrKR5hmZ+vyvg6NB2dOL5vEIQ/9DevivnKWqX5mserYLMj
+Wq0knVfogewZnrDe+zVC3kOogBQvYk8Z53kTY9qpJT85dMCuW4xDx0JU+cWHul9a
+pzF+bvws4paCWIcsGONyocPAx5g07LbPU9civC80QkQqELo1zYiRU1bX8vRJJqbN
+TW2mzl9MN3AnCAYTwq8WhVG/1QR3LPQhPR68/1LWrFefQaEWaXT2s+Xv7K7NDXro
+WSba4SgKdFd6fyUVMVr/ioT1KT45TP5jbRrW5JJUTdpkiXaIucrZg39f6F5gTZGA
+U7bNROUMkqrJJngN9+Hp+YH1GpkKgu9EKA30EQIDAQABMA0GCSqGSIb3DQEBCwUA
+A4IBAQAw91bxiAi7DIVsKL3k4B0I+50ZKq9VMVNE3YCTPygpfuRiGQvlITZ5I8I5
+3Ok2wWltgKx6EnicHIlLg42yRj7j3mdgOLMFMrUCfJmdogwnS+k6veG3G1RHUs9r
+ATfX49u/hEX2pe7Rvx2VYVIugwrQESgQ21iaf6uUMsrq6W8eYZ31as1nJKpqIGbu
+W1fZMSi0RIUJP+mpVBE82IW+gJRi3uKU4HKPqyrU3dviBFdBxb3lNbh34/vdNkIw
+4H2CfBxEvdwLYAhWDerlm4wWCmjkMiHfBHPBhhOICTkR25a7NFy27h/UDHjVC/6m
+fGshVSBtxVPJP7kcvZ1scIctvFZZ
+-----END CERTIFICATE-----
diff --git a/integration_tests/generic/auth/certificate/http-get.yaml b/integration_tests/generic/auth/certificate/http-get.yaml
new file mode 100644
index 0000000000..c52c577a13
--- /dev/null
+++ b/integration_tests/generic/auth/certificate/http-get.yaml
@@ -0,0 +1,15 @@
+id: basic-get-with-cert
+
+info:
+ name: Basic GET with Cert
+ author: pdteam
+ severity: info
+
+requests:
+ - method: GET
+ path:
+ - "{{BaseURL}}"
+ matchers:
+ - type: word
+ words:
+ - "Hello"
\ No newline at end of file
diff --git a/integration_tests/workflow/headless-1.yaml b/integration_tests/workflow/headless-1.yaml
new file mode 100644
index 0000000000..2a5895316b
--- /dev/null
+++ b/integration_tests/workflow/headless-1.yaml
@@ -0,0 +1,16 @@
+id: headless-1
+info:
+ name: Headless 1
+ author: pdteam
+ severity: info
+ tags: headless
+
+headless:
+ - cookie-reuse: true
+ steps:
+ - action: navigate
+ args:
+ url: "{{BaseURL}}/headless1"
+
+ - action: waitload
+
\ No newline at end of file
diff --git a/integration_tests/workflow/http-1.yaml b/integration_tests/workflow/http-1.yaml
new file mode 100644
index 0000000000..cdebddf3d5
--- /dev/null
+++ b/integration_tests/workflow/http-1.yaml
@@ -0,0 +1,12 @@
+id: http1
+
+info:
+ name: http1
+ author: pdteam
+ severity: info
+
+http:
+ - method: GET
+ path:
+ - "{{BaseURL}}/http1"
+ cookie-reuse: true
\ No newline at end of file
diff --git a/integration_tests/workflow/http-2.yaml b/integration_tests/workflow/http-2.yaml
new file mode 100644
index 0000000000..e5000e0561
--- /dev/null
+++ b/integration_tests/workflow/http-2.yaml
@@ -0,0 +1,12 @@
+id: http2
+
+info:
+ name: http2
+ author: pdteam
+ severity: info
+
+http:
+ - method: GET
+ path:
+ - "{{BaseURL}}/http2"
+ cookie-reuse: true
\ No newline at end of file
diff --git a/integration_tests/workflow/http-3.yaml b/integration_tests/workflow/http-3.yaml
new file mode 100644
index 0000000000..230408dd7b
--- /dev/null
+++ b/integration_tests/workflow/http-3.yaml
@@ -0,0 +1,12 @@
+id: http3
+
+info:
+ name: http3
+ author: pdteam
+ severity: info
+
+http:
+ - method: GET
+ path:
+ - "{{BaseURL}}/http3"
+ cookie-reuse: true
\ No newline at end of file
diff --git a/integration_tests/workflow/http-value-share-template-1.yaml b/integration_tests/workflow/http-value-share-template-1.yaml
index a273e113af..c2f6123dfb 100644
--- a/integration_tests/workflow/http-value-share-template-1.yaml
+++ b/integration_tests/workflow/http-value-share-template-1.yaml
@@ -5,7 +5,7 @@ info:
author: pdteam
severity: info
-requests:
+http:
- path:
- "{{BaseURL}}/path1"
extractors:
diff --git a/integration_tests/workflow/http-value-share-template-2.yaml b/integration_tests/workflow/http-value-share-template-2.yaml
index 7bac99f3db..f391aae1b8 100644
--- a/integration_tests/workflow/http-value-share-template-2.yaml
+++ b/integration_tests/workflow/http-value-share-template-2.yaml
@@ -5,7 +5,7 @@ info:
author: pdteam
severity: info
-requests:
+http:
- raw:
- |
GET /path2 HTTP/1.1
diff --git a/integration_tests/workflow/match-1.yaml b/integration_tests/workflow/match-1.yaml
index c7e07e8cf4..854652c98d 100644
--- a/integration_tests/workflow/match-1.yaml
+++ b/integration_tests/workflow/match-1.yaml
@@ -5,7 +5,7 @@ info:
author: pdteam
severity: info
-requests:
+http:
- method: GET
path:
- "{{BaseURL}}"
diff --git a/integration_tests/workflow/match-2.yaml b/integration_tests/workflow/match-2.yaml
index 01e9d4b6f8..2431ad10ca 100644
--- a/integration_tests/workflow/match-2.yaml
+++ b/integration_tests/workflow/match-2.yaml
@@ -5,7 +5,7 @@ info:
author: pdteam
severity: info
-requests:
+http:
- method: GET
path:
- "{{BaseURL}}"
diff --git a/integration_tests/workflow/nomatch-1.yaml b/integration_tests/workflow/nomatch-1.yaml
index 71f126421e..ec71d9be80 100644
--- a/integration_tests/workflow/nomatch-1.yaml
+++ b/integration_tests/workflow/nomatch-1.yaml
@@ -5,7 +5,7 @@ info:
author: pdteam
severity: info
-requests:
+http:
- method: GET
path:
- "{{BaseURL}}"
diff --git a/integration_tests/workflow/shared-cookie.yaml b/integration_tests/workflow/shared-cookie.yaml
new file mode 100644
index 0000000000..f2d68be2cf
--- /dev/null
+++ b/integration_tests/workflow/shared-cookie.yaml
@@ -0,0 +1,15 @@
+id: workflow-shared-cookies
+
+info:
+ name: Test Workflow Shared Cookies
+ author: pdteam
+ severity: info
+
+workflows:
+ # store cookies to standard http client cookie-jar
+ - template: workflow/http-1.yaml
+ - template: workflow/http-2.yaml
+ # store cookie in native browser context
+ - template: workflow/headless-1.yaml
+ # retrive 2 standard library cookies + headless cookie
+ - template: workflow/http-3.yaml
\ No newline at end of file
diff --git a/nuclei-jsonschema.json b/nuclei-jsonschema.json
index 5a4d76573f..452d4fd853 100644
--- a/nuclei-jsonschema.json
+++ b/nuclei-jsonschema.json
@@ -727,6 +727,11 @@
"type": "array",
"title": "fuzzin rules for http fuzzing",
"description": "Fuzzing describes rule schema to fuzz headless requests"
+ },
+ "cookie-reuse": {
+ "type": "boolean",
+ "title": "optional cookie reuse enable",
+ "description": "Optional setting that enables cookie reuse"
}
},
"additionalProperties": false,
diff --git a/v2/.goreleaser.yml b/v2/.goreleaser.yml
index c822480edd..83b95e801b 100644
--- a/v2/.goreleaser.yml
+++ b/v2/.goreleaser.yml
@@ -23,15 +23,15 @@ builds:
flags:
- -trimpath
-- main: cmd/tmc/main.go
- binary: tmc
- id: annotate
-
- env:
- - CGO_ENABLED=0
-
- goos: [linux]
- goarch: [amd64]
+#- main: cmd/tmc/main.go
+# binary: tmc
+# id: annotate
+#
+# env:
+# - CGO_ENABLED=0
+#
+# goos: [linux]
+# goarch: [amd64]
archives:
- format: zip
diff --git a/v2/cmd/integration-test/generic.go b/v2/cmd/integration-test/generic.go
new file mode 100644
index 0000000000..dfd24b1651
--- /dev/null
+++ b/v2/cmd/integration-test/generic.go
@@ -0,0 +1,120 @@
+package main
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "os"
+
+ "github.com/julienschmidt/httprouter"
+
+ "github.com/projectdiscovery/nuclei/v2/pkg/testutils"
+)
+
+var genericTestcases = map[string]testutils.TestCase{
+ "generic/auth/certificate/http-get.yaml": &clientCertificate{},
+}
+
+var (
+ serverCRT = `-----BEGIN CERTIFICATE-----
+MIIDETCCAfkCFHA1RpGfOY5p/vQmeMQ1oRFqH+CGMA0GCSqGSIb3DQEBCwUAMEUx
+CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
+cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMwNjIxMDA0MjQ2WhcNMjMwNzIxMDA0
+MjQ2WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
+CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA3VdrKR5hmZ+vyvg6NB2dOL5vEIQ/9DevivnKWqX5mserYLMj
+Wq0knVfogewZnrDe+zVC3kOogBQvYk8Z53kTY9qpJT85dMCuW4xDx0JU+cWHul9a
+pzF+bvws4paCWIcsGONyocPAx5g07LbPU9civC80QkQqELo1zYiRU1bX8vRJJqbN
+TW2mzl9MN3AnCAYTwq8WhVG/1QR3LPQhPR68/1LWrFefQaEWaXT2s+Xv7K7NDXro
+WSba4SgKdFd6fyUVMVr/ioT1KT45TP5jbRrW5JJUTdpkiXaIucrZg39f6F5gTZGA
+U7bNROUMkqrJJngN9+Hp+YH1GpkKgu9EKA30EQIDAQABMA0GCSqGSIb3DQEBCwUA
+A4IBAQAw91bxiAi7DIVsKL3k4B0I+50ZKq9VMVNE3YCTPygpfuRiGQvlITZ5I8I5
+3Ok2wWltgKx6EnicHIlLg42yRj7j3mdgOLMFMrUCfJmdogwnS+k6veG3G1RHUs9r
+ATfX49u/hEX2pe7Rvx2VYVIugwrQESgQ21iaf6uUMsrq6W8eYZ31as1nJKpqIGbu
+W1fZMSi0RIUJP+mpVBE82IW+gJRi3uKU4HKPqyrU3dviBFdBxb3lNbh34/vdNkIw
+4H2CfBxEvdwLYAhWDerlm4wWCmjkMiHfBHPBhhOICTkR25a7NFy27h/UDHjVC/6m
+fGshVSBtxVPJP7kcvZ1scIctvFZZ
+-----END CERTIFICATE-----
+`
+ serverKey = `-----BEGIN PRIVATE KEY-----
+MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDdV2spHmGZn6/K
++Do0HZ04vm8QhD/0N6+K+cpapfmax6tgsyNarSSdV+iB7BmesN77NULeQ6iAFC9i
+TxnneRNj2qklPzl0wK5bjEPHQlT5xYe6X1qnMX5u/CziloJYhywY43Khw8DHmDTs
+ts9T1yK8LzRCRCoQujXNiJFTVtfy9Ekmps1NbabOX0w3cCcIBhPCrxaFUb/VBHcs
+9CE9Hrz/UtasV59BoRZpdPaz5e/srs0NeuhZJtrhKAp0V3p/JRUxWv+KhPUpPjlM
+/mNtGtbkklRN2mSJdoi5ytmDf1/oXmBNkYBTts1E5QySqskmeA334en5gfUamQqC
+70QoDfQRAgMBAAECggEBALtPsHMSr9vW5Giq2m6iJRwRJGJg2NJukZLVwuYlkW7n
+zGNAFgo1fkfdTfks+Z1u5rTGJPl9XkpNSrAyaqSVtNALCptnvtLMAIGe2Pj2bH0X
+Kb6R1WCqJOn9ZGq4nkQW2D2Ttb2psCn458jvB9NWu6FvfRUbJFIVk1SFXx6c3pFN
+kPCUudAiscaldUDCiz4FccKGXdRjq6HIeeWqvdErteb6JPTs9QXCHfBql9Esl4rK
+SHt9RmAFNY+CLExHiFPBR15hHZRtiVkAVrgnPg1CPGAyVG0hGXj7YMMWpAyfFWpn
+8gWVt7XJ4UX2knUwfU8p8dWe6qwf+AMrhravYJyccoUCgYEA8Ts0kHFnLga8Ewao
+nyDQs5uYGG0PWkbXqnFVYnMeSbXzyC4ouInIk/eOQABCxdjy3NF9QuYvVLpfLJ+9
+a97q1Vyg6lZ4PPuK8ZcPrHFSNNaj4eWNTOMo/Qdzz4bfflTsv8vjeeMxsqb6woXV
++E23UKCPlQPf86jugZVdaMtvZKsCgYEA6uR7glji70pVoG/f3soX1vllmVTtiLnh
+zYMmwPyTRDvoGgg/nGK+GCq//Xyn8D900hbX8KKqGX7ca5FGk5pOpW/QE9uLcuWK
+xcy8KAc05k1u4VaS5loWKnPGWreIpj3RbCfbPs5X/jBC+fPIA4Q8Qor5ZGdqVBvW
+IKejnNqasjMCgYEAqltPUbpkTWLAKweGyWnZOR3mmUlbkDt7Toje7bmyaAew82t1
+omzbU3N958DHZwVA7aSbu0TnpARB9jeRA77XRHo3wYXzP828X8R4cyVMEriJ35vG
+38eESLyckrAC4SqETyZjrM4/aJT3fawaYVIw5SWegHPOEjr4xFaBMuKH9iUCgYEA
+wFpC2kc374UMAcobpjIQu7aYAKyPqDuwMb+I6NjtMB9uvoKqtMIXsWqwtkBytkcA
+v1p9k01hxmcg0eWxygW/CbM6zkgnNfvLXJeALbdZFo+qkVV4DrMPG8ybToalnJ1a
+9hrda91GKZ4T+uQrktWjE0sDV7loVWBGRY+CaFyL+gkCgYEA3Z0j8VOLJnAKdCDp
+3N74460pykwJ2suEYSJG6glXfU3fZ5VwAYjimxgD0S2VU4qK8PYBfa/oFH2vRX5p
+11dWQWbfBdREO70UmJD4Pr6g3q9AF6DXLXb7dVm4y+hX065Xshk8oIuITVyO/XVK
+wWqBD5GScI+Q7PLMes7aqtsDDJI=
+-----END PRIVATE KEY-----
+`
+)
+
+type clientCertificate struct{}
+
+// Execute executes a test case and returns an error if occurred
+func (h *clientCertificate) Execute(filePath string) error {
+ router := httprouter.New()
+
+ router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ if len(r.TLS.PeerCertificates) == 0 {
+ http.Error(w, "Client certificate required", http.StatusForbidden)
+ return
+ }
+
+ fmt.Fprintf(w, "Hello, %s!\n", r.TLS.PeerCertificates[0].Subject)
+ })
+
+ _ = os.WriteFile("server.crt", []byte(serverCRT), os.ModePerm)
+ _ = os.WriteFile("server.key", []byte(serverKey), os.ModePerm)
+ defer os.Remove("server.crt")
+ defer os.Remove("server.key")
+
+ serverCert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
+
+ certPool := x509.NewCertPool()
+ caCert, _ := os.ReadFile("server.crt")
+ certPool.AppendCertsFromPEM(caCert)
+
+ tlsConfig := &tls.Config{
+ Certificates: []tls.Certificate{serverCert},
+ ClientAuth: tls.RequireAndVerifyClientCert,
+ ClientCAs: certPool,
+ }
+
+ ts := httptest.NewUnstartedServer(router)
+
+ ts.TLS = tlsConfig
+
+ ts.StartTLS()
+ defer ts.Close()
+
+ results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug,
+ "-ca", "generic/auth/certificate/assets/server.crt",
+ "-cc", "generic/auth/certificate/assets/client.crt",
+ "-ck", "generic/auth/certificate/assets/client.key")
+ if err != nil {
+ return err
+ }
+
+ return expectResultsCount(results, 1)
+}
diff --git a/v2/cmd/integration-test/integration-test.go b/v2/cmd/integration-test/integration-test.go
index 73ee2a7652..2012f11321 100644
--- a/v2/cmd/integration-test/integration-test.go
+++ b/v2/cmd/integration-test/integration-test.go
@@ -39,6 +39,7 @@ var (
"offlineHttp": offlineHttpTestcases,
"customConfigDir": customConfigDirTestCases,
"fuzzing": fuzzingTestCases,
+ "generic": genericTestcases,
}
// For debug purposes
diff --git a/v2/cmd/integration-test/workflow.go b/v2/cmd/integration-test/workflow.go
index 3fa08523ba..fc22d8d8fb 100644
--- a/v2/cmd/integration-test/workflow.go
+++ b/v2/cmd/integration-test/workflow.go
@@ -18,6 +18,7 @@ var workflowTestcases = map[string]testutils.TestCase{
"workflow/matcher-name.yaml": &workflowMatcherName{},
"workflow/http-value-share-workflow.yaml": &workflowHttpKeyValueShare{},
"workflow/dns-value-share-workflow.yaml": &workflowDnsKeyValueShare{},
+ "workflow/shared-cookie.yaml": &workflowSharedCookies{},
}
type workflowBasic struct{}
@@ -131,3 +132,39 @@ func (h *workflowDnsKeyValueShare) Execute(filePath string) error {
// no results - ensure that the variable sharing works
return expectResultsCount(results, 1)
}
+
+type workflowSharedCookies struct{}
+
+// Execute executes a test case and returns an error if occurred
+func (h *workflowSharedCookies) Execute(filePath string) error {
+ handleFunc := func(name string, w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ cookie := &http.Cookie{Name: name, Value: name}
+ http.SetCookie(w, cookie)
+ }
+
+ var gotCookies []string
+ router := httprouter.New()
+ router.GET("/http1", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
+ handleFunc("http1", w, r, p)
+ })
+ router.GET("/http2", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
+ handleFunc("http2", w, r, p)
+ })
+ router.GET("/headless1", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
+ handleFunc("headless1", w, r, p)
+ })
+ router.GET("/http3", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
+ for _, cookie := range r.Cookies() {
+ gotCookies = append(gotCookies, cookie.Name)
+ }
+ })
+ ts := httptest.NewServer(router)
+ defer ts.Close()
+
+ _, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug, "-headless")
+ if err != nil {
+ return err
+ }
+
+ return expectResultsCount(gotCookies, 3)
+}
diff --git a/v2/go.mod b/v2/go.mod
index bb3bb4500b..ad870cc0f4 100644
--- a/v2/go.mod
+++ b/v2/go.mod
@@ -13,20 +13,20 @@ require (
github.com/go-rod/rod v0.113.0
github.com/gobwas/ws v1.2.1
github.com/google/go-github v17.0.0+incompatible
- github.com/itchyny/gojq v0.12.12
+ github.com/itchyny/gojq v0.12.13
github.com/json-iterator/go v1.1.12
github.com/julienschmidt/httprouter v1.3.0
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/miekg/dns v1.1.55
github.com/olekukonko/tablewriter v0.0.5
github.com/pkg/errors v0.9.1
- github.com/projectdiscovery/clistats v0.0.12
+ github.com/projectdiscovery/clistats v0.0.18
github.com/projectdiscovery/fastdialer v0.0.31
github.com/projectdiscovery/hmap v0.0.13
github.com/projectdiscovery/interactsh v1.1.4
github.com/projectdiscovery/rawhttp v0.1.13
github.com/projectdiscovery/retryabledns v1.0.30
- github.com/projectdiscovery/retryablehttp-go v1.0.17
+ github.com/projectdiscovery/retryablehttp-go v1.0.18
github.com/projectdiscovery/yamldoc-go v1.0.4
github.com/remeh/sizedwaitgroup v1.0.0
github.com/rs/xid v1.5.0
@@ -58,7 +58,7 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.18.27
github.com/aws/aws-sdk-go-v2/credentials v1.13.26
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.67
- github.com/aws/aws-sdk-go-v2/service/s3 v1.33.1
+ github.com/aws/aws-sdk-go-v2/service/s3 v1.35.0
github.com/docker/go-units v0.5.0
github.com/fatih/structs v1.1.0
github.com/go-git/go-git/v5 v5.7.0
@@ -77,8 +77,8 @@ require (
github.com/projectdiscovery/sarif v0.0.1
github.com/projectdiscovery/tlsx v1.1.0
github.com/projectdiscovery/uncover v1.0.6-0.20230601103158-bfd7e02a5bb1
- github.com/projectdiscovery/utils v0.0.39-0.20230621170112-8dd2c290d962
- github.com/projectdiscovery/wappalyzergo v0.0.94
+ github.com/projectdiscovery/utils v0.0.39
+ github.com/projectdiscovery/wappalyzergo v0.0.102
github.com/stretchr/testify v1.8.4
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/yaml.v3 v3.0.1
@@ -93,10 +93,10 @@ require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
- github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.26 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.29 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.3 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bits-and-blooms/bitset v1.3.1 // indirect
github.com/bits-and-blooms/bloom/v3 v3.4.0 // indirect
@@ -126,7 +126,7 @@ require (
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/projectdiscovery/asnmap v1.0.4 // indirect
github.com/projectdiscovery/cdncheck v1.0.6 // indirect
- github.com/projectdiscovery/freeport v0.0.4 // indirect
+ github.com/projectdiscovery/freeport v0.0.5 // indirect
github.com/refraction-networking/utls v1.3.2 // indirect
github.com/sashabaranov/go-openai v1.11.2 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
@@ -187,7 +187,7 @@ require (
github.com/libdns/libdns v0.2.1 // indirect
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
- github.com/mattn/go-isatty v0.0.17 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mholt/acmez v1.0.4 // indirect
github.com/microcosm-cc/bluemonday v1.0.24 // indirect
diff --git a/v2/go.sum b/v2/go.sum
index 2401376404..b37dd5f472 100644
--- a/v2/go.sum
+++ b/v2/go.sum
@@ -94,19 +94,23 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkf
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25 h1:AzwRi5OKKwo4QNqPf7TjeO+tK8AyOK3GVSwmRPo7/Cs=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25/go.mod h1:SUbB4wcbSEyCvqBxv/O/IBf93RbEze7U7OnoTlpPB+g=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.26 h1:wscW+pnn3J1OYnanMnza5ZVYXLX4cKk5rAvUAl4Qu+c=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.26/go.mod h1:MtYiox5gvyB+OyP0Mr0Sm/yzbEAIPL9eijj/ouHAPw0=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28 h1:vGWm5vTpMr39tEZfQeDiDAMgk+5qsnvRny3FjLpnH5w=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28/go.mod h1:spfrICMD6wCAhjhzHuy6DOZZ+LAIY10UxhUmLzpJTTs=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.29 h1:zZSLP3v3riMOP14H7b4XP0uyfREDQOYv2cqIrvTXDNQ=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.29/go.mod h1:z7EjRjVwZ6pWcWdI2H64dKttvzaP99jRIj5hphW0M5U=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2 h1:NbWkRxEEIRSCqxhsHQuMiTH7yo+JZW1gp8v3elSVMTQ=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2/go.mod h1:4tfW5l4IAB32VWCDEBxCRtR9T4BWy4I4kr1spr8NgZM=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.33.1 h1:O+9nAy9Bb6bJFTpeNFtd9UfHbgxO1o4ZDAM9rQp5NsY=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.3 h1:dBL3StFxHtpBzJJ/mNEsjXVgfO+7jR0dAIEwLqMapEA=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.3/go.mod h1:f1QyiAsvIv4B49DmCqrhlXqyaR+0IxMmyX+1P+AnzOM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.33.1/go.mod h1:J9kLNzEiHSeGMyN7238EjJmBpCniVzFda75Gxl/NqB8=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.35.0 h1:ya7fmrN2fE7s1P2gaPbNg5MTkERVWfsH8ToP1YC4Z9o=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.35.0/go.mod h1:aVbf0sko/TsLWHx30c/uVu7c62+0EAJ3vbxaJga0xCw=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY=
@@ -271,8 +275,8 @@ github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439Z
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
-github.com/itchyny/gojq v0.12.12 h1:x+xGI9BXqKoJQZkr95ibpe3cdrTbY8D9lonrK433rcA=
-github.com/itchyny/gojq v0.12.12/go.mod h1:j+3sVkjxwd7A7Z5jrbKibgOLn0ZfLWkV+Awxr/pyzJE=
+github.com/itchyny/gojq v0.12.13 h1:IxyYlHYIlspQHHTE0f3cJF0NKDMfajxViuhBLnHd/QU=
+github.com/itchyny/gojq v0.12.13/go.mod h1:JzwzAqenfhrPUuwbmEz3nu3JQmFLlQTQMUcOdnu/Sf4=
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
@@ -338,8 +342,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
@@ -403,16 +407,16 @@ github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k
github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
github.com/projectdiscovery/cdncheck v1.0.6 h1:bjo4oxCD1Y5972ow0LWCjUpO8KOO12j6uGfPofVpC4c=
github.com/projectdiscovery/cdncheck v1.0.6/go.mod h1:NN0QRfxBzUVZJoS0lN37spElCOXHzFuvq1yg5RhTxCE=
-github.com/projectdiscovery/clistats v0.0.12 h1:KLYJxpiwEFidduU4PbcwEcCQ2L7c5wrf7DI5IN5fZ+8=
-github.com/projectdiscovery/clistats v0.0.12/go.mod h1:9luKJj+7Hjq3+a7g129sKWRYx4SbTdkUWZQxabn3H5Y=
+github.com/projectdiscovery/clistats v0.0.18 h1:WLQNqLXsKvjoieDwXJO/1jlnxR0x9vdFaRUAR3gXfKQ=
+github.com/projectdiscovery/clistats v0.0.18/go.mod h1:YUnUrMHFw+FHwUTIKr1KDUwz81x+SFjPU3xfLqXfzf0=
github.com/projectdiscovery/dsl v0.0.11-0.20230621170216-97e70ffb7efd h1:16DMjd4HeACrC9CkWJkkLeSh+LYPDorwNx11BlTbonU=
github.com/projectdiscovery/dsl v0.0.11-0.20230621170216-97e70ffb7efd/go.mod h1:S72Cq/lfxzkldf64Sul1G2KFbGKNgpRFFCF/FazpznM=
github.com/projectdiscovery/fastdialer v0.0.31 h1:eu0wTBCWjT8dXChmBtnQaAxoFpkLdvq0VroRxZoe/M8=
github.com/projectdiscovery/fastdialer v0.0.31/go.mod h1:ttLvt0xnpNQAStYYQ6ElIBHfSXHuPEiXBkLH/OLbYlc=
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.4 h1:H4VrK/7hUcC1zbg46zv9iSMBACBDpUqcHkV+FUyXISw=
-github.com/projectdiscovery/freeport v0.0.4/go.mod h1:PY0bxSJ34HVy67LHIeF3uIutiCSDwOqKD8ruBkdiCwE=
+github.com/projectdiscovery/freeport v0.0.5 h1:jnd3Oqsl4S8n0KuFkE5Hm8WGDP24ITBvmyw5pFTHS8Q=
+github.com/projectdiscovery/freeport v0.0.5/go.mod h1:PY0bxSJ34HVy67LHIeF3uIutiCSDwOqKD8ruBkdiCwE=
github.com/projectdiscovery/goflags v0.1.10 h1:Gompf8JDy8y+5c4eWlc70KKtPuDH/hqFB3tMeHcMiKk=
github.com/projectdiscovery/goflags v0.1.10/go.mod h1:MHEkqm3XgxBf5fK4gr3IXsj6VeLTq4qJYGC/4JRYQ74=
github.com/projectdiscovery/gologger v1.1.10 h1:XNRdtzLTdxiFGuK9gutoL752mykzXDoii4P2yDovqck=
@@ -435,8 +439,8 @@ github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917 h1:m03X4gB
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917/go.mod h1:JxXtZC9e195awe7EynrcnBJmFoad/BNDzW9mzFkK8Sg=
github.com/projectdiscovery/retryabledns v1.0.30 h1:7bc8Lq3r/qzw4LdXXAxKtQa52iGiEx1WasZLVCO6Oj0=
github.com/projectdiscovery/retryabledns v1.0.30/go.mod h1:+Aqc0TjKGcTtP0HtXE8o1GzrjAHhSno6hSF+L63TBtI=
-github.com/projectdiscovery/retryablehttp-go v1.0.17 h1:oppnrypatWsHxcMU5RuAcUsUu3nxBhId2CF3OBj9XJA=
-github.com/projectdiscovery/retryablehttp-go v1.0.17/go.mod h1:zJh8bQdxhIsaEGnxsacvMbgiCKT4UAOr4T1kZBnSa68=
+github.com/projectdiscovery/retryablehttp-go v1.0.18 h1:3IUxyIOOUVSGEBm4pV0cQSk1i/DausZdHePdGDip0Lg=
+github.com/projectdiscovery/retryablehttp-go v1.0.18/go.mod h1:oE3dmYWMadFWzaIfG1IqINsYAzUWYUtdI4PJ2xo7cXg=
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
@@ -444,10 +448,10 @@ github.com/projectdiscovery/tlsx v1.1.0 h1:6L5VKpHaoqvIHN6lH9zi7jIvph1JwYMYZOIpW
github.com/projectdiscovery/tlsx v1.1.0/go.mod h1:C9xTbU2t54Anmvuq+4jxevR5rzqpp6XUUtV7G9J5CTE=
github.com/projectdiscovery/uncover v1.0.6-0.20230601103158-bfd7e02a5bb1 h1:Pu6LvDqn+iSlhCDKKWm1ItPc++kqqlU8OntZeB/Prak=
github.com/projectdiscovery/uncover v1.0.6-0.20230601103158-bfd7e02a5bb1/go.mod h1:Drl/CWD392mKtdXJhCBPlMkM0I6671pqedFphcnK5f8=
-github.com/projectdiscovery/utils v0.0.39-0.20230621170112-8dd2c290d962 h1:qQnIsYB72MmuaM9orhKpDzY0ddJKHf9Nuih0FnyV6x8=
-github.com/projectdiscovery/utils v0.0.39-0.20230621170112-8dd2c290d962/go.mod h1:rrd8dTBuKEScNMLgs1Xiu8rPCVeR0QTzmRcQ5iM3ymo=
-github.com/projectdiscovery/wappalyzergo v0.0.94 h1:IVRskuU95MajWCKYgvH5L67+MXDOWJDWSeBD61OsS/A=
-github.com/projectdiscovery/wappalyzergo v0.0.94/go.mod h1:HvYuW0Be4JCjVds/+XAEaMSqRG9yrI97UmZq0TPk6A0=
+github.com/projectdiscovery/utils v0.0.39 h1:iyi5qPilENRmFyt16qtd58pb65fUu0wAU2C0Lq5t6zo=
+github.com/projectdiscovery/utils v0.0.39/go.mod h1:rrd8dTBuKEScNMLgs1Xiu8rPCVeR0QTzmRcQ5iM3ymo=
+github.com/projectdiscovery/wappalyzergo v0.0.102 h1:ABjZghof2U2yzGNL+q5ouWHEardLd2o53Ukgrf8CZzE=
+github.com/projectdiscovery/wappalyzergo v0.0.102/go.mod h1:4Z3DKhi75zIPMuA+qSDDWxZvnhL4qTLmDx4dxNMu7MA=
github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE=
github.com/projectdiscovery/yamldoc-go v1.0.4/go.mod h1:8PIPRcUD55UbtQdcfFR1hpIGRWG0P7alClXNGt1TBik=
github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8=
@@ -504,7 +508,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
@@ -657,6 +660,7 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -718,6 +722,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -727,6 +732,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
diff --git a/v2/internal/runner/options.go b/v2/internal/runner/options.go
index a782511a1d..7c93c46ddb 100644
--- a/v2/internal/runner/options.go
+++ b/v2/internal/runner/options.go
@@ -22,6 +22,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
fileutil "github.com/projectdiscovery/utils/file"
+ "github.com/projectdiscovery/utils/generic"
logutil "github.com/projectdiscovery/utils/log"
stringsutil "github.com/projectdiscovery/utils/strings"
)
@@ -128,11 +129,11 @@ func validateOptions(options *types.Options) error {
}
// Verify if any of the client certificate options were set since it requires all three to work properly
- if len(options.ClientCertFile) > 0 || len(options.ClientKeyFile) > 0 || len(options.ClientCAFile) > 0 {
- if len(options.ClientCertFile) == 0 || len(options.ClientKeyFile) == 0 || len(options.ClientCAFile) == 0 {
+ if options.HasClientCertificates() {
+ if generic.EqualsAny("", options.ClientCertFile, options.ClientKeyFile, options.ClientCAFile) {
return errors.New("if a client certification option is provided, then all three must be provided")
}
- validateCertificatePaths([]string{options.ClientCertFile, options.ClientKeyFile, options.ClientCAFile})
+ validateCertificatePaths(options.ClientCertFile, options.ClientKeyFile, options.ClientCAFile)
}
// Verify AWS secrets are passed if a S3 template bucket is passed
if options.AwsBucketName != "" && options.UpdateTemplates {
@@ -334,9 +335,9 @@ func validateTemplatePaths(templatesDirectory string, templatePaths, workflowPat
}
}
-func validateCertificatePaths(certificatePaths []string) {
+func validateCertificatePaths(certificatePaths ...string) {
for _, certificatePath := range certificatePaths {
- if _, err := os.Stat(certificatePath); os.IsNotExist(err) {
+ if !fileutil.FileExists(certificatePath) {
// The provided path to the PEM certificate does not exist for the client authentication. As this is
// required for successful authentication, log and return an error
gologger.Fatal().Msgf("The given path (%s) to the certificate does not exist!", certificatePath)
diff --git a/v2/pkg/catalog/config/constants.go b/v2/pkg/catalog/config/constants.go
index df040cadfd..8ce1fbf952 100644
--- a/v2/pkg/catalog/config/constants.go
+++ b/v2/pkg/catalog/config/constants.go
@@ -7,17 +7,17 @@ import (
)
const (
- TemplateConfigFileName = ".templates-config.json"
- NucleiTemplatesDirName = "nuclei-templates"
+ TemplateConfigFileName = ".templates-config.json"
+ NucleiTemplatesDirName = "nuclei-templates"
OfficialNucleiTemplatesRepoName = "nuclei-templates"
- NucleiIgnoreFileName = ".nuclei-ignore"
- NucleiTemplatesIndexFileName = ".templates-index" // contains index of official nuclei templates
- NucleiTemplatesCheckSumFileName = ".checksum"
- NewTemplateAdditionsFileName = ".new-additions"
- CLIConifgFileName = "config.yaml"
- ReportingConfigFilename = "reporting-config.yaml"
+ NucleiIgnoreFileName = ".nuclei-ignore"
+ NucleiTemplatesIndexFileName = ".templates-index" // contains index of official nuclei templates
+ NucleiTemplatesCheckSumFileName = ".checksum"
+ NewTemplateAdditionsFileName = ".new-additions"
+ CLIConifgFileName = "config.yaml"
+ ReportingConfigFilename = "reporting-config.yaml"
// Version is the current version of nuclei
- Version = `v2.9.6`
+ Version = `v2.9.7`
// Directory Names of custom templates
CustomS3TemplatesDirName = "s3"
CustomGithubTemplatesDirName = "github"
diff --git a/v2/pkg/model/model.go b/v2/pkg/model/model.go
index e57ff896d7..93e92f9664 100644
--- a/v2/pkg/model/model.go
+++ b/v2/pkg/model/model.go
@@ -105,4 +105,4 @@ type Classification struct {
// examples:
// - value: "\"cpe:/a:vendor:product:version\""
CPE string `json:"cpe,omitempty" yaml:"cpe,omitempty" jsonschema:"title=cpe for the template,description=CPE for the template,example=cpe:/a:vendor:product:version"`
-}
\ No newline at end of file
+}
diff --git a/v2/pkg/operators/extractors/extract.go b/v2/pkg/operators/extractors/extract.go
index bf622ff398..19ccab38b3 100644
--- a/v2/pkg/operators/extractors/extract.go
+++ b/v2/pkg/operators/extractors/extract.go
@@ -175,7 +175,7 @@ func (e *Extractor) ExtractDSL(data map[string]interface{}) map[string]struct{}
for _, compiledExpression := range e.dslCompiled {
result, err := compiledExpression.Evaluate(data)
// ignore errors that are related to missing parameters
- // eg: dns dsl can have all the parameters that are not present
+ // eg: dns dsl can have all the parameters that are not present
if err != nil && !strings.HasPrefix(err.Error(), "No parameter") {
return results
}
diff --git a/v2/pkg/parsers/parser.go b/v2/pkg/parsers/parser.go
index b0f78ecee5..2038395cec 100644
--- a/v2/pkg/parsers/parser.go
+++ b/v2/pkg/parsers/parser.go
@@ -80,7 +80,6 @@ func isTemplateInfoMetadataMatch(tagFilter *filter.TagFilter, template *template
match, err := tagFilter.Match(template, extraTags)
if err == filter.ErrExcluded {
-
return false, filter.ErrExcluded
}
diff --git a/v2/pkg/progress/progress.go b/v2/pkg/progress/progress.go
index 6ef593329c..014be676af 100644
--- a/v2/pkg/progress/progress.go
+++ b/v2/pkg/progress/progress.go
@@ -99,15 +99,23 @@ func (p *StatsTicker) Init(hostCount int64, rulesCount int, requestCount int64)
p.stats.AddCounter("total", uint64(requestCount))
if p.active {
- var printCallbackFunc clistats.PrintCallback
+ var printCallbackFunc clistats.DynamicCallback
if p.outputJSON {
printCallbackFunc = printCallbackJSON
} else {
printCallbackFunc = p.makePrintCallback()
}
- if err := p.stats.Start(printCallbackFunc, p.tickDuration); err != nil {
+ p.stats.AddDynamic("summary", printCallbackFunc)
+ if err := p.stats.Start(); err != nil {
gologger.Warning().Msgf("Couldn't start statistics: %s", err)
}
+
+ p.stats.GetStatResponse(p.tickDuration, func(s string, err error) error {
+ if err != nil {
+ gologger.Warning().Msgf("Could not read statistics: %s\n", err)
+ }
+ return nil
+ })
}
}
@@ -145,8 +153,8 @@ func (p *StatsTicker) IncrementFailedRequestsBy(count int64) {
p.stats.IncrementCounter("errors", int(count))
}
-func (p *StatsTicker) makePrintCallback() func(stats clistats.StatisticsClient) {
- return func(stats clistats.StatisticsClient) {
+func (p *StatsTicker) makePrintCallback() func(stats clistats.StatisticsClient) interface{} {
+ return func(stats clistats.StatisticsClient) interface{} {
builder := &strings.Builder{}
var duration time.Duration
@@ -209,14 +217,16 @@ func (p *StatsTicker) makePrintCallback() func(stats clistats.StatisticsClient)
}
fmt.Fprintf(os.Stderr, "%s", builder.String())
+ return builder.String()
}
}
-func printCallbackJSON(stats clistats.StatisticsClient) {
+func printCallbackJSON(stats clistats.StatisticsClient) interface{} {
builder := &strings.Builder{}
if err := json.NewEncoder(builder).Encode(metricsMap(stats)); err == nil {
fmt.Fprintf(os.Stderr, "%s", builder.String())
}
+ return builder.String()
}
func metricsMap(stats clistats.StatisticsClient) map[string]interface{} {
diff --git a/v2/pkg/protocols/common/fuzz/execute.go b/v2/pkg/protocols/common/fuzz/execute.go
index 3c7173b9d7..a16c7107e5 100644
--- a/v2/pkg/protocols/common/fuzz/execute.go
+++ b/v2/pkg/protocols/common/fuzz/execute.go
@@ -6,6 +6,7 @@ import (
"github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
"github.com/projectdiscovery/retryablehttp-go"
urlutil "github.com/projectdiscovery/utils/url"
@@ -13,8 +14,8 @@ import (
// ExecuteRuleInput is the input for rule Execute function
type ExecuteRuleInput struct {
- // URL is the URL for the request
- URL *urlutil.URL
+ // Input is the context args input
+ Input *contextargs.Context
// Callback is the callback for generated rule requests
Callback func(GeneratedRequest) bool
// InteractURLs contains interact urls for execute call
@@ -41,7 +42,7 @@ type GeneratedRequest struct {
// Input is not thread safe and should not be shared between concurrent
// goroutines.
func (rule *Rule) Execute(input *ExecuteRuleInput) error {
- if !rule.isExecutable(input.URL) {
+ if !rule.isExecutable(input.Input) {
return nil
}
baseValues := input.Values
@@ -69,7 +70,11 @@ func (rule *Rule) Execute(input *ExecuteRuleInput) error {
}
// isExecutable returns true if the rule can be executed based on provided input
-func (rule *Rule) isExecutable(parsed *urlutil.URL) bool {
+func (rule *Rule) isExecutable(input *contextargs.Context) bool {
+ parsed, err := urlutil.Parse(input.MetaInput.Input)
+ if err != nil {
+ return false
+ }
if len(parsed.Query()) > 0 && rule.partType == queryPartType {
return true
}
diff --git a/v2/pkg/protocols/common/fuzz/execute_test.go b/v2/pkg/protocols/common/fuzz/execute_test.go
index 7f44f94035..88582e1b35 100644
--- a/v2/pkg/protocols/common/fuzz/execute_test.go
+++ b/v2/pkg/protocols/common/fuzz/execute_test.go
@@ -3,7 +3,7 @@ package fuzz
import (
"testing"
- urlutil "github.com/projectdiscovery/utils/url"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/stretchr/testify/require"
)
@@ -12,11 +12,11 @@ func TestRuleIsExecutable(t *testing.T) {
err := rule.Compile(nil, nil)
require.NoError(t, err, "could not compile rule")
- parsed, _ := urlutil.Parse("https://example.com/?url=localhost")
- result := rule.isExecutable(parsed)
+ input := contextargs.NewWithInput("https://example.com/?url=localhost")
+ result := rule.isExecutable(input)
require.True(t, result, "could not get correct result")
- parsed, _ = urlutil.Parse("https://example.com/")
- result = rule.isExecutable(parsed)
+ input = contextargs.NewWithInput("https://example.com/")
+ result = rule.isExecutable(input)
require.False(t, result, "could not get correct result")
}
diff --git a/v2/pkg/protocols/common/fuzz/parts.go b/v2/pkg/protocols/common/fuzz/parts.go
index 9c0c43025a..0576f302f3 100644
--- a/v2/pkg/protocols/common/fuzz/parts.go
+++ b/v2/pkg/protocols/common/fuzz/parts.go
@@ -24,16 +24,20 @@ func (rule *Rule) executePartRule(input *ExecuteRuleInput, payload string) error
// executeQueryPartRule executes query part rules
func (rule *Rule) executeQueryPartRule(input *ExecuteRuleInput, payload string) error {
- requestURL := input.URL.Clone()
+ requestURL, err := urlutil.Parse(input.Input.MetaInput.Input)
+ if err != nil {
+ return err
+ }
+ origRequestURL := requestURL.Clone()
temp := urlutil.Params{}
- for k, v := range input.URL.Query() {
+ for k, v := range origRequestURL.Query() {
// this has to be a deep copy
x := []string{}
x = append(x, v...)
temp[k] = x
}
- for key, values := range input.URL.Query() {
+ for key, values := range origRequestURL.Query() {
for i, value := range values {
if !rule.matchKeyOrValue(key, value) {
continue
diff --git a/v2/pkg/protocols/common/fuzz/parts_test.go b/v2/pkg/protocols/common/fuzz/parts_test.go
index e4402c946a..e1855a3caf 100644
--- a/v2/pkg/protocols/common/fuzz/parts_test.go
+++ b/v2/pkg/protocols/common/fuzz/parts_test.go
@@ -4,13 +4,13 @@ import (
"testing"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
- urlutil "github.com/projectdiscovery/utils/url"
"github.com/stretchr/testify/require"
)
func TestExecuteQueryPartRule(t *testing.T) {
- parsed, _ := urlutil.Parse("http://localhost:8080/?url=localhost&mode=multiple&file=passwdfile")
+ URL := "http://localhost:8080/?url=localhost&mode=multiple&file=passwdfile"
options := &protocols.ExecutorOptions{
Interactsh: &interactsh.Client{},
}
@@ -22,8 +22,9 @@ func TestExecuteQueryPartRule(t *testing.T) {
options: options,
}
var generatedURL []string
+ input := contextargs.NewWithInput(URL)
err := rule.executeQueryPartRule(&ExecuteRuleInput{
- URL: parsed,
+ Input: input,
Callback: func(gr GeneratedRequest) bool {
generatedURL = append(generatedURL, gr.Request.URL.String())
return true
@@ -44,8 +45,9 @@ func TestExecuteQueryPartRule(t *testing.T) {
options: options,
}
var generatedURL string
+ input := contextargs.NewWithInput(URL)
err := rule.executeQueryPartRule(&ExecuteRuleInput{
- URL: parsed,
+ Input: input,
Callback: func(gr GeneratedRequest) bool {
generatedURL = gr.Request.URL.String()
return true
diff --git a/v2/pkg/protocols/headless/engine/page.go b/v2/pkg/protocols/headless/engine/page.go
index 96bbb15af0..e23d2eb253 100644
--- a/v2/pkg/protocols/headless/engine/page.go
+++ b/v2/pkg/protocols/headless/engine/page.go
@@ -1,7 +1,9 @@
package engine
import (
+ "bufio"
"fmt"
+ "net/http"
"net/url"
"strings"
"sync"
@@ -9,10 +11,14 @@ import (
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/proto"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils"
)
// Page is a single page in an isolated browser instance
type Page struct {
+ input *contextargs.Context
+ options *Options
page *rod.Page
rules []rule
instance *Instance
@@ -30,13 +36,19 @@ type HistoryData struct {
RawResponse string
}
+// Options contains additional configuration options for the browser instance
+type Options struct {
+ Timeout time.Duration
+ CookieReuse bool
+}
+
// Run runs a list of actions by creating a new page in the browser.
-func (i *Instance) Run(baseURL *url.URL, actions []*Action, payloads map[string]interface{}, timeout time.Duration) (map[string]string, *Page, error) {
+func (i *Instance) Run(input *contextargs.Context, actions []*Action, payloads map[string]interface{}, options *Options) (map[string]string, *Page, error) {
page, err := i.engine.Page(proto.TargetCreateTarget{})
if err != nil {
return nil, nil, err
}
- page = page.Timeout(timeout)
+ page = page.Timeout(options.Timeout)
if i.browser.customAgent != "" {
if userAgentErr := page.SetUserAgent(&proto.NetworkSetUserAgentOverride{UserAgent: i.browser.customAgent}); userAgentErr != nil {
@@ -44,7 +56,14 @@ func (i *Instance) Run(baseURL *url.URL, actions []*Action, payloads map[string]
}
}
- createdPage := &Page{page: page, instance: i, mutex: &sync.RWMutex{}, payloads: payloads}
+ createdPage := &Page{
+ options: options,
+ page: page,
+ input: input,
+ instance: i,
+ mutex: &sync.RWMutex{},
+ payloads: payloads,
+ }
// in case the page has request/response modification rules - enable global hijacking
if createdPage.hasModificationRules() || containsModificationActions(actions...) {
@@ -79,18 +98,76 @@ func (i *Instance) Run(baseURL *url.URL, actions []*Action, payloads map[string]
return nil, nil, err
}
- //FIXME: this is a hack, make sure to fix this in the future. See: https://github.com/go-rod/rod/issues/188
- var e proto.NetworkResponseReceived
- wait := page.WaitEvent(&e)
+ // inject cookies
+ // each http request is performed via the native go http client
+ // we first inject the shared cookies
+ URL, err := url.Parse(input.MetaInput.Input)
+ if err != nil {
+ return nil, nil, err
+ }
- data, err := createdPage.ExecuteActions(baseURL, actions)
+ if options.CookieReuse {
+ if cookies := input.CookieJar.Cookies(URL); len(cookies) > 0 {
+ var NetworkCookies []*proto.NetworkCookie
+ for _, cookie := range cookies {
+ networkCookie := &proto.NetworkCookie{
+ Name: cookie.Name,
+ Value: cookie.Value,
+ Domain: cookie.Domain,
+ Path: cookie.Path,
+ HTTPOnly: cookie.HttpOnly,
+ Secure: cookie.Secure,
+ Expires: proto.TimeSinceEpoch(cookie.Expires.Unix()),
+ SameSite: proto.NetworkCookieSameSite(GetSameSite(cookie)),
+ Priority: proto.NetworkCookiePriorityLow,
+ }
+ NetworkCookies = append(NetworkCookies, networkCookie)
+ }
+ params := proto.CookiesToParams(NetworkCookies)
+ for _, param := range params {
+ param.URL = input.MetaInput.Input
+ }
+ err := page.SetCookies(params)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+ }
+
+ data, err := createdPage.ExecuteActions(input, actions)
if err != nil {
return nil, nil, err
}
- wait()
- data["header"] = headersToString(e.Response.Headers)
- data["status_code"] = fmt.Sprint(e.Response.Status)
+ if options.CookieReuse {
+ // at the end of actions pull out updated cookies from the browser and inject them into the shared cookie jar
+ if cookies, err := page.Cookies([]string{URL.String()}); options.CookieReuse && err == nil && len(cookies) > 0 {
+ var httpCookies []*http.Cookie
+ for _, cookie := range cookies {
+ httpCookie := &http.Cookie{
+ Name: cookie.Name,
+ Value: cookie.Value,
+ Domain: cookie.Domain,
+ Path: cookie.Path,
+ HttpOnly: cookie.HTTPOnly,
+ Secure: cookie.Secure,
+ }
+ httpCookies = append(httpCookies, httpCookie)
+ }
+ input.CookieJar.SetCookies(URL, httpCookies)
+ }
+ }
+
+ // The first item of history data will contain the very first request from the browser
+ // we assume it's the one matching the initial URL
+ if len(createdPage.History) > 0 {
+ firstItem := createdPage.History[0]
+ if resp, err := http.ReadResponse(bufio.NewReader(strings.NewReader(firstItem.RawResponse)), nil); err == nil {
+ data["header"] = utils.HeadersToString(resp.Header)
+ data["status_code"] = fmt.Sprint(resp.StatusCode)
+ resp.Body.Close()
+ }
+ }
return data, createdPage, nil
}
@@ -189,14 +266,17 @@ func containsAnyModificationActionType(actionTypes ...ActionType) bool {
return false
}
-// headersToString converts network headers to string
-func headersToString(headers proto.NetworkHeaders) string {
- builder := &strings.Builder{}
- for header, value := range headers {
- builder.WriteString(header)
- builder.WriteString(": ")
- builder.WriteString(value.String())
- builder.WriteRune('\n')
+func GetSameSite(cookie *http.Cookie) string {
+ switch cookie.SameSite {
+ case http.SameSiteNoneMode:
+ return "none"
+ case http.SameSiteLaxMode:
+ return "lax"
+ case http.SameSiteStrictMode:
+ return "strict"
+ case http.SameSiteDefaultMode:
+ fallthrough
+ default:
+ return ""
}
- return builder.String()
}
diff --git a/v2/pkg/protocols/headless/engine/page_actions.go b/v2/pkg/protocols/headless/engine/page_actions.go
index 294abbe6a7..b96d4f264d 100644
--- a/v2/pkg/protocols/headless/engine/page_actions.go
+++ b/v2/pkg/protocols/headless/engine/page_actions.go
@@ -18,6 +18,7 @@ import (
"github.com/go-rod/rod/lib/utils"
"github.com/pkg/errors"
"github.com/projectdiscovery/gologger"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
errorutil "github.com/projectdiscovery/utils/errors"
fileutil "github.com/projectdiscovery/utils/file"
@@ -38,8 +39,11 @@ const (
)
// ExecuteActions executes a list of actions on a page.
-func (p *Page) ExecuteActions(baseURL *url.URL, actions []*Action) (map[string]string, error) {
- var err error
+func (p *Page) ExecuteActions(input *contextargs.Context, actions []*Action) (map[string]string, error) {
+ baseURL, err := url.Parse(input.MetaInput.Input)
+ if err != nil {
+ return nil, err
+ }
outData := make(map[string]string)
for _, act := range actions {
@@ -213,7 +217,7 @@ func (p *Page) ActionDeleteHeader(act *Action, out map[string]string /*TODO revi
}
// ActionSetBody executes a SetBody action.
-func (p *Page) ActionSetBody(act *Action, out map[string]string /*TODO review unused parameter*/) error {
+func (p *Page) ActionSetBody(act *Action, out map[string]string) error {
in := p.getActionArgWithDefaultValues(act, "part")
args := make(map[string]string)
@@ -233,7 +237,7 @@ func (p *Page) ActionSetMethod(act *Action, out map[string]string) error {
}
// NavigateURL executes an ActionLoadURL actions loading a URL for the page.
-func (p *Page) NavigateURL(action *Action, out map[string]string, parsed *url.URL /*TODO review unused parameter*/) error {
+func (p *Page) NavigateURL(action *Action, out map[string]string, parsed *url.URL) error {
URL := p.getActionArgWithDefaultValues(action, "url")
if URL == "" {
return errinvalidArguments
diff --git a/v2/pkg/protocols/headless/engine/page_actions_test.go b/v2/pkg/protocols/headless/engine/page_actions_test.go
index 78602b4aca..3524b47d86 100644
--- a/v2/pkg/protocols/headless/engine/page_actions_test.go
+++ b/v2/pkg/protocols/headless/engine/page_actions_test.go
@@ -5,8 +5,8 @@ import (
"io"
"math/rand"
"net/http"
+ "net/http/cookiejar"
"net/http/httptest"
- "net/url"
"os"
"path/filepath"
"strconv"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v2/pkg/testutils/testheadless"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
@@ -564,9 +565,11 @@ func testHeadless(t *testing.T, actions []*Action, timeout time.Duration, handle
ts := httptest.NewServer(http.HandlerFunc(handler))
defer ts.Close()
- parsed, err := url.Parse(ts.URL)
- require.Nil(t, err, "could not parse URL")
- extractedData, page, err := instance.Run(parsed, actions, nil, timeout)
+ input := contextargs.NewWithInput(ts.URL)
+ input.CookieJar, err = cookiejar.New(nil)
+ require.Nil(t, err)
+
+ extractedData, page, err := instance.Run(input, actions, nil, &Options{Timeout: timeout})
assert(page, err, extractedData)
if page != nil {
diff --git a/v2/pkg/protocols/headless/engine/rules.go b/v2/pkg/protocols/headless/engine/rules.go
index 2768cc99cc..22c0057fe0 100644
--- a/v2/pkg/protocols/headless/engine/rules.go
+++ b/v2/pkg/protocols/headless/engine/rules.go
@@ -35,8 +35,26 @@ func (p *Page) routingRuleHandler(ctx *rod.Hijack) {
ctx.Request.SetBody(body)
}
}
+
+ if p.options.CookieReuse {
+ // each http request is performed via the native go http client
+ // we first inject the shared cookies
+ if cookies := p.input.CookieJar.Cookies(ctx.Request.URL()); len(cookies) > 0 {
+ p.instance.browser.httpclient.Jar.SetCookies(ctx.Request.URL(), cookies)
+ }
+ }
+
+ // perform the request
_ = ctx.LoadResponse(p.instance.browser.httpclient, true)
+ if p.options.CookieReuse {
+ // retrieve the updated cookies from the native http client and inject them into the shared cookie jar
+ // keeps existing one if not present
+ if cookies := p.instance.browser.httpclient.Jar.Cookies(ctx.Request.URL()); len(cookies) > 0 {
+ p.input.CookieJar.SetCookies(ctx.Request.URL(), cookies)
+ }
+ }
+
for _, rule := range p.rules {
if rule.Part != "response" {
continue
diff --git a/v2/pkg/protocols/headless/headless.go b/v2/pkg/protocols/headless/headless.go
index d4ba60b4fc..06203d3edc 100644
--- a/v2/pkg/protocols/headless/headless.go
+++ b/v2/pkg/protocols/headless/headless.go
@@ -58,6 +58,10 @@ type Request struct {
// Fuzzing describes schema to fuzz headless requests
Fuzzing []*fuzz.Rule `yaml:"fuzzing,omitempty" json:"fuzzing,omitempty" jsonschema:"title=fuzzin rules for http fuzzing,description=Fuzzing describes rule schema to fuzz headless requests"`
+
+ // description: |
+ // CookieReuse is an optional setting that enables cookie reuse
+ CookieReuse bool `yaml:"cookie-reuse,omitempty" json:"cookie-reuse,omitempty" jsonschema:"title=optional cookie reuse enable,description=Optional setting that enables cookie reuse"`
}
// RequestPartDefinitions contains a mapping of request part definitions and their
diff --git a/v2/pkg/protocols/headless/request.go b/v2/pkg/protocols/headless/request.go
index 814769d11a..9365d142cf 100644
--- a/v2/pkg/protocols/headless/request.go
+++ b/v2/pkg/protocols/headless/request.go
@@ -19,6 +19,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/responsehighlighter"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/utils/vardump"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
protocolutils "github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils"
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
urlutil "github.com/projectdiscovery/utils/url"
@@ -35,7 +36,6 @@ func (request *Request) Type() templateTypes.ProtocolType {
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
- inputURL := input.MetaInput.Input
if request.options.Browser.UserAgent() == "" {
request.options.Browser.SetUserAgent(request.compiledUserAgent)
}
@@ -56,7 +56,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata,
}
// verify if fuzz elaboration was requested
if len(request.Fuzzing) > 0 {
- return request.executeFuzzingRule(inputURL, payloads, previous, wrappedCallback)
+ return request.executeFuzzingRule(input, payloads, previous, wrappedCallback)
}
if request.generator != nil {
iterator := request.generator.NewIterator()
@@ -69,23 +69,23 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata,
return nil
}
value = generators.MergeMaps(value, payloads)
- if err := request.executeRequestWithPayloads(inputURL, value, previous, wrappedCallback); err != nil {
+ if err := request.executeRequestWithPayloads(input, value, previous, wrappedCallback); err != nil {
return err
}
}
} else {
value := maps.Clone(payloads)
- if err := request.executeRequestWithPayloads(inputURL, value, previous, wrappedCallback); err != nil {
+ if err := request.executeRequestWithPayloads(input, value, previous, wrappedCallback); err != nil {
return err
}
}
return nil
}
-func (request *Request) executeRequestWithPayloads(inputURL string, payloads map[string]interface{}, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
+func (request *Request) executeRequestWithPayloads(input *contextargs.Context, payloads map[string]interface{}, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
instance, err := request.options.Browser.NewInstance()
if err != nil {
- request.options.Output.Request(request.options.TemplatePath, inputURL, request.Type().String(), err)
+ request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), err)
request.options.Progress.IncrementFailedRequestsBy(1)
return errors.Wrap(err, errCouldGetHtmlElement)
}
@@ -97,32 +97,39 @@ func (request *Request) executeRequestWithPayloads(inputURL string, payloads map
instance.SetInteractsh(request.options.Interactsh)
- parsedURL, err := url.Parse(inputURL)
- if err != nil {
- request.options.Output.Request(request.options.TemplatePath, inputURL, request.Type().String(), err)
+ if _, err := url.Parse(input.MetaInput.Input); err != nil {
+ request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), err)
request.options.Progress.IncrementFailedRequestsBy(1)
return errors.Wrap(err, errCouldGetHtmlElement)
}
- timeout := time.Duration(request.options.Options.PageTimeout) * time.Second
- out, page, err := instance.Run(parsedURL, request.Steps, payloads, timeout)
+ options := &engine.Options{
+ Timeout: time.Duration(request.options.Options.PageTimeout) * time.Second,
+ CookieReuse: request.CookieReuse,
+ }
+
+ if options.CookieReuse && input.CookieJar == nil {
+ return errors.New("cookie-reuse set but cookie-jar is nil")
+ }
+
+ out, page, err := instance.Run(input, request.Steps, payloads, options)
if err != nil {
- request.options.Output.Request(request.options.TemplatePath, inputURL, request.Type().String(), err)
+ request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), err)
request.options.Progress.IncrementFailedRequestsBy(1)
return errors.Wrap(err, errCouldGetHtmlElement)
}
defer page.Close()
- request.options.Output.Request(request.options.TemplatePath, inputURL, request.Type().String(), nil)
+ request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), nil)
request.options.Progress.IncrementRequests()
- gologger.Verbose().Msgf("Sent Headless request to %s", inputURL)
+ gologger.Verbose().Msgf("Sent Headless request to %s", input.MetaInput.Input)
reqBuilder := &strings.Builder{}
if request.options.Options.Debug || request.options.Options.DebugRequests || request.options.Options.DebugResponse {
- gologger.Info().Msgf("[%s] Dumped Headless request for %s", request.options.TemplateID, inputURL)
+ gologger.Info().Msgf("[%s] Dumped Headless request for %s", request.options.TemplateID, input.MetaInput.Input)
for _, act := range request.Steps {
actStepStr := act.String()
- actStepStr = strings.ReplaceAll(actStepStr, "{{BaseURL}}", inputURL)
+ actStepStr = strings.ReplaceAll(actStepStr, "{{BaseURL}}", input.MetaInput.Input)
reqBuilder.WriteString("\t" + actStepStr + "\n")
}
gologger.Debug().Msgf(reqBuilder.String())
@@ -135,7 +142,7 @@ func (request *Request) executeRequestWithPayloads(inputURL string, payloads map
responseBody, _ = html.HTML()
}
- outputEvent := request.responseToDSLMap(responseBody, out["header"], out["status_code"], reqBuilder.String(), inputURL, inputURL, page.DumpHistory())
+ outputEvent := request.responseToDSLMap(responseBody, out["header"], out["status_code"], reqBuilder.String(), input.MetaInput.Input, input.MetaInput.Input, page.DumpHistory())
for k, v := range out {
outputEvent[k] = v
}
@@ -161,7 +168,7 @@ func (request *Request) executeRequestWithPayloads(inputURL string, payloads map
event.UsesInteractsh = true
}
- dumpResponse(event, request.options, responseBody, inputURL)
+ dumpResponse(event, request.options, responseBody, input.MetaInput.Input)
return nil
}
@@ -174,26 +181,27 @@ func dumpResponse(event *output.InternalWrappedEvent, requestOptions *protocols.
}
// executeFuzzingRule executes a fuzzing rule in the template request
-func (request *Request) executeFuzzingRule(inputURL string, payloads map[string]interface{}, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
+func (request *Request) executeFuzzingRule(input *contextargs.Context, payloads map[string]interface{}, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
// check for operator matches by wrapping callback
gotmatches := false
fuzzRequestCallback := func(gr fuzz.GeneratedRequest) bool {
if gotmatches && (request.StopAtFirstMatch || request.options.Options.StopAtFirstMatch || request.options.StopAtFirstMatch) {
return true
}
- if err := request.executeRequestWithPayloads(gr.Request.URL.String(), gr.DynamicValues, previous, callback); err != nil {
+ newInput := input.Clone()
+ newInput.MetaInput.Input = gr.Request.URL.String()
+ if err := request.executeRequestWithPayloads(newInput, gr.DynamicValues, previous, callback); err != nil {
return false
}
return true
}
- parsedURL, err := urlutil.Parse(inputURL)
- if err != nil {
+ if _, err := urlutil.Parse(input.MetaInput.Input); err != nil {
return errors.Wrap(err, "could not parse url")
}
for _, rule := range request.Fuzzing {
err := rule.Execute(&fuzz.ExecuteRuleInput{
- URL: parsedURL,
+ Input: input,
Callback: fuzzRequestCallback,
Values: payloads,
BaseRequest: nil,
diff --git a/v2/pkg/protocols/http/httpclientpool/clientpool.go b/v2/pkg/protocols/http/httpclientpool/clientpool.go
index ececb0838a..2cbcdfa33e 100644
--- a/v2/pkg/protocols/http/httpclientpool/clientpool.go
+++ b/v2/pkg/protocols/http/httpclientpool/clientpool.go
@@ -229,6 +229,9 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
ForceAttemptHTTP2: options.ForceAttemptHTTP2,
DialContext: Dialer.Dial,
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+ if options.HasClientCertificates() {
+ return Dialer.DialTLSWithConfig(ctx, network, addr, tlsConfig)
+ }
if options.TlsImpersonate {
return Dialer.DialTLSWithConfigImpersonate(ctx, network, addr, tlsConfig, impersonate.Random, nil)
}
diff --git a/v2/pkg/protocols/http/request.go b/v2/pkg/protocols/http/request.go
index bbadc187d6..304eead2b0 100644
--- a/v2/pkg/protocols/http/request.go
+++ b/v2/pkg/protocols/http/request.go
@@ -228,8 +228,7 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu
// executeFuzzingRule executes fuzzing request for a URL
func (request *Request) executeFuzzingRule(input *contextargs.Context, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
- parsed, err := urlutil.Parse(input.MetaInput.Input)
- if err != nil {
+ if _, err := urlutil.Parse(input.MetaInput.Input); err != nil {
return errors.Wrap(err, "could not parse url")
}
fuzzRequestCallback := func(gr fuzz.GeneratedRequest) bool {
@@ -297,7 +296,7 @@ func (request *Request) executeFuzzingRule(input *contextargs.Context, previous
}
for _, rule := range request.Fuzzing {
err = rule.Execute(&fuzz.ExecuteRuleInput{
- URL: parsed,
+ Input: input,
Callback: fuzzRequestCallback,
Values: generated.dynamicValues,
BaseRequest: generated.request,
diff --git a/v2/pkg/protocols/offlinehttp/request.go b/v2/pkg/protocols/offlinehttp/request.go
index 7bafa3a7bd..2bcbeacc11 100644
--- a/v2/pkg/protocols/offlinehttp/request.go
+++ b/v2/pkg/protocols/offlinehttp/request.go
@@ -2,10 +2,8 @@ package offlinehttp
import (
"io"
- "net/http"
"net/http/httputil"
"os"
- "strings"
"github.com/pkg/errors"
"github.com/remeh/sizedwaitgroup"
@@ -16,6 +14,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/eventcreator"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/tostring"
+ "github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils"
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
)
@@ -86,7 +85,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata
return
}
- outputEvent := request.responseToDSLMap(resp, data, data, data, tostring.UnsafeToString(dumpedResponse), tostring.UnsafeToString(body), headersToString(resp.Header), 0, nil)
+ outputEvent := request.responseToDSLMap(resp, data, data, data, tostring.UnsafeToString(dumpedResponse), tostring.UnsafeToString(body), utils.HeadersToString(resp.Header), 0, nil)
outputEvent["ip"] = ""
for k, v := range previous {
outputEvent[k] = v
@@ -105,25 +104,3 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata
request.options.Progress.IncrementRequests()
return nil
}
-
-// headersToString converts http headers to string
-func headersToString(headers http.Header) string {
- builder := &strings.Builder{}
-
- for header, values := range headers {
- builder.WriteString(header)
- builder.WriteString(": ")
-
- for i, value := range values {
- builder.WriteString(value)
-
- if i != len(values)-1 {
- builder.WriteRune('\n')
- builder.WriteString(header)
- builder.WriteString(": ")
- }
- }
- builder.WriteRune('\n')
- }
- return builder.String()
-}
diff --git a/v2/pkg/protocols/utils/utils.go b/v2/pkg/protocols/utils/utils.go
index cdc0b367d5..505a0a3b7e 100644
--- a/v2/pkg/protocols/utils/utils.go
+++ b/v2/pkg/protocols/utils/utils.go
@@ -3,6 +3,7 @@ package utils
import (
"crypto/tls"
"crypto/x509"
+ "net/http"
"os"
"strings"
@@ -46,3 +47,25 @@ func CalculateContentLength(contentLength, bodyLength int64) int64 {
}
return bodyLength
}
+
+// headersToString converts http headers to string
+func HeadersToString(headers http.Header) string {
+ builder := &strings.Builder{}
+
+ for header, values := range headers {
+ builder.WriteString(header)
+ builder.WriteString(": ")
+
+ for i, value := range values {
+ builder.WriteString(value)
+
+ if i != len(values)-1 {
+ builder.WriteRune('\n')
+ builder.WriteString(header)
+ builder.WriteString(": ")
+ }
+ }
+ builder.WriteRune('\n')
+ }
+ return builder.String()
+}
diff --git a/v2/pkg/templates/templates_doc.go b/v2/pkg/templates/templates_doc.go
index ee857fd9bf..25b094b01f 100644
--- a/v2/pkg/templates/templates_doc.go
+++ b/v2/pkg/templates/templates_doc.go
@@ -1212,7 +1212,7 @@ func init() {
Value: "Headless response received from client (default)",
},
}
- HEADLESSRequestDoc.Fields = make([]encoder.Doc, 8)
+ HEADLESSRequestDoc.Fields = make([]encoder.Doc, 9)
HEADLESSRequestDoc.Fields[0].Name = "id"
HEADLESSRequestDoc.Fields[0].Type = "string"
HEADLESSRequestDoc.Fields[0].Note = ""
@@ -1253,6 +1253,11 @@ func init() {
HEADLESSRequestDoc.Fields[7].Note = ""
HEADLESSRequestDoc.Fields[7].Description = "Fuzzing describes schema to fuzz headless requests"
HEADLESSRequestDoc.Fields[7].Comments[encoder.LineComment] = " Fuzzing describes schema to fuzz headless requests"
+ HEADLESSRequestDoc.Fields[8].Name = "cookie-reuse"
+ HEADLESSRequestDoc.Fields[8].Type = "bool"
+ HEADLESSRequestDoc.Fields[8].Note = ""
+ HEADLESSRequestDoc.Fields[8].Description = "CookieReuse is an optional setting that enables cookie reuse"
+ HEADLESSRequestDoc.Fields[8].Comments[encoder.LineComment] = "CookieReuse is an optional setting that enables cookie reuse"
ENGINEActionDoc.Type = "engine.Action"
ENGINEActionDoc.Comments[encoder.LineComment] = " Action is an action taken by the browser to reach a navigation"
diff --git a/v2/pkg/types/types.go b/v2/pkg/types/types.go
index 0018a804f9..02a9150185 100644
--- a/v2/pkg/types/types.go
+++ b/v2/pkg/types/types.go
@@ -381,6 +381,11 @@ func (options *Options) ShouldFollowHTTPRedirects() bool {
return options.FollowRedirects || options.FollowHostRedirects
}
+// HasClientCertificates determines if any client certificate was specified
+func (options *Options) HasClientCertificates() bool {
+ return options.ClientCertFile != "" || options.ClientCAFile != "" || options.ClientKeyFile != ""
+}
+
// DefaultOptions returns default options for nuclei
func DefaultOptions() *Options {
return &Options{