nuclei/v2/pkg/protocols/headless/engine/page_actions_test.go

594 lines
22 KiB
Go
Raw Normal View History

2021-02-22 12:50:49 +05:30
package engine
import (
"fmt"
"io"
"math/rand"
2021-02-22 12:50:49 +05:30
"net/http"
"net/http/cookiejar"
2021-02-22 12:50:49 +05:30
"net/http/httptest"
2021-10-10 08:29:58 +02:00
"os"
"path/filepath"
"strconv"
2021-02-22 12:50:49 +05:30
"strings"
"testing"
2021-03-01 14:20:56 +05:30
"time"
2021-02-22 12:50:49 +05:30
"github.com/stretchr/testify/require"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
2021-04-19 01:00:59 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v2/pkg/testutils/testheadless"
2021-02-22 12:50:49 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/types"
)
2021-10-10 17:18:53 +02:00
func TestActionNavigate(t *testing.T) {
response := `
2021-02-22 12:50:49 +05:30
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>
<h1>Nuclei Test</h1>
</body>
</html>`
2021-02-22 12:50:49 +05:30
actions := []*Action{{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}}, {ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}}}
2021-02-22 12:50:49 +05:30
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
})
2021-02-22 12:50:49 +05:30
}
func TestActionScript(t *testing.T) {
response := `
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
<script>window.test = 'some-data';</script>
</html>`
timeout := 180 * time.Second
2021-02-22 12:50:49 +05:30
t.Run("run-and-results", func(t *testing.T) {
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
2022-03-22 13:47:13 +01:00
{ActionType: ActionTypeHolder{ActionType: ActionScript}, Name: "test", Data: map[string]string{"code": "() => window.test"}},
2021-02-22 12:50:49 +05:30
}
testHeadlessSimpleResponse(t, response, actions, timeout, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
2021-10-13 20:45:04 +02:00
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
require.Equal(t, "some-data", out["test"], "could not run js and get results correctly")
})
2021-02-22 12:50:49 +05:30
})
t.Run("hook", func(t *testing.T) {
actions := []*Action{
2022-03-22 13:47:13 +01:00
{ActionType: ActionTypeHolder{ActionType: ActionScript}, Data: map[string]string{"code": "() => window.test = 'some-data';", "hook": "true"}},
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
2022-03-22 13:47:13 +01:00
{ActionType: ActionTypeHolder{ActionType: ActionScript}, Name: "test", Data: map[string]string{"code": "() => window.test"}},
2021-02-22 12:50:49 +05:30
}
testHeadlessSimpleResponse(t, response, actions, timeout, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
2021-10-13 20:45:04 +02:00
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
require.Equal(t, "some-data", out["test"], "could not run js and get results correctly with js hook")
})
2021-02-22 12:50:49 +05:30
})
}
func TestActionClick(t *testing.T) {
response := `
2021-02-22 12:50:49 +05:30
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
<button onclick='this.setAttribute("a", "ok")'>click me</button>
</html>`
2021-02-22 12:50:49 +05:30
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionClick}, Data: map[string]string{"selector": "button"}}, // Use css selector for clicking
2021-02-22 12:50:49 +05:30
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
el := page.Page().MustElement("button")
val := el.MustAttribute("a")
require.Equal(t, "ok", *val, "could not click button")
})
2021-02-22 12:50:49 +05:30
}
func TestActionRightClick(t *testing.T) {
response := `
2021-02-22 12:50:49 +05:30
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
<button id="test" onrightclick=''>click me</button>
<script>
elm = document.getElementById("test");
elm.onmousedown = function(event) {
if (event.which == 3) {
elm.setAttribute("a", "ok")
}
}
</script>
</html>`
2021-02-22 12:50:49 +05:30
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionRightClick}, Data: map[string]string{"selector": "button"}}, // Use css selector for clicking
2021-02-22 12:50:49 +05:30
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
el := page.Page().MustElement("button")
val := el.MustAttribute("a")
require.Equal(t, "ok", *val, "could not click button")
})
2021-02-22 12:50:49 +05:30
}
func TestActionTextInput(t *testing.T) {
response := `
2021-02-22 12:50:49 +05:30
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
<input type="text" onchange="this.setAttribute('event', 'input-change')">
</html>`
2021-02-22 12:50:49 +05:30
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionTextInput}, Data: map[string]string{"selector": "input", "value": "test"}},
2021-02-22 12:50:49 +05:30
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
el := page.Page().MustElement("input")
val := el.MustAttribute("event")
require.Equal(t, "input-change", *val, "could not get input change")
require.Equal(t, "test", el.MustText(), "could not get input change value")
})
2021-02-22 12:50:49 +05:30
}
func TestActionHeadersChange(t *testing.T) {
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionSetHeader}, Data: map[string]string{"part": "request", "key": "Test", "value": "Hello"}},
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
2021-02-22 12:50:49 +05:30
}
handler := func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Test") == "Hello" {
_, _ = fmt.Fprintln(w, `found`)
}
}
testHeadless(t, actions, 20*time.Second, handler, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "found", strings.ToLower(strings.TrimSpace(page.Page().MustElement("html").MustText())), "could not set header correctly")
})
2021-02-22 12:50:49 +05:30
}
2021-10-09 13:18:43 +02:00
func TestActionScreenshot(t *testing.T) {
response := `
2021-10-10 08:29:58 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
</html>`
2021-10-10 08:29:58 +02:00
// filePath where screenshot is saved
filePath := filepath.Join(os.TempDir(), "test.png")
2021-10-10 08:29:58 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionScreenshot}, Data: map[string]string{"to": filePath}},
2021-10-10 08:29:58 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
_ = page.Page()
require.FileExists(t, filePath, "could not find screenshot file %v", filePath)
if err := os.RemoveAll(filePath); err != nil {
t.Logf("got error %v while deleting temp file", err)
}
})
}
func TestActionScreenshotToDir(t *testing.T) {
response := `
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
</html>`
filePath := filepath.Join(os.TempDir(), "screenshot-"+strconv.Itoa(rand.Intn(1000)), "test.png")
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionScreenshot}, Data: map[string]string{"to": filePath, "mkdir": "true"}},
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
_ = page.Page()
require.FileExists(t, filePath, "could not find screenshot file %v", filePath)
if err := os.RemoveAll(filePath); err != nil {
t.Logf("got error %v while deleting temp file", err)
}
})
2021-10-09 13:18:43 +02:00
}
func TestActionTimeInput(t *testing.T) {
response := `
2021-10-10 08:29:58 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
<input type="date">
</html>`
2021-10-10 08:29:58 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionTimeInput}, Data: map[string]string{"selector": "input", "value": "2006-01-02T15:04:05Z"}},
2021-10-10 08:29:58 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
el := page.Page().MustElement("input")
require.Equal(t, "2006-01-02", el.MustText(), "could not get input time value")
})
2021-10-09 13:18:43 +02:00
}
func TestActionSelectInput(t *testing.T) {
response := `
2021-10-09 23:19:07 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>
<select name="test" id="test">
<option value="test1">Test1</option>
<option value="test2">Test2</option>
</select>
</body>
</html>`
2021-10-09 23:19:07 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionSelectInput}, Data: map[string]string{"by": "x", "xpath": "//select[@id='test']", "value": "Test2", "selected": "true"}},
2021-10-09 23:19:07 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
el := page.Page().MustElement("select")
require.Equal(t, "Test2", el.MustText(), "could not get input change value")
})
2021-10-09 13:18:43 +02:00
}
func TestActionFilesInput(t *testing.T) {
response := `
2021-10-10 08:29:58 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>Nuclei Test Page</body>
<input type="file">
</html>`
2021-10-10 08:29:58 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionFilesInput}, Data: map[string]string{"selector": "input", "value": "test1.pdf"}},
2021-10-10 08:29:58 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
el := page.Page().MustElement("input")
require.Equal(t, "C:\\fakepath\\test1.pdf", el.MustText(), "could not get input file")
})
2021-10-09 13:18:43 +02:00
}
func TestActionWaitLoad(t *testing.T) {
response := `
2021-10-10 13:17:35 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<button id="test">Wait for me!</button>
<script>
window.onload = () => document.querySelector('#test').style.color = 'red';
</script>
</html>`
2021-10-10 13:17:35 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
2021-10-10 13:17:35 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
el := page.Page().MustElement("button")
style, attributeErr := el.Attribute("style")
require.Nil(t, attributeErr)
require.Equal(t, "color: red;", *style, "could not get color")
})
2021-10-09 13:18:43 +02:00
}
func TestActionGetResource(t *testing.T) {
response := `
2021-10-10 13:17:35 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>
<img id="test" src="https://nuclei.projectdiscovery.io/static/logo.png">
</body>
</html>`
2021-10-10 13:17:35 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionGetResource}, Data: map[string]string{"by": "x", "xpath": "//img[@id='test']"}, Name: "src"},
2021-10-10 13:17:35 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, len(out["src"]), 3159, "could not find resource")
})
2021-10-09 13:18:43 +02:00
}
func TestActionExtract(t *testing.T) {
response := `
2021-10-10 13:17:35 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<button id="test">Wait for me!</button>
</html>`
2021-10-10 13:17:35 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionExtract}, Data: map[string]string{"by": "x", "xpath": "//button[@id='test']"}, Name: "extract"},
2021-10-10 13:17:35 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "Wait for me!", out["extract"], "could not extract text")
})
2021-10-09 13:18:43 +02:00
}
func TestActionSetMethod(t *testing.T) {
response := `
2021-10-10 13:17:35 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
</html>`
2021-10-10 13:17:35 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionSetMethod}, Data: map[string]string{"part": "x", "method": "SET"}},
2021-10-10 13:17:35 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "SET", page.rules[0].Args["method"], "could not find resource")
})
2021-10-09 13:18:43 +02:00
}
func TestActionAddHeader(t *testing.T) {
2021-10-09 19:37:12 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionAddHeader}, Data: map[string]string{"part": "request", "key": "Test", "value": "Hello"}},
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
2021-10-09 19:37:12 +02:00
}
handler := func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Test") == "Hello" {
_, _ = fmt.Fprintln(w, `found`)
}
}
testHeadless(t, actions, 20*time.Second, handler, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "found", strings.ToLower(strings.TrimSpace(page.Page().MustElement("html").MustText())), "could not set header correctly")
})
}
2021-10-09 19:37:12 +02:00
func TestActionDeleteHeader(t *testing.T) {
2021-10-09 19:37:12 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionAddHeader}, Data: map[string]string{"part": "request", "key": "Test1", "value": "Hello"}},
{ActionType: ActionTypeHolder{ActionType: ActionAddHeader}, Data: map[string]string{"part": "request", "key": "Test2", "value": "World"}},
{ActionType: ActionTypeHolder{ActionType: ActionDeleteHeader}, Data: map[string]string{"part": "request", "key": "Test2"}},
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
2021-10-09 19:37:12 +02:00
}
handler := func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Test1") == "Hello" && r.Header.Get("Test2") == "" {
_, _ = fmt.Fprintln(w, `header deleted`)
}
}
testHeadless(t, actions, 20*time.Second, handler, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "header deleted", strings.ToLower(strings.TrimSpace(page.Page().MustElement("html").MustText())), "could not delete header correctly")
})
2021-10-09 13:18:43 +02:00
}
func TestActionSetBody(t *testing.T) {
2021-10-09 19:37:12 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionSetBody}, Data: map[string]string{"part": "request", "body": "hello"}},
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
2021-10-09 19:37:12 +02:00
}
handler := func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
_, _ = fmt.Fprintln(w, string(body))
}
testHeadless(t, actions, 20*time.Second, handler, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.Equal(t, "hello", strings.ToLower(strings.TrimSpace(page.Page().MustElement("html").MustText())), "could not set header correctly")
})
2021-10-09 13:18:43 +02:00
}
func TestActionKeyboard(t *testing.T) {
response := `
2021-10-09 23:19:07 +02:00
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>
2021-10-13 20:29:28 +02:00
<input type="text" name="test" id="test">
2021-10-09 23:19:07 +02:00
</body>
</html>`
2021-10-09 23:19:07 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
{ActionType: ActionTypeHolder{ActionType: ActionClick}, Data: map[string]string{"selector": "input"}},
{ActionType: ActionTypeHolder{ActionType: ActionKeyboard}, Data: map[string]string{"keys": "Test2"}},
2021-10-09 23:19:07 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
el := page.Page().MustElement("input")
require.Equal(t, "Test2", el.MustText(), "could not get input change value")
})
2021-10-09 13:18:43 +02:00
}
func TestActionSleep(t *testing.T) {
response := `
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<button style="display:none" id="test">Wait for me!</button>
<script>
setTimeout(() => document.querySelector('#test').style.display = '', 1000);
</script>
</html>`
2021-10-10 17:18:53 +02:00
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionSleep}, Data: map[string]string{"duration": "2"}},
2021-10-10 17:18:53 +02:00
}
testHeadlessSimpleResponse(t, response, actions, 20*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
require.True(t, page.Page().MustElement("button").MustVisible(), "could not get button")
})
2021-10-09 13:18:43 +02:00
}
2021-10-09 19:37:12 +02:00
2021-10-09 12:26:18 +02:00
func TestActionWaitVisible(t *testing.T) {
response := `
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<button style="display:none" id="test">Wait for me!</button>
<script>
setTimeout(() => document.querySelector('#test').style.display = '', 1000);
</script>
</html>`
actions := []*Action{
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
{ActionType: ActionTypeHolder{ActionType: ActionWaitVisible}, Data: map[string]string{"by": "x", "xpath": "//button[@id='test']"}},
}
2021-10-09 17:30:45 +02:00
t.Run("wait for an element being visible", func(t *testing.T) {
testHeadlessSimpleResponse(t, response, actions, 2*time.Second, func(page *Page, err error, out map[string]string) {
require.Nil(t, err, "could not run page actions")
2021-10-09 12:26:18 +02:00
page.Page().MustElement("button").MustVisible()
})
})
2021-10-09 12:26:18 +02:00
t.Run("timeout because of element not visible", func(t *testing.T) {
// increased timeout from time.Second/2 to time.Second due to random fails (probably due to overhead and system)
testHeadlessSimpleResponse(t, response, actions, time.Second, func(page *Page, err error, out map[string]string) {
require.Error(t, err)
require.Contains(t, err.Error(), "Element did not appear in the given amount of time")
})
})
}
2021-10-09 12:26:18 +02:00
func testHeadlessSimpleResponse(t *testing.T, response string, actions []*Action, timeout time.Duration, assert func(page *Page, pageErr error, out map[string]string)) {
t.Helper()
testHeadless(t, actions, timeout, func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprintln(w, response)
}, assert)
}
2021-10-09 12:26:18 +02:00
func testHeadless(t *testing.T, actions []*Action, timeout time.Duration, handler func(w http.ResponseWriter, r *http.Request), assert func(page *Page, pageErr error, extractedData map[string]string)) {
t.Helper()
_ = protocolstate.Init(&types.Options{})
2021-10-09 17:30:45 +02:00
browser, err := New(&types.Options{ShowBrowser: false, UseInstalledChrome: testheadless.HeadlessLocal})
require.Nil(t, err, "could not create browser")
defer browser.Close()
2021-10-09 17:30:45 +02:00
instance, err := browser.NewInstance()
require.Nil(t, err, "could not create browser instance")
defer instance.Close()
2021-10-09 17:30:45 +02:00
ts := httptest.NewServer(http.HandlerFunc(handler))
defer ts.Close()
2021-10-09 12:26:18 +02:00
input := contextargs.NewWithInput(ts.URL)
input.CookieJar, err = cookiejar.New(nil)
require.Nil(t, err)
extractedData, page, err := instance.Run(input, actions, nil, &Options{Timeout: timeout})
assert(page, err, extractedData)
2021-10-09 17:30:45 +02:00
if page != nil {
page.Close()
}
2021-10-09 12:26:18 +02:00
}
func TestContainsAnyModificationActionType(t *testing.T) {
if containsAnyModificationActionType() {
t.Error("Expected false, got true")
}
if containsAnyModificationActionType(ActionClick) {
t.Error("Expected false, got true")
}
if !containsAnyModificationActionType(ActionSetMethod, ActionAddHeader, ActionExtract) {
t.Error("Expected true, got false")
}
if !containsAnyModificationActionType(ActionSetMethod, ActionAddHeader, ActionSetHeader, ActionDeleteHeader, ActionSetBody) {
t.Error("Expected true, got false")
}
}