2021-02-22 12:50:49 +05:30
package engine
import (
"fmt"
2022-02-23 13:54:46 +01:00
"io"
2023-03-24 00:44:32 +05:30
"math/rand"
2021-02-22 12:50:49 +05:30
"net/http"
2023-06-26 19:25:51 +02:00
"net/http/cookiejar"
2021-02-22 12:50:49 +05:30
"net/http/httptest"
2021-10-10 08:29:58 +02:00
"os"
2023-08-25 18:30:46 +05:30
"os/exec"
2023-03-24 00:44:32 +05:30
"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
2021-10-13 20:08:10 +03:00
"github.com/stretchr/testify/require"
2023-10-17 17:44:13 +05:30
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils/testheadless"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
2023-08-25 18:30:46 +05:30
stringsutil "github.com/projectdiscovery/utils/strings"
2021-02-22 12:50:49 +05:30
)
2021-10-10 17:18:53 +02:00
func TestActionNavigate ( t * testing . T ) {
2021-10-14 19:11:07 +03:00
response := `
2021-02-22 12:50:49 +05:30
< html >
< head >
< title > Nuclei Test Page < / title >
< / head >
< body >
< h1 > Nuclei Test < / h1 >
< / body >
2021-10-14 19:11:07 +03:00
< / html > `
2021-02-22 12:50:49 +05:30
2021-11-25 17:18:54 +02:00
actions := [ ] * Action { { ActionType : ActionTypeHolder { ActionType : ActionNavigate } , Data : map [ string ] string { "url" : "{{BaseURL}}" } } , { ActionType : ActionTypeHolder { ActionType : ActionWaitLoad } } }
2021-02-22 12:50:49 +05:30
2021-10-14 19:11:07 +03:00
testHeadlessSimpleResponse ( t , response , actions , 20 * time . Second , func ( page * Page , err error , out map [ string ] string ) {
2023-08-25 18:30:46 +05:30
require . Nilf ( t , err , "could not run page actions" )
2021-10-14 19:11:07 +03:00
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 ) {
2021-10-14 19:11:07 +03:00
response := `
< html >
< head >
< title > Nuclei Test Page < / title >
< / head >
< body > Nuclei Test Page < / body >
< script > window . test = ' some - data ' ; < / script >
< / html > `
2022-07-14 03:14:13 +05:30
timeout := 180 * time . Second
2021-10-14 19:11:07 +03:00
2021-02-22 12:50:49 +05:30
t . Run ( "run-and-results" , func ( t * testing . T ) {
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-11-18 14:11:10 -06:00
2021-10-14 19:11:07 +03:00
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" } } ,
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03:00
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 ) {
2021-10-14 19:11:07 +03:00
response := `
2021-02-22 12:50:49 +05:30
< html >
2021-10-14 19:11:07 +03:00
< 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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 ( "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 ) {
2021-10-14 19:11:07 +03:00
response := `
2021-02-22 12:50:49 +05:30
< html >
2021-10-14 19:11:07 +03:00
< 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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 ( "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 ) {
2021-10-14 19:11:07 +03:00
response := `
2021-02-22 12:50:49 +05:30
< html >
2021-10-14 19:11:07 +03:00
< 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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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" )
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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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-02-22 12:50:49 +05:30
}
2021-10-09 13:18:43 +02:00
func TestActionScreenshot ( t * testing . T ) {
2021-10-14 19:11:07 +03:00
response := `
2021-10-10 08:29:58 +02:00
< html >
2021-10-14 19:11:07 +03:00
< head >
< title > Nuclei Test Page < / title >
< / head >
< body > Nuclei Test Page < / body >
< / html > `
2021-10-10 08:29:58 +02:00
2023-03-24 00:44:32 +05:30
// filePath where screenshot is saved
filePath := filepath . Join ( os . TempDir ( ) , "test.png" )
2021-10-10 08:29:58 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ ActionType : ActionTypeHolder { ActionType : ActionNavigate } , Data : map [ string ] string { "url" : "{{BaseURL}}" } } ,
{ ActionType : ActionTypeHolder { ActionType : ActionWaitLoad } } ,
2023-03-24 00:44:32 +05:30
{ ActionType : ActionTypeHolder { ActionType : ActionScreenshot } , Data : map [ string ] string { "to" : filePath } } ,
2021-10-10 08:29:58 +02:00
}
2021-10-14 19:11:07 +03: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" )
2023-03-24 00:44:32 +05:30
_ = 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-14 19:11:07 +03:00
} )
2021-10-09 13:18:43 +02:00
}
func TestActionTimeInput ( t * testing . T ) {
2021-10-14 19:11:07 +03:00
response := `
2021-10-10 08:29:58 +02:00
< html >
2021-10-14 19:11:07 +03:00
< 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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 ) {
2021-10-14 19:11:07 +03:00
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 >
2021-10-14 19:11:07 +03:00
< / html > `
2021-10-09 23:19:07 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 ) {
2021-10-14 19:11:07 +03:00
response := `
2021-10-10 08:29:58 +02:00
< html >
2021-10-14 19:11:07 +03:00
< 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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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
}
2023-08-25 18:30:46 +05:30
// Negative testcase for files input where it should fail
func TestActionFilesInputNegative ( t * testing . T ) {
response := `
< html >
< head >
< title > Nuclei Test Page < / title >
< / head >
< body > Nuclei Test Page < / body >
< input type = "file" >
< / html > `
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" } } ,
}
t . Setenv ( "LOCAL_FILE_ACCESS" , "false" )
testHeadlessSimpleResponse ( t , response , actions , 20 * time . Second , func ( page * Page , err error , out map [ string ] string ) {
require . ErrorContains ( t , err , ErrLFAccessDenied . Error ( ) , "got file access when -lfa is false" )
} )
}
2021-10-09 13:18:43 +02:00
func TestActionWaitLoad ( t * testing . T ) {
2021-10-14 19:11:07 +03:00
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 >
2021-10-14 19:11:07 +03:00
< / html > `
2021-10-10 13:17:35 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ ActionType : ActionTypeHolder { ActionType : ActionNavigate } , Data : map [ string ] string { "url" : "{{BaseURL}}" } } ,
{ ActionType : ActionTypeHolder { ActionType : ActionWaitLoad } } ,
2021-10-10 13:17:35 +02:00
}
2021-10-14 19:11:07 +03: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 ) {
2021-10-14 19:11:07 +03:00
response := `
2021-10-10 13:17:35 +02:00
< html >
< head >
< title > Nuclei Test Page < / title >
< / head >
< body >
2023-08-21 20:33:52 +05:30
< img id = "test" src = "https://raw.githubusercontent.com/projectdiscovery/wallpapers/main/pd-floppy.jpg" >
2021-10-10 13:17:35 +02:00
< / body >
2021-10-14 19:11:07 +03:00
< / html > `
2021-10-10 13:17:35 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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" )
2023-08-21 20:33:52 +05:30
require . Equal ( t , len ( out [ "src" ] ) , 121808 , "could not find resource" )
2021-10-14 19:11:07 +03:00
} )
2021-10-09 13:18:43 +02:00
}
func TestActionExtract ( t * testing . T ) {
2021-10-14 19:11:07 +03:00
response := `
2021-10-10 13:17:35 +02:00
< html >
< head >
< title > Nuclei Test Page < / title >
< / head >
< button id = "test" > Wait for me ! < / button >
2021-10-14 19:11:07 +03:00
< / html > `
2021-10-10 13:17:35 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 ) {
2021-10-14 19:11:07 +03:00
response := `
2021-10-10 13:17:35 +02:00
< html >
< head >
< title > Nuclei Test Page < / title >
< / head >
2021-10-14 19:11:07 +03:00
< / html > `
2021-10-10 13:17:35 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 17:38:56 +03:00
2021-10-14 19:11:07 +03:00
handler := func ( w http . ResponseWriter , r * http . Request ) {
if r . Header . Get ( "Test" ) == "Hello" {
_ , _ = fmt . Fprintln ( w , ` found ` )
2021-10-14 17:38:56 +03:00
}
2021-10-14 19:11:07 +03:00
}
2021-10-14 17:38:56 +03:00
2021-10-14 19:11:07 +03:00
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
2021-10-14 19:11:07 +03:00
func TestActionDeleteHeader ( t * testing . T ) {
2021-10-09 19:37:12 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03:00
handler := func ( w http . ResponseWriter , r * http . Request ) {
2022-02-23 13:54:46 +01:00
body , _ := io . ReadAll ( r . Body )
2021-10-14 19:11:07 +03:00
_ , _ = 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 ) {
2021-10-14 19:11:07 +03:00
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 >
2021-10-14 19:11:07 +03:00
< / html > `
2021-10-09 23:19:07 +02:00
actions := [ ] * Action {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 ) {
2021-10-14 19:11:07 +03:00
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 {
2021-11-25 17:18:54 +02:00
{ 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
}
2021-10-14 19:11:07 +03: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 ) {
2021-10-14 19:11:07 +03:00
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 {
2021-11-25 17:18:54 +02:00
{ 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-14 19:11:07 +03:00
}
2021-10-09 17:30:45 +02:00
t . Run ( "wait for an element being visible" , func ( t * testing . T ) {
2021-10-14 19:11:07 +03:00
testHeadlessSimpleResponse ( t , response , actions , 2 * time . Second , func ( page * Page , err error , out map [ string ] string ) {
2021-10-13 20:08:10 +03:00
require . Nil ( t , err , "could not run page actions" )
2021-10-09 12:26:18 +02:00
2021-10-13 20:08:10 +03:00
page . Page ( ) . MustElement ( "button" ) . MustVisible ( )
} )
} )
2021-10-09 12:26:18 +02:00
2021-10-13 20:08:10 +03:00
t . Run ( "timeout because of element not visible" , func ( t * testing . T ) {
2023-01-24 22:04:52 +05:30
// 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 ) {
2021-10-13 20:08:10 +03:00
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
2021-10-14 19:11:07 +03: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
2021-10-14 19:11:07 +03: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 ( )
2021-10-13 20:08:10 +03:00
_ = protocolstate . Init ( & types . Options { } )
2021-10-09 17:30:45 +02:00
2022-09-07 12:39:22 +02:00
browser , err := New ( & types . Options { ShowBrowser : false , UseInstalledChrome : testheadless . HeadlessLocal } )
2021-10-13 20:08:10 +03:00
require . Nil ( t , err , "could not create browser" )
defer browser . Close ( )
2021-10-09 17:30:45 +02:00
2021-10-13 20:08:10 +03:00
instance , err := browser . NewInstance ( )
require . Nil ( t , err , "could not create browser instance" )
2021-10-14 19:11:07 +03:00
defer instance . Close ( )
2021-10-09 17:30:45 +02:00
2021-10-14 19:11:07 +03:00
ts := httptest . NewServer ( http . HandlerFunc ( handler ) )
2021-10-13 20:08:10 +03:00
defer ts . Close ( )
2021-10-09 12:26:18 +02:00
2023-06-26 19:25:51 +02:00
input := contextargs . NewWithInput ( ts . URL )
input . CookieJar , err = cookiejar . New ( nil )
require . Nil ( t , err )
2023-08-25 18:30:46 +05:30
lfa := getBoolFromEnv ( "LOCAL_FILE_ACCESS" , true )
rna := getBoolFromEnv ( "RESTRICED_LOCAL_NETWORK_ACCESS" , false )
extractedData , page , err := instance . Run ( input , actions , nil , & Options { Timeout : timeout , Options : & types . Options { AllowLocalFileAccess : lfa , RestrictLocalNetworkAccess : rna } } ) // allow file access in test
2021-10-14 19:11:07 +03:00
assert ( page , err , extractedData )
2021-10-09 17:30:45 +02:00
2021-10-14 19:11:07 +03:00
if page != nil {
page . Close ( )
2021-10-13 20:08:10 +03:00
}
2021-10-09 12:26:18 +02:00
}
2023-01-05 12:56:18 +01: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" )
}
}
2023-08-25 18:30:46 +05:30
func TestBlockedHeadlessURLS ( t * testing . T ) {
// run this test from binary since we are changing values
// of global variables
if os . Getenv ( "TEST_BLOCK_HEADLESS_URLS" ) != "1" {
cmd := exec . Command ( os . Args [ 0 ] , "-test.run=TestBlockedHeadlessURLS" , "-test.v" )
cmd . Env = append ( cmd . Env , "TEST_BLOCK_HEADLESS_URLS=1" )
out , err := cmd . CombinedOutput ( )
if ! strings . Contains ( string ( out ) , "PASS\n" ) || err != nil {
t . Fatalf ( "%s\n(exit status %v)" , string ( out ) , err )
}
return
}
opts := & types . Options {
AllowLocalFileAccess : false ,
RestrictLocalNetworkAccess : true ,
}
err := protocolstate . Init ( opts )
require . Nil ( t , err , "could not init protocol state" )
browser , err := New ( & types . Options { ShowBrowser : false , UseInstalledChrome : testheadless . HeadlessLocal } )
require . Nil ( t , err , "could not create browser" )
defer browser . Close ( )
instance , err := browser . NewInstance ( )
require . Nil ( t , err , "could not create browser instance" )
defer instance . Close ( )
ts := httptest . NewServer ( nil )
defer ts . Close ( )
testcases := [ ] string {
"file:/etc/hosts" ,
" file:///etc/hosts\r\n" ,
" fILe:/../../../../etc/hosts" ,
ts . URL , // local test server
"fTP://example.com:21\r\n" ,
"ftp://example.com:21" ,
"chrome://settings" ,
" chROme://version" ,
"chrome-extension://version\r" ,
" chrOme-EXTension://settings" ,
"view-source:file:/etc/hosts" ,
}
for _ , testcase := range testcases {
actions := [ ] * Action {
{ ActionType : ActionTypeHolder { ActionType : ActionNavigate } , Data : map [ string ] string { "url" : testcase } } ,
{ ActionType : ActionTypeHolder { ActionType : ActionWaitLoad } } ,
}
data , page , err := instance . Run ( contextargs . NewWithInput ( ts . URL ) , actions , nil , & Options { Timeout : 20 * time . Second , Options : opts } ) // allow file access in test
require . Error ( t , err , "expected error for url %s got %v" , testcase , data )
require . True ( t , stringsutil . ContainsAny ( err . Error ( ) , "net::ERR_ACCESS_DENIED" , "failed to parse url" , "Cannot navigate to invalid URL" , "net::ERR_ABORTED" , "net::ERR_INVALID_URL" ) , "found different error %v for testcases %v" , err , testcase )
require . Len ( t , data , 0 , "expected no data for url %s got %v" , testcase , data )
if page != nil {
page . Close ( )
}
}
}
func getBoolFromEnv ( key string , defaultValue bool ) bool {
val := os . Getenv ( key )
if val == "" {
return defaultValue
}
return strings . EqualFold ( val , "true" )
}