Skip to content

feat(js): add wmi, tsch, scmr, and dcom helper modules - #7388

Merged
Mzack9999 merged 4 commits into
devfrom
dwisiswant0/feat/js/add-wmi-tsch-scmr-and-dcom-helper-modules
May 14, 2026
Merged

feat(js): add wmi, tsch, scmr, and dcom helper modules#7388
Mzack9999 merged 4 commits into
devfrom
dwisiswant0/feat/js/add-wmi-tsch-scmr-and-dcom-helper-modules

Conversation

@dwisiswant0

@dwisiswant0 dwisiswant0 commented May 10, 2026

Copy link
Copy Markdown
Member

Proposed changes

Add shared auth, options, result, redaction, and
adapter support for GoExec-backed JavaScript
helpers, including:

  • nuclei/wmi
  • nuclei/tsch
  • nuclei/scmr
  • nuclei/dcom

through generated bindings, and register them with
the JS compiler.

Also make JS generators to skip internal helper
packages and test files.

Closes #6069

Proof

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • New Features

    • JavaScript-accessible Windows execution clients (WMI, DCOM, Task Scheduler, SCMR) with auth constructors, configurable execution options, and unified execution/results surface.
    • GoExec-backed adapter for running modules with structured results, output collection, and credential redaction.
  • Tests

    • New unit and integration tests covering auth validation, option merging, redaction, target normalization, execution flows, and JS integration cases.
  • Chores

    • Bumped module dependencies and added ignore-marker support for module discovery.

Review Change Stack

@auto-assign
auto-assign Bot requested a review from dogancanbakir May 10, 2026 15:21
@neo-by-projectdiscovery-dev

neo-by-projectdiscovery-dev Bot commented May 10, 2026

Copy link
Copy Markdown

Neo - PR Security Review

No security issues found

Hardening Notes
  • Using a Go pseudo-version for an unreleased commit is standard practice during active upstream development; the hash pinning in go.sum prevents tampering.

Comment @pdneo help for available commands. · Open in Neo

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Integrates FalconOps GoExec into Nuclei: adds Auth/ExecutionOptions models, adapter Run boundary with redaction/truncation, a GoExec runner with WMI/TSCH/SCMR/DCOM handlers, Goja JS client bindings, tooling ignore/support in bindgen/tsgen, tests, and go.mod updates.

Changes

GoExec Windows Execution Integration

Layer / File(s) Summary
Compiler & tooling changes
pkg/js/compiler/pool.go, pkg/js/devtools/bindgen/generator.go, pkg/js/devtools/tsgen/cmd/tsgen/main.go, pkg/js/devtools/tsgen/parser.go
Register additional embedded JS libs, add .nuclei-jsgen-ignore handling, parse non-test Go files deterministically, and guard constant extraction.
Authentication & Data Models
pkg/js/libs/goexec/auth.go, pkg/js/libs/goexec/options.go, pkg/js/libs/goexec/errors.go, pkg/js/libs/goexec/result.go, pkg/js/libs/goexec/redact.go, pkg/js/libs/goexec/target.go
Adds Auth constructors, credential validation/secrets extraction, ExecutionOptions defaults and MergeOptions, predefined errors, Result/CleanupResult modelling, redaction utilities, and target normalization helpers.
Adapter Infrastructure
pkg/js/libs/goexec/adapter.go
Defines Runner/Request boundary, Run entrypoint with target normalization, execution-ID handling, error redaction, duration, and output truncation.
GoExecRunner Implementation
pkg/js/libs/goexec/adapter_goexec.go
Implements GoExecRunner.Run: auth/allowlist checks, optional timeout, security/logging context, and dispatch to module handlers (WMI/TSCH/SCMR/DCOM) with DCE/SMB client setup and execution IO/output collection.
JavaScript Protocol Bindings
pkg/js/libs/dcom/dcom.go, pkg/js/libs/scmr/scmr.go, pkg/js/libs/tsch/tsch.go, pkg/js/libs/wmi/wmi.go
Adds Goja-exposed Client types with NewClient(target, auth), SetOptions, protocol methods (MMC/Create/Demand/Command/Proc/Call), and argument/export helpers.
Unit & Integration Tests
pkg/js/libs/goexec/*_test.go, pkg/js/libs/wmi/wmi_test.go, internal/tests/integration/javascript_test.go, internal/tests/integration/goexec_samba_local_test.go
Adds tests for auth validation/MarshalJSON, option merging behavior, redaction, result serialization/truncation, adapter network-policy and error redaction behavior, execution output collection, WMI request mapping, and new JS integration templates (including SMB readiness check and Samba integration gating).
Module Dependencies
go.mod
Adds github.com/FalconOpsLLC/goexec and github.com/RedTeamPentesting/adauth as direct deps; bumps github.com/rs/zerolog; updates indirect github.com/geoffgarside/ber and adds github.com/spf13/pflag and software.sslmate.com/src/go-pkcs12.

Sequence Diagram(s)

sequenceDiagram
  participant JS as Goja Runtime
  participant Adapter as goexec.Run
  participant Runner as GoExecRunner
  participant DCE as DCE/SMB client
  participant Module as Module Handler

  JS->>Adapter: Run(ctx, Request)
  Adapter->>Adapter: normalizeTarget, WithExecutionID
  Adapter->>Runner: Run(ctx, req)
  Runner->>Runner: validate auth & allowlist
  Runner->>DCE: build dce/smb client (if needed)
  Runner->>Module: dispatch to runWMI/runTSCH/runSCMR/runDCOM
  Module-->>Runner: execution result / output
  Runner-->>Adapter: Result (redacted on error)
  Adapter->>JS: Public() result (truncated/redacted)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

"🐰 I tunneled through Go and JS with care,
Auths kept secret, options set fair,
Modules and bindings now chatter and run,
Results come back tidy — redacted and done,
Hop on: Windows help has joined the fun."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(js): add wmi, tsch, scmr, and dcom helper modules' accurately describes the main change - adding four JavaScript helper modules for Windows management protocols.
Linked Issues check ✅ Passed The PR implements Windows remote management capabilities (WMI, TSCH, SCMR, DCOM) with multiple authentication methods (password, NT hash, Kerberos, AES key, PFX, ccache), supporting #6069's objectives for authentication and remote execution.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to adding Windows helper modules, authentication, execution adapters, testing, and supporting infrastructure. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dwisiswant0/feat/js/add-wmi-tsch-scmr-and-dcom-helper-modules

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

@dwisiswant0 dwisiswant0 changed the title Dwisiswant0/feat/js/add wmi tsch scmr and dcom helper modules feat(js): add wmi, tsch, scmr, and dcom helper modules May 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
pkg/js/libs/goexec/options_test.go (1)

5-28: ⚡ Quick win

Add coverage for struct-based merge semantics.

This test only covers the JavaScript map path. Please add a regression test for MergeOptions(..., ExecutionOptions{...}), especially boolean override behavior.

✅ Suggested test addition
 func TestMergeOptionsFromJavaScriptMap(t *testing.T) {
@@
 }
+
+func TestMergeOptionsFromStructBoolOverrides(t *testing.T) {
+	base := DefaultExecutionOptions()
+	base.Output = true
+	base.NoDeleteOutput = true
+	base.NoSign = true
+	base.NoSeal = true
+	base.EPM = true
+
+	opts := MergeOptions(base, ExecutionOptions{
+		Output:         false,
+		NoDeleteOutput: false,
+		NoSign:         false,
+		NoSeal:         false,
+		EPM:            false,
+	})
+
+	if opts.Output || opts.NoDeleteOutput || opts.NoSign || opts.NoSeal || opts.EPM {
+		t.Fatalf("expected false bool overrides to be applied: %#v", opts)
+	}
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/libs/goexec/options_test.go` around lines 5 - 28, The test file only
exercises MergeOptions with a map input; add a new unit test that covers merging
from a struct to verify struct-based semantics and boolean override behavior:
create a test (e.g., TestMergeOptionsFromStruct) that calls
MergeOptions(DefaultExecutionOptions(), ExecutionOptions{Timeout:10,
Output:true, OutputMethod:"SMB", OutputTimeout:3, NoDeleteOutput:true,
Directory:`C:\Temp`, Endpoint:"ncacn_np:[svcctl]", EPMFilter:"ncacn_ip_tcp:",
NoSign:true, NoSeal:true, MaxOutputSize:42}) and assert fields (Timeout, Output,
OutputMethod normalized, OutputTimeout, NoDeleteOutput, Directory, Endpoint,
EPMFilter, NoSign, NoSeal, MaxOutputSize) match expected values, specifically
checking booleans override correctly; use the same assertion style as
TestMergeOptionsFromJavaScriptMap to keep consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/js/devtools/bindgen/generator.go`:
- Line 118: Remove the leftover debug print by deleting the
fmt.Println(directory) call from pkg/js/devtools/bindgen/generator.go (the stray
print referencing the variable "directory"); ensure no other debugging prints
remain in the surrounding function so the generator’s normal output/return
behavior is unchanged.

In `@pkg/js/libs/goexec/adapter_goexec.go`:
- Around line 295-316: The code unconditionally sets result.Cleanup.Attempted =
true even when output.NoDelete is set, which incorrectly reports cleanup as
attempted; change the logic so Attempted is true only when the output will
actually be deleted (i.e., when !output.NoDelete or when the provider's
DeleteOutputFile would be true). Locate the block that constructs output
(output.NoDelete, output.RemotePath, gosmb.OutputFileFetcher with
DeleteOutputFile) and update the assignment to result.Cleanup.Attempted to be
conditional (e.g., set Attempted = !output.NoDelete or based on the
provider.DeleteOutputFile value); ensure result.Cleanup.Artifacts still contains
output.RemotePath if you want the artifact listed regardless of deletion intent.
- Around line 363-375: The endpointAllowed function is currently parsing the
text inside "[]" instead of extracting the network host portion, allowing
bypasses (e.g., ncacn_ip_tcp:10.0.0.5[135] validated against "135"). Fix by
extracting the host portion before the "[" and after the last ":" (i.e., trim at
'[' first, then take substring after last ':' if present) and use that host
value for the empty/leading-backslash check and the call to
protocolstate.IsHostAllowed(executionID(ctx), host); keep function name
endpointAllowed and leave executionID and protocolstate.IsHostAllowed calls
unchanged.

In `@pkg/js/libs/goexec/adapter.go`:
- Around line 44-53: SetRunnerForTesting currently allows setting defaultRunner
to nil which causes a panic when Run is later called; update SetRunnerForTesting
to guard against a nil runner by rejecting nil assignments or substituting a
safe fallback (e.g., keep previous/defaultRunner) so defaultRunner is never set
to nil. Specifically, in SetRunnerForTesting(lock/unlock using runnerMu) check
the incoming runner (Runner) for nil and if nil do not overwrite defaultRunner
(or set to a known no-op Runner), and ensure the returned restore closure still
reinstates the original previous value safely; update references to
defaultRunner, SetRunnerForTesting, runnerMu, and Runner to implement this
guard.

In `@pkg/js/libs/goexec/auth.go`:
- Around line 155-160: In validate(), stop allowing multiple credential sources
when Kerberos is true: remove the special-case that exempts a.kerberos from the
missing/duplicate checks and always enforce that selected == 1; specifically
update the switch/conditions around selected, a.kerberos to return
ErrMissingAuth when selected == 0 and ErrMultipleCredentialModes when selected >
1 regardless of a.kerberos (refer to the validate() function and symbols
selected, a.kerberos, ErrMissingAuth, ErrMultipleCredentialModes).

In `@pkg/js/libs/goexec/options.go`:
- Around line 113-149: mergeStruct currently only copies boolean fields when
opts.* is true, so callers cannot clear an already-true base value; update
mergeStruct to always assign the boolean fields from opts to base (i.e., remove
the if checks) for the relevant ExecutionOptions fields so explicit false
overrides are applied. Specifically, change the handling of Output,
NoDeleteOutput, EPM, NoSign, and NoSeal in mergeStruct to unconditionally set
base.Output = opts.Output, base.NoDeleteOutput = opts.NoDeleteOutput, base.EPM =
opts.EPM, base.NoSign = opts.NoSign, and base.NoSeal = opts.NoSeal (leave
non-boolean checks like Timeout/Proxy/OutputMethod/etc. as-is).

In `@pkg/js/libs/goexec/target.go`:
- Around line 15-32: The target normalization currently trims bracketed IPv6
addresses before calling net.SplitHostPort and allows url.Parse to set an empty
host; fix by (1) after url.Parse in the block that checks
strings.Contains(target, "://") return ErrMissingTarget if parsed.Host == "" and
otherwise set target = parsed.Host, (2) remove or move the strings.Trim(target,
"[]") call so that net.SplitHostPort is called on the original target first
(this preserves inputs like "[::1]:5985"), and only trim surrounding brackets as
a fallback when SplitHostPort returns an error and the target contains no colon;
update the logic around net.SplitHostPort, target trimming, and error returns
(references: url.Parse usage, parsed.Host, net.SplitHostPort, ErrMissingTarget,
strings.Trim).

---

Nitpick comments:
In `@pkg/js/libs/goexec/options_test.go`:
- Around line 5-28: The test file only exercises MergeOptions with a map input;
add a new unit test that covers merging from a struct to verify struct-based
semantics and boolean override behavior: create a test (e.g.,
TestMergeOptionsFromStruct) that calls MergeOptions(DefaultExecutionOptions(),
ExecutionOptions{Timeout:10, Output:true, OutputMethod:"SMB", OutputTimeout:3,
NoDeleteOutput:true, Directory:`C:\Temp`, Endpoint:"ncacn_np:[svcctl]",
EPMFilter:"ncacn_ip_tcp:", NoSign:true, NoSeal:true, MaxOutputSize:42}) and
assert fields (Timeout, Output, OutputMethod normalized, OutputTimeout,
NoDeleteOutput, Directory, Endpoint, EPMFilter, NoSign, NoSeal, MaxOutputSize)
match expected values, specifically checking booleans override correctly; use
the same assertion style as TestMergeOptionsFromJavaScriptMap to keep
consistency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 20ab7e09-819a-4426-9dc7-9d4314bccb6e

📥 Commits

Reviewing files that changed from the base of the PR and between 2da8669 and 403c847.

⛔ Files ignored due to path filters (13)
  • go.sum is excluded by !**/*.sum
  • internal/tests/integration/testdata/protocols/javascript/goexec-modules.yaml is excluded by !**/*.yaml
  • internal/tests/integration/testdata/protocols/javascript/goexec-redaction.yaml is excluded by !**/*.yaml
  • internal/tests/integration/testdata/protocols/javascript/wmi-command.yaml is excluded by !**/*.yaml
  • pkg/js/generated/go/libdcom/dcom.go is excluded by !**/generated/**
  • pkg/js/generated/go/libscmr/scmr.go is excluded by !**/generated/**
  • pkg/js/generated/go/libtsch/tsch.go is excluded by !**/generated/**
  • pkg/js/generated/go/libwmi/wmi.go is excluded by !**/generated/**
  • pkg/js/generated/ts/dcom.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/index.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/scmr.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/tsch.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/wmi.ts is excluded by !**/generated/**
📒 Files selected for processing (25)
  • go.mod
  • internal/tests/integration/javascript_test.go
  • pkg/js/compiler/pool.go
  • pkg/js/devtools/bindgen/generator.go
  • pkg/js/devtools/tsgen/cmd/tsgen/main.go
  • pkg/js/devtools/tsgen/parser.go
  • pkg/js/libs/dcom/dcom.go
  • pkg/js/libs/goexec/.nuclei-jsgen-ignore
  • pkg/js/libs/goexec/adapter.go
  • pkg/js/libs/goexec/adapter_goexec.go
  • pkg/js/libs/goexec/adapter_test.go
  • pkg/js/libs/goexec/auth.go
  • pkg/js/libs/goexec/auth_test.go
  • pkg/js/libs/goexec/errors.go
  • pkg/js/libs/goexec/options.go
  • pkg/js/libs/goexec/options_test.go
  • pkg/js/libs/goexec/redact.go
  • pkg/js/libs/goexec/redact_test.go
  • pkg/js/libs/goexec/result.go
  • pkg/js/libs/goexec/result_test.go
  • pkg/js/libs/goexec/target.go
  • pkg/js/libs/scmr/scmr.go
  • pkg/js/libs/tsch/tsch.go
  • pkg/js/libs/wmi/wmi.go
  • pkg/js/libs/wmi/wmi_test.go

Comment thread pkg/js/devtools/bindgen/generator.go
Comment on lines +295 to +316
var out bytes.Buffer
output := &upstream.ExecutionOutput{
NoDelete: req.Options.NoDeleteOutput,
RemotePath: `C:\Windows\Temp\` + uuid.NewString(),
Timeout: time.Duration(req.Options.OutputTimeout) * time.Second,
Writer: nopWriteCloser{Writer: &out},
}
smbClient, err := r.smbClient(ctx, req)
if err != nil {
return nil, err
}
output.Provider = &gosmb.OutputFileFetcher{
Client: smbClient,
Share: `ADMIN$`,
SharePath: `C:\Windows`,
File: output.RemotePath,
DeleteOutputFile: !output.NoDelete,
}
execIO.Output = output
result.OutputMethod = DefaultOutputMethod
result.Cleanup.Attempted = true
result.Cleanup.Artifacts = []string{output.RemotePath}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't report cleanup as attempted when NoDeleteOutput keeps the artifact.

Line 315 sets result.Cleanup.Attempted = true even when DeleteOutputFile is disabled. That makes the public result claim cleanup ran although the remote output file was intentionally left behind.

Suggested fix
 	execIO.Output = output
 	result.OutputMethod = DefaultOutputMethod
-	result.Cleanup.Attempted = true
+	result.Cleanup.Attempted = !output.NoDelete
 	result.Cleanup.Artifacts = []string{output.RemotePath}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var out bytes.Buffer
output := &upstream.ExecutionOutput{
NoDelete: req.Options.NoDeleteOutput,
RemotePath: `C:\Windows\Temp\` + uuid.NewString(),
Timeout: time.Duration(req.Options.OutputTimeout) * time.Second,
Writer: nopWriteCloser{Writer: &out},
}
smbClient, err := r.smbClient(ctx, req)
if err != nil {
return nil, err
}
output.Provider = &gosmb.OutputFileFetcher{
Client: smbClient,
Share: `ADMIN$`,
SharePath: `C:\Windows`,
File: output.RemotePath,
DeleteOutputFile: !output.NoDelete,
}
execIO.Output = output
result.OutputMethod = DefaultOutputMethod
result.Cleanup.Attempted = true
result.Cleanup.Artifacts = []string{output.RemotePath}
var out bytes.Buffer
output := &upstream.ExecutionOutput{
NoDelete: req.Options.NoDeleteOutput,
RemotePath: `C:\Windows\Temp\` + uuid.NewString(),
Timeout: time.Duration(req.Options.OutputTimeout) * time.Second,
Writer: nopWriteCloser{Writer: &out},
}
smbClient, err := r.smbClient(ctx, req)
if err != nil {
return nil, err
}
output.Provider = &gosmb.OutputFileFetcher{
Client: smbClient,
Share: `ADMIN$`,
SharePath: `C:\Windows`,
File: output.RemotePath,
DeleteOutputFile: !output.NoDelete,
}
execIO.Output = output
result.OutputMethod = DefaultOutputMethod
result.Cleanup.Attempted = !output.NoDelete
result.Cleanup.Artifacts = []string{output.RemotePath}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/libs/goexec/adapter_goexec.go` around lines 295 - 316, The code
unconditionally sets result.Cleanup.Attempted = true even when output.NoDelete
is set, which incorrectly reports cleanup as attempted; change the logic so
Attempted is true only when the output will actually be deleted (i.e., when
!output.NoDelete or when the provider's DeleteOutputFile would be true). Locate
the block that constructs output (output.NoDelete, output.RemotePath,
gosmb.OutputFileFetcher with DeleteOutputFile) and update the assignment to
result.Cleanup.Attempted to be conditional (e.g., set Attempted =
!output.NoDelete or based on the provider.DeleteOutputFile value); ensure
result.Cleanup.Artifacts still contains output.RemotePath if you want the
artifact listed regardless of deletion intent.

Comment thread pkg/js/libs/goexec/adapter_goexec.go Outdated
Comment on lines +44 to +53
func SetRunnerForTesting(runner Runner) func() {
runnerMu.Lock()
previous := defaultRunner
defaultRunner = runner
runnerMu.Unlock()
return func() {
runnerMu.Lock()
defaultRunner = previous
runnerMu.Unlock()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against nil runner assignment in test hook.

SetRunnerForTesting(nil) makes defaultRunner nil, and Run then panics on runner.Run(...). Add a guard or fallback to keep the adapter safe under test setup mistakes.

Proposed fix
 func SetRunnerForTesting(runner Runner) func() {
+	if runner == nil {
+		runner = &GoExecRunner{}
+	}
 	runnerMu.Lock()
 	previous := defaultRunner
 	defaultRunner = runner
 	runnerMu.Unlock()
 	return func() {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/libs/goexec/adapter.go` around lines 44 - 53, SetRunnerForTesting
currently allows setting defaultRunner to nil which causes a panic when Run is
later called; update SetRunnerForTesting to guard against a nil runner by
rejecting nil assignments or substituting a safe fallback (e.g., keep
previous/defaultRunner) so defaultRunner is never set to nil. Specifically, in
SetRunnerForTesting(lock/unlock using runnerMu) check the incoming runner
(Runner) for nil and if nil do not overwrite defaultRunner (or set to a known
no-op Runner), and ensure the returned restore closure still reinstates the
original previous value safely; update references to defaultRunner,
SetRunnerForTesting, runnerMu, and Runner to implement this guard.

Comment thread pkg/js/libs/goexec/auth.go
Comment on lines +113 to +149
func mergeStruct(base, opts ExecutionOptions) ExecutionOptions {
if opts.Timeout != 0 {
base.Timeout = opts.Timeout
}
if opts.Proxy != "" {
base.Proxy = opts.Proxy
}
if opts.Output {
base.Output = opts.Output
}
if opts.OutputMethod != "" {
base.OutputMethod = strings.ToLower(opts.OutputMethod)
}
if opts.OutputTimeout != 0 {
base.OutputTimeout = opts.OutputTimeout
}
if opts.NoDeleteOutput {
base.NoDeleteOutput = opts.NoDeleteOutput
}
if opts.Directory != "" {
base.Directory = opts.Directory
}
if opts.Endpoint != "" {
base.Endpoint = opts.Endpoint
}
if opts.EPM {
base.EPM = opts.EPM
}
if opts.EPMFilter != "" {
base.EPMFilter = opts.EPMFilter
}
if opts.NoSign {
base.NoSign = opts.NoSign
}
if opts.NoSeal {
base.NoSeal = opts.NoSeal
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

mergeStruct cannot apply explicit false boolean overrides.

Lines 120, 129, 138, 144, and 147 only copy bools when true, so typed struct input cannot clear an already-enabled base option.

🐛 Proposed fix
 func mergeStruct(base, opts ExecutionOptions) ExecutionOptions {
@@
-	if opts.Output {
-		base.Output = opts.Output
-	}
+	base.Output = opts.Output
@@
-	if opts.NoDeleteOutput {
-		base.NoDeleteOutput = opts.NoDeleteOutput
-	}
+	base.NoDeleteOutput = opts.NoDeleteOutput
@@
-	if opts.EPM {
-		base.EPM = opts.EPM
-	}
+	base.EPM = opts.EPM
@@
-	if opts.NoSign {
-		base.NoSign = opts.NoSign
-	}
+	base.NoSign = opts.NoSign
@@
-	if opts.NoSeal {
-		base.NoSeal = opts.NoSeal
-	}
+	base.NoSeal = opts.NoSeal
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func mergeStruct(base, opts ExecutionOptions) ExecutionOptions {
if opts.Timeout != 0 {
base.Timeout = opts.Timeout
}
if opts.Proxy != "" {
base.Proxy = opts.Proxy
}
if opts.Output {
base.Output = opts.Output
}
if opts.OutputMethod != "" {
base.OutputMethod = strings.ToLower(opts.OutputMethod)
}
if opts.OutputTimeout != 0 {
base.OutputTimeout = opts.OutputTimeout
}
if opts.NoDeleteOutput {
base.NoDeleteOutput = opts.NoDeleteOutput
}
if opts.Directory != "" {
base.Directory = opts.Directory
}
if opts.Endpoint != "" {
base.Endpoint = opts.Endpoint
}
if opts.EPM {
base.EPM = opts.EPM
}
if opts.EPMFilter != "" {
base.EPMFilter = opts.EPMFilter
}
if opts.NoSign {
base.NoSign = opts.NoSign
}
if opts.NoSeal {
base.NoSeal = opts.NoSeal
}
func mergeStruct(base, opts ExecutionOptions) ExecutionOptions {
if opts.Timeout != 0 {
base.Timeout = opts.Timeout
}
if opts.Proxy != "" {
base.Proxy = opts.Proxy
}
base.Output = opts.Output
if opts.OutputMethod != "" {
base.OutputMethod = strings.ToLower(opts.OutputMethod)
}
if opts.OutputTimeout != 0 {
base.OutputTimeout = opts.OutputTimeout
}
base.NoDeleteOutput = opts.NoDeleteOutput
if opts.Directory != "" {
base.Directory = opts.Directory
}
if opts.Endpoint != "" {
base.Endpoint = opts.Endpoint
}
base.EPM = opts.EPM
if opts.EPMFilter != "" {
base.EPMFilter = opts.EPMFilter
}
base.NoSign = opts.NoSign
base.NoSeal = opts.NoSeal
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/libs/goexec/options.go` around lines 113 - 149, mergeStruct currently
only copies boolean fields when opts.* is true, so callers cannot clear an
already-true base value; update mergeStruct to always assign the boolean fields
from opts to base (i.e., remove the if checks) for the relevant ExecutionOptions
fields so explicit false overrides are applied. Specifically, change the
handling of Output, NoDeleteOutput, EPM, NoSign, and NoSeal in mergeStruct to
unconditionally set base.Output = opts.Output, base.NoDeleteOutput =
opts.NoDeleteOutput, base.EPM = opts.EPM, base.NoSign = opts.NoSign, and
base.NoSeal = opts.NoSeal (leave non-boolean checks like
Timeout/Proxy/OutputMethod/etc. as-is).

Comment on lines +15 to +32
if strings.Contains(target, "://") {
parsed, err := url.Parse(target)
if err != nil {
return "", fmt.Errorf("parse target: %w", err)
}
target = parsed.Host
}
target = strings.Trim(target, "[]")
if host, port, err := net.SplitHostPort(target); err == nil {
if host == "" {
return "", ErrMissingTarget
}
if port == "" {
return host, nil
}
return net.JoinHostPort(host, port), nil
}
return target, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve bracketed IPv6 host:port during normalization.

Line 22 strips brackets before net.SplitHostPort, which breaks valid inputs like [::1]:5985 into an invalid token. Also, Line 20 can set an empty host from URL parsing and return "" without ErrMissingTarget.

🐛 Proposed fix
 func normalizeTarget(target string) (string, error) {
 	target = strings.TrimSpace(target)
 	if target == "" {
 		return "", ErrMissingTarget
 	}
 	if strings.Contains(target, "://") {
 		parsed, err := url.Parse(target)
 		if err != nil {
 			return "", fmt.Errorf("parse target: %w", err)
 		}
 		target = parsed.Host
+		if target == "" {
+			return "", ErrMissingTarget
+		}
 	}
-	target = strings.Trim(target, "[]")
 	if host, port, err := net.SplitHostPort(target); err == nil {
 		if host == "" {
 			return "", ErrMissingTarget
 		}
 		if port == "" {
 			return host, nil
 		}
 		return net.JoinHostPort(host, port), nil
 	}
+	if strings.HasPrefix(target, "[") && strings.HasSuffix(target, "]") {
+		target = strings.TrimPrefix(strings.TrimSuffix(target, "]"), "[")
+	}
+	if target == "" {
+		return "", ErrMissingTarget
+	}
 	return target, nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if strings.Contains(target, "://") {
parsed, err := url.Parse(target)
if err != nil {
return "", fmt.Errorf("parse target: %w", err)
}
target = parsed.Host
}
target = strings.Trim(target, "[]")
if host, port, err := net.SplitHostPort(target); err == nil {
if host == "" {
return "", ErrMissingTarget
}
if port == "" {
return host, nil
}
return net.JoinHostPort(host, port), nil
}
return target, nil
if strings.Contains(target, "://") {
parsed, err := url.Parse(target)
if err != nil {
return "", fmt.Errorf("parse target: %w", err)
}
target = parsed.Host
if target == "" {
return "", ErrMissingTarget
}
}
if host, port, err := net.SplitHostPort(target); err == nil {
if host == "" {
return "", ErrMissingTarget
}
if port == "" {
return host, nil
}
return net.JoinHostPort(host, port), nil
}
if strings.HasPrefix(target, "[") && strings.HasSuffix(target, "]") {
target = strings.TrimPrefix(strings.TrimSuffix(target, "]"), "[")
}
if target == "" {
return "", ErrMissingTarget
}
return target, nil
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/libs/goexec/target.go` around lines 15 - 32, The target normalization
currently trims bracketed IPv6 addresses before calling net.SplitHostPort and
allows url.Parse to set an empty host; fix by (1) after url.Parse in the block
that checks strings.Contains(target, "://") return ErrMissingTarget if
parsed.Host == "" and otherwise set target = parsed.Host, (2) remove or move the
strings.Trim(target, "[]") call so that net.SplitHostPort is called on the
original target first (this preserves inputs like "[::1]:5985"), and only trim
surrounding brackets as a fallback when SplitHostPort returns an error and the
target contains no colon; update the logic around net.SplitHostPort, target
trimming, and error returns (references: url.Parse usage, parsed.Host,
net.SplitHostPort, ErrMissingTarget, strings.Trim).

Comment thread pkg/js/libs/goexec/adapter_goexec.go Outdated
Add shared auth, options, result, redaction, and
adapter support for GoExec-backed JavaScript
helpers, including:

* `nuclei/wmi`
* `nuclei/tsch`
* `nuclei/scmr`
* `nuclei/dcom`

through generated bindings, and register them with
the JS compiler.

Also make JS generators to skip internal helper
packages and test files.

Signed-off-by: Dwi Siswanto <git@dw1.io>
Signed-off-by: Dwi Siswanto <git@dw1.io>
@dwisiswant0
dwisiswant0 force-pushed the dwisiswant0/feat/js/add-wmi-tsch-scmr-and-dcom-helper-modules branch from 403c847 to ce50479 Compare May 10, 2026 20:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/js/devtools/bindgen/generator.go (1)

297-307: ⚡ Quick win

Handle grouped exported ValueSpecs instead of only index 0.

This still records only spec.Names[0]/spec.Values[0], so declarations like const A, B = 1, 2 will silently drop B from the generated bindings.

Proposed fix
 		case *ast.ValueSpec:
-			if !spec.Names[0].IsExported() {
-				continue
-			}
-			data.PackageVars[spec.Names[0].Name] = spec.Names[0].Name
-			if len(spec.Values) == 0 {
-				continue
-			}
-			if value, ok := spec.Values[0].(*ast.BasicLit); ok {
-				data.PackageVarsValues[spec.Names[0].Name] = value.Value
-			}
+			for i, name := range spec.Names {
+				if !name.IsExported() {
+					continue
+				}
+				data.PackageVars[name.Name] = name.Name
+				if i >= len(spec.Values) {
+					continue
+				}
+				if value, ok := spec.Values[i].(*ast.BasicLit); ok {
+					data.PackageVarsValues[name.Name] = value.Value
+				}
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/devtools/bindgen/generator.go` around lines 297 - 307, The code only
records spec.Names[0] and spec.Values[0] for an ast.ValueSpec, dropping
subsequent names in a grouped declaration (e.g., const A, B = 1, 2); update the
handling in the ast.ValueSpec case to iterate over all spec.Names, check each
name's IsExported() and add each exported name to data.PackageVars, and for
values map the corresponding spec.Values entry to data.PackageVarsValues when
present and the value is an *ast.BasicLit (use the same index as the name); if
there are fewer spec.Values than names, skip value mapping for those names but
still record the name if exported. Ensure you reference ast.ValueSpec,
spec.Names, spec.Values, data.PackageVars and data.PackageVarsValues when making
the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/js/devtools/bindgen/generator.go`:
- Around line 297-307: The code only records spec.Names[0] and spec.Values[0]
for an ast.ValueSpec, dropping subsequent names in a grouped declaration (e.g.,
const A, B = 1, 2); update the handling in the ast.ValueSpec case to iterate
over all spec.Names, check each name's IsExported() and add each exported name
to data.PackageVars, and for values map the corresponding spec.Values entry to
data.PackageVarsValues when present and the value is an *ast.BasicLit (use the
same index as the name); if there are fewer spec.Values than names, skip value
mapping for those names but still record the name if exported. Ensure you
reference ast.ValueSpec, spec.Names, spec.Values, data.PackageVars and
data.PackageVarsValues when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 95235ecb-c2fc-4f4a-8a42-e7f8fc948796

📥 Commits

Reviewing files that changed from the base of the PR and between 403c847 and ce50479.

⛔ Files ignored due to path filters (13)
  • go.sum is excluded by !**/*.sum
  • internal/tests/integration/testdata/protocols/javascript/goexec-modules.yaml is excluded by !**/*.yaml
  • internal/tests/integration/testdata/protocols/javascript/goexec-redaction.yaml is excluded by !**/*.yaml
  • internal/tests/integration/testdata/protocols/javascript/wmi-command.yaml is excluded by !**/*.yaml
  • pkg/js/generated/go/libdcom/dcom.go is excluded by !**/generated/**
  • pkg/js/generated/go/libscmr/scmr.go is excluded by !**/generated/**
  • pkg/js/generated/go/libtsch/tsch.go is excluded by !**/generated/**
  • pkg/js/generated/go/libwmi/wmi.go is excluded by !**/generated/**
  • pkg/js/generated/ts/dcom.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/index.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/scmr.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/tsch.ts is excluded by !**/generated/**
  • pkg/js/generated/ts/wmi.ts is excluded by !**/generated/**
📒 Files selected for processing (26)
  • go.mod
  • internal/tests/integration/javascript_test.go
  • pkg/js/compiler/pool.go
  • pkg/js/devtools/bindgen/generator.go
  • pkg/js/devtools/tsgen/cmd/tsgen/main.go
  • pkg/js/devtools/tsgen/parser.go
  • pkg/js/libs/dcom/dcom.go
  • pkg/js/libs/goexec/.nuclei-jsgen-ignore
  • pkg/js/libs/goexec/adapter.go
  • pkg/js/libs/goexec/adapter_goexec.go
  • pkg/js/libs/goexec/adapter_test.go
  • pkg/js/libs/goexec/auth.go
  • pkg/js/libs/goexec/auth_test.go
  • pkg/js/libs/goexec/errors.go
  • pkg/js/libs/goexec/options.go
  • pkg/js/libs/goexec/options_test.go
  • pkg/js/libs/goexec/redact.go
  • pkg/js/libs/goexec/redact_test.go
  • pkg/js/libs/goexec/result.go
  • pkg/js/libs/goexec/result_test.go
  • pkg/js/libs/goexec/target.go
  • pkg/js/libs/goexec/target_test.go
  • pkg/js/libs/scmr/scmr.go
  • pkg/js/libs/tsch/tsch.go
  • pkg/js/libs/wmi/wmi.go
  • pkg/js/libs/wmi/wmi_test.go
✅ Files skipped from review due to trivial changes (5)
  • pkg/js/libs/goexec/.nuclei-jsgen-ignore
  • pkg/js/libs/goexec/redact.go
  • pkg/js/libs/goexec/result_test.go
  • pkg/js/libs/tsch/tsch.go
  • pkg/js/libs/goexec/auth_test.go
🚧 Files skipped from review as they are similar to previous changes (15)
  • pkg/js/libs/goexec/errors.go
  • pkg/js/devtools/tsgen/cmd/tsgen/main.go
  • pkg/js/libs/goexec/target.go
  • internal/tests/integration/javascript_test.go
  • pkg/js/devtools/tsgen/parser.go
  • pkg/js/libs/scmr/scmr.go
  • pkg/js/libs/goexec/auth.go
  • pkg/js/libs/wmi/wmi.go
  • pkg/js/libs/goexec/options_test.go
  • pkg/js/libs/goexec/options.go
  • pkg/js/libs/dcom/dcom.go
  • pkg/js/libs/wmi/wmi_test.go
  • pkg/js/libs/goexec/adapter.go
  • go.mod
  • pkg/js/compiler/pool.go

@dwisiswant0

dwisiswant0 commented May 10, 2026

Copy link
Copy Markdown
Member Author

Blocker:

…260511073005-3d18e8a5a687

Signed-off-by: Dwi Siswanto <git@dw1.io>
@ehsandeep
ehsandeep requested a review from Mzack9999 May 11, 2026 19:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/tests/integration/javascript_test.go`:
- Around line 305-321: newGoExecCloseListener currently panics on listen/address
errors; change its signature to newGoExecCloseListener() (*goExecCloseListener,
error) and replace panic(fmt.Errorf(...)) and the other panic with returning nil
and the wrapped error, constructing and returning the listener struct on
success. Update any callers (e.g., places in Execute paths that call
newGoExecCloseListener) to handle the error, return it up the stack, and only
defer g.Close() after the listener is successfully created; reference the
goExecCloseListener type and the newGoExecCloseListener function to find all
uses to update.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f636c5c-3f4a-493c-aec0-9f709fc34675

📥 Commits

Reviewing files that changed from the base of the PR and between 940ca60 and 5675f51.

⛔ Files ignored due to path filters (3)
  • internal/tests/integration/testdata/protocols/javascript/goexec-modules.yaml is excluded by !**/*.yaml
  • internal/tests/integration/testdata/protocols/javascript/goexec-redaction.yaml is excluded by !**/*.yaml
  • internal/tests/integration/testdata/protocols/javascript/goexec-samba-ntlm.yaml is excluded by !**/*.yaml
📒 Files selected for processing (6)
  • internal/tests/integration/goexec_samba_local_test.go
  • internal/tests/integration/javascript_test.go
  • pkg/js/libs/goexec/auth.go
  • pkg/js/libs/goexec/auth_test.go
  • pkg/js/libs/goexec/options.go
  • pkg/js/libs/goexec/options_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/js/libs/goexec/auth_test.go
  • pkg/js/libs/goexec/auth.go
  • pkg/js/libs/goexec/options.go

Comment on lines +305 to +321
func newGoExecCloseListener() *goExecCloseListener {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(fmt.Errorf("goexec listener: %w", err))
}
host, port, err := net.SplitHostPort(ln.Addr().String())
if err != nil {
_ = ln.Close()
panic(fmt.Errorf("goexec listener addr: %w", err))
}
g := &goExecCloseListener{
listener: ln,
host: host,
binding: fmt.Sprintf("ncacn_ip_tcp:%s[%s]", host, port),
}
go g.serve()
return g

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return an error here instead of panicking.

newGoExecCloseListener is used from Execute paths that already return error, so panicking on listener setup failure aborts the entire integration package instead of failing just the current case. Please make this constructor return (*goExecCloseListener, error) and propagate the error from the callers.

Proposed direction
-func newGoExecCloseListener() *goExecCloseListener {
+func newGoExecCloseListener() (*goExecCloseListener, error) {
 	ln, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
-		panic(fmt.Errorf("goexec listener: %w", err))
+		return nil, fmt.Errorf("goexec listener: %w", err)
 	}
 	host, port, err := net.SplitHostPort(ln.Addr().String())
 	if err != nil {
 		_ = ln.Close()
-		panic(fmt.Errorf("goexec listener addr: %w", err))
+		return nil, fmt.Errorf("goexec listener addr: %w", err)
 	}
 	g := &goExecCloseListener{
 		listener: ln,
 		host:     host,
 		binding:  fmt.Sprintf("ncacn_ip_tcp:%s[%s]", host, port),
 	}
 	go g.serve()
-	return g
+	return g, nil
 }

Callers would then handle the returned error before deferring Close().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tests/integration/javascript_test.go` around lines 305 - 321,
newGoExecCloseListener currently panics on listen/address errors; change its
signature to newGoExecCloseListener() (*goExecCloseListener, error) and replace
panic(fmt.Errorf(...)) and the other panic with returning nil and the wrapped
error, constructing and returning the listener struct on success. Update any
callers (e.g., places in Execute paths that call newGoExecCloseListener) to
handle the error, return it up the stack, and only defer g.Close() after the
listener is successfully created; reference the goExecCloseListener type and the
newGoExecCloseListener function to find all uses to update.

@Mzack9999
Mzack9999 merged commit ce28f20 into dev May 14, 2026
19 checks passed
@Mzack9999
Mzack9999 deleted the dwisiswant0/feat/js/add-wmi-tsch-scmr-and-dcom-helper-modules branch May 14, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] ...WinRM (Windows Remote Management)

2 participants