128 lines
4.6 KiB
Rust
128 lines
4.6 KiB
Rust
#![forbid(unsafe_code)]
|
|
|
|
use std::{fs, path::PathBuf};
|
|
|
|
fn workspace_file(path: &str) -> String {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.and_then(std::path::Path::parent)
|
|
.expect("workspace root")
|
|
.to_path_buf();
|
|
fs::read_to_string(root.join(path)).unwrap_or_else(|error| panic!("read {path}: {error}"))
|
|
}
|
|
|
|
#[test]
|
|
fn apple_sources_preserve_the_mobile_security_boundary() {
|
|
let app = workspace_file("apple/Sources/App/IronStorageApp.swift");
|
|
let watch = workspace_file("apple/Sources/Watch/IronStorageWatchApp.swift");
|
|
let autofill = workspace_file("apple/Sources/AutoFill/CredentialProviderViewController.swift");
|
|
let bridges = format!(
|
|
"{}\n{}",
|
|
workspace_file("crates/apple/src/lib.rs"),
|
|
workspace_file("crates/watch-apple/src/lib.rs")
|
|
);
|
|
|
|
for (name, source) in [
|
|
("iPhone", app.as_str()),
|
|
("Watch", watch.as_str()),
|
|
("AutoFill", autofill.as_str()),
|
|
("Apple Rust bridges", bridges.as_str()),
|
|
] {
|
|
for forbidden in [
|
|
"Process(",
|
|
"NSTask",
|
|
concat!("Command", "::new("),
|
|
"posix_spawn(",
|
|
] {
|
|
assert!(!source.contains(forbidden), "{name} launches {forbidden}");
|
|
}
|
|
}
|
|
|
|
for (name, source) in [
|
|
("iPhone", app.as_str()),
|
|
("Watch", watch.as_str()),
|
|
("AutoFill", autofill.as_str()),
|
|
] {
|
|
for forbidden in ["print(", "NSLog(", "Logger(", "os_log("] {
|
|
assert!(
|
|
!source.contains(forbidden),
|
|
"{name} logs through {forbidden}"
|
|
);
|
|
}
|
|
}
|
|
|
|
assert!(!app.contains("FileManager.default"));
|
|
for forbidden in ["otpauth://", "HMAC", "SHA1", "SHA256", "SHA512", ".gpg-id"] {
|
|
assert!(
|
|
!watch.contains(forbidden),
|
|
"Watch implements storage or OTP domain behavior: {forbidden}"
|
|
);
|
|
}
|
|
assert!(watch.contains("core.presentationAt"));
|
|
assert!(watch.contains("SecItemCopyMatching"));
|
|
assert!(app.contains("session.sendMessage(payload"));
|
|
assert!(app.contains("session.transferUserInfo(payload)"));
|
|
assert!(app.contains("session.outstandingUserInfoTransfers"));
|
|
assert!(
|
|
app.split_once("@main")
|
|
.expect("iPhone app delegate")
|
|
.0
|
|
.contains("UIApplication.didBecomeActiveNotification")
|
|
);
|
|
assert!(watch.contains("didReceiveMessage message"));
|
|
assert!(watch.contains("didReceiveUserInfo userInfo"));
|
|
assert!(watch.contains("session.outstandingUserInfoTransfers"));
|
|
assert!(watch.contains("session.transferUserInfo(acknowledgement)"));
|
|
assert!(
|
|
watch
|
|
.split_once("func sceneBecameActive()")
|
|
.expect("Watch foreground handler")
|
|
.1
|
|
.split_once("func sceneBecameInactive()")
|
|
.expect("Watch inactive handler")
|
|
.0
|
|
.contains("receivedApplicationContext")
|
|
);
|
|
assert!(watch.contains("applyAuthoritativeSnapshot"));
|
|
}
|
|
|
|
#[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]
|
|
fn every_apple_bundle_has_an_audited_privacy_manifest() {
|
|
let project = workspace_file("apple/project.yml");
|
|
for path in [
|
|
"Resources/App/PrivacyInfo.xcprivacy",
|
|
"Resources/AutoFill/PrivacyInfo.xcprivacy",
|
|
"Resources/Watch/PrivacyInfo.xcprivacy",
|
|
] {
|
|
assert!(project.contains(path), "Xcode target omits {path}");
|
|
}
|
|
|
|
for path in [
|
|
"apple/Resources/App/PrivacyInfo.xcprivacy",
|
|
"apple/Resources/AutoFill/PrivacyInfo.xcprivacy",
|
|
"apple/Resources/Watch/PrivacyInfo.xcprivacy",
|
|
] {
|
|
let manifest = workspace_file(path);
|
|
assert!(manifest.contains("<key>NSPrivacyTracking</key>\n\t<false/>"));
|
|
assert!(manifest.contains("<key>NSPrivacyCollectedDataTypes</key>\n\t<array/>"));
|
|
}
|
|
|
|
let app = workspace_file("apple/Resources/App/PrivacyInfo.xcprivacy");
|
|
assert!(app.contains("NSPrivacyAccessedAPICategoryFileTimestamp"));
|
|
assert!(app.contains("C617.1"));
|
|
assert!(app.contains("NSPrivacyAccessedAPICategorySystemBootTime"));
|
|
assert!(app.contains("35F9.1"));
|
|
assert!(project.contains("NSFaceIDUsageDescription"));
|
|
assert!(project.contains("NSCameraUsageDescription"));
|
|
}
|