Audit Apple mobile integration (#58)

This commit is contained in:
2026-08-16 14:54:53 +02:00
parent e54d91a83a
commit c2c7a64164
6 changed files with 261 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
#![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"));
}
#[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"));
}