From ee597a824b56436704b20a1af0a742646271024e Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Mon, 8 Dec 2025 01:35:47 +0700 Subject: [PATCH 01/28] chore: update issue & PR templates Signed-off-by: Dwi Siswanto --- .github/ISSUE_TEMPLATE/bug-report.yml | 4 +++- .github/PULL_REQUEST_TEMPLATE.md | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index d6cabc425..38f61d358 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -37,6 +37,7 @@ body: Steps to reproduce the behavior, for example, commands to run Nuclei. 📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood. + 📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below. :warning: **Please redact any literal target hosts/URLs or other sensitive information.** placeholder: | @@ -50,6 +51,7 @@ body: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood. + 📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below. :warning: **Please redact any literal target hosts/URLs or other sensitive information.** render: shell @@ -72,7 +74,7 @@ body: attributes: label: Anything else? description: | - Links? References? Templates? Anything that will give us more context about the issue you are encountering! + Links? References? Templates? CPU, memory, and trace profiles? Anything that will give us more context about the issue you are encountering! Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. validations: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 807314510..dbadf5869 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,6 +2,9 @@ +### Proof + + ## Checklist From 746a05dac5aa872c1d7ddb6a7cd8dcf4731e7e88 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto <25837540+dwisiswant0@users.noreply.github.com> Date: Wed, 17 Dec 2025 22:07:14 +0700 Subject: [PATCH 02/28] fix(javascript): restore exec for templates w/o `Port` arg (#6709) Restore backwards compat for JavaScript protocol templates that omit the `Port` argument. Regression was introduced in f4f2e9f2, which removed the fallback for empty `Port` in `(*Request).ExecuteWithResults`, causing templates without `Port` to be silently skipped. Now, if no `Port` is specified, the engine executes the JavaScript block using the target URL's port. Fixes #6708. Signed-off-by: Dwi Siswanto --- cmd/integration-test/javascript.go | 11 +++++++++ .../protocols/javascript/no-port-args.yaml | 23 +++++++++++++++++++ pkg/protocols/javascript/js.go | 3 +++ 3 files changed, 37 insertions(+) create mode 100644 integration_tests/protocols/javascript/no-port-args.yaml diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index c0b8c1990..aad685344 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -18,6 +18,7 @@ var jsTestcases = []TestCaseInfo{ {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{}}, + {Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}}, } var ( @@ -179,6 +180,16 @@ func (j *javascriptMultiPortsSSH) Execute(filePath string) error { return expectResultsCount(results, 1) } +type javascriptNoPortArgs struct{} + +func (j *javascriptNoPortArgs) Execute(filePath string) error { + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "yo.dawg", 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/integration_tests/protocols/javascript/no-port-args.yaml b/integration_tests/protocols/javascript/no-port-args.yaml new file mode 100644 index 000000000..2518a3d58 --- /dev/null +++ b/integration_tests/protocols/javascript/no-port-args.yaml @@ -0,0 +1,23 @@ +id: javascript-no-port-args + +info: + name: JavaScript Template Without Port Args + author: dwisiswant0 + severity: info + description: | + Test backwards compatibility for JavaScript templates without Port in args. + Templates should execute even when Port arg is not explicitly specified. + +javascript: + - code: | + // Simple JavaScript code that does not require Port + let result = "executed"; + Export(result); + + args: + TestArg: "test-value" + + matchers: + - type: dsl + dsl: + - success == true diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index cc7003c53..b95ea6b4a 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -287,6 +287,9 @@ func (request *Request) GetID() string { 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() + if len(ports) == 0 { + return request.executeWithResults("", target, dynamicValues, previous, callback) + } var errs []error From a7df69749e74d164158fd6a096cc2f3f7224465d Mon Sep 17 00:00:00 2001 From: Leon Jacobs Date: Wed, 17 Dec 2025 19:26:19 +0200 Subject: [PATCH 03/28] fix(trackers): paginate gitea to find all issues when searching for duplicates (#6707) * (fix) paginate to find all issues when searching for duplicates * (feat) add configurable limits for perpage and total pages --- cmd/nuclei/issue-tracker-config.yaml | 10 ++++-- pkg/reporting/trackers/gitea/gitea.go | 47 ++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/cmd/nuclei/issue-tracker-config.yaml b/cmd/nuclei/issue-tracker-config.yaml index 07ccf828f..e391f604e 100644 --- a/cmd/nuclei/issue-tracker-config.yaml +++ b/cmd/nuclei/issue-tracker-config.yaml @@ -81,6 +81,10 @@ # severity: low # # duplicate-issue-check (optional) flag to enable duplicate tracking issue check # duplicate-issue-check: false +# # duplicate-issue-page-size (optional) controls how many issues to fetch per page when searching for duplicates +# duplicate-issue-page-size: 100 +# # duplicate-issue-max-pages (optional) limits how many pages to fetch when searching for duplicates (0 = no limit) +# duplicate-issue-max-pages: 0 # # Jira contains configuration options for Jira issue tracker #jira: @@ -118,10 +122,10 @@ # status-not: Closed # # Customfield supports name, id and freeform. name and id are to be used when the custom field is a dropdown. # # freeform can be used if the custom field is just a text entry -# # Variables can be used to pull various pieces of data from the finding itself. +# # Variables can be used to pull various pieces of data from the finding itself. # # Supported variables: $CVSSMetrics, $CVEID, $CWEID, $Host, $Severity, $CVSSScore, $Name # custom-fields: -# customfield_00001: +# customfield_00001: # name: "Nuclei" # customfield_00002: # freeform: $CVSSMetrics @@ -172,4 +176,4 @@ # omit-raw: false # # determines the number of results to be kept in memory before writing it to the database or 0 to # # persist all in memory and write all results at the end (default) -# batch-size: 0 \ No newline at end of file +# batch-size: 0 diff --git a/pkg/reporting/trackers/gitea/gitea.go b/pkg/reporting/trackers/gitea/gitea.go index e433689c7..bf3fd3f03 100644 --- a/pkg/reporting/trackers/gitea/gitea.go +++ b/pkg/reporting/trackers/gitea/gitea.go @@ -42,6 +42,12 @@ type Options struct { DenyList *filters.Filter `yaml:"deny-list"` // DuplicateIssueCheck is a bool to enable duplicate tracking issue check and update the newest DuplicateIssueCheck bool `yaml:"duplicate-issue-check" default:"false"` + // DuplicateIssuePageSize controls how many issues are fetched per page when searching for duplicates. + // If unset or <=0, a default of 100 is used. + DuplicateIssuePageSize int `yaml:"duplicate-issue-page-size" default:"100"` + // DuplicateIssueMaxPages limits how many pages are fetched when searching for duplicates. + // If unset or <=0, all pages are fetched until exhaustion. + DuplicateIssueMaxPages int `yaml:"duplicate-issue-max-pages" default:"0"` HttpClient *retryablehttp.Client `yaml:"-"` OmitRaw bool `yaml:"-"` @@ -151,21 +157,44 @@ func (i *Integration) ShouldFilter(event *output.ResultEvent) bool { } func (i *Integration) findIssueByTitle(title string) (*gitea.Issue, error) { + // Fetch issues in pages to ensure older issues are also checked for duplicates. + pageSize := i.options.DuplicateIssuePageSize + if pageSize <= 0 { + pageSize = 100 + } + maxPages := i.options.DuplicateIssueMaxPages - issueList, _, err := i.client.ListRepoIssues(i.options.ProjectOwner, i.options.ProjectName, gitea.ListIssueOption{ + opts := gitea.ListIssueOption{ State: "all", - }) - if err != nil { - return nil, err + ListOptions: gitea.ListOptions{ + Page: 1, + PageSize: pageSize, + }, } - for _, issue := range issueList { - if issue.Title == title { - return issue, nil + for { + if maxPages > 0 && opts.Page > maxPages { + return nil, nil } - } - return nil, nil + issueList, _, err := i.client.ListRepoIssues(i.options.ProjectOwner, i.options.ProjectName, opts) + if err != nil { + return nil, err + } + + for _, issue := range issueList { + if issue.Title == title { + return issue, nil + } + } + + if len(issueList) < opts.PageSize { + // Last page reached. + return nil, nil + } + + opts.Page++ + } } func (i *Integration) getLabelIDsByNames(labels []string) ([]int64, error) { From 39a07ca15ed08e5641cf6aaba2ea49963c8e9107 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Thu, 18 Dec 2025 00:29:38 +0700 Subject: [PATCH 04/28] fix(trackers): add gitlab paginated dup issue search with configurable limits This patch fixes duplicate issue detection for GitLab trackers by implementing paginated search with configurable page size and max pages. Adds `duplicate-issue-page-size` and `duplicate-issue-max-pages` options to the config. Fixes #6711. Signed-off-by: Dwi Siswanto --- cmd/nuclei/issue-tracker-config.yaml | 6 ++ pkg/reporting/trackers/gitlab/gitlab.go | 104 ++++++++++++++++-------- 2 files changed, 77 insertions(+), 33 deletions(-) diff --git a/cmd/nuclei/issue-tracker-config.yaml b/cmd/nuclei/issue-tracker-config.yaml index 07ccf828f..32d0bd56d 100644 --- a/cmd/nuclei/issue-tracker-config.yaml +++ b/cmd/nuclei/issue-tracker-config.yaml @@ -55,6 +55,12 @@ # # these severity labels or tags (does not affect exporters. set those globally) # deny-list: # severity: low +# # duplicate-issue-check (optional) flag to enable duplicate tracking issue check +# duplicate-issue-check: false +# # duplicate-issue-page-size (optional) controls how many issues to fetch per page when searching for duplicates +# duplicate-issue-page-size: 100 +# # duplicate-issue-max-pages (optional) limits how many pages to fetch when searching for duplicates (0 = no limit) +# duplicate-issue-max-pages: 0 # # Gitea contains configuration options for a gitea issue tracker #gitea: diff --git a/pkg/reporting/trackers/gitlab/gitlab.go b/pkg/reporting/trackers/gitlab/gitlab.go index 3a0ab8397..5a3053e27 100644 --- a/pkg/reporting/trackers/gitlab/gitlab.go +++ b/pkg/reporting/trackers/gitlab/gitlab.go @@ -41,6 +41,12 @@ type Options struct { DenyList *filters.Filter `yaml:"deny-list"` // DuplicateIssueCheck is a bool to enable duplicate tracking issue check and update the newest DuplicateIssueCheck bool `yaml:"duplicate-issue-check" default:"false"` + // DuplicateIssuePageSize controls how many issues are fetched per page when searching for duplicates. + // If unset or <=0, a default of 100 is used. + DuplicateIssuePageSize int `yaml:"duplicate-issue-page-size" default:"100"` + // DuplicateIssueMaxPages limits how many pages are fetched when searching for duplicates. + // If unset or <=0, all pages are fetched until exhaustion. + DuplicateIssueMaxPages int `yaml:"duplicate-issue-max-pages" default:"0"` HttpClient *retryablehttp.Client `yaml:"-"` OmitRaw bool `yaml:"-"` @@ -80,39 +86,36 @@ func (i *Integration) CreateIssue(event *output.ResultEvent) (*filters.CreateIss } customLabels := gitlab.LabelOptions(labels) assigneeIDs := []int{i.userID} + + var issue *gitlab.Issue if i.options.DuplicateIssueCheck { - searchIn := "title" - searchState := "all" - issues, _, err := i.client.Issues.ListProjectIssues(i.options.ProjectName, &gitlab.ListProjectIssuesOptions{ - In: &searchIn, - State: &searchState, - Search: &summary, + var err error + issue, err = i.findIssueByTitle(summary) + if err != nil { + return nil, err + } + } + + if issue != nil { + _, _, err := i.client.Notes.CreateIssueNote(i.options.ProjectName, issue.IID, &gitlab.CreateIssueNoteOptions{ + Body: &description, }) if err != nil { return nil, err } - if len(issues) > 0 { - issue := issues[0] - _, _, err := i.client.Notes.CreateIssueNote(i.options.ProjectName, issue.IID, &gitlab.CreateIssueNoteOptions{ - Body: &description, + if issue.State == "closed" { + reopen := "reopen" + _, _, err := i.client.Issues.UpdateIssue(i.options.ProjectName, issue.IID, &gitlab.UpdateIssueOptions{ + StateEvent: &reopen, }) if err != nil { return nil, err } - if issue.State == "closed" { - reopen := "reopen" - _, _, err := i.client.Issues.UpdateIssue(i.options.ProjectName, issue.IID, &gitlab.UpdateIssueOptions{ - StateEvent: &reopen, - }) - if err != nil { - return nil, err - } - } - return &filters.CreateIssueResponse{ - IssueID: strconv.FormatInt(int64(issue.ID), 10), - IssueURL: issue.WebURL, - }, nil } + return &filters.CreateIssueResponse{ + IssueID: strconv.FormatInt(int64(issue.ID), 10), + IssueURL: issue.WebURL, + }, nil } createdIssue, _, err := i.client.Issues.CreateIssue(i.options.ProjectName, &gitlab.CreateIssueOptions{ Title: &summary, @@ -134,23 +137,15 @@ func (i *Integration) Name() string { } func (i *Integration) CloseIssue(event *output.ResultEvent) error { - searchIn := "title" - searchState := "all" - summary := format.Summary(event) - issues, _, err := i.client.Issues.ListProjectIssues(i.options.ProjectName, &gitlab.ListProjectIssuesOptions{ - In: &searchIn, - State: &searchState, - Search: &summary, - }) + issue, err := i.findIssueByTitle(summary) if err != nil { return err } - if len(issues) <= 0 { + if issue == nil { return nil } - issue := issues[0] state := "close" _, _, err = i.client.Issues.UpdateIssue(i.options.ProjectName, issue.IID, &gitlab.UpdateIssueOptions{ StateEvent: &state, @@ -161,6 +156,49 @@ func (i *Integration) CloseIssue(event *output.ResultEvent) error { return nil } +func (i *Integration) findIssueByTitle(title string) (*gitlab.Issue, error) { + pageSize := i.options.DuplicateIssuePageSize + if pageSize <= 0 { + pageSize = 100 + } + maxPages := i.options.DuplicateIssueMaxPages + + searchIn := "title" + searchState := "all" + page := 1 + + for { + if maxPages > 0 && page > maxPages { + return nil, nil + } + + issues, _, err := i.client.Issues.ListProjectIssues(i.options.ProjectName, &gitlab.ListProjectIssuesOptions{ + In: &searchIn, + State: &searchState, + Search: &title, + ListOptions: gitlab.ListOptions{ + Page: page, + PerPage: pageSize, + }, + }) + if err != nil { + return nil, err + } + + for _, issue := range issues { + if issue.Title == title { + return issue, nil + } + } + + if len(issues) < pageSize { + return nil, nil + } + + page++ + } +} + // ShouldFilter determines if an issue should be logged to this tracker func (i *Integration) ShouldFilter(event *output.ResultEvent) bool { if i.options.AllowList != nil && !i.options.AllowList.GetMatch(event) { From e3d32584ffa2c9442e474d25f320a926be70b82a Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Thu, 18 Dec 2025 05:39:02 +0700 Subject: [PATCH 05/28] feat: enable TLS session cache for client pool This patch enables TLS session resumption by setting a shared LRU session cache (`ClientSessionCache`) in all HTTP client TLS configs. This reduces handshake overhead and CPU usage for repeated conns to the same host, improving throughput and efficiency in clusterbomb/pitchfork modes. This applied to HTTP-request-based and headless- request-based protocols. No runtime/compatibility impact. Signed-off-by: Dwi Siswanto --- pkg/protocols/headless/engine/http_client.go | 1 + pkg/protocols/http/httpclientpool/clientpool.go | 1 + 2 files changed, 2 insertions(+) diff --git a/pkg/protocols/headless/engine/http_client.go b/pkg/protocols/headless/engine/http_client.go index fc8cd0a2c..0d167e12a 100644 --- a/pkg/protocols/headless/engine/http_client.go +++ b/pkg/protocols/headless/engine/http_client.go @@ -29,6 +29,7 @@ func newHttpClient(options *types.Options) (*http.Client, error) { Renegotiation: tls.RenegotiateOnceAsClient, InsecureSkipVerify: true, MinVersion: tls.VersionTLS10, + ClientSessionCache: tls.NewLRUClientSessionCache(1024), } if options.SNI != "" { diff --git a/pkg/protocols/http/httpclientpool/clientpool.go b/pkg/protocols/http/httpclientpool/clientpool.go index 14f4c8dc3..0520985be 100644 --- a/pkg/protocols/http/httpclientpool/clientpool.go +++ b/pkg/protocols/http/httpclientpool/clientpool.go @@ -248,6 +248,7 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl Renegotiation: tls.RenegotiateOnceAsClient, InsecureSkipVerify: true, MinVersion: tls.VersionTLS10, + ClientSessionCache: tls.NewLRUClientSessionCache(1024), } if options.SNI != "" { From e25279255adf80b4a6538f6bb4ae3bb0ae0453b5 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Thu, 18 Dec 2025 23:09:51 +0700 Subject: [PATCH 06/28] chore: update bug report template Signed-off-by: Dwi Siswanto --- .github/ISSUE_TEMPLATE/bug-report.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 38f61d358..405cbbae7 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -36,7 +36,9 @@ body: description: | Steps to reproduce the behavior, for example, commands to run Nuclei. - 📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood. + 💡 Prefer copying text output over using screenshots for easier troubleshooting. + + 📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below. :warning: **Please redact any literal target hosts/URLs or other sensitive information.** @@ -50,7 +52,9 @@ body: description: | Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. - 📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood. + 💡 Prefer copying text output over using screenshots for easier troubleshooting. + + 📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below. :warning: **Please redact any literal target hosts/URLs or other sensitive information.** @@ -60,9 +64,9 @@ body: label: Environment description: | Examples: - - **OS**: Ubuntu 20.04 - - **Nuclei** (`nuclei -version`): v3.3.1 - - **Go** (`go version`): go1.22.0 _(only if you've installed it via the `go install` command)_ + - **OS**: Ubuntu 24.04 + - **Nuclei** (`nuclei -version`): v3.6.0 + - **Go** (`go version`): go1.24.0 _(only if you've installed it via the `go install` command)_ value: | - OS: - Nuclei: From d48c2c38faca629bf7df26fa8d45e9c3878b6800 Mon Sep 17 00:00:00 2001 From: Ice3man Date: Thu, 18 Dec 2025 22:17:45 +0530 Subject: [PATCH 07/28] feat(trackers): add site-url to optionally provide jira server URL for oauth (#6716) * feat: add site-url to optionally provide jira server URL for oauth * chore(cmd): add `site-url` config option Adds optional `site-url` field to JIRA issue tracker configuration for specifying browsable URL when it differs from the API endpoint. This is particularly useful for OAuth-based JIRA Cloud integrations where `issue.Self` contains "api.atlassian.com" instead of the user-facing domain. Signed-off-by: Dwi Siswanto --------- Signed-off-by: Dwi Siswanto Co-authored-by: Dwi Siswanto --- cmd/nuclei/issue-tracker-config.yaml | 3 +++ pkg/reporting/trackers/jira/jira.go | 29 ++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/cmd/nuclei/issue-tracker-config.yaml b/cmd/nuclei/issue-tracker-config.yaml index ef5b8ec2a..7e8acfa63 100644 --- a/cmd/nuclei/issue-tracker-config.yaml +++ b/cmd/nuclei/issue-tracker-config.yaml @@ -100,6 +100,9 @@ # update-existing: false # # URL is the jira application url # url: https://localhost/jira +# # site-url is the browsable URL for the Jira instance (optional) +# # If not provided, issue.Self will be used. Useful for OAuth where issue.Self contains api.atlassian.com +# site-url: https://your-company.atlassian.net # # account-id is the account-id of the Jira user or username in case of on-prem Jira # account-id: test-account-id # # email is the email of the user for Jira instance diff --git a/pkg/reporting/trackers/jira/jira.go b/pkg/reporting/trackers/jira/jira.go index 1fe02b085..2156a354a 100644 --- a/pkg/reporting/trackers/jira/jira.go +++ b/pkg/reporting/trackers/jira/jira.go @@ -184,6 +184,9 @@ type Options struct { UpdateExisting bool `yaml:"update-existing" json:"update_existing"` // URL is the URL of the jira server URL string `yaml:"url" json:"url" validate:"required"` + // SiteURL is the browsable URL for the Jira instance (optional) + // If not provided, issue.Self will be used. Useful for OAuth where issue.Self contains api.atlassian.com + SiteURL string `yaml:"site-url" json:"site_url"` // AccountID is the accountID of the jira user. AccountID string `yaml:"account-id" json:"account_id" validate:"required"` // Email is the email of the user for jira instance @@ -346,16 +349,26 @@ func (i *Integration) CreateNewIssue(event *output.ResultEvent) (*filters.Create } return nil, fmt.Errorf("%w => %s", err, data) } - return getIssueResponseFromJira(createdIssue) + return i.getIssueResponseFromJira(createdIssue) } -func getIssueResponseFromJira(issue *jira.Issue) (*filters.CreateIssueResponse, error) { - parsed, err := url.Parse(issue.Self) - if err != nil { - return nil, err +func (i *Integration) getIssueResponseFromJira(issue *jira.Issue) (*filters.CreateIssueResponse, error) { + var issueURL string + + // Use SiteURL if provided, otherwise fall back to original issue.Self logic + if i.options.SiteURL != "" { + // Use the configured site URL for browsable links (useful for OAuth) + baseURL := strings.TrimRight(i.options.SiteURL, "/") + issueURL = fmt.Sprintf("%s/browse/%s", baseURL, issue.Key) + } else { + // Fall back to original logic using issue.Self + parsed, err := url.Parse(issue.Self) + if err != nil { + return nil, err + } + parsed.Path = fmt.Sprintf("/browse/%s", issue.Key) + issueURL = parsed.String() } - parsed.Path = fmt.Sprintf("/browse/%s", issue.Key) - issueURL := parsed.String() return &filters.CreateIssueResponse{ IssueID: issue.ID, @@ -376,7 +389,7 @@ func (i *Integration) CreateIssue(event *output.ResultEvent) (*filters.CreateIss if err != nil { return nil, errors.Wrap(err, "could not add comment to existing issue") } - return getIssueResponseFromJira(&issue) + return i.getIssueResponseFromJira(&issue) } } resp, err := i.CreateNewIssue(event) From bb79a061bc5ba6b1d08a014140583da61b906ced Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 19 Dec 2025 20:19:37 +0700 Subject: [PATCH 08/28] perf(generators): optimize `MergeMaps` to reduce allocs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `MergeMaps` accounts for 11.41% of allocs (13.8 GB) in clusterbomb mode. With 1,305 combinations per target, this function is called millions of times in the hot path. RCA: * Request generator calls `MergeMaps` with single arg on every payload combination, incurring variadic overhead. * Build request merges same maps multiple times per request. * `BuildPayloadFromOptions` recomputes static CLI options on every call. * Variables calls `MergeMaps` $$2×N$$ times per variable evaluation (once in loop, once in `evaluateVariableValue`) Changes: Core optimizations in maps.go: * Pre-size merged map to avoid rehashing (30-40% reduction) * Add `CopyMap` for efficient single-map copy without variadic overhead. * Add `MergeMapsInto` for in-place mutation when caller owns destination. Hot path fixes: * Replace `MergeMaps(r.currentPayloads)` with `CopyMap(r.currentPayloads)` to eliminates allocation on every combination iteration. * Pre-allocate combined map once, extend in-place during `ForEach` loop instead of creating new map per variable (eliminates $$2×N$$ allocations per request). Caching with concurrency safety: * Cache `BuildPayloadFromOptions` computation in `sync.Map` keyed by `types.Options` ptr, but return copy to prevent concurrent modification. * Cost: shallow copy of ~10-20 entries vs. full merge of vars + env (85-90% savings in typical case) * Clear cache in `closeInternal()` to prevent memory leaks when SDK instances are created or destroyed. Estimated impact: 40-60% reduction in `MergeMaps` allocations (5.5-8.3 GB savings from original 13.8 GB). Safe for concurrent execution and SDK usage with multiple instances. Signed-off-by: Dwi Siswanto --- lib/sdk.go | 4 + pkg/protocols/common/generators/maps.go | 35 +++- pkg/protocols/common/generators/options.go | 37 +++- .../common/generators/options_test.go | 92 +++++++++ pkg/protocols/common/variables/variables.go | 44 +++-- .../common/variables/variables_test.go | 174 ++++++++++++++++++ pkg/protocols/http/request_generator.go | 4 +- 7 files changed, 373 insertions(+), 17 deletions(-) create mode 100644 pkg/protocols/common/generators/options_test.go diff --git a/lib/sdk.go b/lib/sdk.go index 6bd2a93ba..a70c02d61 100644 --- a/lib/sdk.go +++ b/lib/sdk.go @@ -19,6 +19,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/output" "github.com/projectdiscovery/nuclei/v3/pkg/progress" "github.com/projectdiscovery/nuclei/v3/pkg/protocols" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit" @@ -238,6 +239,9 @@ func (e *NucleiEngine) closeInternal() { if e.tmpDir != "" { _ = os.RemoveAll(e.tmpDir) } + if e.opts != nil { + generators.ClearOptionsPayloadMap(e.opts) + } } // Close all resources used by nuclei engine diff --git a/pkg/protocols/common/generators/maps.go b/pkg/protocols/common/generators/maps.go index ed2fc3a64..6464d53ee 100644 --- a/pkg/protocols/common/generators/maps.go +++ b/pkg/protocols/common/generators/maps.go @@ -44,20 +44,51 @@ func MergeMapsMany(maps ...interface{}) map[string][]string { return m } -// MergeMaps merges two maps into a new map +// MergeMaps merges multiple maps into a new map. +// +// Use [CopyMap] if you need to copy a single map. +// Use [MergeMapsInto] to merge into an existing map. func MergeMaps(maps ...map[string]interface{}) map[string]interface{} { - merged := make(map[string]interface{}) + mapsLen := 0 + for _, m := range maps { + mapsLen += len(m) + } + + merged := make(map[string]interface{}, mapsLen) for _, m := range maps { maps0.Copy(merged, m) } + return merged } +// CopyMap creates a shallow copy of a single map. +func CopyMap(m map[string]interface{}) map[string]interface{} { + if m == nil { + return nil + } + + result := make(map[string]interface{}, len(m)) + maps0.Copy(result, m) + + return result +} + +// MergeMapsInto copies all entries from src maps into dst (mutating dst). +// +// Use when dst is a fresh map the caller owns and wants to avoid allocation. +func MergeMapsInto(dst map[string]interface{}, srcs ...map[string]interface{}) { + for _, src := range srcs { + maps0.Copy(dst, src) + } +} + // ExpandMapValues converts values from flat string to string slice func ExpandMapValues(m map[string]string) map[string][]string { m1 := make(map[string][]string, len(m)) for k, v := range m { m1[k] = []string{v} } + return m1 } diff --git a/pkg/protocols/common/generators/options.go b/pkg/protocols/common/generators/options.go index bc077547a..92c8b4355 100644 --- a/pkg/protocols/common/generators/options.go +++ b/pkg/protocols/common/generators/options.go @@ -1,12 +1,32 @@ package generators import ( + "sync" + "github.com/projectdiscovery/nuclei/v3/pkg/types" ) -// BuildPayloadFromOptions returns a map with the payloads provided via CLI +// optionsPayloadMap caches the result of BuildPayloadFromOptions per options +// pointer. This supports multiple SDK instances with different options running +// concurrently. +var optionsPayloadMap sync.Map // map[*types.Options]map[string]interface{} + +// BuildPayloadFromOptions returns a map with the payloads provided via CLI. +// +// The result is cached per options pointer since options don't change during a run. +// Returns a copy of the cached map to prevent concurrent modification issues. +// Safe for concurrent use with multiple SDK instances. func BuildPayloadFromOptions(options *types.Options) map[string]interface{} { + if options == nil { + return make(map[string]interface{}) + } + + if cached, ok := optionsPayloadMap.Load(options); ok { + return CopyMap(cached.(map[string]interface{})) + } + m := make(map[string]interface{}) + // merge with vars if !options.Vars.IsEmpty() { m = MergeMaps(m, options.Vars.AsMap()) @@ -16,5 +36,18 @@ func BuildPayloadFromOptions(options *types.Options) map[string]interface{} { if options.EnvironmentVariables { m = MergeMaps(EnvVars(), m) } - return m + + actual, _ := optionsPayloadMap.LoadOrStore(options, m) + + // Return a copy to prevent concurrent writes to the cached map + return CopyMap(actual.(map[string]interface{})) +} + +// ClearOptionsPayloadMap clears the cached options payload. +// SDK users should call this when disposing of a NucleiEngine instance +// to prevent memory leaks if creating many short-lived instances. +func ClearOptionsPayloadMap(options *types.Options) { + if options != nil { + optionsPayloadMap.Delete(options) + } } diff --git a/pkg/protocols/common/generators/options_test.go b/pkg/protocols/common/generators/options_test.go new file mode 100644 index 000000000..8d8578145 --- /dev/null +++ b/pkg/protocols/common/generators/options_test.go @@ -0,0 +1,92 @@ +package generators + +import ( + "sync" + "testing" + + "github.com/projectdiscovery/goflags" + "github.com/projectdiscovery/nuclei/v3/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestBuildPayloadFromOptionsConcurrency(t *testing.T) { + // Test that BuildPayloadFromOptions is safe for concurrent use + // and returns independent copies that can be modified without races + vars := goflags.RuntimeMap{} + _ = vars.Set("key=value") + + opts := &types.Options{ + Vars: vars, + } + + const numGoroutines = 100 + var wg sync.WaitGroup + wg.Add(numGoroutines) + + // Each goroutine gets a map and modifies it + for i := 0; i < numGoroutines; i++ { + go func(id int) { + defer wg.Done() + + // Get the map (should be a copy of cached data) + m := BuildPayloadFromOptions(opts) + + // Modify it - this should not cause races + m["goroutine_id"] = id + m["test_key"] = "test_value" + + // Verify original cached value is present + require.Equal(t, "value", m["key"]) + }(i) + } + + wg.Wait() +} + +func TestBuildPayloadFromOptionsCaching(t *testing.T) { + // Test that caching actually works + vars := goflags.RuntimeMap{} + _ = vars.Set("cached=yes") + + opts := &types.Options{ + Vars: vars, + EnvironmentVariables: false, + } + + // First call - builds and caches + m1 := BuildPayloadFromOptions(opts) + require.Equal(t, "yes", m1["cached"]) + + // Second call - should return copy of cached result + m2 := BuildPayloadFromOptions(opts) + require.Equal(t, "yes", m2["cached"]) + + // Modify m1 - should not affect m2 since they're copies + m1["modified"] = "in_m1" + require.NotContains(t, m2, "modified") + + // Modify m2 - should not affect future calls + m2["modified"] = "in_m2" + m3 := BuildPayloadFromOptions(opts) + require.NotContains(t, m3, "modified") +} + +func TestClearOptionsPayloadMap(t *testing.T) { + vars := goflags.RuntimeMap{} + _ = vars.Set("temp=data") + + opts := &types.Options{ + Vars: vars, + } + + // Build and cache + m1 := BuildPayloadFromOptions(opts) + require.Equal(t, "data", m1["temp"]) + + // Clear the cache + ClearOptionsPayloadMap(opts) + + // Verify it still works (rebuilds) + m2 := BuildPayloadFromOptions(opts) + require.Equal(t, "data", m2["temp"]) +} diff --git a/pkg/protocols/common/variables/variables.go b/pkg/protocols/common/variables/variables.go index fa5cc1dbc..76bcacd9a 100644 --- a/pkg/protocols/common/variables/variables.go +++ b/pkg/protocols/common/variables/variables.go @@ -70,18 +70,23 @@ func (variables *Variable) UnmarshalJSON(data []byte) error { // Evaluate returns a finished map of variables based on set values func (variables *Variable) Evaluate(values map[string]interface{}) map[string]interface{} { result := make(map[string]interface{}, variables.Len()) + combined := make(map[string]interface{}, len(values)+variables.Len()) + generators.MergeMapsInto(combined, values) + variables.ForEach(func(key string, value interface{}) { if sliceValue, ok := value.([]interface{}); ok { // slices cannot be evaluated result[key] = sliceValue + combined[key] = sliceValue return } valueString := types.ToString(value) - combined := generators.MergeMaps(values, result) - if value, ok := combined[key]; ok { - valueString = types.ToString(value) + if existingValue, ok := combined[key]; ok { + valueString = types.ToString(existingValue) } - result[key] = evaluateVariableValue(valueString, combined, result) + evaluated := evaluateVariableValueWithMap(valueString, combined) + result[key] = evaluated + combined[key] = evaluated }) return result } @@ -98,29 +103,36 @@ func (variables *Variable) GetAll() map[string]interface{} { // EvaluateWithInteractsh returns evaluation results of variables with interactsh func (variables *Variable) EvaluateWithInteractsh(values map[string]interface{}, interact *interactsh.Client) (map[string]interface{}, []string) { result := make(map[string]interface{}, variables.Len()) + combined := make(map[string]interface{}, len(values)+variables.Len()) + generators.MergeMapsInto(combined, values) var interactURLs []string variables.ForEach(func(key string, value interface{}) { if sliceValue, ok := value.([]interface{}); ok { // slices cannot be evaluated result[key] = sliceValue + combined[key] = sliceValue return } valueString := types.ToString(value) + if existingValue, ok := combined[key]; ok { + valueString = types.ToString(existingValue) + } if strings.Contains(valueString, "interactsh-url") { valueString, interactURLs = interact.Replace(valueString, interactURLs) } - combined := generators.MergeMaps(values, result) - if value, ok := combined[key]; ok { - valueString = types.ToString(value) - } - result[key] = evaluateVariableValue(valueString, combined, result) + evaluated := evaluateVariableValueWithMap(valueString, combined) + result[key] = evaluated + combined[key] = evaluated }) return result, interactURLs } -// evaluateVariableValue expression and returns final value -func evaluateVariableValue(expression string, values, processing map[string]interface{}) string { +// evaluateVariableValue expression and returns final value. +// +// Deprecated: use evaluateVariableValueWithMap instead to avoid repeated map +// merging overhead. +func evaluateVariableValue(expression string, values, processing map[string]interface{}) string { // nolint finalMap := generators.MergeMaps(values, processing) result, err := expressions.Evaluate(expression, finalMap) if err != nil { @@ -130,6 +142,16 @@ func evaluateVariableValue(expression string, values, processing map[string]inte return result } +// evaluateVariableValueWithMap evaluates an expression with a pre-merged map. +func evaluateVariableValueWithMap(expression string, combinedMap map[string]interface{}) string { + result, err := expressions.Evaluate(expression, combinedMap) + if err != nil { + return expression + } + + return result +} + // checkForLazyEval checks if the variables have any lazy evaluation i.e any dsl function // and sets the flag accordingly. func (variables *Variable) checkForLazyEval() bool { diff --git a/pkg/protocols/common/variables/variables_test.go b/pkg/protocols/common/variables/variables_test.go index cbf560b4e..0089c92c6 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/protocols/common/interactsh" "github.com/projectdiscovery/nuclei/v3/pkg/utils" "github.com/projectdiscovery/nuclei/v3/pkg/utils/json" "github.com/stretchr/testify/require" @@ -147,3 +148,176 @@ func TestCheckForLazyEval(t *testing.T) { require.True(t, variables.LazyEval, "LazyEval flag should be true") }) } + +func TestVariablesEvaluateChained(t *testing.T) { + t.Run("chained-variable-references", func(t *testing.T) { + // Test that variables can reference previously defined variables + // and that input values (like BaseURL) are available for evaluation + // but not included in the result + variables := &Variable{ + LazyEval: true, // skip auto-evaluation in UnmarshalYAML + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(3), + } + variables.Set("a", "hello") + variables.Set("b", "{{a}} world") + variables.Set("c", "{{b}}!") + + inputValues := map[string]interface{}{ + "BaseURL": "http://example.com", + "Host": "example.com", + } + + result := variables.Evaluate(inputValues) + + // Result should contain only the defined variables, not input values + require.Len(t, result, 3, "result should contain exactly 3 variables") + require.NotContains(t, result, "BaseURL", "result should not contain input values") + require.NotContains(t, result, "Host", "result should not contain input values") + + // Chained evaluation should work correctly + require.Equal(t, "hello", result["a"]) + require.Equal(t, "hello world", result["b"]) + require.Equal(t, "hello world!", result["c"]) + }) + + t.Run("variables-using-input-values", func(t *testing.T) { + // Test that variables can use input values in expressions + variables := &Variable{ + LazyEval: true, + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(2), + } + variables.Set("api_url", "{{BaseURL}}/api/v1") + variables.Set("full_path", "{{api_url}}/users") + + inputValues := map[string]interface{}{ + "BaseURL": "http://example.com", + } + + result := variables.Evaluate(inputValues) + + require.Len(t, result, 2) + require.Equal(t, "http://example.com/api/v1", result["api_url"]) + require.Equal(t, "http://example.com/api/v1/users", result["full_path"]) + require.NotContains(t, result, "BaseURL") + }) + + t.Run("mixed-expressions-and-chaining", func(t *testing.T) { + // Test combining DSL functions with chained variables + variables := &Variable{ + LazyEval: true, + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(3), + } + variables.Set("token", "secret123") + variables.Set("hashed", "{{md5(token)}}") + variables.Set("header", "X-Auth: {{hashed}}") + + result := variables.Evaluate(map[string]interface{}{}) + + require.Equal(t, "secret123", result["token"]) + require.Equal(t, "5d7845ac6ee7cfffafc5fe5f35cf666d", result["hashed"]) // md5("secret123") + require.Equal(t, "X-Auth: 5d7845ac6ee7cfffafc5fe5f35cf666d", result["header"]) + }) + + t.Run("evaluation-order-preserved", func(t *testing.T) { + // Test that evaluation follows insertion order + // (important for variables that depend on previously defined ones) + variables := &Variable{ + LazyEval: true, + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(4), + } + variables.Set("step1", "A") + variables.Set("step2", "{{step1}}B") + variables.Set("step3", "{{step2}}C") + variables.Set("step4", "{{step3}}D") + + result := variables.Evaluate(map[string]interface{}{}) + + require.Equal(t, "A", result["step1"]) + require.Equal(t, "AB", result["step2"]) + require.Equal(t, "ABC", result["step3"]) + require.Equal(t, "ABCD", result["step4"]) + }) +} + +func TestEvaluateWithInteractshOverrideOrder(t *testing.T) { + // This test demonstrates a bug where interactsh URL replacement is wasted + // when an input value exists for the same variable key. + // + // Bug scenario: + // 1. Variable "callback" is defined with "{{interactsh-url}}" + // 2. Input values contain "callback" with some other value + // 3. The interactsh-url is replaced first (wasting an interactsh URL) + // 4. Then immediately overwritten by the input value + // + // Expected behavior: Input override should be checked FIRST, then interactsh + // replacement should happen on the final valueString. + + t.Run("interactsh-replacement-with-input-override", func(t *testing.T) { + variables := &Variable{ + LazyEval: true, + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(1), + } + variables.Set("callback", "{{interactsh-url}}") + + // Input provides an override that also contains interactsh-url + inputValues := map[string]interface{}{ + "callback": "https://custom.{{interactsh-url}}/path", + } + + // Create a real interactsh client for testing + client, err := interactsh.New(&interactsh.Options{ + ServerURL: "oast.fun", + CacheSize: 100, + Eviction: 60 * time.Second, + CooldownPeriod: 5 * time.Second, + PollDuration: 5 * time.Second, + DisableHttpFallback: true, + }) + require.NoError(t, err, "could not create interactsh client") + defer client.Close() + + result, urls := variables.EvaluateWithInteractsh(inputValues, client) + + // The input override contains interactsh-url, so it should be replaced + // and we should have exactly 1 URL from the input override + require.Len(t, urls, 1, "should have 1 interactsh URL from input override") + + // The result should use the input override (with interactsh replaced) + require.Contains(t, result["callback"], "https://custom.", "should use input override pattern") + require.Contains(t, result["callback"], "/path", "should use input override pattern") + require.NotContains(t, result["callback"], "{{interactsh-url}}", "interactsh should be replaced") + }) + + t.Run("interactsh-replacement-without-input-override", func(t *testing.T) { + variables := &Variable{ + LazyEval: true, + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(1), + } + variables.Set("callback", "{{interactsh-url}}") + + // No input override for "callback" + inputValues := map[string]interface{}{ + "other_key": "other_value", + } + + client, err := interactsh.New(&interactsh.Options{ + ServerURL: "oast.fun", + CacheSize: 100, + Eviction: 60 * time.Second, + CooldownPeriod: 5 * time.Second, + PollDuration: 5 * time.Second, + DisableHttpFallback: true, + }) + require.NoError(t, err, "could not create interactsh client") + defer client.Close() + + result, urls := variables.EvaluateWithInteractsh(inputValues, client) + + // Should have 1 URL from the variable definition + require.Len(t, urls, 1, "should have 1 interactsh URL") + + // The result should be the replaced interactsh URL + require.NotContains(t, result["callback"], "{{interactsh-url}}", "interactsh should be replaced") + require.NotEmpty(t, result["callback"], "callback should have a value") + }) +} diff --git a/pkg/protocols/http/request_generator.go b/pkg/protocols/http/request_generator.go index 4c4c701a8..1ef191395 100644 --- a/pkg/protocols/http/request_generator.go +++ b/pkg/protocols/http/request_generator.go @@ -84,10 +84,10 @@ func (r *requestGenerator) nextValue() (value string, payloads map[string]interf r.applyMark(request, Once) } if hasPayloadIterator { - return request, generators.MergeMaps(r.currentPayloads), r.okCurrentPayload + return request, generators.CopyMap(r.currentPayloads), r.okCurrentPayload } // next should return a copy of payloads and not pointer to payload to avoid data race - return request, generators.MergeMaps(r.currentPayloads), true + return request, generators.CopyMap(r.currentPayloads), true } else { return "", nil, false } From 7945b8591cf600b3a2a9c49e65c2a575ed752d94 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Sat, 20 Dec 2025 00:16:01 +0700 Subject: [PATCH 09/28] chore(deps): bump projectdiscovery/utils@v0.8.0 to fix deadlock in `*httputil.ResponseChain` Fixes #6720. Signed-off-by: Dwi Siswanto --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 026b801ea..4da7605eb 100644 --- a/go.mod +++ b/go.mod @@ -110,7 +110,7 @@ require ( github.com/projectdiscovery/tlsx v1.2.2 github.com/projectdiscovery/uncover v1.2.0 github.com/projectdiscovery/useragent v0.0.105 - github.com/projectdiscovery/utils v0.7.3 + github.com/projectdiscovery/utils v0.8.0 github.com/projectdiscovery/wappalyzergo v0.2.59 github.com/redis/go-redis/v9 v9.11.0 github.com/seh-msft/burpxml v1.0.1 @@ -328,7 +328,7 @@ require ( github.com/projectdiscovery/cdncheck v1.2.14 // 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 + github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 // indirect github.com/refraction-networking/utls v1.7.1 // indirect github.com/sashabaranov/go-openai v1.37.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect diff --git a/go.sum b/go.sum index 04d558ffd..38d218d41 100644 --- a/go.sum +++ b/go.sum @@ -857,8 +857,8 @@ github.com/projectdiscovery/interactsh v1.2.4 h1:WUSj+fxbcV53J64oIAhbYzCKD1w/Iye 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= github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb/go.mod h1:vmgC0DTFCfoCLp0RAfsfYTZZan0QMVs+cmTbH6blfjk= -github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE= -github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI= +github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 h1:eR+0HE//Ciyfwy3HC7fjRyKShSJHYoX2Pv7pPshjK/Q= +github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI= github.com/projectdiscovery/mapcidr v1.1.97 h1:7FkxNNVXp+m1rIu5Nv/2SrF9k4+LwP8QuWs2puwy+2w= github.com/projectdiscovery/mapcidr v1.1.97/go.mod h1:9dgTJh1SP02gYZdpzMjm6vtYFkEHQHoTyaVNvaeJ7lA= github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw1pfT6bg35NiN7yd1XKtJap5Nk6lMwQ0RNi8= @@ -885,8 +885,8 @@ github.com/projectdiscovery/uncover v1.2.0 h1:31tjYa0v8FB8Ch8hJTxb+2t63vsljdOo0O github.com/projectdiscovery/uncover v1.2.0/go.mod h1:ozqKb++p39Kmh1SmwIpbQ9p0aVGPXuwsb4/X2Kvx6ms= github.com/projectdiscovery/useragent v0.0.105 h1:yFGFTfWZ/RZP5XbGRJtvKcbRNnlGI6xfPRXHb8DWdhg= github.com/projectdiscovery/useragent v0.0.105/go.mod h1:jWG1BD2yu8EC3olt6Amke7BiyLkXzpErI7Jtzr/tWZM= -github.com/projectdiscovery/utils v0.7.3 h1:kX+77AA58yK6EZgkTRJEnK9V/7AZYzlXdcu/o/kJhFs= -github.com/projectdiscovery/utils v0.7.3/go.mod h1:uDdQ3/VWomai98l+a3Ye/srDXdJ4xUIar/mSXlQ9gBM= +github.com/projectdiscovery/utils v0.8.0 h1:8d79OCs5xGDNXdKxMUKMY/lgQSUWJMYB1B2Sx+oiqkQ= +github.com/projectdiscovery/utils v0.8.0/go.mod h1:CU6tjtyTRxBrnNek+GPJplw4IIHcXNZNKO09kWgqTdg= github.com/projectdiscovery/wappalyzergo v0.2.59 h1:oztLKyEf3yC2ncUIfSXguVA6jwb7iaV5tuK1BF3K9CY= github.com/projectdiscovery/wappalyzergo v0.2.59/go.mod h1:lwuDLdAqWDZ1IL8OQnoNQ0t17UP9AQSvVuFcDAm4FpQ= github.com/projectdiscovery/yamldoc-go v1.0.6 h1:GCEdIRlQjDux28xTXKszM7n3jlMf152d5nqVpVoetas= From 0ab06cc4bf7554890fa6669aedc763ecdc0ef434 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Sat, 20 Dec 2025 00:56:07 +0700 Subject: [PATCH 10/28] test: add maps, options, variables bench Signed-off-by: Dwi Siswanto --- .../common/generators/maps_bench_test.go | 44 ++++++++++ .../common/generators/options_bench_test.go | 41 ++++++++++ .../common/variables/variables_bench_test.go | 80 +++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 pkg/protocols/common/generators/maps_bench_test.go create mode 100644 pkg/protocols/common/generators/options_bench_test.go create mode 100644 pkg/protocols/common/variables/variables_bench_test.go diff --git a/pkg/protocols/common/generators/maps_bench_test.go b/pkg/protocols/common/generators/maps_bench_test.go new file mode 100644 index 000000000..5731e2f4e --- /dev/null +++ b/pkg/protocols/common/generators/maps_bench_test.go @@ -0,0 +1,44 @@ +package generators + +import ( + "fmt" + "testing" +) + +func BenchmarkMergeMaps(b *testing.B) { + map1 := map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + } + map2 := map[string]interface{}{ + "key6": "value6", + "key7": "value7", + "key8": "value8", + "key9": "value9", + "key10": "value10", + } + map3 := map[string]interface{}{ + "key11": "value11", + "key12": "value12", + "key13": "value13", + } + + for i := 1; i <= 3; i++ { + b.Run(fmt.Sprintf("%d-maps", i), func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + switch i { + case 1: + _ = MergeMaps(map1) + case 2: + _ = MergeMaps(map1, map2) + case 3: + _ = MergeMaps(map1, map2, map3) + } + } + }) + } +} diff --git a/pkg/protocols/common/generators/options_bench_test.go b/pkg/protocols/common/generators/options_bench_test.go new file mode 100644 index 000000000..10fc48005 --- /dev/null +++ b/pkg/protocols/common/generators/options_bench_test.go @@ -0,0 +1,41 @@ +package generators + +import ( + "testing" + + "github.com/projectdiscovery/goflags" + "github.com/projectdiscovery/nuclei/v3/pkg/types" +) + +func BenchmarkBuildPayloadFromOptions(b *testing.B) { + // Setup options with vars and env vars + vars := goflags.RuntimeMap{} + _ = vars.Set("key1=value1") + _ = vars.Set("key2=value2") + _ = vars.Set("key3=value3") + _ = vars.Set("key4=value4") + _ = vars.Set("key5=value5") + + opts := &types.Options{ + Vars: vars, + EnvironmentVariables: true, // This adds more entries + } + + b.Run("Sequential", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + _ = BuildPayloadFromOptions(opts) + } + }) + + b.Run("Parallel", func(b *testing.B) { + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + m := BuildPayloadFromOptions(opts) + // Simulate typical usage - read a value + _ = m["key1"] + } + }) + }) +} diff --git a/pkg/protocols/common/variables/variables_bench_test.go b/pkg/protocols/common/variables/variables_bench_test.go new file mode 100644 index 000000000..c94a64a05 --- /dev/null +++ b/pkg/protocols/common/variables/variables_bench_test.go @@ -0,0 +1,80 @@ +package variables + +import ( + "fmt" + "testing" + + "github.com/projectdiscovery/nuclei/v3/pkg/utils" +) + +func BenchmarkVariableEvaluate(b *testing.B) { + // Setup variables with chained references and DSL functions + variables := &Variable{ + LazyEval: true, + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(5), + } + variables.Set("base", "testvalue") + variables.Set("derived1", "{{base}}_suffix") + variables.Set("derived2", "{{md5(derived1)}}") + variables.Set("derived3", "prefix_{{derived2}}") + variables.Set("final", "{{derived3}}_end") + + inputValues := map[string]interface{}{ + "BaseURL": "http://example.com", + "Host": "example.com", + "Path": "/api/v1", + } + + b.Run("Evaluate", func(b *testing.B) { + b.Run("5Variables", func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + _ = variables.Evaluate(inputValues) + } + }) + + b.Run("Parallel", func(b *testing.B) { + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = variables.Evaluate(inputValues) + } + }) + }) + }) +} + +func BenchmarkVariableEvaluateScaling(b *testing.B) { + // Test how the optimization scales with different variable counts + inputValues := map[string]interface{}{ + "BaseURL": "http://example.com", + "Host": "example.com", + } + + benchmarkSizes := []int{1, 5, 10, 20} + + for _, size := range benchmarkSizes { + variables := &Variable{ + LazyEval: true, + InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(size), + } + + // Create chain of variables + for i := range size { + varName := fmt.Sprintf("var%d", i) + if i == 0 { + variables.Set(varName, "initial") + } else { + prevVarName := fmt.Sprintf("var%d", i-1) + variables.Set(varName, fmt.Sprintf("{{%s}}_step", prevVarName)) + } + } + + b.Run(fmt.Sprintf("Variables-%d", size), func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + _ = variables.Evaluate(inputValues) + } + }) + } +} From 0c125e2224fb12b825fe2632a829d3d032ce6424 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Sat, 20 Dec 2025 01:02:16 +0700 Subject: [PATCH 11/28] test(generators): update maps & options benchmarks Signed-off-by: Dwi Siswanto --- .../common/generators/maps_bench_test.go | 65 +++++++++++++++++++ .../common/generators/options_bench_test.go | 4 ++ 2 files changed, 69 insertions(+) diff --git a/pkg/protocols/common/generators/maps_bench_test.go b/pkg/protocols/common/generators/maps_bench_test.go index 5731e2f4e..25088a520 100644 --- a/pkg/protocols/common/generators/maps_bench_test.go +++ b/pkg/protocols/common/generators/maps_bench_test.go @@ -42,3 +42,68 @@ func BenchmarkMergeMaps(b *testing.B) { }) } } + +func BenchmarkCopyMap(b *testing.B) { + map1 := map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + } + + for i := 1; i <= 1; i++ { + b.Run(fmt.Sprintf("%d-maps", i), func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + switch i { + case 1: + _ = CopyMap(map1) + } + } + }) + } +} + +func BenchmarkMergeMapsInto(b *testing.B) { + map1 := map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + } + map2 := map[string]interface{}{ + "key6": "value6", + "key7": "value7", + "key8": "value8", + "key9": "value9", + "key10": "value10", + } + map3 := map[string]interface{}{ + "key11": "value11", + "key12": "value12", + "key13": "value13", + } + map4 := map[string]interface{}{ + "key14": "value14", + "key15": "value15", + "key16": "value16", + } + + for i := 1; i <= 3; i++ { + b.Run(fmt.Sprintf("%d-maps", i), func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + switch i { + case 1: + MergeMapsInto(map1, map2) + case 2: + MergeMapsInto(map1, map2, map3) + case 3: + MergeMapsInto(map1, map2, map3, map4) + } + } + }) + } +} diff --git a/pkg/protocols/common/generators/options_bench_test.go b/pkg/protocols/common/generators/options_bench_test.go index 10fc48005..fe59193d8 100644 --- a/pkg/protocols/common/generators/options_bench_test.go +++ b/pkg/protocols/common/generators/options_bench_test.go @@ -22,6 +22,8 @@ func BenchmarkBuildPayloadFromOptions(b *testing.B) { } b.Run("Sequential", func(b *testing.B) { + ClearOptionsPayloadMap(opts) + b.ReportAllocs() for b.Loop() { _ = BuildPayloadFromOptions(opts) @@ -29,6 +31,8 @@ func BenchmarkBuildPayloadFromOptions(b *testing.B) { }) b.Run("Parallel", func(b *testing.B) { + ClearOptionsPayloadMap(opts) + b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { for pb.Next() { From 329a8910693a896edccd12d2f0839961dba649bb Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Sun, 21 Dec 2025 22:41:40 +0400 Subject: [PATCH 12/28] test: add pwsh integration test (#6724) * adding pwsh tests * chore(cmd): rm template Signed-off-by: Dwi Siswanto --------- Signed-off-by: Dwi Siswanto Co-authored-by: Dwi Siswanto --- cmd/integration-test/code.go | 1 + .../protocols/code/pwsh-echo.yaml | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 integration_tests/protocols/code/pwsh-echo.yaml diff --git a/cmd/integration-test/code.go b/cmd/integration-test/code.go index 95bd28f67..c2c591084 100644 --- a/cmd/integration-test/code.go +++ b/cmd/integration-test/code.go @@ -26,6 +26,7 @@ var codeTestCases = []TestCaseInfo{ {Path: "protocols/code/pre-condition.yaml", TestCase: &codePreCondition{}, DisableOn: isCodeDisabled}, {Path: "protocols/code/sh-virtual.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsLinux() || isCodeDisabled() }}, {Path: "protocols/code/py-virtual.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsLinux() || isCodeDisabled() }}, + {Path: "protocols/code/pwsh-echo.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return isCodeDisabled() }}, } const ( diff --git a/integration_tests/protocols/code/pwsh-echo.yaml b/integration_tests/protocols/code/pwsh-echo.yaml new file mode 100644 index 000000000..23582ae59 --- /dev/null +++ b/integration_tests/protocols/code/pwsh-echo.yaml @@ -0,0 +1,27 @@ +id: pw-echo + +info: + name: PowerShell Echo Test + author: pdteam + severity: info + description: Tests PowerShell execution with an echo-like operation. + tags: test,powershell,echo + +self-contained: true + +code: + - engine: + - pwsh + - powershell + - powershell.exe + args: + - -ExecutionPolicy + - Bypass + pattern: "*.ps1" + source: | + Write-Output "test-output-success" + + matchers: + - type: word + words: + - "test-output-success" \ No newline at end of file From 3fdc06bce3595f5b4e1f3bba52864bc846a7ddbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 05:01:47 +0000 Subject: [PATCH 13/28] chore(deps): bump the modules group with 6 updates Bumps the modules group with 6 updates: | Package | From | To | | --- | --- | --- | | [github.com/projectdiscovery/fastdialer](https://github.com/projectdiscovery/fastdialer) | `0.4.20` | `0.5.0` | | [github.com/projectdiscovery/retryablehttp-go](https://github.com/projectdiscovery/retryablehttp-go) | `1.1.1` | `1.3.0` | | [github.com/projectdiscovery/dsl](https://github.com/projectdiscovery/dsl) | `0.8.8` | `0.8.9` | | [github.com/projectdiscovery/gologger](https://github.com/projectdiscovery/gologger) | `1.1.64` | `1.1.65` | | [github.com/projectdiscovery/wappalyzergo](https://github.com/projectdiscovery/wappalyzergo) | `0.2.59` | `0.2.60` | | [github.com/projectdiscovery/cdncheck](https://github.com/projectdiscovery/cdncheck) | `1.2.14` | `1.2.15` | Updates `github.com/projectdiscovery/fastdialer` from 0.4.20 to 0.5.0 - [Release notes](https://github.com/projectdiscovery/fastdialer/releases) - [Commits](https://github.com/projectdiscovery/fastdialer/compare/v0.4.20...v0.5.0) Updates `github.com/projectdiscovery/retryablehttp-go` from 1.1.1 to 1.3.0 - [Release notes](https://github.com/projectdiscovery/retryablehttp-go/releases) - [Commits](https://github.com/projectdiscovery/retryablehttp-go/compare/v1.1.1...v1.3.0) Updates `github.com/projectdiscovery/dsl` from 0.8.8 to 0.8.9 - [Release notes](https://github.com/projectdiscovery/dsl/releases) - [Commits](https://github.com/projectdiscovery/dsl/compare/v0.8.8...v0.8.9) Updates `github.com/projectdiscovery/gologger` from 1.1.64 to 1.1.65 - [Release notes](https://github.com/projectdiscovery/gologger/releases) - [Commits](https://github.com/projectdiscovery/gologger/compare/v1.1.64...v1.1.65) Updates `github.com/projectdiscovery/wappalyzergo` from 0.2.59 to 0.2.60 - [Release notes](https://github.com/projectdiscovery/wappalyzergo/releases) - [Commits](https://github.com/projectdiscovery/wappalyzergo/compare/v0.2.59...v0.2.60) Updates `github.com/projectdiscovery/cdncheck` from 1.2.14 to 1.2.15 - [Release notes](https://github.com/projectdiscovery/cdncheck/releases) - [Commits](https://github.com/projectdiscovery/cdncheck/compare/v1.2.14...v1.2.15) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/fastdialer dependency-version: 0.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: modules - dependency-name: github.com/projectdiscovery/retryablehttp-go dependency-version: 1.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: modules - dependency-name: github.com/projectdiscovery/dsl dependency-version: 0.8.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/gologger dependency-version: 1.1.65 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/wappalyzergo dependency-version: 0.2.60 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/cdncheck dependency-version: 1.2.15 dependency-type: indirect update-type: version-update:semver-patch dependency-group: modules ... Signed-off-by: dependabot[bot] --- go.mod | 30 ++++++++++++++--------------- go.sum | 61 +++++++++++++++++++++++++++++----------------------------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 4da7605eb..60c61ccc7 100644 --- a/go.mod +++ b/go.mod @@ -22,12 +22,12 @@ 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.20 + github.com/projectdiscovery/fastdialer v0.5.0 github.com/projectdiscovery/hmap v0.0.98 github.com/projectdiscovery/interactsh v1.2.4 github.com/projectdiscovery/rawhttp v0.1.90 github.com/projectdiscovery/retryabledns v1.0.111 - github.com/projectdiscovery/retryablehttp-go v1.1.1 + github.com/projectdiscovery/retryablehttp-go v1.3.0 github.com/projectdiscovery/yamldoc-go v1.0.6 github.com/remeh/sizedwaitgroup v1.0.0 github.com/rs/xid v1.6.0 @@ -39,9 +39,9 @@ require ( github.com/valyala/fasttemplate v1.2.2 github.com/weppos/publicsuffix-go v0.50.1 go.uber.org/multierr v1.11.0 - golang.org/x/net v0.47.0 + golang.org/x/net v0.48.0 golang.org/x/oauth2 v0.30.0 - golang.org/x/text v0.31.0 + golang.org/x/text v0.32.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -92,12 +92,12 @@ require ( github.com/microsoft/go-mssqldb v1.9.2 github.com/ory/dockertest/v3 v3.12.0 github.com/praetorian-inc/fingerprintx v1.1.15 - github.com/projectdiscovery/dsl v0.8.8 + github.com/projectdiscovery/dsl v0.8.9 github.com/projectdiscovery/fasttemplate v0.0.2 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.64 + github.com/projectdiscovery/gologger v1.1.65 github.com/projectdiscovery/gostruct v0.0.2 github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81 github.com/projectdiscovery/httpx v1.7.4 @@ -111,7 +111,7 @@ require ( github.com/projectdiscovery/uncover v1.2.0 github.com/projectdiscovery/useragent v0.0.105 github.com/projectdiscovery/utils v0.8.0 - github.com/projectdiscovery/wappalyzergo v0.2.59 + github.com/projectdiscovery/wappalyzergo v0.2.60 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 @@ -124,7 +124,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.37.0 + golang.org/x/term v0.38.0 gopkg.in/yaml.v3 v3.0.1 moul.io/http2curl v1.0.0 ) @@ -206,6 +206,7 @@ require ( github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/djherbis/times v1.6.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/docker/cli v27.4.1+incompatible // indirect github.com/docker/docker v28.3.3+incompatible // indirect @@ -325,7 +326,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.14 // indirect + github.com/projectdiscovery/cdncheck v1.2.15 // 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-20250715113114-c77eb3567582 // indirect @@ -383,8 +384,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.18.0 // indirect - gopkg.in/djherbis/times.v1 v1.3.0 // indirect + golang.org/x/sync v0.19.0 // indirect mellium.im/sasl v0.3.2 // indirect ) @@ -407,12 +407,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.45.0 // indirect + golang.org/x/crypto v0.46.0 // indirect golang.org/x/exp v0.0.0-20250911091902-df9299821621 - golang.org/x/mod v0.29.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/sys v0.39.0 // indirect golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.38.0 + golang.org/x/tools v0.39.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 diff --git a/go.sum b/go.sum index 38d218d41..22ee6b39b 100644 --- a/go.sum +++ b/go.sum @@ -314,6 +314,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c h1:+Zo5Ca9GH0RoeVZQKzFJcTLoAixx5s5Gq3pTIS+n354= github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:HJGU9ULdREjOcVGZVPB5s6zYmHi1RxzT71l2wQyLmnE= +github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= +github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= @@ -825,14 +827,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.14 h1:mHfNMdFwpQziPxIZnE4i0UERU/8vOcxSTcjYwp1PA2Y= -github.com/projectdiscovery/cdncheck v1.2.14/go.mod h1:wPckYZjRFQydtbosCS0beGjuAP5h4faX7eUqB8L3/cA= +github.com/projectdiscovery/cdncheck v1.2.15 h1:07ati3QcI65HEWP6GgHoTtit6cfu5InEx87rpwoWios= +github.com/projectdiscovery/cdncheck v1.2.15/go.mod h1:Q9HNh2uzO/qFdl8C4RUWOHj3qIhxlaBFeayPQJm2zII= 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.8 h1:UlcCf/2hfqqfDlhHWun3xT0u310iLWMiRoVw1t3TTLg= -github.com/projectdiscovery/dsl v0.8.8/go.mod h1:gXrMGOfBVu4UvgabOfXSMqeC5RnR93aeZ94+V32OlHI= -github.com/projectdiscovery/fastdialer v0.4.20 h1:LBw2pB9Y/0Du+d/asyB4u5vF+QhvkPIo+SLcUaf2pG8= -github.com/projectdiscovery/fastdialer v0.4.20/go.mod h1:GXzyco8SVX6E335FKpIiX0WSRbK2MsNwvqXCr195QtM= +github.com/projectdiscovery/dsl v0.8.9 h1:weluy3pgLKxI0JCwjY6b1u5YEb9falhsiOCT5ouMnRw= +github.com/projectdiscovery/dsl v0.8.9/go.mod h1:hFLa5MOGMZXwF38WcwYPqm0uU6G46s+vbVWdMusdVv0= +github.com/projectdiscovery/fastdialer v0.5.0 h1:7XX11t/7kuenVM09r3+EeNgqYItkoTNVUc6XKFqZpHw= +github.com/projectdiscovery/fastdialer v0.5.0/go.mod h1:v4uDT/Sb0o83KNA+ggxHCw6u6BvJZglpGiAPIufaQ84= 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= @@ -843,8 +845,8 @@ 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.64 h1:GQ1DplEo5YnVozPImYWQqkeU5rwkPBxjMzeZmirVMdA= -github.com/projectdiscovery/gologger v1.1.64/go.mod h1:daez34xaA7LTazBb4t+Ccm9/9Kjvlsu4EY033lQdATA= +github.com/projectdiscovery/gologger v1.1.65 h1:tVIjEke+UaQ3b2dj89AHCHGgeyUYIO0E35Yu+ppU+bk= +github.com/projectdiscovery/gologger v1.1.65/go.mod h1:myCMJMPu1TDl48LQGj+w7MhOdVKYAZNSOURHhK5XaaM= 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= @@ -873,8 +875,8 @@ github.com/projectdiscovery/rdap v0.9.0 h1:wPhHx5pQ2QI+WGhyNb2PjhTl0NtB39Nk7YFZ9 github.com/projectdiscovery/rdap v0.9.0/go.mod h1:zk4yrJFQ2Hy36Aqk+DvotYQxYAeALaCJ5ORySkff36Q= github.com/projectdiscovery/retryabledns v1.0.111 h1:iyMdCDgNmaSRJYcGqB+SLlvlw9WijlbJ6Q9OEpRAWsQ= github.com/projectdiscovery/retryabledns v1.0.111/go.mod h1:6TOPJ3QAE4reBu6bvsGsTcyEb+OypcKYFQH7yVsjyIM= -github.com/projectdiscovery/retryablehttp-go v1.1.1 h1:R1r5DgxXIW+AH98ifKxFpeFbZAXP/eP9bNOMpaO32As= -github.com/projectdiscovery/retryablehttp-go v1.1.1/go.mod h1:D+jVQkvQ3ZNDYX30SfxJQ4ylh//NMPq1Yf11lEo+oyA= +github.com/projectdiscovery/retryablehttp-go v1.3.0 h1:ro/IYNx4fSdz0hFCFIpQ9++SlOAa4WdQS1i6GMUgSEk= +github.com/projectdiscovery/retryablehttp-go v1.3.0/go.mod h1:B+fiMihnqSxCKM0SoEPu92u6FL5hVBfmd6j3SwrFpY0= 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= @@ -887,8 +889,8 @@ github.com/projectdiscovery/useragent v0.0.105 h1:yFGFTfWZ/RZP5XbGRJtvKcbRNnlGI6 github.com/projectdiscovery/useragent v0.0.105/go.mod h1:jWG1BD2yu8EC3olt6Amke7BiyLkXzpErI7Jtzr/tWZM= github.com/projectdiscovery/utils v0.8.0 h1:8d79OCs5xGDNXdKxMUKMY/lgQSUWJMYB1B2Sx+oiqkQ= github.com/projectdiscovery/utils v0.8.0/go.mod h1:CU6tjtyTRxBrnNek+GPJplw4IIHcXNZNKO09kWgqTdg= -github.com/projectdiscovery/wappalyzergo v0.2.59 h1:oztLKyEf3yC2ncUIfSXguVA6jwb7iaV5tuK1BF3K9CY= -github.com/projectdiscovery/wappalyzergo v0.2.59/go.mod h1:lwuDLdAqWDZ1IL8OQnoNQ0t17UP9AQSvVuFcDAm4FpQ= +github.com/projectdiscovery/wappalyzergo v0.2.60 h1:CGpQy2qHxOBQSikdNjZomHUP9fVljKBfDJeodpjVp7A= +github.com/projectdiscovery/wappalyzergo v0.2.60/go.mod h1:8FtSVcmPRZU0g1euBpdSYEBHIvB7Zz9MOb754ZqZmfU= 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= @@ -1214,8 +1216,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.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= 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= @@ -1253,8 +1255,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.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= 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= @@ -1305,8 +1307,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.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= 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= @@ -1335,8 +1337,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.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.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= @@ -1389,6 +1391,7 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1401,8 +1404,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.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.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= @@ -1416,8 +1419,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.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= 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= @@ -1434,8 +1437,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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= 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= @@ -1489,8 +1492,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.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= 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= @@ -1593,8 +1596,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/corvus-ch/zbase32.v1 v1.0.0 h1:K4u1NprbDNvKPczKfHLbwdOWHTZ0zfv2ow71H1nRnFU= gopkg.in/corvus-ch/zbase32.v1 v1.0.0/go.mod h1:T3oKkPOm4AV/bNXCNFUxRmlE9RUyBz/DSo0nK9U+c0Y= -gopkg.in/djherbis/times.v1 v1.3.0 h1:uxMS4iMtH6Pwsxog094W0FYldiNnfY/xba00vq6C2+o= -gopkg.in/djherbis/times.v1 v1.3.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= From 5d792012996389a192fa9b7fd0a9262bb4523a71 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Wed, 24 Dec 2025 00:20:50 +0400 Subject: [PATCH 14/28] fix(js): incorrect postgres exec call signature (#6731) Make sure postgres Exec/ExecContext are invoked with the correct argument order, preventing context from being passed as the query. * fixing pg syntax * adding test --- cmd/integration-test/javascript.go | 65 +++++++++++++++++-- .../javascript/postgres-pass-brute.yaml | 47 ++++++++++++++ pkg/js/libs/postgres/postgres.go | 8 +-- 3 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 integration_tests/protocols/javascript/postgres-pass-brute.yaml diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index aad685344..ccee6a6b9 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -17,17 +17,19 @@ 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/postgres-pass-brute.yaml", TestCase: &javascriptPostgresPassBrute{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }}, {Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}}, {Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}}, } var ( - redisResource *dockertest.Resource - sshResource *dockertest.Resource - oracleResource *dockertest.Resource - vncResource *dockertest.Resource - pool *dockertest.Pool - defaultRetry = 3 + redisResource *dockertest.Resource + sshResource *dockertest.Resource + oracleResource *dockertest.Resource + vncResource *dockertest.Resource + postgresResource *dockertest.Resource + pool *dockertest.Pool + defaultRetry = 3 ) type javascriptNetHttps struct{} @@ -169,6 +171,38 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error { return multierr.Combine(errs...) } +type javascriptPostgresPassBrute struct{} + +func (j *javascriptPostgresPassBrute) Execute(filePath string) error { + if postgresResource == nil || pool == nil { + // skip test as postgres is not running + return nil + } + tempPort := postgresResource.GetPort("5432/tcp") + finalURL := "localhost:" + tempPort + defer purge(postgresResource) + errs := []error{} + for i := 0; i < defaultRetry; i++ { + results := []string{} + var err error + _ = pool.Retry(func() error { + //let postgres server start + time.Sleep(3 * time.Second) + results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug) + return nil + }) + if err != nil { + return err + } + if err := expectResultsCount(results, 1); err == nil { + return nil + } else { + errs = append(errs, err) + } + } + return multierr.Combine(errs...) +} + type javascriptMultiPortsSSH struct{} func (j *javascriptMultiPortsSSH) Execute(filePath string) error { @@ -292,4 +326,23 @@ func init() { if err := vncResource.Expire(30); err != nil { log.Printf("Could not expire resource: %s", err) } + + // setup a temporary postgres instance + postgresResource, err = pool.RunWithOptions(&dockertest.RunOptions{ + Repository: "postgres", + Tag: "latest", + Env: []string{ + "POSTGRES_PASSWORD=postgres", + "POSTGRES_USER=postgres", + }, + Platform: "linux/amd64", + }) + if err != nil { + log.Printf("Could not start postgres resource: %s", err) + return + } + // by default expire after 30 sec + if err := postgresResource.Expire(30); err != nil { + log.Printf("Could not expire postgres resource: %s", err) + } } diff --git a/integration_tests/protocols/javascript/postgres-pass-brute.yaml b/integration_tests/protocols/javascript/postgres-pass-brute.yaml new file mode 100644 index 000000000..6545d4f78 --- /dev/null +++ b/integration_tests/protocols/javascript/postgres-pass-brute.yaml @@ -0,0 +1,47 @@ +id: postgres-pass-brute + +info: + name: PostgreSQL Password Bruteforce + author: pdteam + severity: high + description: | + This template bruteforces passwords for protected PostgreSQL instances. + If PostgreSQL is not protected with password, it is also matched. + metadata: + shodan-query: product:"PostgreSQL" + tags: js,network,postgresql,authentication + +javascript: + - pre-condition: | + isPortOpen(Host,Port) + + code: | + const postgres = require('nuclei/postgres'); + const client = new postgres.PGClient; + success = client.Connect(Host, Port, User, Pass); + + args: + Host: "{{Host}}" + Port: "5432" + User: "{{usernames}}" + Pass: "{{passwords}}" + + attack: clusterbomb + payloads: + usernames: + - postgres + - admin + - root + passwords: + - "" + - postgres + - password + - admin + - root + stop-at-first-match: true + + matchers: + - type: dsl + dsl: + - "success == true" + diff --git a/pkg/js/libs/postgres/postgres.go b/pkg/js/libs/postgres/postgres.go index 5280e3cdc..1504cb43e 100644 --- a/pkg/js/libs/postgres/postgres.go +++ b/pkg/js/libs/postgres/postgres.go @@ -82,7 +82,7 @@ func isPostgres(executionId string, host string, port int) (bool, error) { // const client = new postgres.PGClient; // const connected = client.Connect('acme.com', 5432, 'username', 'password'); // ``` -func (c *PGClient) Connect(ctx context.Context, host string, port int, username, password string) (bool, error) { +func (c *PGClient) Connect(ctx context.Context, host string, port int, username string, password string) (bool, error) { ok, err := c.IsPostgres(ctx, host, port) if err != nil { return false, err @@ -104,7 +104,7 @@ func (c *PGClient) Connect(ctx context.Context, host string, port int, username, // const result = client.ExecuteQuery('acme.com', 5432, 'username', 'password', 'dbname', 'select * from users'); // log(to_json(result)); // ``` -func (c *PGClient) ExecuteQuery(ctx context.Context, host string, port int, username, password, dbName, query string) (*utils.SQLResult, error) { +func (c *PGClient) ExecuteQuery(ctx context.Context, host string, port int, username string, password string, dbName string, query string) (*utils.SQLResult, error) { ok, err := c.IsPostgres(ctx, host, port) if err != nil { return nil, err @@ -157,7 +157,7 @@ func executeQuery(executionId string, host string, port int, username string, pa // const client = new postgres.PGClient; // const connected = client.ConnectWithDB('acme.com', 5432, 'username', 'password', 'dbname'); // ``` -func (c *PGClient) ConnectWithDB(ctx context.Context, host string, port int, username, password, dbName string) (bool, error) { +func (c *PGClient) ConnectWithDB(ctx context.Context, host string, port int, username string, password string, dbName string) (bool, error) { ok, err := c.IsPostgres(ctx, host, port) if err != nil { return false, err @@ -207,7 +207,7 @@ func connect(executionId string, host string, port int, username string, passwor _ = db.Close() }() - _, err := db.Exec(ctx, "select 1") + _, err := db.ExecContext(ctx, "select 1") if err != nil { switch true { case strings.Contains(err.Error(), "connect: connection refused"): From 78e90e300d573bfd65bfe73011003499d910add3 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto <25837540+dwisiswant0@users.noreply.github.com> Date: Thu, 25 Dec 2025 02:35:44 +0700 Subject: [PATCH 15/28] ci: refactor workflows (#6728) Signed-off-by: Dwi Siswanto --- .github/workflows/auto-merge.yaml | 30 +++++------- .github/workflows/compat-checks.yaml | 4 +- .github/workflows/flamegraph.yaml | 61 ++++++++++++++++++++++++ .github/workflows/generate-docs.yaml | 4 +- .github/workflows/generate-pgo.yaml | 66 ++++++++++++-------------- .github/workflows/govulncheck.yaml | 2 +- .github/workflows/perf-regression.yaml | 4 +- .github/workflows/perf-test.yaml | 43 ----------------- .github/workflows/release.yaml | 18 +++++-- .github/workflows/tests.yaml | 39 ++------------- .goreleaser.yml | 5 +- 11 files changed, 132 insertions(+), 144 deletions(-) create mode 100644 .github/workflows/flamegraph.yaml delete mode 100644 .github/workflows/perf-test.yaml diff --git a/.github/workflows/auto-merge.yaml b/.github/workflows/auto-merge.yaml index f6bb2c5c2..8da0eb6e5 100644 --- a/.github/workflows/auto-merge.yaml +++ b/.github/workflows/auto-merge.yaml @@ -1,28 +1,24 @@ -name: 🤖 Auto Merge +name: 🔀 Auto merge PR on: - pull_request_review: - types: [submitted] + # pull_request: + # types: [opened, synchronize, reopened, ready_for_review] + # pull_request_review: + # types: [submitted] workflow_run: - workflows: ["♾️ Compatibility Check"] - types: - - completed + workflows: ["♾️ Compatibility Checks"] + types: [completed] permissions: + contents: write pull-requests: write - issues: write - repository-projects: write jobs: - auto-merge: + auto-merge-dependabot: runs-on: ubuntu-latest - if: github.actor == 'dependabot[bot]' + if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@v6 + - uses: projectdiscovery/actions/pr/approve@v1 + - uses: projectdiscovery/actions/pr/merge@v1 with: - token: ${{ secrets.DEPENDABOT_PAT }} - - - uses: ahmadnassri/action-dependabot-auto-merge@v2 - with: - github-token: ${{ secrets.DEPENDABOT_PAT }} - target: all \ No newline at end of file + auto: "true" \ No newline at end of file diff --git a/.github/workflows/compat-checks.yaml b/.github/workflows/compat-checks.yaml index 093bd6ba0..086bf6422 100644 --- a/.github/workflows/compat-checks.yaml +++ b/.github/workflows/compat-checks.yaml @@ -7,8 +7,8 @@ on: - dev jobs: - check: - if: github.actor == 'dependabot[bot]' + compat-checks: + if: ${{ github.actor == 'dependabot[bot]' }} runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/flamegraph.yaml b/.github/workflows/flamegraph.yaml new file mode 100644 index 000000000..850407eb7 --- /dev/null +++ b/.github/workflows/flamegraph.yaml @@ -0,0 +1,61 @@ +name: 📊 Flamegraph + +on: + workflow_call: {} + +jobs: + flamegraph: + name: "Flamegraph" + env: + PROFILE_MEM: "/tmp/nuclei-profile" + TARGET_URL: "http://honey.scanme.sh/-/?foo=bar" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: projectdiscovery/actions/setup/go@v1 + - uses: projectdiscovery/actions/cache/go-rod-browser@v1 + - uses: projectdiscovery/actions/cache/nuclei@v1 + - run: make build + + - name: "Setup environment (push)" + if: ${{ github.event_name == 'push' }} + run: | + echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV + echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV + - name: "Setup environment (pull_request)" + if: ${{ github.event_name == 'pull_request' }} + run: | + echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV + echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV + + - run: ./bin/nuclei -update-templates + - run: | + ./bin/nuclei -silent -target="${TARGET_URL}" \ + -disable-update-check \ + -no-mhe -no-httpx \ + -code -dast -file -headless \ + -enable-self-contained -enable-global-matchers \ + -rate-limit=0 \ + -concurrency=250 \ + -headless-concurrency=100 \ + -payload-concurrency=250 \ + -bulk-size=250 \ + -headless-bulk-size=100 \ + -profile-mem="${PROFILE_MEM}" + + - uses: projectdiscovery/actions/flamegraph@v1 + id: flamegraph-cpu + with: + profile: "${{ env.PROFILE_MEM }}.cpu" + name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles" + continue-on-error: true + - uses: projectdiscovery/actions/flamegraph@v1 + id: flamegraph-mem + with: + profile: "${{ env.PROFILE_MEM }}.mem" + name: "${{ env.FLAMEGRAPH_NAME }} memory profiles" + continue-on-error: true + - if: ${{ steps.flamegraph-mem.outputs.message == '' }} + run: | + echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}" + echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}" diff --git a/.github/workflows/generate-docs.yaml b/.github/workflows/generate-docs.yaml index 365ad32f9..d40b235c6 100644 --- a/.github/workflows/generate-docs.yaml +++ b/.github/workflows/generate-docs.yaml @@ -4,11 +4,11 @@ on: push: branches: - dev - workflow_dispatch: + workflow_dispatch: {} jobs: publish-docs: - if: "${{ !endsWith(github.actor, '[bot]') }}" + if: ${{ !endsWith(github.actor, '[bot]') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/generate-pgo.yaml b/.github/workflows/generate-pgo.yaml index 349306f2b..ae6ba4c5c 100644 --- a/.github/workflows/generate-pgo.yaml +++ b/.github/workflows/generate-pgo.yaml @@ -1,56 +1,50 @@ name: 👤 Generate PGO on: - push: - branches: ["dev"] - paths: - - '**.go' - - '**.mod' - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -# TODO(dwisiswant0): https://go.dev/doc/pgo#merging-profiles + workflow_dispatch: {} + workflow_call: {} jobs: pgo: - strategy: - matrix: - targets: [150] runs-on: ubuntu-latest if: github.repository == 'projectdiscovery/nuclei' permissions: contents: write env: PGO_FILE: "cmd/nuclei/default.pgo" - LIST_FILE: "/tmp/targets-${{ matrix.targets }}.txt" - PROFILE_MEM: "/tmp/nuclei-profile-${{ matrix.targets }}-targets" + TARGET: "https://honey.scanme.sh" + TARGET_COUNT: "100" + TARGET_LIST: "/tmp/targets.txt" + PROFILE_MEM: "/tmp/nuclei-profile" steps: - uses: actions/checkout@v6 - - uses: projectdiscovery/actions/setup/git@v1 - uses: projectdiscovery/actions/setup/go@v1 + - uses: projectdiscovery/actions/cache/go-rod-browser@v1 - uses: projectdiscovery/actions/cache/nuclei@v1 - - name: Generate list - run: for i in {1..${{ matrix.targets }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done - # NOTE(dwisiswant0): use `-no-mhe` flag to get better samples. - - run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}" -no-mhe - working-directory: cmd/nuclei/ - - run: mv "${PROFILE_MEM}.cpu" ${PGO_FILE} - # NOTE(dwisiswant0): shall we prune $PGO_FILE git history? - # if we prune it, this won't be linear since it requires a force-push. - # if we don't, the git objects will just keep growing bigger. - # - # Ref: - # - https://go.dev/blog/pgo#:~:text=We%20recommend%20committing%20default.pgo%20files%20to%20your%20repository - # - https://gist.github.com/nottrobin/5758221 - - uses: projectdiscovery/actions/commit@v1 - with: - files: "${PGO_FILE}" - message: "build: update PGO profile :robot:" - - run: git push origin $GITHUB_REF + - run: | + for i in {1..${{ env.TARGET_COUNT }}}; do + echo "${{ env.TARGET }}/-/?_=${i}" >> "${{ env.TARGET_LIST }}"; + done + - run: make build + - run: ./bin/nuclei -update-templates + - run: | + ./bin/nuclei -silent -list="${TARGET_LIST}" \ + -disable-update-check \ + -no-mhe -no-httpx \ + -code -dast -file -headless \ + -enable-self-contained -enable-global-matchers \ + -rate-limit=0 \ + -concurrency=250 \ + -headless-concurrency=100 \ + -payload-concurrency=250 \ + -bulk-size=250 \ + -headless-bulk-size=100 \ + -profile-mem="${PROFILE_MEM}" \ + env: + DISABLE_STDOUT: "1" + - run: mv "${PROFILE_MEM}.cpu" "${PGO_FILE}" - uses: actions/upload-artifact@v6 with: name: "pgo" path: "${{ env.PGO_FILE }}" + overwrite: true diff --git a/.github/workflows/govulncheck.yaml b/.github/workflows/govulncheck.yaml index 38edae248..24fc0919f 100644 --- a/.github/workflows/govulncheck.yaml +++ b/.github/workflows/govulncheck.yaml @@ -3,7 +3,7 @@ name: 🐛 govulncheck on: schedule: - cron: '0 0 * * 0' # Weekly - workflow_dispatch: + workflow_dispatch: {} jobs: govulncheck: diff --git a/.github/workflows/perf-regression.yaml b/.github/workflows/perf-regression.yaml index a44845def..b0f362a35 100644 --- a/.github/workflows/perf-regression.yaml +++ b/.github/workflows/perf-regression.yaml @@ -1,8 +1,8 @@ name: 🔨 Performance Regression on: - workflow_call: - workflow_dispatch: + workflow_call: {} + workflow_dispatch: {} jobs: perf-regression: diff --git a/.github/workflows/perf-test.yaml b/.github/workflows/perf-test.yaml deleted file mode 100644 index ff40b824b..000000000 --- a/.github/workflows/perf-test.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: 🔨 Performance Test - -on: - schedule: - - cron: '0 0 * * 0' # Weekly - workflow_dispatch: - -jobs: - perf-test: - strategy: - matrix: - count: [50, 100, 150] - runs-on: ubuntu-latest - if: github.repository == 'projectdiscovery/nuclei' - env: - LIST_FILE: "/tmp/targets-${{ matrix.count }}.txt" - PROFILE_MEM: "/tmp/nuclei-perf-test-${{ matrix.count }}" - steps: - - uses: actions/checkout@v6 - - uses: projectdiscovery/actions/setup/go@v1 - - run: make verify - - name: Generate list - run: for i in {1..${{ matrix.count }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done - - run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}" - env: - NUCLEI_ARGS: host-error-stats - working-directory: cmd/nuclei/ - - uses: projectdiscovery/actions/flamegraph@v1 - id: flamegraph-cpu - with: - profile: "${{ env.PROFILE_MEM }}.cpu" - name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles" - continue-on-error: true - - uses: projectdiscovery/actions/flamegraph@v1 - id: flamegraph-mem - with: - profile: "${{ env.PROFILE_MEM }}.mem" - name: "${{ env.FLAMEGRAPH_NAME }} memory profiles" - continue-on-error: true - - if: ${{ steps.flamegraph-mem.outputs.message == '' }} - run: | - echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}" - echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index acaecb596..84f5ba3a9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,16 +4,28 @@ on: push: tags: - '*' - workflow_dispatch: + workflow_dispatch: {} -jobs: - release: +jobs: + pgo: + name: "Generate PGO" + uses: ./.github/workflows/generate-pgo.yaml + + release: + name: "Release" + needs: ["pgo"] runs-on: ubuntu-latest-16-cores steps: - uses: actions/checkout@v6 with: fetch-depth: 0 + - uses: actions/download-artifact@v6 + with: + name: "pgo" + path: "cmd/nuclei/" - uses: projectdiscovery/actions/setup/go@v1 + with: + go-version: "stable" - uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 42a46a67d..fd82e24d7 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -19,7 +19,7 @@ concurrency: jobs: lint: name: "Lint" - if: "${{ !endsWith(github.actor, '[bot]') }}" + if: ${{ !endsWith(github.actor, '[bot]') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -157,42 +157,9 @@ jobs: flamegraph: name: "Flamegraph" needs: ["tests"] - env: - PROFILE_MEM: "/tmp/nuclei" - TARGET_URL: "http://scanme.sh/a/?b=c" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - run: make build - - name: "Setup environment (push)" - if: ${{ github.event_name == 'push' }} - run: | - echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV - echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV - - name: "Setup environment (pull_request)" - if: ${{ github.event_name == 'pull_request' }} - run: | - echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV - echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV - - run: ./bin/nuclei -silent -update-templates - - run: ./bin/nuclei -silent -u "${TARGET_URL}" -profile-mem="${PROFILE_MEM}" - - uses: projectdiscovery/actions/flamegraph@v1 - id: flamegraph-cpu - with: - profile: "${{ env.PROFILE_MEM }}.cpu" - name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles" - continue-on-error: true - - uses: projectdiscovery/actions/flamegraph@v1 - id: flamegraph-mem - with: - profile: "${{ env.PROFILE_MEM }}.mem" - name: "${{ env.FLAMEGRAPH_NAME }} memory profiles" - continue-on-error: true - - if: ${{ steps.flamegraph-mem.outputs.message == '' }} - run: | - echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}" - echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}" + uses: ./.github/workflows/flamegraph.yaml perf-regression: + name: "Performance regression" needs: ["tests"] uses: ./.github/workflows/perf-regression.yaml diff --git a/.goreleaser.yml b/.goreleaser.yml index 2d2f61379..d95a631d4 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -11,8 +11,9 @@ builds: id: nuclei-cli env: - CGO_ENABLED=0 - goos: [windows,linux,darwin] - goarch: [amd64,'386',arm,arm64] + - GOEXPERIMENT=greenteagc + goos: [windows, linux, darwin] + goarch: [amd64, '386', arm, arm64] ignore: - goos: darwin goarch: '386' From 0eb87c2621da09b45147a7d2974c6b589d5ce20d Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Thu, 25 Dec 2025 17:16:38 +0700 Subject: [PATCH 16/28] fix(js): mysql panic due to missing `executionId` in ctx The `connectWithDSN` func used `db.Exec()` which implicitly uses `context.Background()`[1]. This caused the registered "nucleitcp" dialer callback to receive a ctx missing the `executionId`, leading to a panic during type assertion. Refactor `connectWithDSN` to accept `executionId` explicitly and use it to create a `context` for `db.PingContext()` (yeah, instead of `db.Exec()`). And, add a defensive check in the dialer callback to handle nil values gracefully. Fixes #6733 regression introduced in #6296. [1]: "Exec uses `context.Background` internally" - https://pkg.go.dev/database/sql#DB.Exec. Signed-off-by: Dwi Siswanto --- pkg/js/libs/mysql/memo.mysql.go | 4 ++-- pkg/js/libs/mysql/memo.mysql_private.go | 6 +++--- pkg/js/libs/mysql/mysql.go | 7 ++++--- pkg/js/libs/mysql/mysql_private.go | 6 ++++-- pkg/protocols/common/protocolstate/state.go | 8 +++++++- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/pkg/js/libs/mysql/memo.mysql.go b/pkg/js/libs/mysql/memo.mysql.go index a2c1d2d09..2cbe71db3 100755 --- a/pkg/js/libs/mysql/memo.mysql.go +++ b/pkg/js/libs/mysql/memo.mysql.go @@ -9,7 +9,7 @@ import ( ) func memoizedisMySQL(executionId string, host string, port int) (bool, error) { - hash := "isMySQL" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isMySQL" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isMySQL(executionId, host, port) @@ -25,7 +25,7 @@ func memoizedisMySQL(executionId string, host string, port int) (bool, error) { } func memoizedfingerprintMySQL(executionId string, host string, port int) (MySQLInfo, error) { - hash := "fingerprintMySQL" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "fingerprintMySQL" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return fingerprintMySQL(executionId, host, port) diff --git a/pkg/js/libs/mysql/memo.mysql_private.go b/pkg/js/libs/mysql/memo.mysql_private.go index 19d7e81b0..506aff8a9 100755 --- a/pkg/js/libs/mysql/memo.mysql_private.go +++ b/pkg/js/libs/mysql/memo.mysql_private.go @@ -8,11 +8,11 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate" ) -func memoizedconnectWithDSN(dsn string) (bool, error) { - hash := "connectWithDSN" + ":" + fmt.Sprint(dsn) +func memoizedconnectWithDSN(executionId string, dsn string) (bool, error) { + hash := "connectWithDSN" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(dsn) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { - return connectWithDSN(dsn) + return connectWithDSN(executionId, dsn) }) if err != nil { return false, err diff --git a/pkg/js/libs/mysql/mysql.go b/pkg/js/libs/mysql/mysql.go index 5e57aa66c..d7f6e224d 100644 --- a/pkg/js/libs/mysql/mysql.go +++ b/pkg/js/libs/mysql/mysql.go @@ -108,7 +108,7 @@ func (c *MySQLClient) Connect(ctx context.Context, host string, port int, userna if err != nil { return false, err } - return connectWithDSN(dsn) + return connectWithDSN(executionId, dsn) } type ( @@ -190,8 +190,9 @@ func fingerprintMySQL(executionId string, host string, port int) (MySQLInfo, err // const client = new mysql.MySQLClient; // const connected = client.ConnectWithDSN('username:password@tcp(acme.com:3306)/'); // ``` -func (c *MySQLClient) ConnectWithDSN(dsn string) (bool, error) { - return memoizedconnectWithDSN(dsn) +func (c *MySQLClient) ConnectWithDSN(ctx context.Context, dsn string) (bool, error) { + executionId := ctx.Value("executionId").(string) + return memoizedconnectWithDSN(executionId, dsn) } // ExecuteQueryWithOpts connects to Mysql database using given credentials diff --git a/pkg/js/libs/mysql/mysql_private.go b/pkg/js/libs/mysql/mysql_private.go index c731efd93..a1d81e5f0 100644 --- a/pkg/js/libs/mysql/mysql_private.go +++ b/pkg/js/libs/mysql/mysql_private.go @@ -1,6 +1,7 @@ package mysql import ( + "context" "database/sql" "fmt" "net" @@ -72,7 +73,7 @@ func BuildDSN(opts MySQLOptions) (string, error) { } // @memo -func connectWithDSN(dsn string) (bool, error) { +func connectWithDSN(executionId string, dsn string) (bool, error) { db, err := sql.Open("mysql", dsn) if err != nil { return false, err @@ -83,7 +84,8 @@ func connectWithDSN(dsn string) (bool, error) { db.SetMaxOpenConns(1) db.SetMaxIdleConns(0) - _, err = db.Exec("select 1") + ctx := context.WithValue(context.Background(), "executionId", executionId) + err = db.PingContext(ctx) if err != nil { return false, err } diff --git a/pkg/protocols/common/protocolstate/state.go b/pkg/protocols/common/protocolstate/state.go index 98d30a424..61232df1a 100644 --- a/pkg/protocols/common/protocolstate/state.go +++ b/pkg/protocols/common/protocolstate/state.go @@ -200,8 +200,14 @@ func initDialers(options *types.Options) error { addr += ":3306" } - executionId := ctx.Value("executionId").(string) + var executionId string + if val := ctx.Value("executionId"); val != nil { + executionId = val.(string) + } dialer := GetDialersWithId(executionId) + if dialer == nil { + return nil, fmt.Errorf("dialers not initialized for %s", executionId) + } return dialer.Fastdialer.Dial(ctx, "tcp", addr) }) From 12176d67a9fb2949d202f3f95c29252c2b2ca8ca Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 01:08:00 +0700 Subject: [PATCH 17/28] test(javascript): add mysql-connect integration test Signed-off-by: Dwi Siswanto --- cmd/integration-test/javascript.go | 52 +++++++++++++++++++ .../protocols/javascript/mysql-connect.yaml | 23 ++++++++ 2 files changed, 75 insertions(+) create mode 100644 integration_tests/protocols/javascript/mysql-connect.yaml diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index ccee6a6b9..63b2d59e8 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -18,6 +18,7 @@ var jsTestcases = []TestCaseInfo{ {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/postgres-pass-brute.yaml", TestCase: &javascriptPostgresPassBrute{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }}, + {Path: "protocols/javascript/mysql-connect.yaml", TestCase: &javascriptMySQLConnect{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }}, {Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}}, {Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}}, } @@ -28,6 +29,7 @@ var ( oracleResource *dockertest.Resource vncResource *dockertest.Resource postgresResource *dockertest.Resource + mysqlResource *dockertest.Resource pool *dockertest.Pool defaultRetry = 3 ) @@ -203,6 +205,38 @@ func (j *javascriptPostgresPassBrute) Execute(filePath string) error { return multierr.Combine(errs...) } +type javascriptMySQLConnect struct{} + +func (j *javascriptMySQLConnect) Execute(filePath string) error { + if mysqlResource == nil || pool == nil { + // skip test as mysql is not running + return nil + } + tempPort := mysqlResource.GetPort("3306/tcp") + finalURL := "localhost:" + tempPort + defer purge(mysqlResource) + errs := []error{} + for i := 0; i < defaultRetry; i++ { + results := []string{} + var err error + _ = pool.Retry(func() error { + //let mysql server start + time.Sleep(5 * time.Second) + results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug) + return nil + }) + if err != nil { + return err + } + if err := expectResultsCount(results, 1); err == nil { + return nil + } else { + errs = append(errs, err) + } + } + return multierr.Combine(errs...) +} + type javascriptMultiPortsSSH struct{} func (j *javascriptMultiPortsSSH) Execute(filePath string) error { @@ -345,4 +379,22 @@ func init() { if err := postgresResource.Expire(30); err != nil { log.Printf("Could not expire postgres resource: %s", err) } + + // setup a temporary mysql instance + mysqlResource, err = pool.RunWithOptions(&dockertest.RunOptions{ + Repository: "mysql", + Tag: "latest", + Env: []string{ + "MYSQL_ROOT_PASSWORD=secret", + }, + Platform: "linux/amd64", + }) + if err != nil { + log.Printf("Could not start mysql resource: %s", err) + return + } + // by default expire after 30 sec + if err := mysqlResource.Expire(30); err != nil { + log.Printf("Could not expire mysql resource: %s", err) + } } diff --git a/integration_tests/protocols/javascript/mysql-connect.yaml b/integration_tests/protocols/javascript/mysql-connect.yaml new file mode 100644 index 000000000..56952f90f --- /dev/null +++ b/integration_tests/protocols/javascript/mysql-connect.yaml @@ -0,0 +1,23 @@ +id: mysql-connect + +info: + name: MySQL Connect Test + author: pdteam + severity: high + +javascript: + - pre-condition: | + isPortOpen(Host, Port) + code: | + const mysql = require('nuclei/mysql'); + const client = new mysql.MySQLClient; + success = client.Connect(Host, Port, User, Pass); + args: + Host: "{{Host}}" + Port: "3306" + User: "root" + Pass: "secret" + matchers: + - type: dsl + dsl: + - "success == true" From 22469bdc2f7ef12a79506761c17f1825180316d5 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 01:08:06 +0700 Subject: [PATCH 18/28] chore(js): update memoized functions Signed-off-by: Dwi Siswanto --- pkg/js/libs/mssql/memo.mssql.go | 4 ++-- pkg/js/libs/oracle/memo.oracle.go | 2 +- pkg/js/libs/pop3/memo.pop3.go | 2 +- pkg/js/libs/postgres/memo.postgres.go | 10 +++++----- pkg/js/libs/rdp/memo.rdp.go | 6 +++--- pkg/js/libs/redis/memo.redis.go | 8 ++++---- pkg/js/libs/rsync/memo.rsync.go | 2 +- pkg/js/libs/smb/memo.smb.go | 4 ++-- pkg/js/libs/smb/memo.smb_private.go | 2 +- pkg/js/libs/smb/memo.smbghost.go | 3 ++- pkg/js/libs/telnet/memo.telnet.go | 2 +- pkg/js/libs/vnc/memo.vnc.go | 2 +- 12 files changed, 24 insertions(+), 23 deletions(-) diff --git a/pkg/js/libs/mssql/memo.mssql.go b/pkg/js/libs/mssql/memo.mssql.go index a8af1a6af..2451d4ab1 100755 --- a/pkg/js/libs/mssql/memo.mssql.go +++ b/pkg/js/libs/mssql/memo.mssql.go @@ -11,7 +11,7 @@ import ( ) func memoizedconnect(executionId string, host string, port int, username string, password string, dbName string) (bool, error) { - hash := "connect" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) + hash := "connect" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return connect(executionId, host, port, username, password, dbName) @@ -27,7 +27,7 @@ func memoizedconnect(executionId string, host string, port int, username string, } func memoizedisMssql(executionId string, host string, port int) (bool, error) { - hash := "isMssql" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isMssql" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isMssql(executionId, host, port) diff --git a/pkg/js/libs/oracle/memo.oracle.go b/pkg/js/libs/oracle/memo.oracle.go index 20931f280..da5106814 100755 --- a/pkg/js/libs/oracle/memo.oracle.go +++ b/pkg/js/libs/oracle/memo.oracle.go @@ -9,7 +9,7 @@ import ( ) func memoizedisOracle(executionId string, host string, port int) (IsOracleResponse, error) { - hash := "isOracle" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isOracle" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isOracle(executionId, host, port) diff --git a/pkg/js/libs/pop3/memo.pop3.go b/pkg/js/libs/pop3/memo.pop3.go index 61ef1dcd0..92a600ce4 100755 --- a/pkg/js/libs/pop3/memo.pop3.go +++ b/pkg/js/libs/pop3/memo.pop3.go @@ -9,7 +9,7 @@ import ( ) func memoizedisPoP3(executionId string, host string, port int) (IsPOP3Response, error) { - hash := "isPoP3" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isPoP3" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isPoP3(executionId, host, port) diff --git a/pkg/js/libs/postgres/memo.postgres.go b/pkg/js/libs/postgres/memo.postgres.go index 4cee2ddd5..f1f515d0c 100755 --- a/pkg/js/libs/postgres/memo.postgres.go +++ b/pkg/js/libs/postgres/memo.postgres.go @@ -5,15 +5,15 @@ import ( "errors" "fmt" - _ "github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap" - utils "github.com/projectdiscovery/nuclei/v3/pkg/js/utils" + _ "github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap" + "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate" ) func memoizedisPostgres(executionId string, host string, port int) (bool, error) { - hash := "isPostgres" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isPostgres" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isPostgres(executionId, host, port) @@ -29,7 +29,7 @@ func memoizedisPostgres(executionId string, host string, port int) (bool, error) } func memoizedexecuteQuery(executionId string, host string, port int, username string, password string, dbName string, query string) (*utils.SQLResult, error) { - hash := "executeQuery" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) + ":" + fmt.Sprint(query) + hash := "executeQuery" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) + ":" + fmt.Sprint(query) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return executeQuery(executionId, host, port, username, password, dbName, query) @@ -45,7 +45,7 @@ func memoizedexecuteQuery(executionId string, host string, port int, username st } func memoizedconnect(executionId string, host string, port int, username string, password string, dbName string) (bool, error) { - hash := "connect" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) + hash := "connect" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return connect(executionId, host, port, username, password, dbName) diff --git a/pkg/js/libs/rdp/memo.rdp.go b/pkg/js/libs/rdp/memo.rdp.go index f295e97b7..d73ee0517 100755 --- a/pkg/js/libs/rdp/memo.rdp.go +++ b/pkg/js/libs/rdp/memo.rdp.go @@ -9,7 +9,7 @@ import ( ) func memoizedisRDP(executionId string, host string, port int) (IsRDPResponse, error) { - hash := "isRDP" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isRDP" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isRDP(executionId, host, port) @@ -25,7 +25,7 @@ func memoizedisRDP(executionId string, host string, port int) (IsRDPResponse, er } func memoizedcheckRDPAuth(executionId string, host string, port int) (CheckRDPAuthResponse, error) { - hash := "checkRDPAuth" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "checkRDPAuth" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return checkRDPAuth(executionId, host, port) @@ -41,7 +41,7 @@ func memoizedcheckRDPAuth(executionId string, host string, port int) (CheckRDPAu } func memoizedcheckRDPEncryption(executionId string, host string, port int) (RDPEncryptionResponse, error) { - hash := "checkRDPEncryption" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "checkRDPEncryption" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return checkRDPEncryption(executionId, host, port) diff --git a/pkg/js/libs/redis/memo.redis.go b/pkg/js/libs/redis/memo.redis.go index ab587e111..0b3fc91b7 100755 --- a/pkg/js/libs/redis/memo.redis.go +++ b/pkg/js/libs/redis/memo.redis.go @@ -9,7 +9,7 @@ import ( ) func memoizedgetServerInfo(executionId string, host string, port int) (string, error) { - hash := "getServerInfo" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "getServerInfo" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return getServerInfo(executionId, host, port) @@ -25,7 +25,7 @@ func memoizedgetServerInfo(executionId string, host string, port int) (string, e } func memoizedconnect(executionId string, host string, port int, password string) (bool, error) { - hash := "connect" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password) + hash := "connect" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return connect(executionId, host, port, password) @@ -41,7 +41,7 @@ func memoizedconnect(executionId string, host string, port int, password string) } func memoizedgetServerInfoAuth(executionId string, host string, port int, password string) (string, error) { - hash := "getServerInfoAuth" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password) + hash := "getServerInfoAuth" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return getServerInfoAuth(executionId, host, port, password) @@ -57,7 +57,7 @@ func memoizedgetServerInfoAuth(executionId string, host string, port int, passwo } func memoizedisAuthenticated(executionId string, host string, port int) (bool, error) { - hash := "isAuthenticated" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isAuthenticated" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isAuthenticated(executionId, host, port) diff --git a/pkg/js/libs/rsync/memo.rsync.go b/pkg/js/libs/rsync/memo.rsync.go index 98bd45c49..0ab88cfef 100755 --- a/pkg/js/libs/rsync/memo.rsync.go +++ b/pkg/js/libs/rsync/memo.rsync.go @@ -9,7 +9,7 @@ import ( ) func memoizedisRsync(executionId string, host string, port int) (IsRsyncResponse, error) { - hash := "isRsync" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isRsync" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isRsync(executionId, host, port) diff --git a/pkg/js/libs/smb/memo.smb.go b/pkg/js/libs/smb/memo.smb.go index 96bdb036a..e2a4b375d 100755 --- a/pkg/js/libs/smb/memo.smb.go +++ b/pkg/js/libs/smb/memo.smb.go @@ -11,7 +11,7 @@ import ( ) func memoizedconnectSMBInfoMode(executionId string, host string, port int) (*smb.SMBLog, error) { - hash := "connectSMBInfoMode" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "connectSMBInfoMode" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return connectSMBInfoMode(executionId, host, port) @@ -27,7 +27,7 @@ func memoizedconnectSMBInfoMode(executionId string, host string, port int) (*smb } func memoizedlistShares(executionId string, host string, port int, user string, password string) ([]string, error) { - hash := "listShares" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(user) + ":" + fmt.Sprint(password) + hash := "listShares" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(user) + ":" + fmt.Sprint(password) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return listShares(executionId, host, port, user, password) diff --git a/pkg/js/libs/smb/memo.smb_private.go b/pkg/js/libs/smb/memo.smb_private.go index c209a61f1..7940d2413 100755 --- a/pkg/js/libs/smb/memo.smb_private.go +++ b/pkg/js/libs/smb/memo.smb_private.go @@ -13,7 +13,7 @@ import ( ) func memoizedcollectSMBv2Metadata(executionId string, host string, port int, timeout time.Duration) (*plugins.ServiceSMB, error) { - hash := "collectSMBv2Metadata" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(timeout) + hash := "collectSMBv2Metadata" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(timeout) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return collectSMBv2Metadata(executionId, host, port, timeout) diff --git a/pkg/js/libs/smb/memo.smbghost.go b/pkg/js/libs/smb/memo.smbghost.go index 43eee8441..dfb89b985 100755 --- a/pkg/js/libs/smb/memo.smbghost.go +++ b/pkg/js/libs/smb/memo.smbghost.go @@ -3,13 +3,14 @@ package smb import ( "errors" + "fmt" "github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate" ) func memoizeddetectSMBGhost(executionId string, host string, port int) (bool, error) { - hash := "detectSMBGhost" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "detectSMBGhost" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return detectSMBGhost(executionId, host, port) diff --git a/pkg/js/libs/telnet/memo.telnet.go b/pkg/js/libs/telnet/memo.telnet.go index 0c02169f6..855aa6c16 100755 --- a/pkg/js/libs/telnet/memo.telnet.go +++ b/pkg/js/libs/telnet/memo.telnet.go @@ -9,7 +9,7 @@ import ( ) func memoizedisTelnet(executionId string, host string, port int) (IsTelnetResponse, error) { - hash := "isTelnet" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isTelnet" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isTelnet(executionId, host, port) diff --git a/pkg/js/libs/vnc/memo.vnc.go b/pkg/js/libs/vnc/memo.vnc.go index c0639d216..2ca343b75 100755 --- a/pkg/js/libs/vnc/memo.vnc.go +++ b/pkg/js/libs/vnc/memo.vnc.go @@ -9,7 +9,7 @@ import ( ) func memoizedisVNC(executionId string, host string, port int) (IsVNCResponse, error) { - hash := "isVNC" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + hash := "isVNC" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { return isVNC(executionId, host, port) From 49309b4ac8ffaf837aeef7dab781c7c97833d0a4 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 06:06:41 +0700 Subject: [PATCH 19/28] chore(js): no staticcheck lint Signed-off-by: Dwi Siswanto --- pkg/js/libs/mysql/mysql_private.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/js/libs/mysql/mysql_private.go b/pkg/js/libs/mysql/mysql_private.go index a1d81e5f0..fae42ecd1 100644 --- a/pkg/js/libs/mysql/mysql_private.go +++ b/pkg/js/libs/mysql/mysql_private.go @@ -84,7 +84,7 @@ func connectWithDSN(executionId string, dsn string) (bool, error) { db.SetMaxOpenConns(1) db.SetMaxIdleConns(0) - ctx := context.WithValue(context.Background(), "executionId", executionId) + ctx := context.WithValue(context.Background(), "executionId", executionId) // nolint: staticcheck err = db.PingContext(ctx) if err != nil { return false, err From 496a71aea10140747b4551850f28af86d509cb6f Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 06:32:48 +0700 Subject: [PATCH 20/28] ci(tests): use stable go-version for release test Signed-off-by: Dwi Siswanto --- .github/workflows/tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index fd82e24d7..d1cb632f0 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -152,6 +152,8 @@ jobs: steps: - uses: actions/checkout@v6 - uses: projectdiscovery/actions/setup/go@v1 + with: + go-version: "stable" - uses: projectdiscovery/actions/goreleaser@v1 flamegraph: From 0d4edc7841194d3ab70cc8c1226b23b1bef7a9c5 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 13:49:00 +0700 Subject: [PATCH 21/28] chore(flow): disable global recover handler Signed-off-by: Dwi Siswanto --- pkg/tmplexec/flow/flow_executor.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/tmplexec/flow/flow_executor.go b/pkg/tmplexec/flow/flow_executor.go index cb9c70b52..7ce316270 100644 --- a/pkg/tmplexec/flow/flow_executor.go +++ b/pkg/tmplexec/flow/flow_executor.go @@ -14,7 +14,6 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/scan" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" - "github.com/kitabisa/go-ci" "github.com/projectdiscovery/nuclei/v3/pkg/types" "github.com/projectdiscovery/utils/errkit" fileutil "github.com/projectdiscovery/utils/file" @@ -211,16 +210,11 @@ func (f *FlowExecutor) ExecuteWithResults(ctx *scan.ScanContext) error { runtime.RemoveContextValue("executionId") }() - // TODO(dwisiswant0): remove this once we get the RCA. - defer func() { - if ci.IsCI() { - return - } - + /* if r := recover(); r != nil { f.ctx.LogError(fmt.Errorf("panic occurred while executing flow: %v", r)) } - }() + */ if ctx.OnResult == nil { return fmt.Errorf("output callback cannot be nil") From 8b3485abffb0036aef4d5a4678f59bf299332413 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 14:11:43 +0700 Subject: [PATCH 22/28] test(flow): add util tests Signed-off-by: Dwi Siswanto --- pkg/tmplexec/flow/util_test.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 pkg/tmplexec/flow/util_test.go diff --git a/pkg/tmplexec/flow/util_test.go b/pkg/tmplexec/flow/util_test.go new file mode 100644 index 000000000..cebcc3a7d --- /dev/null +++ b/pkg/tmplexec/flow/util_test.go @@ -0,0 +1,34 @@ +package flow + +import ( + "testing" + + "github.com/projectdiscovery/nuclei/v3/pkg/operators" + "github.com/stretchr/testify/require" +) + +func TestHasMatchersPanicRegression(t *testing.T) { + // This test ensures that hasMatchers does not panic when passed a slice containing nil. + // This was the source of a reported panic when a request had no local operators. + + require.NotPanics(t, func() { + all := []*operators.Operators{nil} + result := hasMatchers(all) + require.False(t, result) + }, "hasMatchers should not panic with nil element in slice") + + require.NotPanics(t, func() { + all := []*operators.Operators{nil, {}} + result := hasMatchers(all) + require.False(t, result) + }, "hasMatchers should not panic with mix of nil and empty operators") +} + +func TestHasOperatorsPanicRegression(t *testing.T) { + // Also ensure hasOperators is safe + require.NotPanics(t, func() { + all := []*operators.Operators{nil} + result := hasOperators(all) + require.False(t, result) + }, "hasOperators should not panic with nil element in slice") +} From 22b64b670259a47d94be787c82c8266e858dbac6 Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 14:22:48 +0700 Subject: [PATCH 23/28] fix(flow): segfault in `hasMatchers` `hasMatchers` was not nil-safe when iterating over the slice of operators. Check if the operator is nil before accessing `*operators.Operators.Matchers` to prevent a panic when a protocol implementation returns a slice containing a nil element. This can happen when a request has no local matchers/extractors but is processed in a flow where global matchers are present. Fixes #6738. Signed-off-by: Dwi Siswanto --- pkg/tmplexec/flow/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tmplexec/flow/util.go b/pkg/tmplexec/flow/util.go index 0e3e6fbd0..83f79d260 100644 --- a/pkg/tmplexec/flow/util.go +++ b/pkg/tmplexec/flow/util.go @@ -5,7 +5,7 @@ import "github.com/projectdiscovery/nuclei/v3/pkg/operators" // Checks if template has matchers func hasMatchers(all []*operators.Operators) bool { for _, operator := range all { - if len(operator.Matchers) > 0 { + if operator != nil && len(operator.Matchers) > 0 { return true } } From 592b689b152b2bfa056b62ac88a44112f969866f Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 26 Dec 2025 14:24:47 +0700 Subject: [PATCH 24/28] Revert "chore(flow): disable global recover handler" This reverts commit 0d4edc7841194d3ab70cc8c1226b23b1bef7a9c5. --- pkg/tmplexec/flow/flow_executor.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/tmplexec/flow/flow_executor.go b/pkg/tmplexec/flow/flow_executor.go index 7ce316270..cb9c70b52 100644 --- a/pkg/tmplexec/flow/flow_executor.go +++ b/pkg/tmplexec/flow/flow_executor.go @@ -14,6 +14,7 @@ import ( "github.com/projectdiscovery/nuclei/v3/pkg/scan" templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types" + "github.com/kitabisa/go-ci" "github.com/projectdiscovery/nuclei/v3/pkg/types" "github.com/projectdiscovery/utils/errkit" fileutil "github.com/projectdiscovery/utils/file" @@ -210,11 +211,16 @@ func (f *FlowExecutor) ExecuteWithResults(ctx *scan.ScanContext) error { runtime.RemoveContextValue("executionId") }() - /* + // TODO(dwisiswant0): remove this once we get the RCA. + defer func() { + if ci.IsCI() { + return + } + if r := recover(); r != nil { f.ctx.LogError(fmt.Errorf("panic occurred while executing flow: %v", r)) } - */ + }() if ctx.OnResult == nil { return fmt.Errorf("output callback cannot be nil") From 1eaecb633a1fdaa24ffe551fd13eb66a19a2728b Mon Sep 17 00:00:00 2001 From: Dwi Siswanto <25837540+dwisiswant0@users.noreply.github.com> Date: Wed, 31 Dec 2025 08:04:21 +0700 Subject: [PATCH 25/28] ci(compat-checks): use stable go-version (#6743) Signed-off-by: Dwi Siswanto --- .github/workflows/compat-checks.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/compat-checks.yaml b/.github/workflows/compat-checks.yaml index 086bf6422..b75a634d9 100644 --- a/.github/workflows/compat-checks.yaml +++ b/.github/workflows/compat-checks.yaml @@ -16,4 +16,5 @@ jobs: - uses: actions/checkout@v6 - uses: projectdiscovery/actions/setup/go/compat-checks@v1 with: + go-version: "stable" release-test: true From 02434b553724f8502fb0a9fd20313c1ee23d9dcb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Dec 2025 02:13:42 +0000 Subject: [PATCH 26/28] chore(deps): bump actions/download-artifact in the workflows group (#6742) Bumps the workflows group with 1 update: [actions/download-artifact](https://github.com/actions/download-artifact). Updates `actions/download-artifact` from 6 to 7 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: workflows ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 84f5ba3a9..0be407a55 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/download-artifact@v6 + - uses: actions/download-artifact@v7 with: name: "pgo" path: "cmd/nuclei/" From 4de2bdb8836a25f75a4dac4fc079883808abff5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Dec 2025 09:18:45 +0700 Subject: [PATCH 27/28] chore(deps): bump the modules group with 10 updates (#6741) Bumps the modules group with 10 updates: | Package | From | To | | --- | --- | --- | | [github.com/projectdiscovery/fastdialer](https://github.com/projectdiscovery/fastdialer) | `0.5.0` | `0.5.1` | | [github.com/projectdiscovery/hmap](https://github.com/projectdiscovery/hmap) | `0.0.98` | `0.0.99` | | [github.com/projectdiscovery/retryabledns](https://github.com/projectdiscovery/retryabledns) | `1.0.111` | `1.0.112` | | [github.com/projectdiscovery/retryablehttp-go](https://github.com/projectdiscovery/retryablehttp-go) | `1.3.0` | `1.3.1` | | [github.com/projectdiscovery/dsl](https://github.com/projectdiscovery/dsl) | `0.8.9` | `0.8.10` | | [github.com/projectdiscovery/gologger](https://github.com/projectdiscovery/gologger) | `1.1.65` | `1.1.66` | | [github.com/projectdiscovery/networkpolicy](https://github.com/projectdiscovery/networkpolicy) | `0.1.32` | `0.1.33` | | [github.com/projectdiscovery/useragent](https://github.com/projectdiscovery/useragent) | `0.0.105` | `0.0.106` | | [github.com/projectdiscovery/wappalyzergo](https://github.com/projectdiscovery/wappalyzergo) | `0.2.60` | `0.2.61` | | [github.com/projectdiscovery/cdncheck](https://github.com/projectdiscovery/cdncheck) | `1.2.15` | `1.2.16` | Updates `github.com/projectdiscovery/fastdialer` from 0.5.0 to 0.5.1 - [Release notes](https://github.com/projectdiscovery/fastdialer/releases) - [Commits](https://github.com/projectdiscovery/fastdialer/compare/v0.5.0...v0.5.1) Updates `github.com/projectdiscovery/hmap` from 0.0.98 to 0.0.99 - [Release notes](https://github.com/projectdiscovery/hmap/releases) - [Commits](https://github.com/projectdiscovery/hmap/compare/v0.0.98...v0.0.99) Updates `github.com/projectdiscovery/retryabledns` from 1.0.111 to 1.0.112 - [Release notes](https://github.com/projectdiscovery/retryabledns/releases) - [Commits](https://github.com/projectdiscovery/retryabledns/compare/v1.0.111...v1.0.112) Updates `github.com/projectdiscovery/retryablehttp-go` from 1.3.0 to 1.3.1 - [Release notes](https://github.com/projectdiscovery/retryablehttp-go/releases) - [Commits](https://github.com/projectdiscovery/retryablehttp-go/compare/v1.3.0...v1.3.1) Updates `github.com/projectdiscovery/dsl` from 0.8.9 to 0.8.10 - [Release notes](https://github.com/projectdiscovery/dsl/releases) - [Commits](https://github.com/projectdiscovery/dsl/compare/v0.8.9...v0.8.10) Updates `github.com/projectdiscovery/gologger` from 1.1.65 to 1.1.66 - [Release notes](https://github.com/projectdiscovery/gologger/releases) - [Commits](https://github.com/projectdiscovery/gologger/compare/v1.1.65...v1.1.66) Updates `github.com/projectdiscovery/networkpolicy` from 0.1.32 to 0.1.33 - [Release notes](https://github.com/projectdiscovery/networkpolicy/releases) - [Commits](https://github.com/projectdiscovery/networkpolicy/compare/v0.1.32...v0.1.33) Updates `github.com/projectdiscovery/useragent` from 0.0.105 to 0.0.106 - [Release notes](https://github.com/projectdiscovery/useragent/releases) - [Commits](https://github.com/projectdiscovery/useragent/compare/v0.0.105...v0.0.106) Updates `github.com/projectdiscovery/wappalyzergo` from 0.2.60 to 0.2.61 - [Release notes](https://github.com/projectdiscovery/wappalyzergo/releases) - [Commits](https://github.com/projectdiscovery/wappalyzergo/compare/v0.2.60...v0.2.61) Updates `github.com/projectdiscovery/cdncheck` from 1.2.15 to 1.2.16 - [Release notes](https://github.com/projectdiscovery/cdncheck/releases) - [Commits](https://github.com/projectdiscovery/cdncheck/compare/v1.2.15...v1.2.16) --- updated-dependencies: - dependency-name: github.com/projectdiscovery/fastdialer dependency-version: 0.5.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/hmap dependency-version: 0.0.99 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/retryabledns dependency-version: 1.0.112 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/retryablehttp-go dependency-version: 1.3.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/dsl dependency-version: 0.8.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/gologger dependency-version: 1.1.66 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/networkpolicy dependency-version: 0.1.33 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/useragent dependency-version: 0.0.106 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/wappalyzergo dependency-version: 0.2.61 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: modules - dependency-name: github.com/projectdiscovery/cdncheck dependency-version: 1.2.16 dependency-type: indirect update-type: version-update:semver-patch dependency-group: modules ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 20 ++++++++++---------- go.sum | 40 ++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 60c61ccc7..c27befc3c 100644 --- a/go.mod +++ b/go.mod @@ -22,12 +22,12 @@ 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.5.0 - github.com/projectdiscovery/hmap v0.0.98 + github.com/projectdiscovery/fastdialer v0.5.1 + github.com/projectdiscovery/hmap v0.0.99 github.com/projectdiscovery/interactsh v1.2.4 github.com/projectdiscovery/rawhttp v0.1.90 - github.com/projectdiscovery/retryabledns v1.0.111 - github.com/projectdiscovery/retryablehttp-go v1.3.0 + github.com/projectdiscovery/retryabledns v1.0.112 + github.com/projectdiscovery/retryablehttp-go v1.3.1 github.com/projectdiscovery/yamldoc-go v1.0.6 github.com/remeh/sizedwaitgroup v1.0.0 github.com/rs/xid v1.6.0 @@ -92,26 +92,26 @@ require ( github.com/microsoft/go-mssqldb v1.9.2 github.com/ory/dockertest/v3 v3.12.0 github.com/praetorian-inc/fingerprintx v1.1.15 - github.com/projectdiscovery/dsl v0.8.9 + github.com/projectdiscovery/dsl v0.8.10 github.com/projectdiscovery/fasttemplate v0.0.2 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.65 + github.com/projectdiscovery/gologger v1.1.66 github.com/projectdiscovery/gostruct v0.0.2 github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81 github.com/projectdiscovery/httpx v1.7.4 github.com/projectdiscovery/mapcidr v1.1.97 github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 - github.com/projectdiscovery/networkpolicy v0.1.32 + github.com/projectdiscovery/networkpolicy v0.1.33 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.2 github.com/projectdiscovery/uncover v1.2.0 - github.com/projectdiscovery/useragent v0.0.105 + github.com/projectdiscovery/useragent v0.0.106 github.com/projectdiscovery/utils v0.8.0 - github.com/projectdiscovery/wappalyzergo v0.2.60 + github.com/projectdiscovery/wappalyzergo v0.2.61 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 @@ -326,7 +326,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.15 // indirect + github.com/projectdiscovery/cdncheck v1.2.16 // 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-20250715113114-c77eb3567582 // indirect diff --git a/go.sum b/go.sum index 22ee6b39b..76e4463fe 100644 --- a/go.sum +++ b/go.sum @@ -827,14 +827,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.15 h1:07ati3QcI65HEWP6GgHoTtit6cfu5InEx87rpwoWios= -github.com/projectdiscovery/cdncheck v1.2.15/go.mod h1:Q9HNh2uzO/qFdl8C4RUWOHj3qIhxlaBFeayPQJm2zII= +github.com/projectdiscovery/cdncheck v1.2.16 h1:m6sQh5VAWN2sVp3o8LSFtAfnv3V0Dpoqqw83ZwnrA2c= +github.com/projectdiscovery/cdncheck v1.2.16/go.mod h1:2OsjLn4x7VsM+ZNccGU185pd43U+0h5gDDStU0GtNl8= 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.9 h1:weluy3pgLKxI0JCwjY6b1u5YEb9falhsiOCT5ouMnRw= -github.com/projectdiscovery/dsl v0.8.9/go.mod h1:hFLa5MOGMZXwF38WcwYPqm0uU6G46s+vbVWdMusdVv0= -github.com/projectdiscovery/fastdialer v0.5.0 h1:7XX11t/7kuenVM09r3+EeNgqYItkoTNVUc6XKFqZpHw= -github.com/projectdiscovery/fastdialer v0.5.0/go.mod h1:v4uDT/Sb0o83KNA+ggxHCw6u6BvJZglpGiAPIufaQ84= +github.com/projectdiscovery/dsl v0.8.10 h1:7t8KMukLlNGiOPdESGCkpOxu9RAT3RLRBY0Z5CoZIgs= +github.com/projectdiscovery/dsl v0.8.10/go.mod h1:66WXaiVEOA2LlZuH81/izzybA3s++zX/nrKwgQV/2S0= +github.com/projectdiscovery/fastdialer v0.5.1 h1:/4PX1u80QfZ8+DdF9jdtz1in5eajJhw89/xouVc9/+c= +github.com/projectdiscovery/fastdialer v0.5.1/go.mod h1:34SS9VxrrmUhO67LYioxjrAWOQ5/kwgJNj9CiFgRJso= 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= @@ -845,14 +845,14 @@ 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.65 h1:tVIjEke+UaQ3b2dj89AHCHGgeyUYIO0E35Yu+ppU+bk= -github.com/projectdiscovery/gologger v1.1.65/go.mod h1:myCMJMPu1TDl48LQGj+w7MhOdVKYAZNSOURHhK5XaaM= +github.com/projectdiscovery/gologger v1.1.66 h1:dYXfCWWNNiC7AgIDBkIF+3sTGyQbrpncsQEFQwyKCOg= +github.com/projectdiscovery/gologger v1.1.66/go.mod h1:lm3vFKt52lXgo799dBtySe/1zRN1lMGUQuNb5YtwkYQ= 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.98 h1:XxYIi7yJCNiDAKCJXvuY9IBM5O6OgDgx4XHgKxkR4eg= -github.com/projectdiscovery/hmap v0.0.98/go.mod h1:bgN5fuZPJMj2YnAGEEnCypoifCnALJixHEVQszktQIU= +github.com/projectdiscovery/hmap v0.0.99 h1:XPfLnD3CUrMqVCIdpK9ozD7Xmp3simx3T+2j4WWhHnU= +github.com/projectdiscovery/hmap v0.0.99/go.mod h1:koyUJi83K5G3w35ZLFXOYZIyYJsO+6hQrgDDN1RBrVE= github.com/projectdiscovery/httpx v1.7.4 h1:GUEklAZ71VKM0krmgck2Km5odJi5dwfo0euc2r88tj0= github.com/projectdiscovery/httpx v1.7.4/go.mod h1:sPnFYIXh0RAdjb02vpUGOsP5j28VDZs5khjMbXwEUGQ= github.com/projectdiscovery/interactsh v1.2.4 h1:WUSj+fxbcV53J64oIAhbYzCKD1w/IyenyRBhkI5jiqI= @@ -865,18 +865,18 @@ 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.32 h1:MD561zl0/fSzcNTlDlf/7pcQbYzulYoQssFBFjb2pcg= -github.com/projectdiscovery/networkpolicy v0.1.32/go.mod h1:5x4rGh4XhnoYl9wACnZyrjDGKIB/bQqxw2KrIM5V+XU= +github.com/projectdiscovery/networkpolicy v0.1.33 h1:bVgp+XpLEsQ7ZEJt3UaUqIwhI01MMdt7F2dfIKFQg/w= +github.com/projectdiscovery/networkpolicy v0.1.33/go.mod h1:YAPddAXUc/lhoU85AFdvgOQKx8Qh8r0vzSjexRWk6Yk= 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= github.com/projectdiscovery/rawhttp v0.1.90/go.mod h1:VZYAM25UI/wVB3URZ95ZaftgOnsbphxyAw/XnQRRz4Y= github.com/projectdiscovery/rdap v0.9.0 h1:wPhHx5pQ2QI+WGhyNb2PjhTl0NtB39Nk7YFZ9cp8ZGA= github.com/projectdiscovery/rdap v0.9.0/go.mod h1:zk4yrJFQ2Hy36Aqk+DvotYQxYAeALaCJ5ORySkff36Q= -github.com/projectdiscovery/retryabledns v1.0.111 h1:iyMdCDgNmaSRJYcGqB+SLlvlw9WijlbJ6Q9OEpRAWsQ= -github.com/projectdiscovery/retryabledns v1.0.111/go.mod h1:6TOPJ3QAE4reBu6bvsGsTcyEb+OypcKYFQH7yVsjyIM= -github.com/projectdiscovery/retryablehttp-go v1.3.0 h1:ro/IYNx4fSdz0hFCFIpQ9++SlOAa4WdQS1i6GMUgSEk= -github.com/projectdiscovery/retryablehttp-go v1.3.0/go.mod h1:B+fiMihnqSxCKM0SoEPu92u6FL5hVBfmd6j3SwrFpY0= +github.com/projectdiscovery/retryabledns v1.0.112 h1:4iCiuo6jMnw/pdOZRzBQrbUOUu5tOeuvGupxVV8RDLw= +github.com/projectdiscovery/retryabledns v1.0.112/go.mod h1:xsJTKbo+KGqd7+88z1naEUFJybLH2yjB/zUyOweA7k0= +github.com/projectdiscovery/retryablehttp-go v1.3.1 h1:ds0dcKa3565pYdIJrLcwcbrb9cH6MojY2uHwGPoXubg= +github.com/projectdiscovery/retryablehttp-go v1.3.1/go.mod h1:ISMHtd5DrSSDenQ23Vz9cIyIj3XlccZzZhuWHUJTa60= 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= @@ -885,12 +885,12 @@ github.com/projectdiscovery/tlsx v1.2.2 h1:Y96QBqeD2anpzEtBl4kqNbwzXh2TrzJuXfgiB github.com/projectdiscovery/tlsx v1.2.2/go.mod h1:ZJl9F1sSl0sdwE+lR0yuNHVX4Zx6tCSTqnNxnHCFZB4= github.com/projectdiscovery/uncover v1.2.0 h1:31tjYa0v8FB8Ch8hJTxb+2t63vsljdOo0OSFylJcX4M= github.com/projectdiscovery/uncover v1.2.0/go.mod h1:ozqKb++p39Kmh1SmwIpbQ9p0aVGPXuwsb4/X2Kvx6ms= -github.com/projectdiscovery/useragent v0.0.105 h1:yFGFTfWZ/RZP5XbGRJtvKcbRNnlGI6xfPRXHb8DWdhg= -github.com/projectdiscovery/useragent v0.0.105/go.mod h1:jWG1BD2yu8EC3olt6Amke7BiyLkXzpErI7Jtzr/tWZM= +github.com/projectdiscovery/useragent v0.0.106 h1:9fS08MRUUJvfBskTxcXY9TA4X1TwpH6iJ3P3YNaXNlo= +github.com/projectdiscovery/useragent v0.0.106/go.mod h1:9oVMjgd7CchIsyeweyigIPtW83gpiGf2NtR6UM5XK+o= github.com/projectdiscovery/utils v0.8.0 h1:8d79OCs5xGDNXdKxMUKMY/lgQSUWJMYB1B2Sx+oiqkQ= github.com/projectdiscovery/utils v0.8.0/go.mod h1:CU6tjtyTRxBrnNek+GPJplw4IIHcXNZNKO09kWgqTdg= -github.com/projectdiscovery/wappalyzergo v0.2.60 h1:CGpQy2qHxOBQSikdNjZomHUP9fVljKBfDJeodpjVp7A= -github.com/projectdiscovery/wappalyzergo v0.2.60/go.mod h1:8FtSVcmPRZU0g1euBpdSYEBHIvB7Zz9MOb754ZqZmfU= +github.com/projectdiscovery/wappalyzergo v0.2.61 h1:TxiYJvXqReiscuWKtGKhFx3VxbVVjHOgECNX709AEX4= +github.com/projectdiscovery/wappalyzergo v0.2.61/go.mod h1:8FtSVcmPRZU0g1euBpdSYEBHIvB7Zz9MOb754ZqZmfU= 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= From 63aed75474c9251fabb663561527a95bad46d72d Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Wed, 31 Dec 2025 09:30:28 +0700 Subject: [PATCH 28/28] chore: bump version v3.6.2 Signed-off-by: Dwi Siswanto --- pkg/catalog/config/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/catalog/config/constants.go b/pkg/catalog/config/constants.go index 5fca8a8b8..ebd49dda7 100644 --- a/pkg/catalog/config/constants.go +++ b/pkg/catalog/config/constants.go @@ -31,7 +31,7 @@ const ( CLIConfigFileName = "config.yaml" ReportingConfigFilename = "reporting-config.yaml" // Version is the current version of nuclei - Version = `v3.6.1` + Version = `v3.6.2` // Directory Names of custom templates CustomS3TemplatesDirName = "s3" CustomGitHubTemplatesDirName = "github"