Skip to content

Disable JS debug tracers by default with native tracer allowlist#3718

Open
masih wants to merge 3 commits into
mainfrom
masih/config-allowed-tracers
Open

Disable JS debug tracers by default with native tracer allowlist#3718
masih wants to merge 3 commits into
mainfrom
masih/config-allowed-tracers

Conversation

@masih

@masih masih commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

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.

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.
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes public RPC security behavior: untrusted JS tracer execution is blocked unless explicitly opted in, which may break clients that relied on geth-default JS tracers; misconfiguration could also deny legitimate native tracers.

Overview
Adds [evm].trace_allowed_tracers and trace_allow_js_tracers so nodes control which TraceConfig.Tracer values are accepted—unlike upstream geth, request-supplied JavaScript tracers are off by default and only a native-name allowlist (validated at startup, deduped/trimmed) can be used unless JS is explicitly enabled.

validateTraceTracer runs on all major debug_trace* entry points (including debug_traceTransactionProfile) before cache hits and tracer construction; it trims top-level names, recursively validates muxTracer nested configs (depth cap, whitespace rules), and keeps the default struct logger when tracer is omitted. trace_bake_tracers gets the same native-only startup validation.

Docker app.toml samples and seid init template document the new settings; tests cover config parsing, allowlist/JS opt-in, mux nesting, and pinning defaults against the geth tracer registry.

Reviewed by Cursor Bugbot for commit 4011e43. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedJul 8, 2026, 2:23 PM

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.97872% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 58.38%. Comparing base (4d7769c) to head (4011e43).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
evmrpc/tracers.go 75.43% 7 Missing and 7 partials ⚠️
evmrpc/trace_profile.go 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            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     
Flag Coverage Δ
sei-chain-pr 69.28% <82.97%> (?)
sei-db 70.62% <ø> (ø)
sei-db-state-db ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
evmrpc/config/config.go 77.47% <100.00%> (+6.04%) ⬆️
evmrpc/trace_profile.go 65.93% <0.00%> (-1.49%) ⬇️
evmrpc/tracers.go 70.05% <75.43%> (+0.72%) ⬆️

... and 87 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread evmrpc/tracers.go Outdated
return nil
}

func validateMuxTraceConfig(raw json.RawMessage, allowed map[string]struct{}, allowJS bool) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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.

@masih masih requested review from codchen and sei-will July 8, 2026 10:57

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.go background baking path calls the raw geth tracersAPI.TraceBlockByNumber directly, bypassing validateTraceTracer. 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.md reported "No material findings" and cursor-review.md was empty (no output).

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_tracers is now normalized native-only at startup (normalizeNativeTracerNames). Any existing operator whose app.toml lists 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.md reported 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), and cursor-review.md was 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 when trace_allow_js_tracers = true so operators who flip it are reminded at runtime.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

@masih masih added this pull request to the merge queue Jul 8, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants