Skip to content

Commit

Permalink
Headless issue with root user (#132)
Browse files Browse the repository at this point in the history
* Headless issue with root user. #131

* Linting

* Linting

* Add input check

* misc option update

* linting

* Fix flag names in check

* readme update

Co-authored-by: sandeep <[email protected]>
  • Loading branch information
edoardottt and ehsandeep authored Nov 11, 2022
1 parent bedf7cc commit bc05957
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 3 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ CONFIGURATION:
-fc, -form-config string path to custom form configuration file

HEADLESS:
-hl, -headless enable headless hybrid crawling (experimental)
-sc, -system-chrome use local installed chrome browser instead of katana installed
-sb, -show-browser show the browser on the screen with headless mode
-hl, -headless enable headless hybrid crawling (experimental)
-sc, -system-chrome use local installed chrome browser instead of katana installed
-sb, -show-browser show the browser on the screen with headless mode
-ho, -headless-options string[] start headless chrome with additional options
-nos, -no-sandbox start headless chrome in --no-sandbox mode

SCOPE:
-cs, -crawl-scope string[] in scope url regex to be followed by crawler
Expand Down
2 changes: 2 additions & 0 deletions cmd/katana/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pipelines offering both headless and non-headless crawling.`)
flagSet.BoolVarP(&options.Headless, "headless", "hl", false, "enable headless hybrid crawling (experimental)"),
flagSet.BoolVarP(&options.UseInstalledChrome, "system-chrome", "sc", false, "use local installed chrome browser instead of katana installed"),
flagSet.BoolVarP(&options.ShowBrowser, "show-browser", "sb", false, "show the browser on the screen with headless mode"),
flagSet.StringSliceVarP(&options.HeadlessOptionalArguments, "headless-options", "ho", nil, "start headless chrome with additional options", goflags.FileCommaSeparatedStringSliceOptions),
flagSet.BoolVarP(&options.HeadlessNoSandbox, "no-sandbox", "nos", false, "start headless chrome in --no-sandbox mode"),
)

flagSet.CreateGroup("scope", "Scope",
Expand Down
3 changes: 3 additions & 0 deletions internal/runner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ func validateOptions(options *types.Options) error {
if len(options.URLs) == 0 && !fileutil.HasStdin() {
return errors.New("no inputs specified for crawler")
}
if (options.HeadlessOptionalArguments != nil || options.HeadlessNoSandbox) && !options.Headless {
return errors.New("headless mode (-hl) is required if -ho or -nos are set")
}
gologger.DefaultLogger.SetFormatter(formatter.NewCLI(options.NoColors))
return nil
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/engine/hybrid/hybrid.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
"github.com/go-rod/rod/lib/launcher/flags"
"github.com/pkg/errors"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/katana/pkg/engine/common"
Expand Down Expand Up @@ -76,6 +77,14 @@ func New(options *types.CrawlerOptions) (*Crawler, error) {
chromeLauncher = chromeLauncher.Headless(true)
}

if options.Options.HeadlessNoSandbox {
chromeLauncher.Set("no-sandbox", "true")
}

for k, v := range options.Options.ParseHeadlessOptionalArguments() {
chromeLauncher.Set(flags.Flag(k), v)
}

launcherURL, err := chromeLauncher.Launch()
if err != nil {
return nil, err
Expand Down
18 changes: 18 additions & 0 deletions pkg/types/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ type Options struct {
UseInstalledChrome bool
// ShowBrowser specifies whether the show the browser in headless mode
ShowBrowser bool
// HeadlessOptionalArguments specifies optional arguments to pass to Chrome
HeadlessOptionalArguments goflags.StringSlice
// HeadlessNoSandbox specifies if chrome should be start in --no-sandbox mode
HeadlessNoSandbox bool
}

func (options *Options) ParseCustomHeaders() map[string]string {
Expand All @@ -90,3 +94,17 @@ func (options *Options) ParseCustomHeaders() map[string]string {
}
return customHeaders
}

func (options *Options) ParseHeadlessOptionalArguments() map[string]string {
optionalArguments := make(map[string]string)
for _, v := range options.HeadlessOptionalArguments {
if argParts := strings.SplitN(v, "=", 2); len(argParts) >= 2 {
key := strings.TrimSpace(argParts[0])
value := strings.TrimSpace(argParts[1])
if key != "" && value != "" {
optionalArguments[key] = value
}
}
}
return optionalArguments
}
101 changes: 101 additions & 0 deletions pkg/types/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package types

import (
"strings"
"testing"

"github.com/projectdiscovery/goflags"
"github.com/stretchr/testify/require"
)

func TestParseCustomHeaders(t *testing.T) {
tests := []struct {
name string
input string
want map[string]string
}{
{
name: "single value",
input: "a:b",
want: map[string]string{"a": "b"},
},
{
name: "empty string",
input: "",
want: map[string]string{},
},
{
name: "empty value",
input: "a:",
want: map[string]string{"a": ""},
},
{
name: "double input",
input: "a:b,c:d",
want: map[string]string{"a": "b", "c": "d"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strsl := goflags.StringSlice{}
for _, v := range strings.Split(tt.input, ",") {
//nolint
strsl.Set(v)
}
opt := Options{CustomHeaders: strsl}
got := opt.ParseCustomHeaders()
require.Equal(t, tt.want, got)
})
}
}

func TestParseHeadlessOptionalArguments(t *testing.T) {
tests := []struct {
name string
input string
want map[string]string
}{
{
name: "single value",
input: "a=b",
want: map[string]string{"a": "b"},
},
{
name: "empty string",
input: "",
want: map[string]string{},
},
{
name: "empty key",
input: "=b",
want: map[string]string{},
},
{
name: "empty value",
input: "a=",
want: map[string]string{},
},
{
name: "double input",
input: "a=b,c=d",
want: map[string]string{"a": "b", "c": "d"},
},
{
name: "duplicated input",
input: "a=b,a=b",
want: map[string]string{"a": "b"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
strsl := goflags.StringSlice{}
for _, v := range strings.Split(tt.input, ",") {
//nolint
strsl.Set(v)
}
opt := Options{HeadlessOptionalArguments: strsl}
got := opt.ParseHeadlessOptionalArguments()
require.Equal(t, tt.want, got)
})
}
}

0 comments on commit bc05957

Please sign in to comment.