Skip to content

Commit dee98b3

Browse files
fix: add audited macOS beta emergency rollback (#10051)
## Summary - add an explicit, admin-authenticated emergency rollback operation restricted to the macOS beta pointer; - require a registered, passed-T2, strictly earlier release plus exact current release/generation CAS; - write an immutable rollback audit record transactionally before replacing only the beta pointer; - retain normal roll-forward-only promotion and Stable/production isolation. ## Verification - `/Users/dazheng/workspace/omi/backend/.venv/bin/python -m pytest tests/unit/test_desktop_update_channels.py tests/unit/test_desktop_updates.py -q` — 109 passed. - Independent security/correctness review confirmed beta-only scope, target qualification, transactional CAS, immutable audit semantics, and no Stable/prod mutation path. Failure-Class: none <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/BasedHardware/omi/pull/10051?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
2 parents 2da0374 + f8e2cec commit dee98b3

6 files changed

Lines changed: 398 additions & 2 deletions

File tree

backend/database/desktop_update_channels.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import re
55
from typing import Any, cast
66
from urllib.parse import urlparse
7+
from uuid import uuid4
78

89
from google.cloud.firestore import transactional
910

1011
from database._client import get_firestore_client
1112

1213
CHANNELS_COLLECTION = "desktop_update_channels"
1314
MANIFESTS_COLLECTION = "desktop_release_manifests"
15+
ROLLBACK_AUDITS_COLLECTION = "desktop_update_channel_rollback_audits"
1416
VALID_CHANNELS = frozenset({"stable", "beta"})
1517
VALID_PLATFORMS = frozenset({"macos", "windows", "linux"})
1618
SHA40_RE = re.compile(r"^[0-9a-f]{40}$", re.IGNORECASE)
@@ -182,6 +184,46 @@ def _build_channel_pointer(
182184
}
183185

184186

187+
def _build_beta_rollback_pointer(
188+
current: dict[str, Any],
189+
manifest: dict[str, Any],
190+
*,
191+
release_id: str,
192+
expected_current_release_id: str,
193+
expected_generation: int,
194+
updated_at: datetime | None = None,
195+
) -> dict[str, Any]:
196+
"""Build the sole permitted non-monotonic pointer transition: macOS beta rollback."""
197+
if manifest["platform"] != "macos":
198+
raise ValueError("rollback target must be a macos release manifest")
199+
qualification = cast(dict[str, Any], manifest["qualification"])
200+
if qualification.get("passed") is not True or str(qualification.get("tier", "")).upper() != "T2":
201+
raise ValueError("rollback target is missing passed T2 qualification evidence")
202+
203+
current_release_id = current.get("release_id")
204+
if current_release_id != expected_current_release_id:
205+
raise ValueError(
206+
f"current release mismatch: expected {expected_current_release_id}, current {current_release_id or 'missing'}"
207+
)
208+
current_generation = _generation(current.get("generation", 0))
209+
if expected_generation != current_generation:
210+
raise ValueError(f"generation mismatch: expected {expected_generation}, current {current_generation}")
211+
212+
current_build = _generation(current.get("build_number"))
213+
if release_id == current_release_id or manifest["build_number"] >= current_build:
214+
raise ValueError("rollback target must be an earlier qualified beta release")
215+
216+
return {
217+
"platform": "macos",
218+
"channel": "beta",
219+
"release_id": release_id,
220+
"version": manifest["version"],
221+
"build_number": manifest["build_number"],
222+
"generation": current_generation + 1,
223+
"updated_at": updated_at or datetime.now(timezone.utc),
224+
}
225+
226+
185227
@transactional
186228
def _promote_channel_transaction(
187229
transaction: Any,
@@ -249,6 +291,92 @@ def promote_channel(
249291
)
250292

251293

294+
@transactional
295+
def _rollback_macos_beta_transaction(
296+
transaction: Any,
297+
pointer_ref: Any,
298+
manifest_ref: Any,
299+
audit_ref: Any,
300+
*,
301+
release_id: str,
302+
expected_current_release_id: str,
303+
expected_generation: int,
304+
audit_id: str,
305+
occurred_at: datetime,
306+
) -> dict[str, Any]:
307+
manifest_snapshot = manifest_ref.get(transaction=transaction)
308+
if not getattr(manifest_snapshot, "exists", False):
309+
raise ValueError("rollback target release manifest does not exist")
310+
raw_manifest: object = manifest_snapshot.to_dict()
311+
manifest_data = cast(dict[str, Any], raw_manifest) if isinstance(raw_manifest, dict) else {}
312+
manifest = normalize_release_manifest(manifest_data)
313+
314+
pointer_snapshot = pointer_ref.get(transaction=transaction)
315+
if not getattr(pointer_snapshot, "exists", False):
316+
raise ValueError("current macos beta pointer does not exist")
317+
current_raw: object = pointer_snapshot.to_dict()
318+
current = cast(dict[str, Any], current_raw) if isinstance(current_raw, dict) else {}
319+
pointer = _build_beta_rollback_pointer(
320+
current,
321+
manifest,
322+
release_id=release_id,
323+
expected_current_release_id=expected_current_release_id,
324+
expected_generation=expected_generation,
325+
updated_at=occurred_at,
326+
)
327+
audit = {
328+
"audit_id": audit_id,
329+
"operation": "macos_beta_rollback",
330+
"platform": "macos",
331+
"channel": "beta",
332+
"previous_release_id": expected_current_release_id,
333+
"previous_generation": expected_generation,
334+
"target_release_id": release_id,
335+
"generation": pointer["generation"],
336+
"occurred_at": occurred_at,
337+
}
338+
# create() provides an immutable, append-only audit record. All reads above
339+
# occur before this first transactional write.
340+
transaction.create(audit_ref, audit)
341+
transaction.set(pointer_ref, pointer)
342+
return {"pointer": pointer, "audit": audit}
343+
344+
345+
def rollback_macos_beta_channel(
346+
release_id: str,
347+
*,
348+
expected_current_release_id: str,
349+
expected_generation: int,
350+
firestore_client: Any = None,
351+
) -> dict[str, Any]:
352+
"""Atomically roll macOS beta back to an earlier, qualified registered release only."""
353+
release_id = release_id.strip()
354+
expected_current_release_id = expected_current_release_id.strip()
355+
if not release_id:
356+
raise ValueError("release_id is required")
357+
if not expected_current_release_id:
358+
raise ValueError("expected_current_release_id is required")
359+
if expected_generation < 0:
360+
raise ValueError("expected_generation must be a non-negative integer")
361+
362+
client = firestore_client if firestore_client is not None else get_firestore_client()
363+
pointer_ref = client.collection(CHANNELS_COLLECTION).document("macos-beta")
364+
manifest_ref = client.collection(MANIFESTS_COLLECTION).document(release_id)
365+
audit_id = uuid4().hex
366+
audit_ref = client.collection(ROLLBACK_AUDITS_COLLECTION).document(audit_id)
367+
return _rollback_macos_beta_transaction(
368+
client.transaction(),
369+
pointer_ref,
370+
manifest_ref,
371+
audit_ref,
372+
release_id=release_id,
373+
expected_current_release_id=expected_current_release_id,
374+
expected_generation=expected_generation,
375+
audit_id=audit_id,
376+
occurred_at=datetime.now(timezone.utc),
377+
)
378+
379+
252380
def get_channel_release(platform: str, channel: str, *, firestore_client: Any = None) -> dict[str, Any] | None:
253381
"""Resolve one explicit channel pointer to its immutable manifest."""
254382
if platform not in VALID_PLATFORMS or channel not in VALID_CHANNELS:

backend/route_policy_manifest.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,28 @@ routes:
116116
data_domain: desktop_updates
117117
deprecation:
118118
state: active
119+
- route_type: http
120+
method: POST
121+
path: /v2/desktop/channels/rollback
122+
policy:
123+
review_status: reviewed
124+
auth:
125+
mechanisms:
126+
- admin_key
127+
placement: inline
128+
scopes: []
129+
byok: not_applicable
130+
rate_limit:
131+
policy_name: none
132+
key_subject: none
133+
enforcement: none
134+
placement: none
135+
timeout_class: default_method
136+
surface: desktop_update
137+
visibility: admin
138+
data_domain: desktop_updates
139+
deprecation:
140+
state: active
119141
- route_type: http
120142
method: POST
121143
path: /v2/desktop/channels/promote

backend/routers/updates.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
import os
55
import random
66
import re
7-
from typing import Any, Optional, List, Dict
7+
from typing import Any, Optional, List, Dict, Literal
88
from xml.sax.saxutils import escape as xml_escape
99

1010
from fastapi import APIRouter, HTTPException, Header, Query
1111
from fastapi.responses import RedirectResponse, Response, HTMLResponse
1212
from pydantic import BaseModel, Field
1313

1414
from database.desktop_previews import delist_preview, get_current_preview, get_preview_manifest, publish_preview
15-
from database.desktop_update_channels import promote_channel, register_release_manifest
15+
from database.desktop_update_channels import promote_channel, register_release_manifest, rollback_macos_beta_channel
1616
from database.desktop_update_policy import default_desktop_update_policy, get_desktop_update_policy
1717
from database.redis_db import delete_generic_cache
1818
from utils.desktop_update_resolver import live_cache_key, resolve_pointer_release
@@ -78,6 +78,16 @@ class DesktopChannelPromotionRequest(BaseModel):
7878
expected_generation: Optional[int] = Field(default=None, ge=0)
7979

8080

81+
class DesktopBetaRollbackRequest(BaseModel):
82+
"""Emergency-only rollback request; the literal fields prevent cross-channel reuse."""
83+
84+
platform: Literal["macos"]
85+
channel: Literal["beta"]
86+
release_id: str = Field(min_length=1)
87+
expected_current_release_id: str = Field(min_length=1)
88+
expected_generation: int = Field(ge=0)
89+
90+
8191
class DesktopPreviewPublishRequest(BaseModel):
8292
"""Immutable metadata for a signed desktop preview artifact."""
8393

@@ -886,3 +896,22 @@ async def promote_desktop_channel(request: DesktopChannelPromotionRequest, secre
886896
live_cache_key(request.platform, request.channel),
887897
)
888898
return {"success": True, "pointer": pointer}
899+
900+
901+
@router.post("/v2/desktop/channels/rollback")
902+
async def rollback_macos_beta_channel_endpoint(request: DesktopBetaRollbackRequest, secret_key: str = Header(...)):
903+
"""Emergency-only, compare-and-swap rollback for the macOS beta pointer."""
904+
if secret_key != os.getenv('ADMIN_KEY'):
905+
raise HTTPException(status_code=403, detail='You are not authorized to perform this action')
906+
try:
907+
result = await run_blocking(
908+
db_executor,
909+
rollback_macos_beta_channel,
910+
request.release_id,
911+
expected_current_release_id=request.expected_current_release_id,
912+
expected_generation=request.expected_generation,
913+
)
914+
except ValueError as exc:
915+
raise HTTPException(status_code=409, detail=str(exc)) from exc
916+
await run_blocking(db_executor, delete_generic_cache, live_cache_key("macos", "beta"))
917+
return {"success": True, **result}

backend/tests/unit/test_desktop_update_channels.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from database.desktop_update_channels import (
66
_build_channel_pointer,
7+
_build_beta_rollback_pointer,
8+
_rollback_macos_beta_transaction,
79
get_channel_release,
810
normalize_release_manifest,
911
register_release_manifest,
@@ -159,3 +161,124 @@ def test_rejects_unqualified_release(self):
159161
release_id=manifest["release_id"],
160162
expected_generation=None,
161163
)
164+
165+
166+
class TestMacosBetaRollbackRules:
167+
def test_rolls_back_qualified_release_and_creates_immutable_audit(self):
168+
current = {
169+
"platform": "macos",
170+
"channel": "beta",
171+
"release_id": "v0.12.84+12084-macos",
172+
"version": "0.12.84+12084",
173+
"build_number": 12084,
174+
"generation": 7,
175+
}
176+
target = normalize_release_manifest(
177+
_manifest(release_id="v0.12.73+12073-macos", version="0.12.73+12073", build_number=12073)
178+
)
179+
pointer = _build_beta_rollback_pointer(
180+
current,
181+
target,
182+
release_id=target["release_id"],
183+
expected_current_release_id=current["release_id"],
184+
expected_generation=7,
185+
)
186+
187+
pointer_snapshot = MagicMock(exists=True)
188+
pointer_snapshot.to_dict.return_value = current
189+
manifest_snapshot = MagicMock(exists=True)
190+
manifest_snapshot.to_dict.return_value = target
191+
pointer_ref = MagicMock()
192+
pointer_ref.get.return_value = pointer_snapshot
193+
manifest_ref = MagicMock()
194+
manifest_ref.get.return_value = manifest_snapshot
195+
audit_ref = MagicMock()
196+
transaction = MagicMock()
197+
198+
result = _rollback_macos_beta_transaction.to_wrap(
199+
transaction,
200+
pointer_ref,
201+
manifest_ref,
202+
audit_ref,
203+
release_id=target["release_id"],
204+
expected_current_release_id=current["release_id"],
205+
expected_generation=7,
206+
audit_id="audit-123",
207+
occurred_at=pointer["updated_at"],
208+
)
209+
210+
assert result["pointer"]["release_id"] == target["release_id"]
211+
assert result["pointer"]["generation"] == 8
212+
assert result["audit"] == {
213+
"audit_id": "audit-123",
214+
"operation": "macos_beta_rollback",
215+
"platform": "macos",
216+
"channel": "beta",
217+
"previous_release_id": current["release_id"],
218+
"previous_generation": 7,
219+
"target_release_id": target["release_id"],
220+
"generation": 8,
221+
"occurred_at": pointer["updated_at"],
222+
}
223+
transaction.create.assert_called_once_with(audit_ref, result["audit"])
224+
transaction.set.assert_called_once_with(pointer_ref, result["pointer"])
225+
226+
def test_rejects_stale_current_release_or_generation(self):
227+
current = {"release_id": "v0.12.84+12084-macos", "build_number": 12084, "generation": 7}
228+
target = normalize_release_manifest(
229+
_manifest(release_id="v0.12.73+12073-macos", version="0.12.73+12073", build_number=12073)
230+
)
231+
232+
with pytest.raises(ValueError, match="current release mismatch"):
233+
_build_beta_rollback_pointer(
234+
current,
235+
target,
236+
release_id=target["release_id"],
237+
expected_current_release_id="v0.12.83+12083-macos",
238+
expected_generation=7,
239+
)
240+
with pytest.raises(ValueError, match="generation mismatch"):
241+
_build_beta_rollback_pointer(
242+
current,
243+
target,
244+
release_id=target["release_id"],
245+
expected_current_release_id=current["release_id"],
246+
expected_generation=6,
247+
)
248+
249+
def test_rejects_unqualified_or_non_macos_target(self):
250+
current = {"release_id": "v0.12.84+12084-macos", "build_number": 12084, "generation": 7}
251+
unqualified = normalize_release_manifest(
252+
_manifest(
253+
release_id="v0.12.73+12073-macos",
254+
version="0.12.73+12073",
255+
build_number=12073,
256+
qualification={"tier": "T2", "passed": False},
257+
)
258+
)
259+
with pytest.raises(ValueError, match="qualification"):
260+
_build_beta_rollback_pointer(
261+
current,
262+
unqualified,
263+
release_id=unqualified["release_id"],
264+
expected_current_release_id=current["release_id"],
265+
expected_generation=7,
266+
)
267+
268+
windows = normalize_release_manifest(
269+
_manifest(
270+
release_id="v0.12.73+12073-windows",
271+
platform="windows",
272+
version="0.12.73+12073",
273+
build_number=12073,
274+
dmg_url=None,
275+
)
276+
)
277+
with pytest.raises(ValueError, match="macos"):
278+
_build_beta_rollback_pointer(
279+
current,
280+
windows,
281+
release_id=windows["release_id"],
282+
expected_current_release_id=current["release_id"],
283+
expected_generation=7,
284+
)

0 commit comments

Comments
 (0)