Prepare App Store 1.0 build 4

This commit is contained in:
2026-08-17 16:50:30 +02:00
parent f894d43eb6
commit 0b7c938b23
19 changed files with 191 additions and 62 deletions

View File

@@ -229,7 +229,10 @@ impl WatchSnapshotReceiver {
}
};
let mut pairing_changed = false;
if let Some(accepted) = self.accepted {
// A valid revocation can only remove secrets, so it must survive a sender reset.
if !snapshot.is_revocation()
&& let Some(accepted) = self.accepted
{
if snapshot.revision < accepted.revision {
return Ok(WatchSnapshotApply::Stale);
} else if snapshot.revision == accepted.revision {
@@ -249,10 +252,10 @@ impl WatchSnapshotReceiver {
revision: snapshot.revision,
digest: snapshot.digest,
};
let result = if pairing_changed {
WatchSnapshotApply::PairingChanged
} else if snapshot.is_revocation() {
let result = if snapshot.is_revocation() {
WatchSnapshotApply::Revoked
} else if pairing_changed {
WatchSnapshotApply::PairingChanged
} else {
WatchSnapshotApply::Replaced
};

View File

@@ -60,6 +60,20 @@ fn apple_sources_preserve_the_mobile_security_boundary() {
}
assert!(watch.contains("core.presentationAt"));
assert!(watch.contains("SecItemCopyMatching"));
assert!(app.contains("session.sendMessage(payload"));
assert!(app.contains("session.transferUserInfo(payload)"));
assert!(watch.contains("didReceiveMessage message"));
assert!(watch.contains("didReceiveUserInfo userInfo"));
}
#[test]
fn iphone_entry_editor_keeps_add_action_accessible_while_typing() {
let app = workspace_file("apple/Sources/App/IronStorageApp.swift");
assert!(app.contains("navigationItem.rightBarButtonItems = [save, add]"));
assert!(app.contains("tableView.keyboardDismissMode = .interactive"));
assert!(app.contains("field.returnKeyType = .done"));
assert!(app.contains("textField.resignFirstResponder()"));
assert!(!app.contains("inputAccessoryView = keyboardToolbar"));
}
#[test]

View File

@@ -104,6 +104,32 @@ fn replacement_snapshots_reject_replays_conflicts_and_pairing_changes() -> TestR
WatchSnapshotApply::Stale
);
let reset_journal = directory.path().join("reset-watch-snapshot.toml");
let mut reset_sender = WatchSnapshotSender::load(reset_journal);
let reset_revocation = reset_sender.prepare("paired-watch-a", Vec::new())?;
assert_eq!(reset_revocation.revision(), 1);
assert_eq!(
receiver.apply(SecretBytes::new(
reset_revocation.snapshot().expose().to_vec()
))?,
WatchSnapshotApply::Revoked
);
assert!(
receiver
.current()
.expect("reset revocation")
.is_revocation()
);
let after_reset = reset_sender.prepare(
"paired-watch-a",
vec![entry("otp/reset", "Acme", "reset", b"reset-secret")],
)?;
assert_eq!(after_reset.revision(), 2);
assert_eq!(
receiver.apply(SecretBytes::new(after_reset.snapshot().expose().to_vec()))?,
WatchSnapshotApply::Replaced
);
let changed_watch = sender.prepare(
"paired-watch-b",
vec![entry("otp/carol", "Acme", "carol", b"third-secret")],
@@ -138,6 +164,7 @@ fn replacement_snapshots_reject_replays_conflicts_and_pairing_changes() -> TestR
"first-secret",
"second-secret",
"third-secret",
"reset-secret",
"otpauth://",
"94287082",
] {