Skip to content

test(relay): adversarial multi-tenant isolation + ingest robustness battery#2281

Open
s00ly wants to merge 1 commit into
block:mainfrom
s00ly:test/adversarial-tenant-isolation-battery
Open

test(relay): adversarial multi-tenant isolation + ingest robustness battery#2281
s00ly wants to merge 1 commit into
block:mainfrom
s00ly:test/adversarial-tenant-isolation-battery

Conversation

@s00ly

@s00ly s00ly commented Jul 21, 2026

Copy link
Copy Markdown

What this is

An independent, wire-level verification of the relay's core security invariant —
all tenant-observable state under a URL is community-local — from two hostile
communities (a.localhost, b.localhost) sharing one relay process, one
Postgres, one Redis
(the hosted multi-tenant shape). Blue-team framing:
every probe verifies a control is present and correct, anchored to
OWASP Top 10:2025 / CWE.

The team's conformance_multitenant suite is the obligation table; this
battery shares no helpers or assumptions with it, and deliberately re-probes
two lanes the table has todo!()-stubbed (event-id cross-fetch, DM fanout) —
a stub means unverified, not verified safe. Every probe is control-armed:
community A must observe the planted state, so a pass can never be faked by
the state simply not existing.

Probes (8)

# Vector Framework
P1 Event-id cross-tenant oracle (REQ {ids} + author scan from B) CWE-200/668
P2 NIP-17 gift-wrap live fanout to same-key subscription on B OWASP A01
P3 Mention (#p) query from B CWE-668
P4 Blossom existence-oracle: B's error shape for A-hash vs never-hash CWE-200
P7 Workflow webhook: A's UUID+secret fired at B; wrong-secret on A; correct on A OWASP A01 / CWE-200
P8 POST /count bridge from B CWE-668
P9 Same-coordinate (kind, author, d-tag) addressables in A and B CWE-668
P10 Ingest-door hostile battery: truncated JSON, bad sig/id/kind/tags/pubkey, far-future created_at, 2MB content, empty shapes — all must 4xx, never 5xx, relay stays healthy OWASP A05/A10, CWE-20

Evidence

Run against a local two-host deployment (setup in the file header):

  • Every fence held. Notably P4: B 404s A's blob hash and a never-uploaded
    hash identically — the build is stricter than the shared-bytes option the
    conformance stub allows.
  • 5× full 8/8 green runs; one additional run had a single unreproduced probe
    flake (suspected test-side timing in the live-fanout probe; no fence
    breach was observed in any run
    — flagging transparently rather than
    rounding to 100%).
  • OPEN legs recorded in the file header (explicitly not green): git
    smart-HTTP pointer isolation (needs a real push fixture), raw-frame WS
    fuzzing, /moderation/audit (needs a community-admin fixture),
    approval-token row (blocked on WF-08 per the suite's own note).
  • No cross-tenant leak and no remotely-triggerable 5xx found → no security
    disclosure triggered (per SECURITY.md).
  • cargo fmt --check + cargo clippy -p buzz-test-client --all-targets -- -D warnings: clean.

Related note (no code change proposed here)

While establishing the baseline, nip11_relay_info::nip11_is_not_a_cross_community_enumeration_oracle
fails on current main: the A/B NIP-11 documents differ only in origin
(ws://<request-host>). origin echoes the caller's own host, so it cannot
enumerate other tenants — arguably not an oracle, and the test's
"identical apart from icon" invariant may be too strict. Two resolutions:
allowlist origin in the test with a rationale comment (my recommendation),
or relay-side normalize origin to the canonical relay URL. Happy to send
either as a follow-up — flagging here rather than bundling into this PR.

Composes with (does not overlap) #2268 and the gate-restore fix for
define_workflow sent separately.

…attery

Independent wire-level verification of the relay's core security
invariant — all tenant-observable state under a URL is community-local —
from two hostile communities (a/b.localhost) sharing one relay process,
one Postgres, and one Redis.

8 probes, each control-armed (A must observe the planted state, so a
pass can never be faked by the state simply not existing):

- event-id cross-tenant oracle (CWE-200/668)
- NIP-17 gift-wrap live fanout confinement (OWASP A01)
- mention (#p) query fencing (CWE-668)
- Blossom existence-oracle error-shape indistinguishability (CWE-200)
- workflow webhook door fail-closed cross-tenant + wrong-secret 401
- POST /count bridge fencing (CWE-668)
- same-coordinate addressable coexistence, each tenant-scoped
- ingest-door hostile battery: 10 malformed/oversized inputs, all 4xx,
  zero 5xx, relay healthy afterwards (OWASP A05/A10, CWE-20)

Deliberately re-probes two lanes the conformance table has stubbed
(event-id cross-fetch, DM fanout) because a stub means unverified, not
verified-safe.

Evidence: 5x 8/8 full green runs against a local two-host deployment;
one unreproduced single-probe flake (no fence breach ever observed).
OPEN legs recorded in-file: git smart-HTTP pointer isolation, raw-frame
WS fuzzing, /moderation/audit fixture, approval-token row (blocked on
WF-08).

Signed-off-by: Sooly Kobayashi <yloos@protonmail.com>
@s00ly
s00ly requested a review from a team as a code owner July 21, 2026 20:26
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.

1 participant