agents/skills/injectable/l1/rpc-surface-audit/SKILL.md
L1 trigger - audits JSON-RPC and Engine API surfaces for authentication bypass, rate limiting, subscription buffer overflows, and method-specific DoS.
npx skillsauth add plamentsv/plamen rpc-surface-auditInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
L1 trigger:
L1_PATTERN=trueAND (rpc/ORjsonrpcORengine_apiOReth/apiORwebsocketORipcdetected in recon subsystem map) Inject Into:depth-network-surfaceLanguage: Go and Rust Finding prefix:[RPC-N]Status: v0.1 draft, Round 4 exemplars pending
Recon identifies an RPC subsystem. RPC is the most publicly exposed attack surface on any L1 node — typically unauthenticated (HTTP JSON-RPC, WebSocket) or semi-authenticated (Engine API with JWT). Even a Medium-severity bug here often upgrades to High because of permissionless reachability (see severity-matrix.md modifier).
Enumerate all RPC entry points via LSP workspace/symbol filtered by known method-registration patterns:
| Transport | Registration pattern | Example |
|---|---|---|
| HTTP JSON-RPC | rpc.Register, httpServer.Handle, #[method(name=...)] | eth_*, debug_*, admin_* |
| WebSocket | Same as HTTP + subscription handlers | eth_subscribe |
| IPC (unix socket) | Often same as HTTP | Geth admin namespace |
| Engine API | JWT-authenticated | engine_newPayloadV*, engine_forkchoiceUpdatedV* |
| Prometheus metrics | /metrics endpoint | often bound to all interfaces |
| PPROF profiling | /debug/pprof/ (Go) | should NEVER be public |
Write the enumeration to scratchpad/rpc_surface.md.
eth, net, web3. Not admin, debug, personal.)admin_* — should never be exposed on HTTP; check the config validationdebug_* — can expose internal state; check defaultpersonal_* — key management; deprecated in Geth but still present in forksengine_* — JWT-authenticated; check the JWT secret handlingminer_* / txpool_* — varies by clientiat claim: is clock drift tolerated? Excessive tolerance is a replay windowalg=none and key-confusion attacksTag: [RPC-AUTH:{issue}]
Some RPC methods are cheap (eth_blockNumber), others are expensive (eth_getLogs with wide range, debug_traceTransaction). Is there a cost model that bounds work per request?
Check:
eth_getLogs with max_logs_per_requesteth_getLogs with fromBlock → toBlock spanTag: [RPC-RATE:{scope}:{limit-or-unbounded}]
The skill historically focused on incoming RPC. Audit OUTBOUND HTTP clients used by the node for peer fetches and inter-node API calls — they are a symmetric DoS vector.
Check:
reqwest::Client::builder(), awc::Client::builder(), hyper::Client::builder(), and http::Client::new() site, verify .timeout(...) AND .connect_timeout(...) are set explicitly. The library defaults are NO timeout.tokio::time::timeout wrapper around an outbound call, verify the timeout bound is a constant (not derived from peer-controlled input).http.Client{} literal (zero value) has NO timeout — flag every site that constructs http.Client without setting Timeout. Also check Transport.DialContext for connect-timeout.Tag: [RPC-CLIENT-NO-TIMEOUT:{file}:{line}]
JavaScript clients (and many other dynamic languages) cannot represent integers above 2^53 - 1 precisely. L1 nodes that serialize u64 fields as JSON numbers (not strings) will silently corrupt block heights, balances, gas values, and timestamps when consumed by JS clients.
Check:
serde::Serialize impl on a struct exposed via RPC, find every u64 / u128 / i64 / i128 field#[serde(with = "as_string")] or a similar string-coercion attribute, OR that the field's max value provably fits in 53 bitsjson.Marshal of uint64 fields and *hexutil.Uint64 wrappers (the latter is correct, the former is not)Tag: [RPC-JSON-PRECISION:{type}.{field}]
For each RPC handler, peer HTTP endpoint, and outbound API client wrapper that returns Ok(()), HTTP 2xx, a success JSON body, or increments peer score, verify that the underlying side effect completed first.
Check:
Ok(()), StatusCode::OK, HttpResponse::Ok, or equivalent success path, trace the immediately preceding awaited call and ensure it is guarded by ?, explicit error mapping, or a checked status branch.return nil paths after Do(req), Write, Encode, channel send, or peer delivery; success must be after the side effect, not before it.Tag: [RPC-OK-BEFORE-EFFECT:{file}:{line}]
WebSocket subscriptions are a classic DoS vector: slow consumer, fast producer.
Check:
eth_subscribe type (newHeads, logs, newPendingTransactions, syncing), what is the outbound buffer depth per subscriber?Tag: [RPC-SUB:{type}:{buffer-policy}]
eth_getLogsdebug_traceTransaction / debug_traceBlocketh_call / eth_estimateGaseth_getProofengine_newPayload (Engine API)An RPC method that panics crashes the node. The Go standard library recovers in HTTP handlers by default, but custom dispatch may not.
Check:
recover / panic catcher?panic!(), .unwrap() on user input, integer division, out-of-bounds access.unwrap() and .expect() on decoded user input are panic-on-attackTag: [RPC-PANIC:{method}:{path}]
| State | Test | Expected | Observed | |---|---|---|---| | Empty params | method with empty params | spec-defined (often error) | | | Huge params | 10 MB of JSON in request | rejected before parsing | | | Deeply nested JSON | 1000-level nesting | rejected (stack overflow guard) | | | Unknown method | method not registered | error, not crash | | | Concurrent duplicate sub | same subscription twice | spec-defined | | | getLogs 10M blocks | wide range | rejected | |
[FUZZ-PASS] > [LSP-TRACE] > [CODE-TRACE]Geth GraphQL DoS (CVE-2023-42319, GHSA-v9jh-j8px-98vq) — with --http --graphql enabled, a crafted GraphQL query consumed memory and hung the daemon. Fixed in v1.13.5. GHSA-v9jh-j8px-98vq. Skill catch point: Section 3a — every user-controlled query parser needs query depth cap, memory cap per request, total time cap, allocated-bytes cap.
Geth CVE-2025-24883 p2p handshake invalid EC point — RLPx handshake accepted an all-zero secp256k1 public key without validating it lies on the curve. ECDH math with the bogus point produced undefined behavior and DoS. DailyCVE. Methodology nuance: the p2p handshake is part of the RPC attack surface, not a separate domain. Any attacker-supplied curve point must be validated on-curve AND in prime-order subgroup before any math.
Geth snap/1 trie-node nil-deref panic (CVE-2021-41173, GHSA-59hh-656j-3p7v) — trie.TryGetNode returns nil for child of fullnode when path exhausted; caller dereferenced without a nil check → origNode.cache() panic. Any malicious snap peer could kill any node. GHSA-59hh-656j-3p7v. Skill catch point: Section 6 (Panic surfaces) — every p2p message handler must never panic. Each .unwrap(), nil-deref, panic-on-slice-index, uncovered switch is a finding.
Engine API JWT replay / time-skew exploitation — the Engine API spec allows ±60s iat window. Implementations that fail to enforce freshness allow JWT reuse across rotation boundaries. execution-apis auth spec. Skill catch point: Section 2d — hard iat window, no clock-jump tolerance, rotation-on-restart, no persistence across process boundaries.
Add to Section 1: The attack surface enumeration MUST include p2p handshake and pre-auth message handlers, not just JSON-RPC methods. CVE-2025-24883 was classified in the RPC surface because the handshake is the first code that touches untrusted input. Cross-reference with p2p-dos-and-eclipse Section 2f (pre-auth panic check).
"eth_" / "debug_" / "admin_" method namesmempool-asymmetric-dos (eth_sendRawTransaction is an insertion), execution-client-hardening (debug methods touch VM)depth-network-surfacedocs/l1-mode/severity-matrix.mddevelopment
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline