@zigrivers/scaffold 3.6.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +127 -12
  2. package/content/knowledge/backend/backend-api-design.md +103 -0
  3. package/content/knowledge/backend/backend-architecture.md +100 -0
  4. package/content/knowledge/backend/backend-async-patterns.md +101 -0
  5. package/content/knowledge/backend/backend-auth-patterns.md +100 -0
  6. package/content/knowledge/backend/backend-conventions.md +105 -0
  7. package/content/knowledge/backend/backend-data-modeling.md +102 -0
  8. package/content/knowledge/backend/backend-deployment.md +100 -0
  9. package/content/knowledge/backend/backend-dev-environment.md +102 -0
  10. package/content/knowledge/backend/backend-observability.md +102 -0
  11. package/content/knowledge/backend/backend-project-structure.md +100 -0
  12. package/content/knowledge/backend/backend-requirements.md +103 -0
  13. package/content/knowledge/backend/backend-security.md +104 -0
  14. package/content/knowledge/backend/backend-testing.md +101 -0
  15. package/content/knowledge/backend/backend-worker-patterns.md +100 -0
  16. package/content/knowledge/cli/cli-architecture.md +101 -0
  17. package/content/knowledge/cli/cli-conventions.md +117 -0
  18. package/content/knowledge/cli/cli-dev-environment.md +121 -0
  19. package/content/knowledge/cli/cli-distribution-patterns.md +106 -0
  20. package/content/knowledge/cli/cli-interactivity-patterns.md +116 -0
  21. package/content/knowledge/cli/cli-output-patterns.md +107 -0
  22. package/content/knowledge/cli/cli-project-structure.md +124 -0
  23. package/content/knowledge/cli/cli-requirements.md +101 -0
  24. package/content/knowledge/cli/cli-shell-integration.md +130 -0
  25. package/content/knowledge/cli/cli-testing.md +134 -0
  26. package/content/knowledge/library/library-api-design.md +306 -0
  27. package/content/knowledge/library/library-architecture.md +247 -0
  28. package/content/knowledge/library/library-bundling.md +244 -0
  29. package/content/knowledge/library/library-conventions.md +229 -0
  30. package/content/knowledge/library/library-dev-environment.md +220 -0
  31. package/content/knowledge/library/library-documentation.md +300 -0
  32. package/content/knowledge/library/library-project-structure.md +237 -0
  33. package/content/knowledge/library/library-requirements.md +173 -0
  34. package/content/knowledge/library/library-security.md +257 -0
  35. package/content/knowledge/library/library-testing.md +319 -0
  36. package/content/knowledge/library/library-type-definitions.md +284 -0
  37. package/content/knowledge/library/library-versioning.md +300 -0
  38. package/content/knowledge/mobile-app/mobile-app-architecture.md +283 -0
  39. package/content/knowledge/mobile-app/mobile-app-conventions.md +180 -0
  40. package/content/knowledge/mobile-app/mobile-app-deployment.md +298 -0
  41. package/content/knowledge/mobile-app/mobile-app-dev-environment.md +257 -0
  42. package/content/knowledge/mobile-app/mobile-app-distribution.md +264 -0
  43. package/content/knowledge/mobile-app/mobile-app-observability.md +317 -0
  44. package/content/knowledge/mobile-app/mobile-app-offline-patterns.md +311 -0
  45. package/content/knowledge/mobile-app/mobile-app-project-structure.md +245 -0
  46. package/content/knowledge/mobile-app/mobile-app-push-notifications.md +321 -0
  47. package/content/knowledge/mobile-app/mobile-app-requirements.md +147 -0
  48. package/content/knowledge/mobile-app/mobile-app-security.md +338 -0
  49. package/content/knowledge/mobile-app/mobile-app-testing.md +400 -0
  50. package/content/knowledge/web-app/web-app-api-patterns.md +224 -0
  51. package/content/knowledge/web-app/web-app-architecture.md +116 -0
  52. package/content/knowledge/web-app/web-app-auth-patterns.md +256 -0
  53. package/content/knowledge/web-app/web-app-conventions.md +121 -0
  54. package/content/knowledge/web-app/web-app-data-patterns.md +218 -0
  55. package/content/knowledge/web-app/web-app-deployment-workflow.md +143 -0
  56. package/content/knowledge/web-app/web-app-deployment.md +134 -0
  57. package/content/knowledge/web-app/web-app-design-system.md +158 -0
  58. package/content/knowledge/web-app/web-app-dev-environment.md +173 -0
  59. package/content/knowledge/web-app/web-app-observability.md +221 -0
  60. package/content/knowledge/web-app/web-app-project-structure.md +160 -0
  61. package/content/knowledge/web-app/web-app-rendering-strategies.md +133 -0
  62. package/content/knowledge/web-app/web-app-requirements.md +112 -0
  63. package/content/knowledge/web-app/web-app-security.md +193 -0
  64. package/content/knowledge/web-app/web-app-session-patterns.md +214 -0
  65. package/content/knowledge/web-app/web-app-testing.md +249 -0
  66. package/content/knowledge/web-app/web-app-ux-patterns.md +162 -0
  67. package/content/methodology/backend-overlay.yml +73 -0
  68. package/content/methodology/cli-overlay.yml +69 -0
  69. package/content/methodology/library-overlay.yml +67 -0
  70. package/content/methodology/mobile-app-overlay.yml +71 -0
  71. package/content/methodology/web-app-overlay.yml +79 -0
  72. package/dist/cli/commands/init.d.ts +21 -0
  73. package/dist/cli/commands/init.d.ts.map +1 -1
  74. package/dist/cli/commands/init.js +261 -13
  75. package/dist/cli/commands/init.js.map +1 -1
  76. package/dist/cli/commands/init.test.js +206 -0
  77. package/dist/cli/commands/init.test.js.map +1 -1
  78. package/dist/config/schema.d.ts +1392 -64
  79. package/dist/config/schema.d.ts.map +1 -1
  80. package/dist/config/schema.js +82 -5
  81. package/dist/config/schema.js.map +1 -1
  82. package/dist/config/schema.test.js +302 -1
  83. package/dist/config/schema.test.js.map +1 -1
  84. package/dist/core/assembly/overlay-loader.d.ts.map +1 -1
  85. package/dist/core/assembly/overlay-loader.js +2 -1
  86. package/dist/core/assembly/overlay-loader.js.map +1 -1
  87. package/dist/core/assembly/overlay-loader.test.js +56 -0
  88. package/dist/core/assembly/overlay-loader.test.js.map +1 -1
  89. package/dist/e2e/game-pipeline.test.js +1 -0
  90. package/dist/e2e/game-pipeline.test.js.map +1 -1
  91. package/dist/e2e/project-type-overlays.test.d.ts +16 -0
  92. package/dist/e2e/project-type-overlays.test.d.ts.map +1 -0
  93. package/dist/e2e/project-type-overlays.test.js +834 -0
  94. package/dist/e2e/project-type-overlays.test.js.map +1 -0
  95. package/dist/types/config.d.ts +19 -2
  96. package/dist/types/config.d.ts.map +1 -1
  97. package/dist/types/index.d.ts +0 -1
  98. package/dist/types/index.d.ts.map +1 -1
  99. package/dist/types/index.js +0 -1
  100. package/dist/types/index.js.map +1 -1
  101. package/dist/wizard/questions.d.ts +27 -1
  102. package/dist/wizard/questions.d.ts.map +1 -1
  103. package/dist/wizard/questions.js +142 -3
  104. package/dist/wizard/questions.js.map +1 -1
  105. package/dist/wizard/questions.test.js +206 -8
  106. package/dist/wizard/questions.test.js.map +1 -1
  107. package/dist/wizard/wizard.d.ts +21 -0
  108. package/dist/wizard/wizard.d.ts.map +1 -1
  109. package/dist/wizard/wizard.js +27 -1
  110. package/dist/wizard/wizard.js.map +1 -1
  111. package/package.json +1 -1
  112. package/dist/types/wizard.d.ts +0 -14
  113. package/dist/types/wizard.d.ts.map +0 -1
  114. package/dist/types/wizard.js +0 -2
  115. package/dist/types/wizard.js.map +0 -1
@@ -0,0 +1,147 @@
1
+ ---
2
+ name: mobile-app-requirements
3
+ description: Platform guidelines (Apple HIG, Material Design), performance budgets, device matrix, and accessibility requirements for mobile apps
4
+ topics: [mobile-app, requirements, hig, material-design, accessibility, performance, device-matrix]
5
+ ---
6
+
7
+ Mobile app requirements differ fundamentally from web requirements: platform guidelines are design law, not suggestions; performance budgets are stricter because CPU and battery are finite; accessibility is both a legal obligation and a market expansion. Define all of these explicitly before writing code — changing navigation patterns or accessibility models mid-project is expensive.
8
+
9
+ ## Summary
10
+
11
+ Mobile app requirements encompass platform design guidelines (Apple HIG for iOS, Material Design for Android), performance budgets (launch time, frame rate, memory), device matrix coverage, and accessibility compliance. iOS and Android have distinct design languages, interaction patterns, and review processes — apps that ignore platform conventions face App Store rejection or poor user ratings. Document requirements across all four dimensions before implementation begins.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Apple Human Interface Guidelines (HIG)
16
+
17
+ The HIG is not aspirational — App Store review uses it as a rejection criterion. Key rules:
18
+
19
+ **Navigation patterns**
20
+ - iOS uses tab bars for top-level navigation, not drawers. Bottom navigation bars with 2–5 items represent the canonical iOS navigation pattern.
21
+ - Navigation controllers manage hierarchical content with back-button semantics. Never intercept the swipe-back gesture unless replacing it with an equivalent interaction.
22
+ - Modal presentations are for transient tasks that interrupt the main flow. Use `.sheet` (card modal) or `.fullScreenCover` (full-screen modal) appropriately.
23
+ - Avoid Android-style hamburger drawers on iOS — they are foreign to the platform and fail review for poor HIG compliance.
24
+
25
+ **Visual design**
26
+ - Use SF Symbols for iconography on iOS — they scale with Dynamic Type and render at all weights automatically.
27
+ - Respect the safe area insets: status bar, home indicator, and notch are system-owned. Never place interactive controls behind them.
28
+ - Support Dynamic Type: all text must scale from accessibility-small to accessibility-xxxlarge. Hard-coded font sizes fail accessibility audits.
29
+ - Dark mode is not optional — it has been mandatory since iOS 13. Every screen must render correctly in both modes.
30
+
31
+ **Interaction**
32
+ - Minimum tap target size: 44×44 points. Smaller targets fail accessibility review and frustrate users on small devices.
33
+ - Haptic feedback should mirror system patterns: `.impactOccurred()` for selections, `.notificationOccurred(.success/.error/.warning)` for outcomes.
34
+ - Do not disable system gestures (pull-to-refresh, swipe-back, swipe-from-edge) without providing explicit equivalents.
35
+
36
+ ### Material Design Guidelines (Android)
37
+
38
+ Material Design 3 (Material You) is the current standard for Android apps. Google Play review does not enforce it as strictly as Apple enforces HIG, but departure from Material patterns drives poor reviews.
39
+
40
+ **Navigation patterns**
41
+ - Bottom Navigation Bar for 3–5 top-level destinations (matches iOS tab bar in position and purpose).
42
+ - Navigation Drawer for apps with 6+ top-level destinations or complex content hierarchies.
43
+ - Top App Bar for screen titles, primary actions, and back navigation.
44
+ - Use Navigation Component (Jetpack) with a NavGraph to manage the back stack. Manual FragmentTransaction management leads to back-stack corruption.
45
+
46
+ **Visual design**
47
+ - Material You uses dynamic color: the system extracts a palette from the wallpaper and applies it app-wide. Apps must use Material theme color roles (`colorPrimary`, `colorSurface`, etc.) — hard-coded hex colors break dynamic theming.
48
+ - Typography scale: use `MaterialTheme.typography.*` roles (displayLarge, headlineMedium, bodyLarge, etc.), not arbitrary font sizes.
49
+ - Elevation and shadows communicate hierarchy — use Material elevation tokens, not custom shadow implementations.
50
+
51
+ **Components**
52
+ - Use Material components from `androidx.compose.material3` (Compose) or `com.google.android.material` (View system) — custom implementations break adaptive theming.
53
+ - Extended FAB for primary actions, regular FAB only when the action is obvious from context.
54
+ - BottomSheet for contextual actions; Dialog for decisions requiring user input.
55
+
56
+ ### Performance Budgets
57
+
58
+ Define performance budgets before implementation — retrofitting them is expensive.
59
+
60
+ **App launch time**
61
+ - Cold start target: under 2 seconds to interactive on a mid-range device (Pixel 4a / iPhone SE 3rd gen)
62
+ - Warm start target: under 500ms
63
+ - Measure with: Instruments (iOS Time Profiler), Android Studio CPU Profiler, Firebase Performance Monitoring
64
+ - Common cold-start killers: synchronous disk I/O on main thread, large dependency graphs initialized eagerly, synchronous network calls before first frame
65
+
66
+ **Frame rate**
67
+ - Target: 60fps sustained (16ms frame budget), 120fps on ProMotion/high-refresh displays where supported
68
+ - Drop below 60fps is visible as "jank" — users notice immediately
69
+ - iOS: use Instruments > Core Animation template to identify dropped frames
70
+ - Android: Profile GPU Rendering overlay (`adb shell setprop debug.hwui.profile true`) and Systrace
71
+
72
+ **Memory**
73
+ - iOS: no hard memory limit, but the OS kills apps under memory pressure. Monitor with Instruments > Allocations and Leaks.
74
+ - Android: device RAM tiers vary wildly. Budget 100MB heap for mid-range, test on 2GB RAM devices.
75
+ - Avoid memory leaks from retained Contexts (Android), strong reference cycles (iOS), and non-cancelled subscriptions.
76
+
77
+ **Battery**
78
+ - Background work must use platform APIs: WorkManager (Android), BGTaskScheduler (iOS). Raw background threads are killed and drain battery.
79
+ - Network: batch requests, respect retry-after headers, use conditional requests (ETags) to avoid redundant data transfer.
80
+ - Location: use the least precise location mode needed. Continuous GPS is the fastest path to a 1-star review.
81
+
82
+ **Network**
83
+ - Assume 3G on first launch — many users install apps on slow connections.
84
+ - App binary size: iOS App Store thin-slicing reduces download size per device; still target under 50MB download, under 200MB install.
85
+ - Android App Bundle (AAB) enables Play Store to serve device-optimized APKs — always build AAB for production, not APK.
86
+
87
+ ### Device Matrix
88
+
89
+ Define the device matrix explicitly. Not all devices need testing — define tiers:
90
+
91
+ **Tier 1 (must pass)**
92
+ - iOS: current iPhone model, iPhone SE (latest), iPad (if universal app)
93
+ - Android: current Pixel, Samsung Galaxy S-series, mid-range device (e.g., Pixel 4a or Samsung A-series)
94
+ - OS versions: current and N-1 major (e.g., iOS 18 and 17, Android 15 and 14)
95
+
96
+ **Tier 2 (should pass)**
97
+ - iOS: 2-generation-old iPhone, older iPad models
98
+ - Android: budget device with 2GB RAM, tablet (if supporting large-screen)
99
+ - OS versions: N-2 for iOS, Android 13 for broader coverage
100
+
101
+ **Minimum OS versions**
102
+ - iOS: typically N-2 years of support. In 2025, iOS 16+ covers ~95% of active devices.
103
+ - Android: API level 26 (Android 8.0) covers ~98% of active devices as of 2025. Most new projects target minSdk 26.
104
+ - Document the min-OS decision explicitly — lowering it later breaks APIs; raising it is a business decision.
105
+
106
+ **Screen size breakpoints (iOS)**
107
+ - 375pt wide: iPhone SE/iPhone mini
108
+ - 390pt wide: iPhone 14/15 standard
109
+ - 430pt wide: iPhone 14/15 Pro Max
110
+ - 768pt wide: iPad (compact width in split view)
111
+ - 1024pt+: iPad full screen
112
+
113
+ **Screen size breakpoints (Android)**
114
+ - 360dp: compact (phone portrait)
115
+ - 600dp: medium (phone landscape, small tablet)
116
+ - 840dp+: expanded (tablet, foldable unfolded)
117
+ - Use Window Size Classes (Jetpack Compose) to adapt layout to these breakpoints.
118
+
119
+ ### Accessibility Requirements
120
+
121
+ Accessibility is legally required (ADA in the US, EN 301 549 in the EU) and a quality signal in app store reviews.
122
+
123
+ **iOS VoiceOver**
124
+ - Every interactive element must have an `accessibilityLabel` that describes its purpose (not its visual appearance).
125
+ - Semantic roles: use `accessibilityTraits` (.button, .header, .image, .link) so VoiceOver announces element type.
126
+ - Custom controls must implement `UIAccessibilityElement` or use SwiftUI's `.accessibilityElement(children:)` modifier.
127
+ - Group related elements with `shouldGroupAccessibilityChildren` to reduce VoiceOver navigation steps.
128
+
129
+ **Android TalkBack**
130
+ - `contentDescription` on all `ImageView`, `ImageButton`, and custom views. Views with `android:text` inherit their label automatically.
131
+ - `android:importantForAccessibility="no"` for decorative elements that should be skipped.
132
+ - Jetpack Compose: use `Modifier.semantics { contentDescription = "..." }` and role modifiers.
133
+
134
+ **Minimum contrast ratios (WCAG AA)**
135
+ - Normal text: 4.5:1 contrast ratio against background
136
+ - Large text (18pt+ or 14pt+ bold): 3:1 contrast ratio
137
+ - Interactive elements and focus indicators: 3:1 against adjacent colors
138
+ - Use `Color Contrast Analyzer` or Xcode Accessibility Inspector to verify.
139
+
140
+ **Dynamic Type / Font Scaling**
141
+ - iOS: test with Accessibility > Larger Text > maximum setting (~235% scale). All text must remain readable and not overflow containers.
142
+ - Android: test with Display > Font size at the largest setting. Use `sp` units (never `dp` or `px`) for all text.
143
+
144
+ **Motor accessibility**
145
+ - Switch Control (iOS) and Switch Access (Android): all interactive elements must be reachable via sequential switch scanning.
146
+ - Do not rely solely on swipe gestures for core functionality — provide tap-target alternatives.
147
+ - Minimum touch target: 44pt (iOS) / 48dp (Android).
@@ -0,0 +1,338 @@
1
+ ---
2
+ name: mobile-app-security
3
+ description: Secure storage (Keychain/Keystore), certificate pinning, biometric authentication, jailbreak/root detection, and data protection for mobile apps
4
+ topics: [mobile-app, security, keychain, keystore, certificate-pinning, biometrics, jailbreak-detection, data-protection]
5
+ ---
6
+
7
+ Mobile apps operate in an adversarial environment: devices are lost or stolen, users jailbreak/root their devices, and network traffic is subject to interception. Security must be designed in, not bolted on. The most common mobile security failures are storing secrets in plaintext, trusting all TLS certificates (or disabling certificate validation), and failing to protect data at rest. Implement defense in depth — assume any single control will fail.
8
+
9
+ ## Summary
10
+
11
+ Mobile security requires secure storage (iOS Keychain, Android Keystore/EncryptedSharedPreferences), certificate pinning to prevent MITM attacks, biometric authentication for sensitive operations, and data-at-rest protection. Avoid storing sensitive data in logs, shared preferences, UserDefaults, or anywhere outside secure enclaves. Jailbreak/root detection adds a deterrent layer but is not a reliable security boundary. Apply OWASP Mobile Top 10 as the minimum security baseline.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Secure Storage
16
+
17
+ **iOS Keychain**
18
+
19
+ The Keychain is the correct location for all sensitive data: tokens, passwords, cryptographic keys, and session identifiers. It survives app reinstallation (with the right accessibility class) and is hardware-backed on devices with a Secure Enclave.
20
+
21
+ ```swift
22
+ import Security
23
+
24
+ enum KeychainError: Error {
25
+ case duplicateEntry, itemNotFound, unexpectedStatus(OSStatus)
26
+ }
27
+
28
+ struct Keychain {
29
+ static func save(key: String, data: Data, accessibility: CFString = kSecAttrAccessibleWhenUnlocked) throws {
30
+ let query: [CFString: Any] = [
31
+ kSecClass: kSecClassGenericPassword,
32
+ kSecAttrAccount: key,
33
+ kSecValueData: data,
34
+ kSecAttrAccessible: accessibility
35
+ ]
36
+
37
+ let status = SecItemAdd(query as CFDictionary, nil)
38
+ if status == errSecDuplicateItem {
39
+ // Update existing item
40
+ let updateQuery: [CFString: Any] = [kSecClass: kSecClassGenericPassword, kSecAttrAccount: key]
41
+ let updateFields: [CFString: Any] = [kSecValueData: data]
42
+ let updateStatus = SecItemUpdate(updateQuery as CFDictionary, updateFields as CFDictionary)
43
+ guard updateStatus == errSecSuccess else { throw KeychainError.unexpectedStatus(updateStatus) }
44
+ } else if status != errSecSuccess {
45
+ throw KeychainError.unexpectedStatus(status)
46
+ }
47
+ }
48
+
49
+ static func load(key: String) throws -> Data {
50
+ let query: [CFString: Any] = [
51
+ kSecClass: kSecClassGenericPassword,
52
+ kSecAttrAccount: key,
53
+ kSecReturnData: true,
54
+ kSecMatchLimit: kSecMatchLimitOne
55
+ ]
56
+ var result: AnyObject?
57
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
58
+ guard status == errSecSuccess, let data = result as? Data else {
59
+ throw KeychainError.itemNotFound
60
+ }
61
+ return data
62
+ }
63
+
64
+ static func delete(key: String) {
65
+ let query: [CFString: Any] = [kSecClass: kSecClassGenericPassword, kSecAttrAccount: key]
66
+ SecItemDelete(query as CFDictionary)
67
+ }
68
+ }
69
+ ```
70
+
71
+ **Keychain accessibility classes — choose carefully**
72
+ - `kSecAttrAccessibleWhenUnlocked`: accessible only when device is unlocked. Correct for most cases.
73
+ - `kSecAttrAccessibleAfterFirstUnlock`: accessible after first unlock until device restarts. For background-accessible tokens (e.g., background sync auth).
74
+ - `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly`: requires a passcode to be set; deleted if passcode is removed. Highest security, not backed up.
75
+ - `kSecAttrAccessibleAlways`: accessible even when locked. Never use — completely defeats protection.
76
+ - Append `ThisDeviceOnly` to any class to prevent iCloud Keychain sync (appropriate for session tokens).
77
+
78
+ **Android: Keystore + EncryptedSharedPreferences**
79
+
80
+ The Android Keystore stores cryptographic keys in hardware (on supported devices). Use it to encrypt data stored on disk:
81
+
82
+ ```kotlin
83
+ // EncryptedSharedPreferences — simplest secure storage for small values
84
+ val masterKey = MasterKey.Builder(context)
85
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
86
+ .build()
87
+
88
+ val securePrefs = EncryptedSharedPreferences.create(
89
+ context,
90
+ "secure_prefs",
91
+ masterKey,
92
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
93
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
94
+ )
95
+ securePrefs.edit().putString("auth_token", token).apply()
96
+ val token = securePrefs.getString("auth_token", null)
97
+ ```
98
+
99
+ For larger data (full database encryption), use SQLCipher or Room with SQLCipher:
100
+ ```kotlin
101
+ val passphrase = SQLiteDatabase.getBytes(keyFromKeystore)
102
+ val factory = SupportFactory(passphrase)
103
+ Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
104
+ .openHelperFactory(factory)
105
+ .build()
106
+ ```
107
+
108
+ **What NOT to store in insecure storage**
109
+ - Never use `NSUserDefaults`/`UserDefaults` for tokens, passwords, or PII
110
+ - Never use `SharedPreferences` (unencrypted) for sensitive data
111
+ - Never write credentials to log output — logs are readable by other apps and crash reporters
112
+ - Never store sensitive data in the app's Documents or Cache directories without encryption
113
+ - Never put secrets in `Info.plist`, `AndroidManifest.xml`, or any file tracked in git
114
+
115
+ ### Certificate Pinning
116
+
117
+ Certificate pinning prevents MITM attacks by refusing connections to servers that don't present the expected certificate (or a certificate from the expected CA chain).
118
+
119
+ **iOS: TrustKit or URLSession manual pinning**
120
+ ```swift
121
+ // URLSession delegate-based pinning
122
+ class PinnedURLSessionDelegate: NSObject, URLSessionDelegate {
123
+ // Expected certificate public key hashes (SHA-256, base64-encoded)
124
+ private let pinnedHashes: Set<String> = [
125
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Current cert
126
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Backup cert
127
+ ]
128
+
129
+ func urlSession(
130
+ _ session: URLSession,
131
+ didReceive challenge: URLAuthenticationChallenge,
132
+ completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
133
+ ) {
134
+ guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
135
+ let serverTrust = challenge.protectionSpace.serverTrust else {
136
+ completionHandler(.cancelAuthenticationChallenge, nil)
137
+ return
138
+ }
139
+
140
+ // Extract public key hash from server certificate
141
+ if let hash = publicKeyHash(from: serverTrust), pinnedHashes.contains(hash) {
142
+ completionHandler(.useCredential, URLCredential(trust: serverTrust))
143
+ } else {
144
+ completionHandler(.cancelAuthenticationChallenge, nil)
145
+ }
146
+ }
147
+ }
148
+ ```
149
+
150
+ **Android: OkHttp CertificatePinner**
151
+ ```kotlin
152
+ val certificatePinner = CertificatePinner.Builder()
153
+ .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
154
+ .add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") // backup pin
155
+ .build()
156
+
157
+ val client = OkHttpClient.Builder()
158
+ .certificatePinner(certificatePinner)
159
+ .build()
160
+ ```
161
+
162
+ **Certificate pinning operational rules**
163
+ - Always pin at least two certificates: the current certificate and a backup (intermediate CA or pre-generated backup)
164
+ - A pinned app with no backup pin is bricked when the certificate rotates — this is an outage
165
+ - Certificate rotation procedure: add new cert pin 30+ days before rotating, deploy app update, rotate certificate, remove old pin in next app version
166
+ - Never pin in debug builds — intercepting proxies (Charles, mitmproxy) are required for debugging
167
+ - Consider using a reporting-only mode that logs failures without blocking before enforcing
168
+
169
+ ### Biometric Authentication
170
+
171
+ **iOS: LocalAuthentication**
172
+ ```swift
173
+ import LocalAuthentication
174
+
175
+ func authenticateWithBiometrics() async throws {
176
+ let context = LAContext()
177
+ var error: NSError?
178
+
179
+ guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
180
+ throw error ?? LAError(.biometryNotAvailable)
181
+ }
182
+
183
+ try await context.evaluatePolicy(
184
+ .deviceOwnerAuthenticationWithBiometrics,
185
+ localizedReason: "Authenticate to view your account"
186
+ )
187
+ }
188
+ ```
189
+
190
+ - `.deviceOwnerAuthenticationWithBiometrics`: Face ID / Touch ID only. Falls back to nothing if unavailable.
191
+ - `.deviceOwnerAuthentication`: Face ID / Touch ID, then falls back to device passcode. Preferred for high-security gates.
192
+ - `LAContext.biometryType`: check whether device has Face ID, Touch ID, or neither — customize the prompt accordingly
193
+ - Store the biometric-protected secret in the Keychain with `SecAccessControl` binding the item to biometric authentication
194
+
195
+ **iOS: Keychain + biometric binding**
196
+ ```swift
197
+ var error: Unmanaged<CFError>?
198
+ guard let access = SecAccessControlCreateWithFlags(
199
+ nil,
200
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
201
+ [.biometryCurrentSet, .privateKeyUsage], // biometryCurrentSet invalidates if biometrics change
202
+ &error
203
+ ) else { throw error!.takeRetainedValue() as Error }
204
+
205
+ let query: [CFString: Any] = [
206
+ kSecClass: kSecClassGenericPassword,
207
+ kSecAttrAccount: "high_value_token",
208
+ kSecValueData: tokenData,
209
+ kSecAttrAccessControl: access
210
+ ]
211
+ SecItemAdd(query as CFDictionary, nil)
212
+ ```
213
+
214
+ **Android: BiometricPrompt**
215
+ ```kotlin
216
+ val biometricPrompt = BiometricPrompt(
217
+ fragment,
218
+ executor,
219
+ object : BiometricPrompt.AuthenticationCallback() {
220
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
221
+ // result.cryptoObject contains cipher if used with CryptoObject
222
+ onSuccess()
223
+ }
224
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
225
+ onError(errString.toString())
226
+ }
227
+ }
228
+ )
229
+
230
+ val promptInfo = BiometricPrompt.PromptInfo.Builder()
231
+ .setTitle("Authenticate")
232
+ .setSubtitle("Access your account")
233
+ .setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
234
+ .build()
235
+
236
+ biometricPrompt.authenticate(promptInfo)
237
+ ```
238
+
239
+ ### Jailbreak and Root Detection
240
+
241
+ **iOS jailbreak detection heuristics**
242
+ ```swift
243
+ func isJailbroken() -> Bool {
244
+ #if targetEnvironment(simulator)
245
+ return false
246
+ #else
247
+ // Check for common jailbreak files
248
+ let jailbreakPaths = [
249
+ "/Applications/Cydia.app",
250
+ "/usr/sbin/sshd",
251
+ "/etc/apt",
252
+ "/private/var/lib/apt/"
253
+ ]
254
+ if jailbreakPaths.contains(where: { FileManager.default.fileExists(atPath: $0) }) {
255
+ return true
256
+ }
257
+
258
+ // Check if sandboxing has been violated
259
+ let testPath = "/private/jailbreak_test_\(UUID().uuidString)"
260
+ do {
261
+ try "test".write(toFile: testPath, atomically: true, encoding: .utf8)
262
+ try FileManager.default.removeItem(atPath: testPath)
263
+ return true // Wrote outside sandbox — jailbroken
264
+ } catch { }
265
+
266
+ return false
267
+ #endif
268
+ }
269
+ ```
270
+
271
+ **Android root detection**
272
+ ```kotlin
273
+ fun isRooted(): Boolean {
274
+ val rootIndicators = listOf(
275
+ "/system/app/Superuser.apk",
276
+ "/sbin/su",
277
+ "/system/bin/su",
278
+ "/system/xbin/su"
279
+ )
280
+ if (rootIndicators.any { File(it).exists() }) return true
281
+
282
+ // Try to execute su
283
+ return try {
284
+ Runtime.getRuntime().exec(arrayOf("/system/xbin/which", "su"))
285
+ true
286
+ } catch (e: Exception) { false }
287
+ }
288
+ ```
289
+
290
+ **Jailbreak detection limitations**
291
+ - All detection methods can be bypassed by a sufficiently motivated attacker — jailbreak detection is a deterrent, not a security boundary
292
+ - Use it to degrade security guarantees (warn users) rather than block the app entirely
293
+ - Consider integrating a commercial solution (Guardsquare, Promon, Approov) for higher-assurance requirements
294
+
295
+ ### Data Protection
296
+
297
+ **iOS Data Protection API**
298
+ Files stored on iOS can be encrypted with additional protection tied to the device unlock state:
299
+
300
+ ```swift
301
+ // Write file with data protection
302
+ let data = sensitiveData
303
+ let url = documentsDirectory.appendingPathComponent("sensitive.dat")
304
+ try data.write(to: url, options: [.completeFileProtection])
305
+ // kSecAttrAccessibleWhenUnlocked equivalent for files
306
+ ```
307
+
308
+ Protection classes:
309
+ - `completeFileProtection`: accessible only when unlocked
310
+ - `completeFileProtectionUnlessOpen`: accessible when unlocked or if the file was open when locked
311
+ - `completeFileProtectionUntilFirstUserAuthentication`: accessible after first unlock
312
+ - Set app-wide default in Info.plist: `NSFileProtectionKey: NSFileProtectionComplete`
313
+
314
+ **Android: Scoped Storage**
315
+ Android 10+ enforces scoped storage — apps can no longer access arbitrary filesystem paths:
316
+ - Use `MediaStore` API for photos, videos, and audio shared with other apps
317
+ - Use app-specific directories (`context.filesDir`, `context.cacheDir`) for private data
318
+ - Request `READ_MEDIA_IMAGES` permission (Android 13+) instead of `READ_EXTERNAL_STORAGE`
319
+
320
+ **Network security configuration (Android)**
321
+ ```xml
322
+ <!-- res/xml/network_security_config.xml -->
323
+ <network-security-config>
324
+ <base-config cleartextTrafficPermitted="false">
325
+ <trust-anchors>
326
+ <certificates src="system"/>
327
+ </trust-anchors>
328
+ </base-config>
329
+ <!-- Allow cleartext for localhost in debug only -->
330
+ <debug-overrides>
331
+ <domain-config cleartextTrafficPermitted="true">
332
+ <domain includeSubdomains="true">localhost</domain>
333
+ </domain-config>
334
+ </debug-overrides>
335
+ </network-security-config>
336
+ ```
337
+
338
+ Set `cleartextTrafficPermitted="false"` globally and enforce HTTPS everywhere. This is the Android equivalent of iOS's App Transport Security.