2026-07-29 13:42:33 +00:00
|
|
|
# Mutation cases for check-collab-security-boundaries.test.sh.
|
|
|
|
|
# Sourced after the fixture and assertion helpers have been initialized.
|
|
|
|
|
|
|
|
|
|
new_fixture baseline
|
|
|
|
|
expect_pass "accepts the minimal safe collaboration boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture wasm-native-dependency
|
|
|
|
|
: > "$fixture_root/.fake-wasm-forbidden"
|
|
|
|
|
expect_failure "rejects native dependencies in the wasm closure" \
|
|
|
|
|
"WASM boundary includes native/auth dependencies"
|
|
|
|
|
|
|
|
|
|
new_fixture credential-clone-assertion-removed
|
|
|
|
|
sed '/assert_not_impl_any!(OpaqueTicket: Clone);/d' \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab/tests/credential_ownership.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs"
|
|
|
|
|
expect_failure "requires compile-time non-Clone credential assertions" \
|
|
|
|
|
"credential-bearing protocol type must remain non-Clone"
|
|
|
|
|
|
|
|
|
|
new_fixture dedicated-ticket-codec-removed
|
|
|
|
|
: > "$fixture_root/crates/op-collab/src/error.rs"
|
|
|
|
|
expect_failure "requires the dedicated credential codec failure" \
|
|
|
|
|
"dedicated credential codec failure"
|
|
|
|
|
|
|
|
|
|
new_fixture credential-preflight-moved-after-value
|
|
|
|
|
awk '
|
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 01:48:23 +00:00
|
|
|
index($0, " declared_kind_rejecting_renew_ticket(bytes)?;") == 1 {
|
2026-07-29 13:42:33 +00:00
|
|
|
held = $0
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
held != "" && index($0, " let mut value = decode_json_value(bytes, limits)?;") == 1 {
|
|
|
|
|
print
|
|
|
|
|
print held
|
|
|
|
|
held = ""
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
{ print }
|
|
|
|
|
' \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/codec.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab/src/codec.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/codec.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/codec.rs"
|
|
|
|
|
expect_failure "requires credential classification before generic Value decoding" \
|
|
|
|
|
"generic credential discriminator must run before JSON Value decoding"
|
|
|
|
|
|
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 01:48:23 +00:00
|
|
|
new_fixture inbound-direction-budget-moved-after-discriminator
|
|
|
|
|
awk '
|
|
|
|
|
index($0, " enforce_inbound_envelope_limit(inbound_direction, bytes.len(), limits)?;") == 1 {
|
|
|
|
|
held = $0
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
held != "" && index($0, " declared_kind_rejecting_renew_ticket(bytes)?;") == 1 {
|
|
|
|
|
print
|
|
|
|
|
print held
|
|
|
|
|
held = ""
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
{ print }
|
|
|
|
|
' \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/codec.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab/src/codec.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/codec.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/codec.rs"
|
|
|
|
|
expect_failure "requires trusted direction budgeting before wire discrimination" \
|
|
|
|
|
"trusted per-direction inbound envelope limit must run before discriminator and JSON Value decoding"
|
|
|
|
|
|
2026-07-29 13:42:33 +00:00
|
|
|
new_fixture dedicated-ticket-zeroizing-decoder-removed
|
|
|
|
|
sed '/Zeroizing::new(String::with_capacity/d' \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/ticket_json.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab/src/ticket_json.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/ticket_json.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/ticket_json.rs"
|
|
|
|
|
expect_failure "requires direct zeroizing ticket string decoding" \
|
|
|
|
|
"direct zeroizing ticket string decoder"
|
|
|
|
|
|
|
|
|
|
new_fixture dedicated-ticket-ordinary-string-deserializer
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'fn bad() { let _ = String::deserialize(deserializer); }' \
|
|
|
|
|
>> "$fixture_root/crates/op-collab/src/ticket_json.rs"
|
|
|
|
|
expect_failure "rejects ordinary String deserialization in the ticket decoder" \
|
|
|
|
|
"dedicated ticket decoder must not materialize ordinary strings or Values"
|
|
|
|
|
|
|
|
|
|
new_fixture opaque-ticket-generic-string-deserializer
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'fn bad() { let _ = String::deserialize(deserializer); }' \
|
|
|
|
|
>> "$fixture_root/crates/op-collab/src/protocol.rs"
|
|
|
|
|
expect_failure "rejects ordinary String deserialization in OpaqueTicket" \
|
|
|
|
|
"OpaqueTicket must not deserialize through an ordinary String"
|
|
|
|
|
|
|
|
|
|
new_fixture generic-renewal-deserialize-assertion-removed
|
|
|
|
|
sed '/assert_not_impl_any!(RenewTicket: serde::de::DeserializeOwned);/d' \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab/tests/credential_ownership.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs"
|
|
|
|
|
expect_failure "requires the generic renewal Deserialize compile-time boundary" \
|
|
|
|
|
"credential-bearing protocol type must not implement generic Deserialize"
|
|
|
|
|
|
|
|
|
|
new_fixture derived-collab-message-deserializer
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'#[derive(PartialEq, Serialize, Deserialize)]' \
|
|
|
|
|
>> "$fixture_root/crates/op-collab/src/protocol.rs"
|
|
|
|
|
expect_failure "rejects derived adjacent-tag CollabMessage deserialization" \
|
|
|
|
|
"CollabMessage must not use derived Deserialize"
|
|
|
|
|
|
|
|
|
|
new_fixture direct-serde-renewal-serialization-regression-removed
|
|
|
|
|
sed '/direct_serde_renewal_serialization_is_fail_closed/d' \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab/tests/credential_ownership.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab/tests/credential_ownership.rs"
|
|
|
|
|
expect_failure "requires the direct serde renewal serialization regression" \
|
|
|
|
|
"direct serde credential serialization rejection test"
|
|
|
|
|
|
|
|
|
|
new_fixture mislabeled-renewal-regression-removed
|
|
|
|
|
: > "$fixture_root/crates/op-collab-transport/src/frame.rs"
|
|
|
|
|
expect_failure "requires the mislabeled renewal transport regression" \
|
|
|
|
|
"mislabeled credential transport regression test"
|
|
|
|
|
|
|
|
|
|
new_fixture credential-transport-workflow-test-removed
|
|
|
|
|
sed '/cargo test --locked -p op-collab-transport frame::tests/d' \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml" \
|
|
|
|
|
> "$fixture_root/.github/workflows/collab-security.yml.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml.next" \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml"
|
|
|
|
|
expect_failure "requires the credential transport codec workflow test" \
|
|
|
|
|
"credential transport codec workflow test"
|
|
|
|
|
|
|
|
|
|
new_fixture desktop-renewal-vec-copy
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'fn bad(ticket: Ticket) { let _ = ticket.expose().as_bytes().to_vec(); }' \
|
2026-08-08 03:52:34 +00:00
|
|
|
>> "$fixture_root/crates/op-collab-host/src/runtime/types.rs"
|
2026-07-29 13:42:33 +00:00
|
|
|
expect_failure "rejects ordinary Vec copies in desktop renewal commands" \
|
|
|
|
|
"desktop renewal commands must move OpaqueTicket"
|
|
|
|
|
|
|
|
|
|
new_fixture non-mit-crate
|
|
|
|
|
cat > "$fixture_root/crates/op-collab-transport/Cargo.toml" <<'EOF'
|
|
|
|
|
[package]
|
|
|
|
|
name = "op-collab-transport"
|
|
|
|
|
version = "0.0.0"
|
|
|
|
|
license = "Apache-2.0"
|
|
|
|
|
EOF
|
|
|
|
|
expect_failure "rejects a non-MIT collaboration crate" \
|
|
|
|
|
"must inherit or declare the MIT license"
|
|
|
|
|
|
|
|
|
|
new_fixture deterministic-production-seed
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'const PRODUCTION_SIGNING_SEED: [u8; 32] = [9; 32];' \
|
|
|
|
|
>> "$fixture_root/crates/op-collab/src/protocol.rs"
|
|
|
|
|
expect_failure "rejects deterministic key material in production source" \
|
|
|
|
|
"deterministic signing/key seed leaked"
|
|
|
|
|
|
|
|
|
|
new_fixture deterministic-production-seed-after-test-module
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'const PRODUCTION_SIGNING_SEED: [u8; 32] = [9; 32];' \
|
|
|
|
|
>> "$fixture_root/crates/op-auth-bridge/src/collab_verifier.rs"
|
|
|
|
|
expect_failure "scans production items after an inline cfg(test) module" \
|
|
|
|
|
"deterministic signing/key seed leaked"
|
|
|
|
|
|
|
|
|
|
new_fixture deterministic-external-test-without-cfg
|
|
|
|
|
sed '/#!\[cfg(test)\]/d' \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache_cancellation_tests.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache_cancellation_tests.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache_cancellation_tests.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache_cancellation_tests.rs"
|
|
|
|
|
expect_failure "requires an explicit cfg(test) boundary for external unit tests" \
|
|
|
|
|
"deterministic signing/key seed leaked"
|
|
|
|
|
|
|
|
|
|
new_fixture deterministic-path-test-without-parent-cfg
|
|
|
|
|
sed '/#\[cfg(test)\]/d' \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_jwks_cache.rs"
|
|
|
|
|
expect_failure "requires cfg(test) on path-based external unit-test modules" \
|
|
|
|
|
"deterministic signing/key seed leaked"
|
|
|
|
|
|
|
|
|
|
new_fixture production-root-fixture-regression-removed
|
|
|
|
|
sed '/verifies_the_frozen_go_production_root_fixture/d' \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_union_policy_tests.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-auth-bridge/src/collab_union_policy_tests.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_union_policy_tests.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_union_policy_tests.rs"
|
|
|
|
|
expect_failure "requires the split production root fixture regression" \
|
|
|
|
|
"production trust-root fixture regression test"
|
|
|
|
|
|
|
|
|
|
new_fixture production-policy-fail-closed-regression-removed
|
|
|
|
|
sed '/production_signed_policy_path_never_falls_back_to_raw_jwks/d' \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_verifier.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-auth-bridge/src/collab_verifier.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_verifier.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-auth-bridge/src/collab_verifier.rs"
|
|
|
|
|
expect_failure "requires the split production policy fail-closed regression" \
|
|
|
|
|
"production/test issuer isolation regression test"
|
|
|
|
|
|
|
|
|
|
new_fixture sensitive-key-file
|
|
|
|
|
: > "$fixture_root/crates/op-collab-transport/peer.key"
|
|
|
|
|
expect_failure "rejects key-shaped repository fixtures" \
|
|
|
|
|
"sensitive key/token-shaped files are forbidden"
|
|
|
|
|
|
|
|
|
|
new_fixture sealed-relay-key-json
|
|
|
|
|
: > "$fixture_root/deploy/collab-relay/relay-x25519-keys.json"
|
|
|
|
|
expect_failure "rejects sealed Relay private-key JSON in the repository" \
|
|
|
|
|
"sensitive key/token-shaped files are forbidden"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-key-dockerignore-removed
|
|
|
|
|
sed '/relay-x25519-keys/d' \
|
|
|
|
|
"$fixture_root/.dockerignore" \
|
|
|
|
|
> "$fixture_root/.dockerignore.next"
|
|
|
|
|
mv "$fixture_root/.dockerignore.next" "$fixture_root/.dockerignore"
|
|
|
|
|
expect_failure "requires private Relay key JSON exclusion from Docker builds" \
|
|
|
|
|
"Docker build-context private-key exclusion"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-key-gitignore-removed
|
|
|
|
|
sed '/relay-x25519-keys/d' \
|
|
|
|
|
"$fixture_root/.gitignore" \
|
|
|
|
|
> "$fixture_root/.gitignore.next"
|
|
|
|
|
mv "$fixture_root/.gitignore.next" "$fixture_root/.gitignore"
|
|
|
|
|
expect_failure "requires private Relay key JSON exclusion from Git staging" \
|
|
|
|
|
"Git private-key exclusion"
|
|
|
|
|
|
|
|
|
|
new_fixture compact-token
|
|
|
|
|
mkdir -p "$fixture_root/crates/op-collab/fixtures"
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'"abcdefghijklmnop.qrstuvwxyzABCDEF.abcdefghijklmnopqrstuvwxyzABCDEF0123456789"' \
|
|
|
|
|
> "$fixture_root/crates/op-collab/fixtures/captured-ticket.txt"
|
|
|
|
|
expect_failure "rejects compact bearer tokens in non-source fixtures" \
|
|
|
|
|
"high-signal credential/private-key material detected"
|
|
|
|
|
|
|
|
|
|
new_fixture smoke-compact-token
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'"abcdefghijklmnop.qrstuvwxyzABCDEF.abcdefghijklmnopqrstuvwxyzABCDEF0123456789"' \
|
|
|
|
|
> "$fixture_root/crates/op-collab-smoke/captured-ticket.txt"
|
|
|
|
|
expect_failure "rejects compact bearer tokens in the smoke crate" \
|
|
|
|
|
"high-signal credential/private-key material detected"
|
|
|
|
|
|
|
|
|
|
new_fixture desktop-sensitive-file
|
2026-08-08 03:52:34 +00:00
|
|
|
: > "$fixture_root/crates/op-collab-host/src/runtime/runtime-ticket.token"
|
2026-07-29 13:42:33 +00:00
|
|
|
expect_failure "rejects sensitive files in desktop collaboration integration" \
|
|
|
|
|
"sensitive key/token-shaped files are forbidden"
|
|
|
|
|
|
|
|
|
|
new_fixture avatar-redirect-limit-removed
|
|
|
|
|
sed '/MAX_REDIRECTS/d' \
|
|
|
|
|
"$fixture_root/crates/op-host-services/src/profile_avatar_fetch.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-host-services/src/profile_avatar_fetch.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-host-services/src/profile_avatar_fetch.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-host-services/src/profile_avatar_fetch.rs"
|
|
|
|
|
expect_failure "requires the shared avatar redirect limit" \
|
|
|
|
|
"bounded collaboration avatar fetch"
|
|
|
|
|
|
|
|
|
|
new_fixture desktop-public-avatar-delegation-removed
|
|
|
|
|
sed '/fetch_profile_avatar_blocking(request.url())/d' \
|
|
|
|
|
"$fixture_root/crates/op-host-desktop/src/collab_avatar_host.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-host-desktop/src/collab_avatar_host.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-host-desktop/src/collab_avatar_host.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-host-desktop/src/collab_avatar_host.rs"
|
|
|
|
|
expect_failure "requires public-only desktop collaboration avatar delegation" \
|
|
|
|
|
"desktop avatar security-policy delegation"
|
|
|
|
|
|
|
|
|
|
new_fixture avatar-proxy-bypass-removed
|
|
|
|
|
: > "$fixture_root/crates/op-host-services/src/provider_dial.rs"
|
|
|
|
|
expect_failure "requires proxy-free pinned avatar dialing" \
|
|
|
|
|
"public HTTPS proxy bypass prevention"
|
|
|
|
|
|
|
|
|
|
new_fixture auth-artifact-integrity-removed
|
|
|
|
|
: > "$fixture_root/crates/op-auth-bridge/build.rs"
|
|
|
|
|
expect_failure "requires authentication artifact integrity verification" \
|
|
|
|
|
"authentication artifact integrity gate"
|
|
|
|
|
|
|
|
|
|
new_fixture auth-artifact-signature-removed
|
|
|
|
|
: > "$fixture_root/crates/op-auth-bridge/prebuilt_provenance.rs"
|
|
|
|
|
expect_failure "requires authentication artifact signature verification" \
|
|
|
|
|
"authentication artifact signature verification"
|
|
|
|
|
|
|
|
|
|
new_fixture auth-matrix-test-removed
|
|
|
|
|
sed \
|
|
|
|
|
'/cargo test --locked -p op-auth-bridge --test prebuilt_provenance/d' \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml" \
|
|
|
|
|
> "$fixture_root/.github/workflows/collab-security.yml.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml.next" \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml"
|
|
|
|
|
expect_failure "requires the committed authentication matrix test" \
|
|
|
|
|
"committed authentication matrix test"
|
|
|
|
|
|
|
|
|
|
new_fixture integration-line-cap
|
|
|
|
|
awk 'BEGIN { for (line = 1; line <= 801; line++) print "// integration line" }' \
|
|
|
|
|
> "$fixture_root/crates/op-editor-host-core/src/collab/oversized.rs"
|
|
|
|
|
expect_failure "enforces the line cap across collaboration integration source" \
|
|
|
|
|
"has 801 lines; maximum is 800"
|
|
|
|
|
|
|
|
|
|
new_fixture missing-workflow-trigger
|
|
|
|
|
awk '
|
|
|
|
|
!removed && index($0, "crates/op-host-desktop/**") {
|
|
|
|
|
removed = 1
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
{ print }
|
|
|
|
|
' \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml" \
|
|
|
|
|
> "$fixture_root/.github/workflows/collab-security.yml.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml.next" \
|
|
|
|
|
"$fixture_root/.github/workflows/collab-security.yml"
|
|
|
|
|
expect_failure "rejects removal of either integration workflow trigger" \
|
|
|
|
|
"collaboration security workflow path trigger"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-edge-mtls-verification-removed
|
|
|
|
|
sed '/proxy_ssl_verify on;/d' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/global-nginx.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-edge/global-nginx.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/global-nginx.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/global-nginx.conf"
|
|
|
|
|
expect_failure "requires Global-to-CN outer-mTLS server verification" \
|
|
|
|
|
"Global-to-CN inner-TLS passthrough boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-edge-client-crl-removed
|
|
|
|
|
awk '!index($0, "ssl_crl /run/secrets/global-edge-client-crl.pem;")' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/cn-federation-nginx.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-edge/cn-federation-nginx.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/cn-federation-nginx.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/cn-federation-nginx.conf"
|
|
|
|
|
expect_failure "requires revocation checking for Global edge client certificates" \
|
|
|
|
|
"CN outer-mTLS federation boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-edge-crl-production-validation-removed
|
|
|
|
|
sed '/OPENPENCIL_RELAY_EDGE_VALIDATION_MODE=production/d' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/rotate-cn-crl.sh" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-edge/rotate-cn-crl.sh.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/rotate-cn-crl.sh.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/rotate-cn-crl.sh"
|
|
|
|
|
expect_failure "requires production validation before Relay CRL activation" \
|
|
|
|
|
"relay federation CRL activation boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-edge-source-rate-removed
|
|
|
|
|
awk '!index($0, "limit rate over 60/minute burst 20 packets")' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/install-global-new-connection-rate.sh" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-edge/install-global-new-connection-rate.sh.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/install-global-new-connection-rate.sh.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/install-global-new-connection-rate.sh"
|
|
|
|
|
expect_failure "requires the overseas relay per-source connection-rate gate" \
|
|
|
|
|
"overseas relay per-source connection-rate boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-edge-auto-restart-enabled
|
|
|
|
|
sed 's/restart: "no"/restart: unless-stopped/' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/compose.global.yaml" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-edge/compose.global.yaml.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/compose.global.yaml.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-edge/compose.global.yaml"
|
|
|
|
|
expect_failure "requires supervised relay startup after the nftables gate" \
|
|
|
|
|
"overseas relay supervised fixed-port boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-edge-verifier-not-executable
|
|
|
|
|
chmod -x "$fixture_root/deploy/collab-relay-edge/verify-rate-rules.py"
|
|
|
|
|
expect_failure "requires executable deployment gate helpers" \
|
|
|
|
|
"deployment gate executable boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture relay-bearer-header-buffer-removed
|
|
|
|
|
awk '
|
|
|
|
|
!removed && /client_header_buffer_size 64k;/ {
|
|
|
|
|
removed = 1
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
{ print }
|
|
|
|
|
' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/nginx.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay/nginx.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/nginx.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/nginx.conf"
|
|
|
|
|
expect_failure "requires 48 KiB bearer buffers on both relay ingresses" \
|
|
|
|
|
"48 KiB relay bearer ingress header boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture cn-federation-aggregate-limit-removed
|
|
|
|
|
sed '/limit_conn relay_federation_connections 512;/d' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/nginx.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay/nginx.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/nginx.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/nginx.conf"
|
|
|
|
|
expect_failure "keeps the trusted federation backhaul off the public per-IP ceiling" \
|
|
|
|
|
"CN WSS/federation ingress boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture locator-hsm-boundary-removed
|
|
|
|
|
sed '/OPENPENCIL_COLLAB_LOCATOR_HSM_SOCKET:/d' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/compose.yaml" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator/compose.yaml.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/compose.yaml.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/compose.yaml"
|
|
|
|
|
expect_failure "requires the external locator HSM socket boundary" \
|
|
|
|
|
"locator production container boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture locator-per-source-limit-removed
|
|
|
|
|
sed '/limit_req_zone.*openpencil_locator_per_source/d' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/nginx-http-limits.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator/nginx-http-limits.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/nginx-http-limits.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/nginx-http-limits.conf"
|
|
|
|
|
expect_failure "requires per-source locator ingress throttling" \
|
|
|
|
|
"locator per-source ingress boundary"
|
|
|
|
|
|
2026-08-02 00:31:13 +00:00
|
|
|
new_fixture locator-pairing-route-removed
|
|
|
|
|
awk '!index($0, "location = /v1/pairing-code {")' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/nginx-location.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator/nginx-location.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/nginx-location.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator/nginx-location.conf"
|
|
|
|
|
expect_failure "requires the pairing publish route at the locator ingress" \
|
|
|
|
|
"locator exact-route ingress boundary"
|
|
|
|
|
|
2026-07-29 13:42:33 +00:00
|
|
|
new_fixture locator-edge-crl-secure-ownership-removed
|
|
|
|
|
awk '!index($0, "CRL/CA files must be root:101 mode 0440")' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/rotate-cn-crl.sh" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator-edge/rotate-cn-crl.sh.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/rotate-cn-crl.sh.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/rotate-cn-crl.sh"
|
|
|
|
|
expect_failure "requires secure ownership before locator CRL activation" \
|
|
|
|
|
"locator federation CRL activation boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture mutable-relay-container-base
|
|
|
|
|
awk '
|
|
|
|
|
/^FROM rust:/ {
|
|
|
|
|
print "FROM rust:1.94-bookworm AS build"
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
{ print }
|
|
|
|
|
' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/Dockerfile" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay/Dockerfile.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/Dockerfile.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay/Dockerfile"
|
|
|
|
|
expect_failure "requires immutable digests for relay container base images" \
|
|
|
|
|
"relay container base images must use reviewed immutable SHA-256 digests"
|
|
|
|
|
|
|
|
|
|
new_fixture locator-edge-mtls-verification-removed
|
|
|
|
|
sed '/proxy_ssl_verify on;/d' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/global-nginx.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator-edge/global-nginx.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/global-nginx.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/global-nginx.conf"
|
|
|
|
|
expect_failure "requires outer-mTLS verification on the overseas locator ingress" \
|
|
|
|
|
"overseas locator inner-TLS passthrough boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture locator-edge-exact-host-removed
|
|
|
|
|
awk '!index($0, "if ($http_host != locator.example.cn) {")' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf"
|
|
|
|
|
expect_failure "requires an exact inner HTTPS Host at the CN locator terminator" \
|
|
|
|
|
"CN locator exact inner-HTTPS boundary"
|
|
|
|
|
|
2026-08-02 00:31:13 +00:00
|
|
|
new_fixture locator-edge-pairing-claim-route-removed
|
|
|
|
|
awk '!index($0, "location = /v1/pairing-code/claim {")' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/cn-locator-https-nginx.conf"
|
|
|
|
|
expect_failure "requires the pairing claim route at the CN locator terminator" \
|
|
|
|
|
"CN locator exact inner-HTTPS boundary"
|
|
|
|
|
|
2026-07-29 13:42:33 +00:00
|
|
|
new_fixture locator-edge-source-rate-removed
|
|
|
|
|
awk '!index($0, "limit rate over 60/minute burst 20 packets")' \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/install-global-new-connection-rate.sh" \
|
|
|
|
|
> "$fixture_root/deploy/collab-relay-locator-edge/install-global-new-connection-rate.sh.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/install-global-new-connection-rate.sh.next" \
|
|
|
|
|
"$fixture_root/deploy/collab-relay-locator-edge/install-global-new-connection-rate.sh"
|
|
|
|
|
expect_failure "requires the overseas locator per-source connection-rate gate" \
|
|
|
|
|
"overseas locator per-source connection-rate boundary"
|
|
|
|
|
|
|
|
|
|
new_fixture missing-hard-limit
|
|
|
|
|
awk '!/MAX_OPS_PER_TXN/' \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/protocol.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab/src/protocol.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/protocol.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab/src/protocol.rs"
|
|
|
|
|
expect_failure "rejects removal of a protocol hard-limit anchor" \
|
|
|
|
|
"protocol hard limit"
|
|
|
|
|
|
|
|
|
|
new_fixture public-transport-queue
|
|
|
|
|
sed \
|
|
|
|
|
's/pub(crate) struct BoundedTransferQueue/pub struct BoundedTransferQueue/' \
|
|
|
|
|
"$fixture_root/crates/op-collab-transport/src/queue.rs" \
|
|
|
|
|
> "$fixture_root/crates/op-collab-transport/src/queue.rs.next"
|
|
|
|
|
mv \
|
|
|
|
|
"$fixture_root/crates/op-collab-transport/src/queue.rs.next" \
|
|
|
|
|
"$fixture_root/crates/op-collab-transport/src/queue.rs"
|
|
|
|
|
expect_failure "rejects exposing the transport queue implementation" \
|
|
|
|
|
"bounded queue/rate type"
|
|
|
|
|
|
|
|
|
|
new_fixture untyped-boundary-error
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
'fn bad_boundary() -> Result<(), String> { Ok(()) }' \
|
|
|
|
|
>> "$fixture_root/crates/op-collab/src/protocol.rs"
|
|
|
|
|
expect_failure "rejects untyped public boundary errors" \
|
|
|
|
|
"untyped Result<_, String/&str>"
|
|
|
|
|
|
|
|
|
|
if [[ "$failure_count" -ne 0 ]]; then
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
"check-collab-security-boundaries.test.sh: $failure_count mutation test(s) failed." \
|
|
|
|
|
>&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
printf '%s\n' \
|
|
|
|
|
"check-collab-security-boundaries.test.sh: all $test_index mutation tests pass."
|