openpencil/.github/workflows/collab-security.yml
Kayshen-X f9f9c8574a fix(collab): harden p2p collaboration against resource exhaustion
Addresses a security review of the collaboration subsystem. No auth
bypass, key leak, or document-plaintext exposure was found; every
finding below is availability or trust-boundary hardening.

Landed as one commit because the pieces are not separable: the
inbound-direction ceiling spans op-collab, op-collab-transport, and the
desktop host atomically, the guarded accept spans transport, smoke, and
the desktop host, and the boundary-gate rules only hold against the
final state. Splitting would produce commits that fail to build or fail
the gate.

Relay server (public, internet-facing):
- Charge pre-pairing capacity per source address. The auth-concurrency
  semaphore was taken before the WebSocket upgrade and the peer address
  was discarded, so one host could pin every permit by connecting and
  going silent.
- Give renewals their own budget. Reauthentication competed for the same
  semaphore, so an unauthenticated flood progressively closed live
  tunnels with a policy error.
- Release the pair registration when the ready status fails to send; the
  counterpart only reclaims it if it reads its pairing notice.
- Require the X25519 key file to be owned by the running user; mode bits
  alone do not establish trust.
- Summarise capacity rejections instead of logging one line each.

Locator service:
- Rate-limit publishes per client instead of process-wide. One
  unauthenticated caller could consume the whole budget and 429 every
  tenant's invite issuance.

Collaboration protocol:
- Size the inbound envelope ceiling from the authenticated remote role
  rather than sharing the 64 MiB snapshot ceiling in both directions, so
  an admitted guest cannot force a 64 MiB JSON parse per frame. The
  ceiling is applied before the discriminator and before the generic
  value decode; a peer-declared snapshot kind cannot raise it.
- Reject display names carrying Unicode format characters, which render
  identically to an existing participant's name.
- Reject avatar URLs pointing at non-globally-routable addresses.

Transport:
- Reclaim a pending-handshake seat from a peer that has not produced a
  valid first handshake message, and raise the global ceiling. Sixteen
  seats held for the full handshake window let four addresses deny every
  join.
- Put inbound reassembly under an aggregate budget; only the outbound
  aggregate was bounded.
- Stop heartbeats from refreshing the idle deadline in receive_transfer.
- Filter IPv4 link-local discovery advertisements, matching IPv6.

Relay client and trust roots:
- Bound server-initiated reauthentication per connection by count and
  minimum interval, sized from the protocol's own cadence.
- Close the policy-file TOCTOU window by identity-checking the opened
  file, and reject group/world-writable or foreign-owned policy files.
- Stop discarding bootstrap cache-write failures, which silently
  disabled the anti-rollback generation floor.
2026-08-01 09:48:23 +08:00

162 lines
6.3 KiB
YAML

name: P2P collaboration security boundaries
on:
pull_request:
paths:
- 'Cargo.toml'
- 'Cargo.lock'
- '.dockerignore'
- '.gitignore'
- 'rust-toolchain.toml'
- 'crates/op-collab/**'
- 'crates/op-collab-smoke/**'
- 'crates/op-collab-transport/**'
- 'crates/op-collab-relay-protocol/**'
- 'crates/op-collab-relay-client/**'
- 'crates/op-collab-relay-server/**'
- 'crates/op-collab-relay-control-plane/**'
- 'crates/op-collab-policy-file/**'
- 'crates/op-collab-relay-locator-server/**'
- 'crates/op-auth-bridge/**'
- 'crates/op-util/**'
- 'crates/op-editor-core/**'
- 'crates/op-editor-host-core/**'
- 'crates/op-editor-ui/**'
- 'crates/op-host-native/**'
- 'crates/op-host-desktop/**'
- 'crates/op-host-services/**'
- 'crates/op-i18n/**'
- 'deploy/collab-relay/**'
- 'deploy/collab-relay-edge/**'
- 'deploy/collab-relay-locator/**'
- 'deploy/collab-relay-locator-edge/**'
- 'docs/security/p2p-collaboration-threat-model.md'
- 'docs/testing/p2p-collaboration-platform-acceptance.md'
- 'tools/check-collab-security-boundaries.sh'
- 'tools/check-collab-security-boundaries.test.sh'
- 'tools/check-op-auth-prebuilt.sh'
- 'tools/check-op-auth-prebuilt.test.sh'
- 'tools/package-op-auth-prebuilt.sh'
- '.github/workflows/collab-security.yml'
- '.github/workflows/collab-platform-acceptance.yml'
push:
branches: ['**']
paths:
- 'Cargo.toml'
- 'Cargo.lock'
- '.dockerignore'
- '.gitignore'
- 'rust-toolchain.toml'
- 'crates/op-collab/**'
- 'crates/op-collab-smoke/**'
- 'crates/op-collab-transport/**'
- 'crates/op-collab-relay-protocol/**'
- 'crates/op-collab-relay-client/**'
- 'crates/op-collab-relay-server/**'
- 'crates/op-collab-relay-control-plane/**'
- 'crates/op-collab-policy-file/**'
- 'crates/op-collab-relay-locator-server/**'
- 'crates/op-auth-bridge/**'
- 'crates/op-util/**'
- 'crates/op-editor-core/**'
- 'crates/op-editor-host-core/**'
- 'crates/op-editor-ui/**'
- 'crates/op-host-native/**'
- 'crates/op-host-desktop/**'
- 'crates/op-host-services/**'
- 'crates/op-i18n/**'
- 'deploy/collab-relay/**'
- 'deploy/collab-relay-edge/**'
- 'deploy/collab-relay-locator/**'
- 'deploy/collab-relay-locator-edge/**'
- 'docs/security/p2p-collaboration-threat-model.md'
- 'docs/testing/p2p-collaboration-platform-acceptance.md'
- 'tools/check-collab-security-boundaries.sh'
- 'tools/check-collab-security-boundaries.test.sh'
- 'tools/check-op-auth-prebuilt.sh'
- 'tools/check-op-auth-prebuilt.test.sh'
- 'tools/package-op-auth-prebuilt.sh'
- '.github/workflows/collab-security.yml'
- '.github/workflows/collab-platform-acceptance.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
collaboration-security:
name: Static boundaries and targeted tests
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
submodules: recursive
persist-credentials: false
- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable, pinned 2026-07-28
with:
toolchain: '1.94'
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
shared-key: collab-security
- name: Check boundary-script syntax
run: |
bash -n tools/check-collab-security-boundaries.sh
bash -n tools/check-collab-security-boundaries.test.sh
bash -n tools/check-collab-security-boundaries-cases.sh
bash -n tools/check-collab-deployment-boundaries.sh
bash -n tools/check-op-auth-prebuilt.sh
bash -n tools/check-op-auth-prebuilt.test.sh
bash -n tools/package-op-auth-prebuilt.sh
- name: Mutation-test the boundary gate
run: |
bash tools/check-collab-security-boundaries.test.sh
bash tools/check-op-auth-prebuilt.test.sh
- name: Verify public/private and secret boundaries
run: |
bash tools/check-collab-security-boundaries.sh
bash tools/check-op-auth-prebuilt.sh
bash deploy/collab-relay-edge/validate.sh
bash deploy/collab-relay-locator/validate.sh
bash deploy/collab-relay-locator-edge/validate.sh
- name: Compile the public protocol core for wasm
run: |
cargo check --locked --target wasm32-unknown-unknown \
-p op-collab --no-default-features
cargo check --locked --target wasm32-unknown-unknown \
-p op-collab-relay-protocol --no-default-features
- name: Test protocol state machines, properties, and resource limits
run: |
cargo test --locked -p op-collab
cargo test --locked -p op-collab-transport
cargo test --locked -p op-collab-transport config::tests
cargo test --locked -p op-collab-transport frame::tests
cargo test --locked -p op-collab-relay-protocol --all-features
cargo test --locked -p op-collab-relay-client
cargo test --locked -p op-collab-relay-server
cargo test --locked -p op-collab-relay-control-plane
cargo test --locked -p op-collab-policy-file
cargo test --locked -p op-collab-relay-locator-server
- name: Test production/test trust-root isolation
run: |
cargo test --locked -p op-auth-bridge --features test-issuer \
verifies_the_frozen_go_production_root_fixture
cargo test --locked -p op-auth-bridge --features test-issuer \
production_signed_policy_path_never_falls_back_to_raw_jwks
cargo test --locked -p op-auth-bridge --features test-issuer \
--test collab_verifier
- name: Verify every committed authentication archive
run: cargo test --locked -p op-auth-bridge --test prebuilt_provenance
- name: Exercise two-process authenticated collaboration
run: |
cargo run --locked -p op-collab-smoke --features test-issuer -- run