Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend headless contextargs #3850

Merged
merged 10 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
go-version: [1.20.x]
os: [ubuntu-latest, windows-latest, macOS-13]
os: [ubuntu-latest, windows-latest, macOS-latest]

runs-on: ${{ matrix.os }}
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/functional-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-13]
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go
uses: actions/setup-go@v4
Expand Down
16 changes: 16 additions & 0 deletions integration_tests/workflow/headless-1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
id: headless-1
info:
name: Headless 1
author: pdteam
severity: info
tags: headless

headless:
- cookie-reuse: true
steps:
- action: navigate
args:
url: "{{BaseURL}}/headless1"

- action: waitload

12 changes: 12 additions & 0 deletions integration_tests/workflow/http-1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
id: http1

info:
name: http1
author: pdteam
severity: info

http:
- method: GET
path:
- "{{BaseURL}}/http1"
cookie-reuse: true
12 changes: 12 additions & 0 deletions integration_tests/workflow/http-2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
id: http2

info:
name: http2
author: pdteam
severity: info

http:
- method: GET
path:
- "{{BaseURL}}/http2"
cookie-reuse: true
12 changes: 12 additions & 0 deletions integration_tests/workflow/http-3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
id: http3

info:
name: http3
author: pdteam
severity: info

http:
- method: GET
path:
- "{{BaseURL}}/http3"
cookie-reuse: true
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ info:
author: pdteam
severity: info

requests:
http:
- path:
- "{{BaseURL}}/path1"
extractors:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ info:
author: pdteam
severity: info

requests:
http:
- raw:
- |
GET /path2 HTTP/1.1
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/workflow/match-1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ info:
author: pdteam
severity: info

requests:
http:
- method: GET
path:
- "{{BaseURL}}"
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/workflow/match-2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ info:
author: pdteam
severity: info

requests:
http:
- method: GET
path:
- "{{BaseURL}}"
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/workflow/nomatch-1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ info:
author: pdteam
severity: info

requests:
http:
- method: GET
path:
- "{{BaseURL}}"
Expand Down
15 changes: 15 additions & 0 deletions integration_tests/workflow/shared-cookie.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
id: workflow-shared-cookies

info:
name: Test Workflow Shared Cookies
author: pdteam
severity: info

workflows:
# store cookies to standard http client cookie-jar
- template: workflow/http-1.yaml
- template: workflow/http-2.yaml
# store cookie in native browser context
- template: workflow/headless-1.yaml
# retrive 2 standard library cookies + headless cookie
- template: workflow/http-3.yaml
37 changes: 37 additions & 0 deletions v2/cmd/integration-test/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var workflowTestcases = map[string]testutils.TestCase{
"workflow/matcher-name.yaml": &workflowMatcherName{},
"workflow/http-value-share-workflow.yaml": &workflowHttpKeyValueShare{},
"workflow/dns-value-share-workflow.yaml": &workflowDnsKeyValueShare{},
"workflow/shared-cookie.yaml": &workflowSharedCookies{},
}

type workflowBasic struct{}
Expand Down Expand Up @@ -131,3 +132,39 @@ func (h *workflowDnsKeyValueShare) Execute(filePath string) error {
// no results - ensure that the variable sharing works
return expectResultsCount(results, 1)
}

type workflowSharedCookies struct{}

// Execute executes a test case and returns an error if occurred
func (h *workflowSharedCookies) Execute(filePath string) error {
handleFunc := func(name string, w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
cookie := &http.Cookie{Name: name, Value: name}
http.SetCookie(w, cookie)
}

var gotCookies []string
router := httprouter.New()
router.GET("/http1", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
handleFunc("http1", w, r, p)
})
router.GET("/http2", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
handleFunc("http2", w, r, p)
})
router.GET("/headless1", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
handleFunc("headless1", w, r, p)
})
router.GET("/http3", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
for _, cookie := range r.Cookies() {
gotCookies = append(gotCookies, cookie.Name)
}
})
ts := httptest.NewServer(router)
defer ts.Close()

_, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug, "-headless")
if err != nil {
return err
}

return expectResultsCount(gotCookies, 3)
}
13 changes: 9 additions & 4 deletions v2/pkg/protocols/common/fuzz/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import (

"github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
"github.com/projectdiscovery/retryablehttp-go"
urlutil "github.com/projectdiscovery/utils/url"
)

// ExecuteRuleInput is the input for rule Execute function
type ExecuteRuleInput struct {
// URL is the URL for the request
URL *urlutil.URL
// Input is the context args input
Input *contextargs.Context
// Callback is the callback for generated rule requests
Callback func(GeneratedRequest) bool
// InteractURLs contains interact urls for execute call
Expand All @@ -41,7 +42,7 @@ type GeneratedRequest struct {
// Input is not thread safe and should not be shared between concurrent
// goroutines.
func (rule *Rule) Execute(input *ExecuteRuleInput) error {
if !rule.isExecutable(input.URL) {
if !rule.isExecutable(input.Input) {
return nil
}
baseValues := input.Values
Expand Down Expand Up @@ -69,7 +70,11 @@ func (rule *Rule) Execute(input *ExecuteRuleInput) error {
}

// isExecutable returns true if the rule can be executed based on provided input
func (rule *Rule) isExecutable(parsed *urlutil.URL) bool {
func (rule *Rule) isExecutable(input *contextargs.Context) bool {
parsed, err := urlutil.Parse(input.MetaInput.Input)
if err != nil {
return false
}
if len(parsed.Query()) > 0 && rule.partType == queryPartType {
return true
}
Expand Down
10 changes: 5 additions & 5 deletions v2/pkg/protocols/common/fuzz/execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package fuzz
import (
"testing"

urlutil "github.com/projectdiscovery/utils/url"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/stretchr/testify/require"
)

Expand All @@ -12,11 +12,11 @@ func TestRuleIsExecutable(t *testing.T) {
err := rule.Compile(nil, nil)
require.NoError(t, err, "could not compile rule")

parsed, _ := urlutil.Parse("https://example.com/?url=localhost")
result := rule.isExecutable(parsed)
input := contextargs.NewWithInput("https://example.com/?url=localhost")
result := rule.isExecutable(input)
require.True(t, result, "could not get correct result")

parsed, _ = urlutil.Parse("https://example.com/")
result = rule.isExecutable(parsed)
input = contextargs.NewWithInput("https://example.com/")
result = rule.isExecutable(input)
require.False(t, result, "could not get correct result")
}
10 changes: 7 additions & 3 deletions v2/pkg/protocols/common/fuzz/parts.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,20 @@ func (rule *Rule) executePartRule(input *ExecuteRuleInput, payload string) error

// executeQueryPartRule executes query part rules
func (rule *Rule) executeQueryPartRule(input *ExecuteRuleInput, payload string) error {
requestURL := input.URL.Clone()
requestURL, err := urlutil.Parse(input.Input.MetaInput.Input)
if err != nil {
return err
}
origRequestURL := requestURL.Clone()
temp := urlutil.Params{}
for k, v := range input.URL.Query() {
for k, v := range origRequestURL.Query() {
// this has to be a deep copy
x := []string{}
x = append(x, v...)
temp[k] = x
}

for key, values := range input.URL.Query() {
for key, values := range origRequestURL.Query() {
for i, value := range values {
if !rule.matchKeyOrValue(key, value) {
continue
Expand Down
10 changes: 6 additions & 4 deletions v2/pkg/protocols/common/fuzz/parts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"testing"

"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
urlutil "github.com/projectdiscovery/utils/url"
"github.com/stretchr/testify/require"
)

func TestExecuteQueryPartRule(t *testing.T) {
parsed, _ := urlutil.Parse("http://localhost:8080/?url=localhost&mode=multiple&file=passwdfile")
URL := "http://localhost:8080/?url=localhost&mode=multiple&file=passwdfile"
options := &protocols.ExecutorOptions{
Interactsh: &interactsh.Client{},
}
Expand All @@ -22,8 +22,9 @@ func TestExecuteQueryPartRule(t *testing.T) {
options: options,
}
var generatedURL []string
input := contextargs.NewWithInput(URL)
err := rule.executeQueryPartRule(&ExecuteRuleInput{
URL: parsed,
Input: input,
Callback: func(gr GeneratedRequest) bool {
generatedURL = append(generatedURL, gr.Request.URL.String())
return true
Expand All @@ -44,8 +45,9 @@ func TestExecuteQueryPartRule(t *testing.T) {
options: options,
}
var generatedURL string
input := contextargs.NewWithInput(URL)
err := rule.executeQueryPartRule(&ExecuteRuleInput{
URL: parsed,
Input: input,
Callback: func(gr GeneratedRequest) bool {
generatedURL = gr.Request.URL.String()
return true
Expand Down
Loading
Loading