httpx/runner/headless.go
seeyarh 91139dbc91
Fix chrome unsafe port err by setting explicitly-allowed-ports (#1343)
* Update headless.go

* adding headless generic option

---------

Co-authored-by: mzack <marco.rivoli.nvh@gmail.com>
2023-11-28 18:18:29 +05:30

138 lines
3.6 KiB
Go

package runner
import (
"fmt"
"os"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
"github.com/go-rod/rod/lib/launcher/flags"
"github.com/go-rod/rod/lib/proto"
"github.com/pkg/errors"
fileutil "github.com/projectdiscovery/utils/file"
osutils "github.com/projectdiscovery/utils/os"
)
// MustDisableSandbox determines if the current os and user needs sandbox mode disabled
func MustDisableSandbox() bool {
// linux with root user needs "--no-sandbox" option
// https://github.com/chromium/chromium/blob/c4d3c31083a2e1481253ff2d24298a1dfe19c754/chrome/test/chromedriver/client/chromedriver.py#L209
return osutils.IsLinux() && os.Geteuid() == 0
}
type Browser struct {
tempDir string
engine *rod.Browser
// TODO: Remove the Chrome PID kill code in favor of using Leakless(true).
// This change will be made if there are no complaints about zombie Chrome processes.
// Reference: https://github.com/projectdiscovery/httpx/pull/1426
// pids map[int32]struct{}
}
func NewBrowser(proxy string, useLocal bool, optionalArgs map[string]string) (*Browser, error) {
dataStore, err := os.MkdirTemp("", "nuclei-*")
if err != nil {
return nil, errors.Wrap(err, "could not create temporary directory")
}
// pids := processutil.FindProcesses(processutil.IsChromeProcess)
chromeLauncher := launcher.New().
Leakless(true).
Set("disable-gpu", "true").
Set("ignore-certificate-errors", "true").
Set("ignore-certificate-errors", "1").
Set("disable-crash-reporter", "true").
Set("disable-notifications", "true").
Set("hide-scrollbars", "true").
Set("window-size", fmt.Sprintf("%d,%d", 1080, 1920)).
Set("mute-audio", "true").
Set("incognito", "true").
Delete("use-mock-keychain").
Headless(true).
UserDataDir(dataStore)
if MustDisableSandbox() {
chromeLauncher = chromeLauncher.NoSandbox(true)
}
executablePath, err := os.Executable()
if err != nil {
return nil, err
}
// if musl is used, most likely we are on alpine linux which is not supported by go-rod, so we fallback to default chrome
useMusl, _ := fileutil.UseMusl(executablePath)
if useLocal || useMusl {
if chromePath, hasChrome := launcher.LookPath(); hasChrome {
chromeLauncher.Bin(chromePath)
} else {
return nil, errors.New("the chrome browser is not installed")
}
}
if proxy != "" {
chromeLauncher = chromeLauncher.Proxy(proxy)
}
for k, v := range optionalArgs {
chromeLauncher.Set(flags.Flag(k), v)
}
launcherURL, err := chromeLauncher.Launch()
if err != nil {
return nil, err
}
browser := rod.New().ControlURL(launcherURL)
if browserErr := browser.Connect(); browserErr != nil {
return nil, browserErr
}
engine := &Browser{
tempDir: dataStore,
engine: browser,
// pids: pids,
}
return engine, nil
}
func (b *Browser) ScreenshotWithBody(url string, timeout time.Duration) ([]byte, string, error) {
page, err := b.engine.Page(proto.TargetCreateTarget{})
if err != nil {
return nil, "", err
}
page = page.Timeout(timeout)
defer page.Close()
if err := page.Navigate(url); err != nil {
return nil, "", err
}
page.Timeout(5 * time.Second).WaitNavigation(proto.PageLifecycleEventNameFirstMeaningfulPaint)()
if err := page.WaitLoad(); err != nil {
return nil, "", err
}
_ = page.WaitIdle(1 * time.Second)
screenshot, err := page.Screenshot(true, &proto.PageCaptureScreenshot{})
if err != nil {
return nil, "", err
}
body, err := page.HTML()
if err != nil {
return screenshot, "", err
}
return screenshot, body, nil
}
func (b *Browser) Close() {
b.engine.Close()
os.RemoveAll(b.tempDir)
// processutil.CloseProcesses(processutil.IsChromeProcess, b.pids)
}