testing/cross_backend/rpc_adversarial.ts

Cross-process malformed-envelope and invalid-params suite for a JSON-RPC endpoint.

describe_rpc_attack_surface_tests runs its envelope and params groups in-process against stub apps, so they never reach a spawned backend — and the Rust spine parses envelopes and params by hand, while the action-manifest parity gate compares declarations and is blind to param schemas. This suite sends the same requests over fixture.transport to each backend.

Two groups:

  • Envelopes — create_rpc_envelope_cases, the in-process group's set: non-JSON body, wrong or missing jsonrpc, missing method or id, a batch array, non-object POST params, an unknown method, GET missing method or id, GET invalid-JSON params, GET non-object params on every read, and a GET of a mutating method. Each is sent by the principal its method's auth admits, so the envelope check is what refuses, and each pins status, JSON-RPC code, the echoed id, and data.reason (exact where the TS dispatcher sets one, absent otherwise). Three ordering cases go without a credential: an anonymous GET of a mutating method is refused invalid_request rather than 401 (the read restriction runs after the method lookup and before authorization), an anonymous batch array is refused invalid_request with id: null (the envelope parse runs before auth, and batches are unsupported), and an anonymous GET with non-object params is the 401 (params validation runs after authorization).
  • Params — for each declared method (and each live-mounted one, below), generate_rpc_params_test_cases (the in-process generator minus its structural cases), each sent as the method's authorized principal and owing 400 invalid_params with the data.reason the broken field declares (invalid_reason in its .meta(), read by expected_invalid_reason) — none for an undeclared field or a whole-body case. One test per method, reporting every case that answers otherwise; xfail_params runs named cases of a method as their own xfail_until tests, for a known divergence on one backend.

The authorized principal is the keeper on the first channel the method's auth admits: its session, else its daemon token or API token over a no-Origin transport (resolve_conformance_principal), mirroring the in-process select_auth_app. Preconditions: the keeper holds every role a declared method gates on (default_cross_process_setup seeds keeper and admin; pass extra_keeper_roles for others), and it has a single actor. No generated body carries a valid acting — the generator's base fills required and defaulted fields, and an optional one only where a refinement needs it (each alone first, so an acting declared ahead of an at-least-one or exactly-one-of pair stays out) — and a malformed acting is read as omitted ahead of validation on both spines, so a single-actor keeper resolves its actor and validation is what refuses. A keeper with a second actor answers actor_required first; such a suite lists the actor-required methods in skip_methods.

Off the declared surface. live_rpc_endpoints adds the methods the backend live-mounts at rpc_path beyond the declared surface — cell verbs, opt-in resolvers, the _testing_* backdoors. Both groups run over them too: the envelope cases built from those methods, in a group of their own, and a params test per method, _testing_* included (a daemon-only backdoor is sent with the keeper's daemon token). A spine that keeps a method family off its declared surface still owes the twin's answer to bad params there.

Skipped: _testing_* methods on the declared surface (a declared surface lists them only by accident; the live mount is where they are walked), and the protocol actions (PROTOCOL_ACTION_METHODS, fixed shared specs covered by the WS suites, which generate_rpc_auth_conformance_cases skips too).

Cross-process only: in-process, describe_rpc_attack_surface_tests runs the same envelope cases and params generator.

view source

Declarations
#

3 declarations

describe_rpc_adversarial_cross_tests
#

testing/cross_backend/rpc_adversarial.ts view source

(options: RpcAdversarialCrossTestOptions): void import {describe_rpc_adversarial_cross_tests} from '@fuzdev/fuz_app/testing/cross_backend/rpc_adversarial.js';

Run the cross-process malformed-envelope and invalid-params suite.

options

the fixture, the declared surface and live mount, the RPC path, and methods to skip

returns

void

RpcAdversarialCrossTestOptions
#

testing/cross_backend/rpc_adversarial.ts view source

RpcAdversarialCrossTestOptions import type {RpcAdversarialCrossTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/rpc_adversarial.js';

inheritance

surface_source

The declared surface; the endpoint at rpc_path supplies the methods and their input schemas.

type AppSurfaceSpec

readonly

live_rpc_endpoints?

The endpoints the backend live-mounts (resolved — resolve_rpc_endpoints_for_setup turns the factory form into this). The methods the one at rpc_path carries beyond the declared surface run through both groups too. Omitted, only declared methods run.

type readonly RpcEndpointSpec[]

readonly

skip_methods?

Methods to leave out, beyond the protocol actions always skipped.

type readonly string[]

readonly

input_overrides?

Valid params per method (method name → params) to build its params cases from, in place of the generated base — for an input whose valid shape the generator can't reach. Each must pass the method's input schema; one that doesn't throws at collection, naming the method. The cases are sent as a single-actor principal, so an override omits acting. describe_spine_cross_suites passes its rpc_input_overrides here.

type ReadonlyMap<string, Record<string, unknown>>

readonly

xfail_params?

Generated params cases a backend is known to answer otherwise, each run as its own xfail_until test; the method's other cases keep their test. Pass it only for the backend that diverges.

type readonly RpcParamsXfail[]

readonly

RpcParamsXfail
#

testing/cross_backend/rpc_adversarial.ts view source

RpcParamsXfail import type {RpcParamsXfail} from '@fuzdev/fuz_app/testing/cross_backend/rpc_adversarial.js';

Generated params cases of one method marked xfail_until, by case label.

method

type string

readonly

labels

InputTestCase.labels of generate_rpc_params_test_cases(method's input).

type readonly string[]

readonly

tracking_id

type string

readonly

reason

type string

readonly

Depends on
#

Imported by
#