fix(mobile): fail open when age checks are unavailable - #7714
Conversation
Signed-off-by: Tom Brow <tomb@block.xyz>
🔐 Codex Security Review
|
|
@codex review |
|
@builderbot review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8273f1885a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…cess Signed-off-by: Tom Brow <tomb@block.xyz>
|
@buzz-security-review 157226b |
|
@builderbot review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 157226b6e6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
|
@codex review |
|
@builderbot review |
|
@buzz-security-review 40056c9 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 40056c9403
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — REQUEST CHANGES
Reviewed 01b6174a1cbad249e93f31df97d4b2ed1d0e8638...40056c94030f9d7925f23f23b9ad9044d58ae06b with systems/integration and adversarial product/native lanes. Two restriction-boundary defects remain.
1. Confirmed restriction can lose to an in-progress notification handoff
BuzzAgeRestrictionSession.handoffIfAllowed now correctly holds LOCK_SH through synchronous delivery, closing the prior check/use race (mobile/ios/BuzzPushKit/Sources/BuzzPushKit/BuzzAgeRestrictionSession.swift:48-60). However, restrict still uses LOCK_EX | LOCK_NB and throws whenever a handoff currently owns the shared lock (BuzzAgeRestrictionSession.swift:17-27). The new test at BuzzAgeRestrictionSessionTests.swift:43-53 codifies that failed acquisition.
After Dart commits AgeSignalState.restricted, this failure returns through purgeAgeRestrictedNotifications; _AgeRestrictedPushCleanup then waits five seconds before retrying (mobile/lib/features/age_gate/age_signal_push_bootstrap.dart:12-25,153-179). During that gap no exclusive authority exists, so a newer notification extension can acquire LOCK_SH and deliver private content after under-18 status is confirmed. Repeated handoffs can extend the gap.
Author action: make restriction acquisition wait through the short already-committed synchronous handoff on the bridge’s background serial queue (handling EINTR), or use an equivalent protocol that immediately prevents newer shared handoffs once restriction is pending. Replace the contention test with deterministic proof that the in-progress handoff completes, restriction becomes authoritative before restrict returns, and every subsequent handoff is suppressed.
Verification owner: author supplies the production-seam Swift contention regression; reviewer revalidates it at the new exact head.
2. Confirmed restriction still permits fresh authenticated HTTP relay queries
The websocket path now watches ageSignalProvider, tears down on restriction, and blocks _connect (mobile/lib/shared/relay/relay_session.dart:133-152,478-502). But queryRelay() has no _ageRestricted or disposal guard and always calls _httpQueryClient.post (relay_session.dart:155-180). RelayHttpQueryClient.close() clears current generations without becoming terminal, so its next post() creates a fresh client (mobile/lib/shared/relay/relay_http_query_client.dart:18-59). Retained production callers include channel, activity, and membership providers.
A focused mutation invoking queryRelay after the age state became restricted observed one HTTP call where zero was required. The probe was removed and the reviewed tree returned clean.
Author action: reject queryRelay while restricted and after terminal disposal, and extend the retained-provider restriction test to prove no HTTP request occurs. Prefer making the session/transport terminal during restriction so future request APIs cannot reopen access accidentally.
Verification owner: author supplies failing-before/passing-after focused coverage; reviewer reruns the relay and age suites at the new exact head.
Validation and confidence gaps
At clean exact head 40056c94030f9d7925f23f23b9ad9044d58ae06b:
swift testinmobile/ios/BuzzPushKit: PASS — 78 XCTest + 34 Swift Testing tests.flutter test test/shared/relay/relay_session_test.dart test/features/age_gate: PASS — 95 tests.git diff --check 01b6174a...HEAD: PASS.- PR head and review account were independently verified; the PR author is
brow, reviewer account isjedwards27.
The current CI mobile and mobile-Swift jobs were still running at verdict time, with no completed failures. Real-device age API/account/signing/consent behavior remains dogfood-owned. Another full mobile run reported seven apparently unrelated push/settings failures after 2,238 passes; that is a confidence gap pending CI, not a basis for these request-changes findings.
Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
|
🤖 Both requested changes are fixed and regression-tested.
Validation: 46 relay tests, 129 pairing/age tests, 78 XCTest plus 42 Swift Testing package tests, all 68 native simulator tests, and the full mobile pre-push gate passed. Real-device OS/account behavior remains deferred to dogfood. |
|
@codex review |
|
@builderbot review |
|
@buzz-security-review f86dc77 |
There was a problem hiding this comment.
💡 Codex Review
buzz/mobile/lib/features/pairing/pairing_provider.dart
Lines 757 to 759 in f86dc77
If restriction is confirmed while authenticateWithCommunity() is awaiting its storage writes, reset() increments the pairing generation but cannot cancel those writes; this post-await check only suppresses the success state after the community and active ID have already been persisted. Fresh evidence in this revision is that the new generation fence is placed after the durable authentication call, so the previously reported pairing import can still complete during this narrower race. Move the generation/restriction check into the persistence boundary or make the import cancellable/rollback-safe.
AGENTS.md reference: AGENTS.md:L176-L179
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Tom Brow <tomb@block.xyz>
Signed-off-by: Tom Brow <tomb@block.xyz>
|
🤖 Regarding “Fence credential persistence before it commits”: an already-started, user-requested credential save is allowed to finish. Age restriction is an access decision for this launch; it does not delete saved credentials or roll back partially completed authentication storage writes. The new integration test pauses the real authentication/storage path, confirms restriction, then completes persistence. It verifies that pairing remains cancelled, the restriction page remains visible, and the relay opens no connection. The separate Huddle fix and regression also closes active media/transport and prevents rejoining while restricted. I am retaining the completed credential save to avoid introducing partial rollback or credential-loss risks into the fail-open age check. The save cannot override the current restriction. |
|
@buzz-security-review 171f723 |
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — APPROVE
Reviewed: 01b6174a1cbad249e93f31df97d4b2ed1d0e8638...171f7238f2718d754bafad654dd2b7e0ce26943a (exact head 171f7238f2718d754bafad654dd2b7e0ce26943a)
Risk: high — this changes the mobile startup/restriction boundary across pairing/auth persistence, authenticated relay HTTP/WebSocket work, Huddle media/transport, and iOS notification handoff concurrency.
Integrated verdict
No unresolved author-actionable defect remains.
The prior notification race and relay-HTTP reopening defects are fixed at their production seams: restriction authority suppresses newer notification handoffs while separately serializing cleanup, and restricted/disposed relay sessions reject new HTTP work and retire stale responses without incorrectly cancelling ordinary reconnect-era requests.
I also traced the disputed late pairing commit. reset() retires pairing UI continuations, but an already-entered authenticateWithCommunity() may still finish its secure-storage writes and publish authenticated state (mobile/lib/features/pairing/pairing_provider.dart:754-760,861-865; mobile/lib/shared/auth/auth_provider.dart:59-74). The exact-head test now makes that behavior explicit: it pauses the real storage path, commits restriction, permits the user-initiated save to settle, and proves the restriction page remains authoritative while relay connections stay at zero and Huddle stays idle (mobile/test/features/age_gate/age_gate_app_test.dart:36-119).
I do not classify the retained save as a defect in this PR. The declared contract is a process-scoped access decision, not credential deletion or transactional rollback; normal startup intentionally begins allowed on every launch (mobile/lib/features/age_gate/age_signal_provider.dart:30-57), and the app gates authenticated relay startup while restricted (mobile/lib/app.dart:341-356). Requiring deletion/rollback would enlarge this fix into destructive identity lifecycle behavior and create partial-write/credential-loss risks outside its stated contract. The important invariant here is that late work cannot reopen restricted access in the confirming process, and the production-facing regression proves that boundary.
The new Huddle fence is consistent with that invariant: the session watches age state, disposal retires active work and releases resources, join() rejects while restricted, and a focused test covers connected teardown, blocked rejoin, and allowed recovery (mobile/lib/shared/huddle/huddle_session.dart:203-233; mobile/test/shared/huddle/huddle_session_test.dart:16-54).
Author action: none.
Verification owner: release/dogfood owns physical-device age API, account/signing/consent, and native post-confirmation observation.
Validation at exact clean head
just mobile-test: PASS — 2,251 passed, 4 skipped, including configured and unconfigured push recipes.swift testinmobile/ios/BuzzPushKit: PASS — 78 XCTest + 42 Swift Testing tests.- Independent focused Flutter age/pairing/auth/Huddle/relay run: PASS — 117 tests.
- My clean exact-head run of
flutter test test/features/age_gate/age_gate_app_test.dart test/features/pairing/pairing_provider_test.dart test/shared/huddle/huddle_session_test.dart test/shared/relay/relay_session_test.dart: PASS — 108 tests. git diff --check 01b6174a1cbad249e93f31df97d4b2ed1d0e8638..171f7238f2718d754bafad654dd2b7e0ce26943a: PASS.- Reviewer
jedwards27and PR authorbrowwere verified independently. HEAD and worktree cleanliness were rechecked after execution.
CI: no completed failure observed at review time; the current mobile and mobile-Swift jobs were still running. Those external gates retain merge ownership.
Residual risk: no physical-device/native UI journey was independently observed at this head. Real OS prompt/account/signing behavior remains a dogfood confidence gap, not an author code defect.
* origin/main: (75 commits) fix(workflows): make deletion persistent and retryable (#7735) fix(mobile): preserve thread replies through refresh failures (#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (#7758) fix(relay): exclude ephemeral activity from message quota (#7736) release: push gateway chart 0.3.1 (#7749) fix(push): label plaintext push gateway service as HTTP (#7717) Replace personal and internal data in desktop test fixtures (#7748) Add mobile VISION (#7710) fix(mobile): keep relay sessions stable during push lease updates (#7745) fix(desktop): keep managed agent avatars usable across communities (#7732) fix(mobile): fail open when age checks are unavailable (#7714) fix(ci): don't run desktop tests for purely mobile client changes (#7709) fix(mobile): temporarily disable age gating (#7708) feat(db): expose connection setup metrics (#7286) Isolate S3 storage metrics from the relay (#7543) fix(web): route mobile invite downloads to app stores (#7702) feat(mobile): show build number with version in settings (#7697) release(mobile-infra): buzz-push-gateway 0.3.0 (#7685) Add authenticated WebSocket recovery telemetry (#7546) Instrument database pool roles (#7356) ... # Conflicts: # crates/buzz-db/src/runtime/migration.rs
* origin/main: (81 commits) fix(mobile): avoid opening empty threads on message tap (#7756) fix(workflows): make deletion persistent and retryable (#7735) fix(mobile): preserve thread replies through refresh failures (#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (#7758) fix(relay): exclude ephemeral activity from message quota (#7736) release: push gateway chart 0.3.1 (#7749) fix(push): label plaintext push gateway service as HTTP (#7717) Replace personal and internal data in desktop test fixtures (#7748) Add mobile VISION (#7710) fix(mobile): keep relay sessions stable during push lease updates (#7745) fix(desktop): keep managed agent avatars usable across communities (#7732) fix(mobile): fail open when age checks are unavailable (#7714) fix(ci): don't run desktop tests for purely mobile client changes (#7709) fix(mobile): temporarily disable age gating (#7708) feat(db): expose connection setup metrics (#7286) Isolate S3 storage metrics from the relay (#7543) fix(web): route mobile invite downloads to app stores (#7702) feat(mobile): show build number with version in settings (#7697) release(mobile-infra): buzz-push-gateway 0.3.0 (#7685) Add authenticated WebSocket recovery telemetry (#7546) ... Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
* origin/main: (75 commits) fix(workflows): make deletion persistent and retryable (#7735) fix(mobile): preserve thread replies through refresh failures (#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (#7758) fix(relay): exclude ephemeral activity from message quota (#7736) release: push gateway chart 0.3.1 (#7749) fix(push): label plaintext push gateway service as HTTP (#7717) Replace personal and internal data in desktop test fixtures (#7748) Add mobile VISION (#7710) fix(mobile): keep relay sessions stable during push lease updates (#7745) fix(desktop): keep managed agent avatars usable across communities (#7732) fix(mobile): fail open when age checks are unavailable (#7714) fix(ci): don't run desktop tests for purely mobile client changes (#7709) fix(mobile): temporarily disable age gating (#7708) feat(db): expose connection setup metrics (#7286) Isolate S3 storage metrics from the relay (#7543) fix(web): route mobile invite downloads to app stores (#7702) feat(mobile): show build number with version in settings (#7697) release(mobile-infra): buzz-push-gateway 0.3.0 (#7685) Add authenticated WebSocket recovery telemetry (#7546) Instrument database pool roles (#7356) ...
* origin/main: (75 commits) fix(workflows): make deletion persistent and retryable (#7735) fix(mobile): preserve thread replies through refresh failures (#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (#7758) fix(relay): exclude ephemeral activity from message quota (#7736) release: push gateway chart 0.3.1 (#7749) fix(push): label plaintext push gateway service as HTTP (#7717) Replace personal and internal data in desktop test fixtures (#7748) Add mobile VISION (#7710) fix(mobile): keep relay sessions stable during push lease updates (#7745) fix(desktop): keep managed agent avatars usable across communities (#7732) fix(mobile): fail open when age checks are unavailable (#7714) fix(ci): don't run desktop tests for purely mobile client changes (#7709) fix(mobile): temporarily disable age gating (#7708) feat(db): expose connection setup metrics (#7286) Isolate S3 storage metrics from the relay (#7543) fix(web): route mobile invite downloads to app stores (#7702) feat(mobile): show build number with version in settings (#7697) release(mobile-infra): buzz-push-gateway 0.3.0 (#7685) Add authenticated WebSocket recovery telemetry (#7546) Instrument database pool roles (#7356) ... Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…p-20260915 * origin/main: (28 commits) fix(mobile): avoid opening empty threads on message tap (block#7756) fix(workflows): make deletion persistent and retryable (block#7735) fix(mobile): preserve thread replies through refresh failures (block#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (block#7758) fix(relay): exclude ephemeral activity from message quota (block#7736) release: push gateway chart 0.3.1 (block#7749) fix(push): label plaintext push gateway service as HTTP (block#7717) Replace personal and internal data in desktop test fixtures (block#7748) Add mobile VISION (block#7710) fix(mobile): keep relay sessions stable during push lease updates (block#7745) fix(desktop): keep managed agent avatars usable across communities (block#7732) fix(mobile): fail open when age checks are unavailable (block#7714) fix(ci): don't run desktop tests for purely mobile client changes (block#7709) fix(mobile): temporarily disable age gating (block#7708) feat(db): expose connection setup metrics (block#7286) Isolate S3 storage metrics from the relay (block#7543) fix(web): route mobile invite downloads to app stores (block#7702) feat(mobile): show build number with version in settings (block#7697) release(mobile-infra): buzz-push-gateway 0.3.0 (block#7685) Add authenticated WebSocket recovery telemetry (block#7546) ... Signed-off-by: Brainy Bumble <0ed7657b57c0e8a9f5288390dd6c8d5d0a3a06abe9b01b9006814f52077d6cdf@buzz.block.builderlab.xyz>
…ecurity * origin/main: (22 commits) feat(relay): add admin HTTP routes for member restriction management (#7302) fix(relay): fire kick live side effects at convergence; persist target; fence re-add race with held lock (#7298) feat(relay): add atomic complete read-state snapshots (#7572) fix(desktop): register macOS badges for new and existing installs (#7783) fix(mobile): avoid opening empty threads on message tap (#7756) fix(workflows): make deletion persistent and retryable (#7735) fix(mobile): preserve thread replies through refresh failures (#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (#7758) fix(relay): exclude ephemeral activity from message quota (#7736) release: push gateway chart 0.3.1 (#7749) fix(push): label plaintext push gateway service as HTTP (#7717) Replace personal and internal data in desktop test fixtures (#7748) Add mobile VISION (#7710) fix(mobile): keep relay sessions stable during push lease updates (#7745) fix(desktop): keep managed agent avatars usable across communities (#7732) fix(mobile): fail open when age checks are unavailable (#7714) fix(ci): don't run desktop tests for purely mobile client changes (#7709) fix(mobile): temporarily disable age gating (#7708) feat(db): expose connection setup metrics (#7286) Isolate S3 storage metrics from the relay (#7543) ... Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
…ness-overload * origin/main: (74 commits) feat(relay): add admin HTTP routes for member restriction management (#7302) fix(relay): fire kick live side effects at convergence; persist target; fence re-add race with held lock (#7298) feat(relay): add atomic complete read-state snapshots (#7572) fix(desktop): register macOS badges for new and existing installs (#7783) fix(mobile): avoid opening empty threads on message tap (#7756) fix(workflows): make deletion persistent and retryable (#7735) fix(mobile): preserve thread replies through refresh failures (#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (#7758) fix(relay): exclude ephemeral activity from message quota (#7736) release: push gateway chart 0.3.1 (#7749) fix(push): label plaintext push gateway service as HTTP (#7717) Replace personal and internal data in desktop test fixtures (#7748) Add mobile VISION (#7710) fix(mobile): keep relay sessions stable during push lease updates (#7745) fix(desktop): keep managed agent avatars usable across communities (#7732) fix(mobile): fail open when age checks are unavailable (#7714) fix(ci): don't run desktop tests for purely mobile client changes (#7709) fix(mobile): temporarily disable age gating (#7708) feat(db): expose connection setup metrics (#7286) Isolate S3 storage metrics from the relay (#7543) ... Signed-off-by: tornquist <tornquist@squareup.com>
…rcement * origin/main: (87 commits) feat(relay): add admin HTTP routes for member restriction management (#7302) fix(relay): fire kick live side effects at convergence; persist target; fence re-add race with held lock (#7298) feat(relay): add atomic complete read-state snapshots (#7572) fix(desktop): register macOS badges for new and existing installs (#7783) fix(mobile): avoid opening empty threads on message tap (#7756) fix(workflows): make deletion persistent and retryable (#7735) fix(mobile): preserve thread replies through refresh failures (#7757) fix(mobile): keep iOS message menu actions responsive after rebuilds (#7758) fix(relay): exclude ephemeral activity from message quota (#7736) release: push gateway chart 0.3.1 (#7749) fix(push): label plaintext push gateway service as HTTP (#7717) Replace personal and internal data in desktop test fixtures (#7748) Add mobile VISION (#7710) fix(mobile): keep relay sessions stable during push lease updates (#7745) fix(desktop): keep managed agent avatars usable across communities (#7732) fix(mobile): fail open when age checks are unavailable (#7714) fix(ci): don't run desktop tests for purely mobile client changes (#7709) fix(mobile): temporarily disable age gating (#7708) feat(db): expose connection setup metrics (#7286) Isolate S3 storage metrics from the relay (#7543) ... Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # crates/buzz-auth/src/nip98.rs # crates/buzz-relay/Cargo.toml
Age-check errors previously blocked Buzz startup. Normal app startup now proceeds immediately; only a valid under-18 response from the current launch request can restrict access.
Age check enforcement remains disabled at compile time by default. Builds can opt in with
--dart-define=BUZZ_AGE_GATING_ENABLED=true.