Skip to content

fix(security): enforce HTTPS for rpcUrl in validation config schema#172

Open
memosr wants to merge 1 commit into
base:mainfrom
memosr:fix/rpc-url-https-validation
Open

fix(security): enforce HTTPS for rpcUrl in validation config schema#172
memosr wants to merge 1 commit into
base:mainfrom
memosr:fix/rpc-url-https-validation

Conversation

@memosr

@memosr memosr commented Jun 6, 2026

Copy link
Copy Markdown

Summary

The README documents rpcUrl as an HTTPS endpoint, but the schema accepts any URL scheme. For a tool that signs governance transactions, this is a real attack surface — added .refine() to enforce https://.

The drift

README.md:169 — rpcUrl is documented as:

"rpcUrl — HTTPS RPC endpoint used for simulation"

src/lib/config-schemas.ts:78 — actual schema:

rpcUrl: z.string().url()

z.string().url() accepts http://, ws://, wss://, file:// — anything that parses as a URL. The documentation says one thing, the enforced contract says another.

Why this matters for a signing tool

The signing flow simulates a transaction over the configured RPC to display the resulting state diff to the signer. The signer reviews that diff and signs a hash derived from it.

An attacker positioned on the network path to an HTTP RPC (corporate proxy, untrusted Wi-Fi, compromised ISP, etc.) can:

  1. Intercept the eth_call / state simulation response
  2. Return a benign-looking state diff
  3. Wait for the signer to approve a hash that matches the attacker-supplied state
  4. The signed transaction, when broadcast, performs whatever the attacker chose

The simulation is the signer's only window into what they're approving. If that window is HTTP, the signer is trusting the network.

HTTPS doesn't eliminate this entirely (the RPC provider itself could be compromised), but it closes the MITM vector that's trivially exploitable on hostile networks.

The fix

- rpcUrl: z.string().url()
+ rpcUrl: z.string().url().refine(
+   (val) => val.startsWith('https://'),
+   (val) => ({ message: `rpcUrl must use HTTPS (got: ${val})` })
+ )
  • Existing z.string().url() is preserved — still rejects malformed URLs
  • .refine() adds the HTTPS check on top
  • Error message echoes the offending URL so operators get immediate, actionable feedback

Verification

  • ✅ One file modified: src/lib/config-schemas.ts
  • ✅ 4 insertions, 1 deletion
  • ✅ Existing URL validation preserved
  • ✅ HTTP/WS/WSS URLs now fail at config load with a clear error
  • ✅ HTTPS URLs continue to work without change

The README at line 169 documents rpcUrl as an "HTTPS RPC endpoint", but
the Zod schema at src/lib/config-schemas.ts:78 used z.string().url()
which accepts http://, ws://, and wss:// without complaint.

For a governance signing tool that simulates state diffs over the
provided RPC, an HTTP endpoint allows a network-positioned attacker to
MITM the simulation response. Signers would see and approve a hash
derived from attacker-supplied state, while the actual chain state
remains untouched until the malicious transaction lands.

Added a .refine() check that enforces the https:// scheme. The error
message echoes the offending URL so operators get immediate, actionable
feedback rather than a generic "invalid config" failure.

The existing z.string().url() check is preserved so malformed URLs are
still rejected with the standard Zod error.
@cb-heimdall

Copy link
Copy Markdown
Collaborator

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 1
Sum 2

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.

2 participants