Disable JS debug tracers by default with native tracer allowlist#3718
Disable JS debug tracers by default with native tracer allowlist#3718masih wants to merge 3 commits into
Conversation
Add EVM RPC configuration for trace_allowed_tracers and trace_allow_js_tracers. The native tracer allowlist is validated at startup and remains native-only, while JavaScript tracer execution requires an explicit separate opt-in. Enforce the tracer policy across debug_traceCall, debug_traceTransaction, debug_traceBlockByNumber, debug_traceBlockByHash, and debug_traceTransactionProfile. Validate muxTracer nested configs as well so request-supplied JavaScript cannot bypass the top-level check through nested tracer names. Default the allowlist to the registered native tracer names and keep JS tracers disabled in generated and Docker configs. Add focused tests for config parsing, native allowlist behavior, JS opt-in behavior, and muxTracer nesting.
PR SummaryHigh Risk Overview
Docker Reviewed by Cursor Bugbot for commit 4011e43. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3718 +/- ##
==========================================
- Coverage 59.32% 58.38% -0.94%
==========================================
Files 2274 2187 -87
Lines 188703 178975 -9728
==========================================
- Hits 111939 104490 -7449
+ Misses 66691 65206 -1485
+ Partials 10073 9279 -794
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
A well-scoped security hardening PR that disables request-supplied JavaScript debug tracers by default and enforces a native-only tracer allowlist (validated at startup and per-request, including nested muxTracer configs) across all five config-accepting debug_trace* endpoints. The implementation is correct and well-tested; I have only minor non-blocking notes.
Findings: 0 blocking | 4 non-blocking | 1 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Coverage looks complete: all five config-accepting endpoints (traceTransaction, traceBlockByNumber, traceBlockByHash, traceCall, traceTransactionProfile) call validateTraceTracer, and TraceStateAccess takes no TraceConfig so it needs no guard. The native allowlist also matches geth's full set of native tracers (callTracer, flatCallTracer, 4byteTracer, prestateTracer, noopTracer, muxTracer), so no legitimate native tracer is inadvertently blocked. Nice defense-in-depth via the nested muxTracer validation and the separate JS opt-in flag.
- Second-opinion passes produced no findings: the OpenAI Codex review reported "No material findings in the reviewed diff" and the Cursor review file was empty. No additional issues to merge in.
- Optional: consider documenting (e.g. in evmrpc/AGENTS.md or the README) the new trace_allowed_tracers / trace_allow_js_tracers config knobs and the security rationale, since operators upgrading will now get JS tracers disabled by default — a potentially breaking behavior change for anyone relying on request-supplied JS tracers.
- 1 suggestion(s)/nit(s) flagged inline on specific lines.
| return nil | ||
| } | ||
|
|
||
| func validateMuxTraceConfig(raw json.RawMessage, allowed map[string]struct{}, allowJS bool) error { |
There was a problem hiding this comment.
[nit] validateMuxTraceConfig recurses once per nested muxTracer key with no depth bound. A caller could submit a deeply nested {"muxTracer":{"muxTracer":{...}}} payload to drive deep recursion (stack exhaustion). In practice this is low risk — it's gated behind debug RPC access and mitigated by request body-size limits, and geth's own tracer construction would recurse similarly — but a small explicit depth cap would make the guard robust regardless of those assumptions.
There was a problem hiding this comment.
A well-implemented, thoroughly-tested EVM RPC hardening change that disables request-supplied JavaScript debug tracers by default and gates native tracers behind a startup-validated allowlist, with correct enforcement (including recursive muxTracer config validation) across all five request-facing debug trace endpoints. No blocking issues found; only a backward-compatibility note.
Findings: 0 blocking | 3 non-blocking | 0 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Behavioral/backward-compat change: JavaScript tracers previously worked and are now rejected by default, and only the listed native tracers are accepted. This is intentional and documented in the config comments, but operators relying on JS tracers (or on native tracers they'd have to explicitly list) must opt in via
evm.trace_allow_js_tracers/evm.trace_allowed_tracers. Ensure this is called out in release/upgrade notes so it isn't a silent break for existing RPC consumers. - The
trace_baker.gobackground baking path calls the raw gethtracersAPI.TraceBlockByNumberdirectly, bypassingvalidateTraceTracer. This is safe today because it only uses operator-configured internal tracer names (never request input), but it's worth a brief comment noting the invariant so a future change doesn't inadvertently route request-controlled names through it. - Second-opinion passes produced no findings:
codex-review.mdreported "No material findings" andcursor-review.mdwas empty (no output).
There was a problem hiding this comment.
Solid, well-tested security hardening that disables request-supplied JavaScript debug tracers by default and gates native tracer names through a validated allowlist, enforced consistently across all trace entry points including nested muxTracer configs. No blocking issues; one operational behavior change worth flagging in release notes.
Findings: 0 blocking | 3 non-blocking | 0 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- Behavioral/operational change:
trace_bake_tracersis now normalized native-only at startup (normalizeNativeTracerNames). Any existing operator whoseapp.tomllists a non-native or typo'd bake-tracer name (previously tolerated) will now fail node startup. This is intentional and documented in AGENTS.md, but is worth calling out explicitly in release notes/upgrade docs since it can block a node from booting after upgrade. - External second-opinion reviews yielded no findings:
codex-review.mdreported no material findings (and noted it could not run Go tests because the workspace had Go 1.24.13 vs the required 1.25.6), andcursor-review.mdwas empty (no output produced). - Minor: the JS-tracer opt-in (
trace_allow_js_tracers) executes request-supplied code in-process; the generated template comment warns about this, but consider also surfacing a one-time startup log warning whentrace_allow_js_tracers = trueso operators who flip it are reminded at runtime.
There was a problem hiding this comment.
Security-sensitive change to public debug RPC surface (deviates from geth defaults for JS tracers) — worth a human look before merge.
Extended reasoning...
Overview
This PR adds [evm].trace_allowed_tracers and trace_allow_js_tracers to gate caller-supplied TraceConfig.Tracer values on debug_traceCall, debug_traceTransaction, debug_traceBlockBy{Number,Hash}, and debug_traceTransactionProfile. Enforcement lives in validateTraceTracer in evmrpc/tracers.go, runs before trace-cache lookups and tracer construction, and recursively validates muxTracer nested configs with a depth cap of 16. trace_bake_tracers gets the same native-only rule at startup. Docker app.toml samples and the seid init template are updated; tests cover config parsing, allowlist behavior, JS opt-in, and mux nesting.
Security risks
The change itself is a hardening measure — request-supplied JavaScript tracer source (which executes in-process) is off by default and requires an explicit opt-in, an improvement over upstream geth. The main risk is behavioral: clients that relied on the previous geth-default of accepting JS tracers will get errors from Sei RPC nodes after this ships, and operators who mis-configure trace_allowed_tracers could deny legitimate native tracers. The validation logic itself looks careful: it trims and rewrites the top-level tracer name, rejects padded nested names outright (since they cannot be safely rewritten inside forwarded raw JSON), and now bounds mux recursion depth.
Level of scrutiny
Higher than average — this touches public RPC security policy, changes externally-visible behavior compared to geth defaults, and modifies default Docker configs. The bug hunting system found no defects and prior reviewer feedback (seidroid's nit about unbounded mux recursion) has been addressed with the depth cap, but a human should confirm the behavior change is intentional, that operators are aware, and that no internal tooling silently depends on JS-tracer availability.
Other factors
Test coverage is solid (config parsing, allowlist/JS opt-in behavior, mux nesting, depth-limit boundary, and a registry-pinning test that fails CI if geth changes what counts as native). Codecov reports ~82% patch coverage. No CODEOWNER inline comments beyond the addressed nit. Deferring rather than approving because the scope (public RPC + security policy + defaults change) exceeds the bar for shadow-approval.
Add EVM RPC configuration for trace_allowed_tracers and trace_allow_js_tracers. The native tracer allowlist is validated at startup and remains native-only, while JavaScript tracer execution requires an explicit separate opt-in.
Enforce the tracer policy across debug_traceCall, debug_traceTransaction, debug_traceBlockByNumber, debug_traceBlockByHash, and debug_traceTransactionProfile. Validate muxTracer nested configs as well so request-supplied JavaScript cannot bypass the top-level check through nested tracer names.
Default the allowlist to the registered native tracer names and keep JS tracers disabled in generated and Docker configs. Add focused tests for config parsing, native allowlist behavior, JS opt-in behavior, and muxTracer nesting.