openpencil/tools/check-collab-security-boundaries-cases.sh

655 lines
29 KiB
Bash
Raw Normal View History

# Mutation cases for check-collab-security-boundaries.test.sh.
# Sourced after the fixture and assertion helpers have been initialized.
write_collab_security_workflow_fixture() {
cat > "$fixture_root/.github/workflows/collab-security.yml" <<'EOF'
pull_request:
paths:
- '.dockerignore'
- '.gitignore'
- 'crates/op-collab-smoke/**'
- '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-hsm/**'
- 'crates/op-collab-relay-locator-server/**'
- '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-hsm/**'
- 'deploy/collab-relay-locator-edge/**'
- 'tools/check-collab-security-boundaries-cases.sh'
- 'tools/check-collab-deployment-boundaries.sh'
- 'tools/check-op-auth-prebuilt.sh'
- 'tools/check-op-auth-prebuilt.test.sh'
- 'tools/package-op-auth-prebuilt.sh'
push:
paths:
- '.dockerignore'
- '.gitignore'
- 'crates/op-collab-smoke/**'
- '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-hsm/**'
- 'crates/op-collab-relay-locator-server/**'
- '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-hsm/**'
- 'deploy/collab-relay-locator-edge/**'
- 'tools/check-collab-security-boundaries-cases.sh'
- 'tools/check-collab-deployment-boundaries.sh'
- 'tools/check-op-auth-prebuilt.sh'
- 'tools/check-op-auth-prebuilt.test.sh'
- 'tools/package-op-auth-prebuilt.sh'
steps:
- run: bash tools/check-op-auth-prebuilt.sh
- run: bash tools/check-op-auth-prebuilt.test.sh
- run: bash -n tools/package-op-auth-prebuilt.sh
- run: cargo test --locked -p op-auth-bridge --test prebuilt_provenance
- run: cargo test --locked -p op-collab-transport
- run: cargo test --locked -p op-collab-transport config::tests
- run: cargo test --locked -p op-collab-transport frame::tests
- run: cargo test --locked -p op-collab-relay-locator-hsm
- run: |
docker build --target test \
-f deploy/collab-relay-locator-hsm/Dockerfile .
- run: bash deploy/collab-relay-edge/validate.sh
- run: bash deploy/collab-relay-locator/validate.sh
- run: bash deploy/collab-relay-locator-edge/validate.sh
EOF
}
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 {
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"
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 complete-transport-workflow-test-removed
sed '/cargo test --locked -p op-collab-transport$/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 complete transport resource-limit test suite" \
"complete transport resource-limit workflow test"
new_fixture locator-hsm-workflow-tests-removed
sed \
-e '/cargo test --locked -p op-collab-relay-locator-hsm/d' \
-e '/docker build --target test/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 locator HSM unit and real SoftHSM workflow tests" \
"locator HSM crate workflow test"
new_fixture locator-hsm-soft-token-target-removed
sed 's/docker build --target test/docker build/' \
"$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 real SoftHSM Docker test stage" \
"real SoftHSM workflow test target"
new_fixture locator-hsm-production-seed
printf '%s\n' \
'const PRODUCTION_SIGNING_SEED: [u8; 32] = [9; 32];' \
>> "$fixture_root/crates/op-collab-relay-locator-hsm/src/lib.rs"
expect_failure "scans the locator HSM crate for deterministic production keys" \
"deterministic signing/key seed leaked"
new_fixture desktop-renewal-vec-copy
printf '%s\n' \
'fn bad(ticket: Ticket) { let _ = ticket.expose().as_bytes().to_vec(); }' \
>> "$fixture_root/crates/op-collab-host/src/runtime/types.rs"
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
: > "$fixture_root/crates/op-collab-host/src/runtime/runtime-ticket.token"
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"
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"
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"
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"
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."