-
Notifications
You must be signed in to change notification settings - Fork 3k
fix(lib): segfault when init engine with EnableHeadlessWithOpts
#6602
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
base: dev
Are you sure you want to change the base?
fix(lib): segfault when init engine with EnableHeadlessWithOpts
#6602
Conversation
WalkthroughAdds runtime headless browser initialization during engine init, ensures default Logger is non-nil and wired earlier, updates constructors to set Logger/ctx, increases a test timeout, tightens Makefile test flags, and adds a headless-init unit test. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant NewEngine as NewNucleiEngineCtx
participant Engine
participant Logger
participant HeadlessEngine as headless/engine
Caller->>NewEngine: NewNucleiEngineCtx(ctx, options...)
NewEngine->>Engine: apply options (may set Headless)
Engine->>Logger: ensure e.opts.Logger != nil (DefaultOptions provides Logger)
Engine->>Engine: init(ctx)
alt e.opts.Headless == true
Engine->>HeadlessEngine: MustDisableSandbox()
HeadlessEngine-->>Engine: result (warning/info)
Engine->>HeadlessEngine: New(e.opts)
HeadlessEngine-->>Engine: browser instance / error
Engine->>Engine: store e.browserInstance
else
Engine->>Engine: skip browser initialization
end
Engine-->>NewEngine: init result
NewEngine-->>Caller: return engine or error
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/sdk_private.go (1)
101-111: Guard against dropping back to a nil logger.If a caller passes
WithOptionswhereopts.Loggerisnil(very common for manually constructed options), this block overwritese.Loggerwithnil. The nexte.Logger.SetMaxLevel(...)then panics, which regresses existing setups that relied on the engine providing a default logger.Please retain the existing default when
opts.Loggerisnil, e.g.:- if e.opts.Logger != e.Logger { - e.Logger = e.opts.Logger - } + if e.opts.Logger != nil { + if e.opts.Logger != e.Logger { + e.Logger = e.opts.Logger + } + } else { + if e.Logger == nil { + e.Logger = gologger.DefaultLogger + } + e.opts.Logger = e.Logger + }Remember to import
github.com/projectdiscovery/gologgerif it is no longer in scope.
🧹 Nitpick comments (1)
lib/sdk_test.go (1)
39-57: LGTM! Test effectively verifies the segfault fix.The test successfully reproduces the scenario that previously caused a nil pointer panic (initializing the engine with
EnableHeadlessWithOpts) and verifies that the logger is now properly initialized before option functions execute. The assertions are appropriate and the cleanup is handled correctly.Optional enhancement: Consider adding a brief comment explaining the regression this test prevents, for example:
+// TestHeadlessOptionInitialization verifies that initializing the engine with +// EnableHeadlessWithOpts does not panic due to nil logger (regression test for #6601). func TestHeadlessOptionInitialization(t *testing.T) {You might also consider adding a test case for
EnableHeadlessWithOpts(nil)to ensure the nil HeadlessOpts path is also covered:func TestHeadlessOptionInitializationWithNilOpts(t *testing.T) { ne, err := nuclei.NewNucleiEngineCtx( context.Background(), nuclei.EnableHeadlessWithOpts(nil), ) require.NoError(t, err) require.NotNil(t, ne) require.NotNil(t, ne.Logger) defer ne.Close() }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
lib/config.go(1 hunks)lib/multi.go(1 hunks)lib/sdk.go(1 hunks)lib/sdk_private.go(3 hunks)lib/sdk_test.go(1 hunks)pkg/types/types.go(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.go: Format Go code using go fmt
Run static analysis with go vet
Files:
pkg/types/types.golib/sdk_test.golib/config.golib/sdk_private.golib/sdk.golib/multi.go
🧠 Learnings (1)
📚 Learning: 2025-09-05T14:23:55.257Z
Learnt from: CR
Repo: projectdiscovery/nuclei PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-05T14:23:55.257Z
Learning: Applies to pkg/protocols/**/*.go : Each protocol implementation must provide a Request interface with methods Compile(), ExecuteWithResults(), Match(), and Extract()
Applied to files:
lib/config.golib/sdk_private.go
🧬 Code graph analysis (3)
lib/sdk_test.go (2)
lib/sdk.go (1)
NewNucleiEngineCtx(312-332)lib/config.go (2)
EnableHeadlessWithOpts(201-212)HeadlessOpts(189-194)
lib/sdk_private.go (3)
pkg/protocols/headless/engine/engine.go (2)
MustDisableSandbox(132-136)New(37-129)internal/runner/runner.go (1)
New(109-394)pkg/core/engine.go (1)
New(27-34)
lib/multi.go (1)
lib/sdk.go (1)
NucleiEngine(58-95)
🔇 Additional comments (1)
lib/multi.go (1)
91-95: Nice job preserving the logger on the thread-safe path.Setting
Logger: defaultOptions.Loggerkeeps SDK options that log during registration from dereferencing nil. Nice catch.
The panic was caused by attempting to log a sandbox warning before the logger was initialized. RCA: * SDK option funcs were exec'd before logger init. * `EnableHeadlessWithOpts()` attempted to create browser instance & log warnings during the config phase. * `Logger` was only init'd later in `init()` phase. * This caused nil pointer dereference when `MustDisableSandbox()` returned true (root on Linux/Unix or Windows). Changes: * Init `Logger` in `types.DefaultOptions()` to ensure it's always available before any option functions execute. * Init `Logger` field in both `NewNucleiEngineCtx()` and `NewThreadSafeNucleiEngineCtx()` from `defaultOptions.Logger`. * Move browser instance creation from `EnableHeadlessWithOpts()` to the `init()` phase where `Logger` is guaranteed to be available. * Simplify logger sync logic in `init()` to only update if changed by `WithLogger` option. * Add test case to verify headless initialization works without panic. The fix maintains backward compatibility while make sure the logger is always available when needed by any SDK option function. Fixes #6601. Signed-off-by: Dwi Siswanto <[email protected]>
Signed-off-by: Dwi Siswanto <[email protected]>
c725955 to
d085554
Compare
…pts`" let see if this pass flaky test. This reverts commit 63fcb6a.
Signed-off-by: Dwi Siswanto <[email protected]>
…essWithOpts`"" This reverts commit 62b4223.
|
OK, this is weird. The (flaky?) |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 5201 🏁 Script executed: Length of output: 1878 🏁 Script executed: Length of output: 5954 🏁 Script executed: Length of output: 288 🏁 Script executed: Length of output: 3643 🏁 Script executed: Length of output: 1404 🏁 Script executed: Length of output: 13715 🏁 Script executed: Length of output: 253 🏁 Script executed: Length of output: 920 🏁 Script executed: Length of output: 751 🏁 Script executed: Length of output: 12009 🏁 Script executed: Length of output: 11311 🏁 Script executed: Length of output: 868 🏁 Script executed: Length of output: 2619 🏁 Script executed: Length of output: 1402 🏁 Script executed: Length of output: 1376 🏁 Script executed: Length of output: 1266 @dwisiswant0, I've dug into this and found the root cause! The failure isn't directly related to your PR's changes. Here's what's happening: The ProblemThe Why it's flaky on Linux only:
The FixOption 1 (Quick): Increase the timeout for func TestActionNavigate(t *testing.T) {
response := `
<html>
<head>
<title>Nuclei Test Page</title>
</head>
<body>
<h1>Nuclei Test</h1>
</body>
</html>`
actions := []*Action{{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}}, {ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}}}
testHeadlessSimpleResponse(t, response, actions, 60*time.Second, func(page *Page, err error, out ActionData) { // Increased from 20s to 60s
require.Nilf(t, err, "could not run page actions")
require.Equal(t, "Nuclei Test Page", page.Page().MustInfo().Title, "could not navigate correctly")
})
}Option 2 (Better): Skip or shorten headless tests when running with The flakiness existed before your PR but is now more exposed due to the |
Signed-off-by: Dwi Siswanto <[email protected]>
This reverts commit d27cd98.
Mzack9999
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm - Could be that the test might fail due to missing|late browser initialization (previously done with the SDK option?
Proposed changes
Proof
Checklist
Summary by CodeRabbit
Tests
New Features
Chores