buildanything 1.7.0 → 1.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 (222) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +55 -0
  4. package/README.md +71 -61
  5. package/agents/ios-app-review-guardian.md +49 -0
  6. package/agents/ios-foundation-models-specialist.md +46 -0
  7. package/agents/ios-storekit-specialist.md +52 -0
  8. package/agents/ios-swift-architect.md +102 -0
  9. package/agents/ios-swift-search.md +130 -0
  10. package/agents/ios-swift-ui-design.md +104 -0
  11. package/commands/build.md +80 -176
  12. package/commands/fix.md +65 -0
  13. package/commands/setup.md +73 -0
  14. package/commands/ux-review.md +63 -0
  15. package/commands/verify.md +72 -0
  16. package/hooks/session-start +18 -1
  17. package/package.json +5 -2
  18. package/protocols/brainstorm.md +99 -0
  19. package/protocols/build-fix.md +52 -0
  20. package/protocols/cleanup.md +54 -0
  21. package/protocols/design.md +269 -0
  22. package/protocols/eval-harness.md +61 -0
  23. package/protocols/fake-data-detector.md +64 -0
  24. package/protocols/ios-context.md +235 -0
  25. package/protocols/ios-frameworks-map.md +323 -0
  26. package/protocols/ios-phase-branches.md +162 -0
  27. package/protocols/ios-preflight.md +27 -0
  28. package/protocols/metric-loop.md +93 -0
  29. package/protocols/planning.md +87 -0
  30. package/protocols/smoke-test.md +110 -0
  31. package/protocols/verify.md +67 -0
  32. package/protocols/web-phase-branches.md +201 -0
  33. package/skills/ios/_VENDORED.md +60 -0
  34. package/skills/ios/activitykit/LICENSE +131 -0
  35. package/skills/ios/activitykit/SKILL.md +505 -0
  36. package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
  37. package/skills/ios/app-intents/LICENSE +131 -0
  38. package/skills/ios/app-intents/SKILL.md +494 -0
  39. package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
  40. package/skills/ios/apple-on-device-ai/LICENSE +131 -0
  41. package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
  42. package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
  43. package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
  44. package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
  45. package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
  46. package/skills/ios/ios-26-platform/SKILL.md +53 -0
  47. package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
  48. package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
  49. package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
  50. package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
  51. package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
  52. package/skills/ios/ios-bootstrap/SKILL.md +98 -0
  53. package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
  54. package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
  55. package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
  56. package/skills/ios/ios-debugger-agent/LICENSE +21 -0
  57. package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
  58. package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
  59. package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
  60. package/skills/ios/ios-hig/SKILL.md +41 -0
  61. package/skills/ios/ios-hig/references/accessibility.md +81 -0
  62. package/skills/ios/ios-hig/references/content.md +142 -0
  63. package/skills/ios/ios-hig/references/feedback.md +123 -0
  64. package/skills/ios/ios-hig/references/interaction.md +199 -0
  65. package/skills/ios/ios-hig/references/performance-platform.md +129 -0
  66. package/skills/ios/ios-hig/references/privacy-permissions.md +181 -0
  67. package/skills/ios/ios-hig/references/visual-design.md +84 -0
  68. package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
  69. package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
  70. package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
  71. package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
  72. package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
  73. package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
  74. package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
  75. package/skills/ios/swift-accessibility/LICENSE +21 -0
  76. package/skills/ios/swift-accessibility/SKILL.md +371 -0
  77. package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
  78. package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
  79. package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
  80. package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
  81. package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
  82. package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
  83. package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
  84. package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
  85. package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
  86. package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
  87. package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
  88. package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
  89. package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
  90. package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
  91. package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
  92. package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
  93. package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
  94. package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
  95. package/skills/ios/swift-concurrency/LICENSE +21 -0
  96. package/skills/ios/swift-concurrency/SKILL.md +171 -0
  97. package/skills/ios/swift-concurrency/references/_index.md +50 -0
  98. package/skills/ios/swift-concurrency/references/actors.md +660 -0
  99. package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
  100. package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
  101. package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
  102. package/skills/ios/swift-concurrency/references/core-data.md +560 -0
  103. package/skills/ios/swift-concurrency/references/glossary.md +135 -0
  104. package/skills/ios/swift-concurrency/references/linting.md +155 -0
  105. package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
  106. package/skills/ios/swift-concurrency/references/migration.md +1104 -0
  107. package/skills/ios/swift-concurrency/references/performance.md +593 -0
  108. package/skills/ios/swift-concurrency/references/sendable.md +598 -0
  109. package/skills/ios/swift-concurrency/references/tasks.md +636 -0
  110. package/skills/ios/swift-concurrency/references/testing.md +592 -0
  111. package/skills/ios/swift-concurrency/references/threading.md +495 -0
  112. package/skills/ios/swift-security-expert/LICENSE +21 -0
  113. package/skills/ios/swift-security-expert/SKILL.md +470 -0
  114. package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
  115. package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
  116. package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
  117. package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
  118. package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
  119. package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
  120. package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
  121. package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
  122. package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
  123. package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
  124. package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
  125. package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
  126. package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
  127. package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
  128. package/skills/ios/swift-testing-expert/LICENSE +21 -0
  129. package/skills/ios/swift-testing-expert/SKILL.md +79 -0
  130. package/skills/ios/swift-testing-expert/references/_index.md +12 -0
  131. package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
  132. package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
  133. package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
  134. package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
  135. package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
  136. package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
  137. package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
  138. package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
  139. package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
  140. package/skills/ios/swiftdata-pro/LICENSE +21 -0
  141. package/skills/ios/swiftdata-pro/SKILL.md +102 -0
  142. package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
  143. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
  144. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
  145. package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
  146. package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
  147. package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
  148. package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
  149. package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
  150. package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
  151. package/skills/ios/swiftui-design-principles/LICENSE +21 -0
  152. package/skills/ios/swiftui-design-principles/README.md +41 -0
  153. package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
  154. package/skills/ios/swiftui-design-principles/metadata.json +10 -0
  155. package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
  156. package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
  157. package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
  158. package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
  159. package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
  160. package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
  161. package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
  162. package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
  163. package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
  164. package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
  165. package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
  166. package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
  167. package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
  168. package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
  169. package/skills/ios/swiftui-pro/LICENSE +21 -0
  170. package/skills/ios/swiftui-pro/SKILL.md +108 -0
  171. package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
  172. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
  173. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
  174. package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
  175. package/skills/ios/swiftui-pro/references/api.md +39 -0
  176. package/skills/ios/swiftui-pro/references/data.md +43 -0
  177. package/skills/ios/swiftui-pro/references/design.md +31 -0
  178. package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
  179. package/skills/ios/swiftui-pro/references/navigation.md +14 -0
  180. package/skills/ios/swiftui-pro/references/performance.md +46 -0
  181. package/skills/ios/swiftui-pro/references/swift.md +56 -0
  182. package/skills/ios/swiftui-pro/references/views.md +35 -0
  183. package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
  184. package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
  185. package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
  186. package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
  187. package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
  188. package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
  189. package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
  190. package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
  191. package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
  192. package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
  193. package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
  194. package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
  195. package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  196. package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  197. package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
  198. package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  199. package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
  200. package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  201. package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
  202. package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
  203. package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
  204. package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
  205. package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
  206. package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
  207. package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  208. package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
  209. package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
  210. package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
  211. package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
  212. package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
  213. package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
  214. package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
  215. package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
  216. package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
  217. package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
  218. package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
  219. package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
  220. package/skills/ios/widgetkit/LICENSE +131 -0
  221. package/skills/ios/widgetkit/SKILL.md +502 -0
  222. package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
@@ -0,0 +1,690 @@
1
+ # Common Anti-Patterns
2
+
3
+ > **Scope:** The 10 most dangerous security anti-patterns that AI coding assistants generate for iOS apps. Each entry includes the vulnerability explanation, realistic ❌ insecure code, ✅ correct replacement, detection heuristic, and OWASP risk mapping. This is the skill's backbone — the single most important file for correcting AI-generated security code.
4
+ >
5
+ > **Cross-references:** `biometric-authentication.md` (anti-pattern #3 deep dive), `keychain-fundamentals.md` (anti-pattern #4 CRUD patterns), `keychain-access-control.md` (anti-pattern #5 protection classes), `cryptokit-symmetric.md` (anti-patterns #6–7), `credential-storage-patterns.md` (anti-patterns #1–2 token lifecycle), `migration-legacy-stores.md` (anti-pattern #9 first-launch cleanup), `compliance-owasp-mapping.md` (full OWASP/MASVS mapping).
6
+
7
+ ---
8
+
9
+ ## Why AI Generates Insecure iOS Code
10
+
11
+ AI assistants optimize for functional correctness, not security — reproducing the most common patterns from training data, which are overwhelmingly insecure-by-default. Veracode's 2025 analysis: 45% of AI-generated code fails security tests. Cybernews: 815,000+ hardcoded secrets across 156,000 iOS apps (71% leaking ≥1 credential). Stanford: developers using AI write less secure code yet feel more confident.
12
+
13
+ Apple's security primitives (Keychain, CryptoKit, Secure Enclave) are excellent but AI consistently bypasses them. CISA/FBI classified hardcoded credentials as elevating "risk to national security" in their January 2025 Bad Practices v2.0 (CWE-798).
14
+
15
+ **OWASP standard:** Mobile Top 10 (2024) with MASTG v2 test IDs. Legacy MSTG-\* identifiers noted where commonly referenced.
16
+
17
+ ---
18
+
19
+ ## Anti-Pattern #1 — Storing Secrets in UserDefaults
20
+
21
+ **Severity:** CRITICAL | **OWASP:** M9 (Insecure Data Storage) | **Fix effort:** Medium
22
+
23
+ UserDefaults writes to an unencrypted XML plist at `~/Library/Preferences/{BUNDLE_ID}.plist`. Apple's documentation: "Don't store personal or sensitive information as settings." Readable from unencrypted backups, jailbroken devices (Objection `ios nsuserdefaults get`), and third-party SDKs. **SwiftUI's `@AppStorage` is a wrapper over `UserDefaults`** — it has identical security properties and must never be used for tokens, keys, or credentials.
24
+
25
+ **❌ Insecure — AI-generated pattern:**
26
+
27
+ ```swift
28
+ // Plaintext on disk, readable from backups
29
+ func saveAuthToken(_ token: String) {
30
+ UserDefaults.standard.set(token, forKey: "userAuthToken")
31
+ UserDefaults.standard.set(refreshToken, forKey: "refreshToken")
32
+ UserDefaults.standard.synchronize()
33
+ }
34
+
35
+ let token = UserDefaults.standard.string(forKey: "userAuthToken")
36
+ ```
37
+
38
+ **✅ Secure — Keychain with add-or-update:**
39
+
40
+ ```swift
41
+ func saveTokenToKeychain(_ token: Data, account: String) throws {
42
+ let query: [String: Any] = [
43
+ kSecClass as String: kSecClassGenericPassword,
44
+ kSecAttrService as String: "com.myapp.auth",
45
+ kSecAttrAccount as String: account,
46
+ kSecValueData as String: token,
47
+ kSecAttrAccessible as String:
48
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly
49
+ ]
50
+ let status = SecItemAdd(query as CFDictionary, nil)
51
+ if status == errSecDuplicateItem {
52
+ // Full add-or-update pattern → see anti-pattern #4
53
+ let search: [String: Any] = [
54
+ kSecClass as String: kSecClassGenericPassword,
55
+ kSecAttrService as String: "com.myapp.auth",
56
+ kSecAttrAccount as String: account
57
+ ]
58
+ let updateStatus = SecItemUpdate(
59
+ search as CFDictionary,
60
+ [kSecValueData as String: token] as CFDictionary)
61
+ guard updateStatus == errSecSuccess else {
62
+ throw KeychainError.unexpectedStatus(updateStatus)
63
+ }
64
+ } else if status != errSecSuccess {
65
+ throw KeychainError.unexpectedStatus(status)
66
+ }
67
+ }
68
+ ```
69
+
70
+ **MASTG tests:** MASTG-TEST-0300, MASTG-TEST-0302. **MASWE:** MASWE-0006. **Legacy:** MSTG-STORAGE-1.
71
+
72
+ **Detection heuristic:**
73
+
74
+ ```bash
75
+ grep -rn "UserDefaults" --include="*.swift" | \
76
+ grep -iE "token|password|secret|credential|auth|session|api.?key|jwt|bearer"
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Anti-Pattern #2 — Hardcoded API Keys
82
+
83
+ **Severity:** CRITICAL | **OWASP:** M1 (Improper Credential Usage) | **Fix effort:** High
84
+
85
+ API keys compiled into Swift appear in the binary's `__TEXT.__cstring` segment — `strings MyApp.app/MyApp` extracts them instantly. Even `.xcconfig` or `Info.plist` values ship inside the IPA. Cybernews found 78,800 Google API keys across 156,000 iOS apps.
86
+
87
+ **❌ Insecure — AI-generated pattern:**
88
+
89
+ ```swift
90
+ class PaymentService {
91
+ private let stripeKey = "sk_live_51H7bK2E..." // In binary
92
+ private let firebaseKey = "AIzaSyB..." // In binary
93
+
94
+ func charge(amount: Int) async throws {
95
+ var request = URLRequest(
96
+ url: URL(string: "https://api.stripe.com/v1/charges")!)
97
+ request.setValue("Bearer \(stripeKey)",
98
+ forHTTPHeaderField: "Authorization")
99
+ let (data, _) = try await URLSession.shared.data(for: request)
100
+ }
101
+ }
102
+
103
+ // Also dangerous: key in Info.plist or .xcconfig bundled in app
104
+ let key = Bundle.main.infoDictionary?["API_KEY"] as? String
105
+ ```
106
+
107
+ **✅ Secure — server proxy + Keychain cache:**
108
+
109
+ ```swift
110
+ class SecureAPIKeyManager {
111
+ static let shared = SecureAPIKeyManager()
112
+
113
+ /// Best: proxy through your server (key never on device)
114
+ func secureRequest(endpoint: String, params: [String: Any]) async throws -> Data {
115
+ var request = URLRequest(
116
+ url: URL(string: "https://api.myserver.com/proxy/\(endpoint)")!)
117
+ request.httpMethod = "POST"
118
+ request.httpBody = try JSONSerialization.data(withJSONObject: params)
119
+ let (data, _) = try await URLSession.shared.data(for: request)
120
+ return data
121
+ }
122
+
123
+ /// If client must hold key: fetch at runtime, cache in Keychain
124
+ func getAPIKey() async throws -> String {
125
+ if let cached = try? readFromKeychain(service: "api-keys", account: "primary") {
126
+ return String(data: cached, encoding: .utf8)!
127
+ }
128
+ let (data, _) = try await URLSession.shared.data(
129
+ from: URL(string: "https://api.myserver.com/config/key")!)
130
+ try saveToKeychain(data, service: "api-keys", account: "primary")
131
+ return String(data: data, encoding: .utf8)!
132
+ }
133
+ }
134
+ ```
135
+
136
+ Apple's DeviceCheck and App Attest frameworks provide server-side device verification without embedding secrets. WWDC 2019-709 advises storing credentials in Keychain, not in code.
137
+
138
+ **MASTG tests:** MASTG-TEST-0213, MASTG-TEST-0214. **MASWE:** MASWE-0005. **Legacy:** MSTG-STORAGE-12. **CISA/FBI:** CWE-798 — Product Security Bad Practices v2.0 (January 2025).
139
+
140
+ **Detection heuristic:**
141
+
142
+ ```bash
143
+ grep -rn 'let.*[Kk]ey.*=.*"[A-Za-z0-9_\-]\{20,\}"' --include="*.swift"
144
+ grep -rn '"sk_live_\|"pk_live_\|"AIza[A-Za-z0-9]\|"AKIA[A-Z0-9]' \
145
+ --include="*.swift" --include="*.plist" --include="*.xcconfig"
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Anti-Pattern #3 — LAContext-Only Biometric Authentication
151
+
152
+ **Severity:** CRITICAL | **OWASP:** M3 (Insecure Authentication) | **Fix effort:** Medium
153
+
154
+ Using `LAContext.evaluatePolicy()` alone is the single most reproduced insecure pattern across iOS tutorials. The method returns a simple boolean callback in user-space — no cryptographic binding. Frida forces `success = true` in one command; Objection packages this as `ios ui biometrics_bypass`. OWASP MASTG: "Biometric authentication must be based on unlocking the keychain." Full deep dive: see `biometric-authentication.md`.
155
+
156
+ **❌ Insecure — AI-generated pattern:**
157
+
158
+ ```swift
159
+ func authenticateUser(completion: @escaping (Bool) -> Void) {
160
+ let context = LAContext()
161
+ context.evaluatePolicy(
162
+ .deviceOwnerAuthenticationWithBiometrics,
163
+ localizedReason: "Authenticate to access your account"
164
+ ) { success, authError in
165
+ DispatchQueue.main.async {
166
+ if success {
167
+ self.showSensitiveData() // Gated on a hookable boolean
168
+ }
169
+ completion(success)
170
+ }
171
+ }
172
+ }
173
+ ```
174
+
175
+ **✅ Secure — Keychain + SecAccessControl hardware binding:**
176
+
177
+ ```swift
178
+ // STORE: biometric-protected via Secure Enclave
179
+ func storeWithBiometric(secret: Data, account: String) throws {
180
+ let access = SecAccessControlCreateWithFlags(
181
+ nil,
182
+ kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
183
+ .biometryCurrentSet, nil)!
184
+
185
+ let query: [String: Any] = [
186
+ kSecClass as String: kSecClassGenericPassword,
187
+ kSecAttrService as String: "com.myapp.biometric",
188
+ kSecAttrAccount as String: account,
189
+ kSecAttrAccessControl as String: access,
190
+ kSecValueData as String: secret
191
+ ]
192
+ let status = SecItemAdd(query as CFDictionary, nil)
193
+ guard status == errSecSuccess || status == errSecDuplicateItem else {
194
+ throw KeychainError.unexpectedStatus(status)
195
+ }
196
+ }
197
+
198
+ // READ: Secure Enclave enforces biometric before releasing data
199
+ func readWithBiometric(account: String) throws -> Data {
200
+ let context = LAContext()
201
+ context.localizedReason = "Access your secure data"
202
+ let query: [String: Any] = [
203
+ kSecClass as String: kSecClassGenericPassword,
204
+ kSecAttrService as String: "com.myapp.biometric",
205
+ kSecAttrAccount as String: account,
206
+ kSecReturnData as String: true,
207
+ kSecMatchLimit as String: kSecMatchLimitOne,
208
+ kSecUseAuthenticationContext as String: context
209
+ ]
210
+ var result: AnyObject?
211
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
212
+ guard status == errSecSuccess, let data = result as? Data else {
213
+ throw KeychainError.unexpectedStatus(status)
214
+ }
215
+ return data // Only returned after hardware biometric validation
216
+ }
217
+ ```
218
+
219
+ The `.biometryCurrentSet` flag invalidates the item if biometrics change, preventing an attacker with physical access from enrolling their own biometric. Objection's documentation confirms this bypass "will NOT work" with keychain-bound biometric items.
220
+
221
+ **MASTG tests:** MASTG-TEST-0266, MASTG-TEST-0267. **MASWE:** MASWE-0044. **Legacy:** MSTG-AUTH-8. **WWDC:** 2014-711 introduced `SecAccessControlCreateWithFlags`.
222
+
223
+ **Detection heuristic:**
224
+
225
+ ```bash
226
+ # evaluatePolicy without SecAccessControl → insecure
227
+ grep -rn "evaluatePolicy" --include="*.swift" -l | \
228
+ xargs grep -L "SecAccessControlCreateWithFlags"
229
+ # Verify secure pattern exists
230
+ grep -rn "\.biometryCurrentSet\|\.biometryAny" --include="*.swift"
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Anti-Pattern #4 — Ignoring SecItem Error Codes
236
+
237
+ **Severity:** HIGH | **OWASP:** M8 (Security Misconfiguration) | **Fix effort:** Low
238
+
239
+ `errSecDuplicateItem` (OSStatus -25299) is the most common Keychain failure. When `SecItemAdd` hits a duplicate, it silently discards the new value. Password updates never persist, refreshed tokens are lost, and auth breaks in hard-to-debug ways. Other critical codes: `errSecItemNotFound` (-25300), `errSecAuthFailed` (-25293), `errSecInteractionNotAllowed` (-25308).
240
+
241
+ Full CRUD patterns: see `keychain-fundamentals.md`.
242
+
243
+ **❌ Insecure — AI-generated pattern:**
244
+
245
+ ```swift
246
+ func saveToken(_ token: Data) {
247
+ let query: [String: Any] = [
248
+ kSecClass as String: kSecClassGenericPassword,
249
+ kSecAttrService as String: "com.app.auth",
250
+ kSecAttrAccount as String: "accessToken",
251
+ kSecValueData as String: token
252
+ ]
253
+ SecItemAdd(query as CFDictionary, nil) // Return value ignored!
254
+ }
255
+ ```
256
+
257
+ **✅ Secure — OSStatus switch with add-or-update:**
258
+
259
+ ```swift
260
+ func saveToKeychain(value: Data, service: String, account: String) throws {
261
+ let query: [String: Any] = [
262
+ kSecClass as String: kSecClassGenericPassword,
263
+ kSecAttrService as String: service,
264
+ kSecAttrAccount as String: account,
265
+ kSecValueData as String: value,
266
+ kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
267
+ ]
268
+ let status = SecItemAdd(query as CFDictionary, nil)
269
+ switch status {
270
+ case errSecSuccess: return
271
+ case errSecDuplicateItem:
272
+ let search: [String: Any] = [
273
+ kSecClass as String: kSecClassGenericPassword,
274
+ kSecAttrService as String: service,
275
+ kSecAttrAccount as String: account
276
+ ]
277
+ let updateStatus = SecItemUpdate(
278
+ search as CFDictionary, [kSecValueData as String: value] as CFDictionary)
279
+ guard updateStatus == errSecSuccess else { throw KeychainError.updateFailed(updateStatus) }
280
+ case errSecInteractionNotAllowed: throw KeychainError.deviceLocked
281
+ case errSecAuthFailed: throw KeychainError.authenticationFailed
282
+ default: throw KeychainError.unexpectedStatus(status)
283
+ }
284
+ }
285
+ ```
286
+
287
+ Critical detail: `SecItemUpdate` takes two dictionaries — search query (without `kSecValueData`) and attributes to update. Passing the full query as the search parameter is a common mistake.
288
+
289
+ **MASTG tests:** MASTG-TEST-0300, MASTG-TEST-0301. **Legacy:** MASVS-STORAGE-2.
290
+
291
+ **Detection heuristic:**
292
+
293
+ ```bash
294
+ grep -rn "SecItemAdd" --include="*.swift" -l | \
295
+ xargs grep -L "errSecDuplicateItem\|DuplicateItem\|-25299"
296
+ grep -rn "SecItemAdd(" --include="*.swift" | \
297
+ grep -v "let\|var\|status\|=\|switch\|if\|guard"
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Anti-Pattern #5 — Wrong or Missing Data Protection Class
303
+
304
+ **Severity:** HIGH | **OWASP:** M9 (Insecure Data Storage) | **Fix effort:** Low
305
+
306
+ Omitting `kSecAttrAccessible` inherits a default that may be insufficient. Using deprecated `kSecAttrAccessibleAlways` (deprecated iOS 12) leaves data decryptable on a locked device. Missing `ThisDeviceOnly` suffix means items are included in backups. Full protection class guide: see `keychain-access-control.md`.
307
+
308
+ **❌ Insecure — AI-generated patterns:**
309
+
310
+ ```swift
311
+ // Missing kSecAttrAccessible entirely
312
+ let query: [String: Any] = [
313
+ kSecClass as String: kSecClassGenericPassword,
314
+ kSecAttrAccount as String: "user_password",
315
+ kSecValueData as String: passwordData
316
+ ]
317
+ SecItemAdd(query as CFDictionary, nil)
318
+
319
+ // Deprecated — accessible when device is locked
320
+ kSecAttrAccessible as String: kSecAttrAccessibleAlways
321
+ ```
322
+
323
+ **✅ Secure — selection by use case:**
324
+
325
+ ```swift
326
+ // Passwords, auth tokens (foreground-only)
327
+ kSecAttrAccessible as String:
328
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly
329
+
330
+ // Highest sensitivity — requires passcode to exist
331
+ kSecAttrAccessible as String:
332
+ kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
333
+
334
+ // Background-access items (push tokens, refresh tokens)
335
+ kSecAttrAccessible as String:
336
+ kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
337
+ ```
338
+
339
+ WWDC 2014-711: "Always use the most restrictive option that makes sense for your app."
340
+
341
+ **MASTG test:** MASTG-TEST-0299. **Legacy:** MASTG-STORAGE-3.
342
+
343
+ **Detection heuristic:**
344
+
345
+ ```bash
346
+ grep -rn "kSecAttrAccessibleAlways\b" --include="*.swift"
347
+ grep -rn "SecItemAdd" --include="*.swift" -l | \
348
+ xargs grep -L "kSecAttrAccessible\|kSecAttrAccessControl"
349
+ grep -rn "kSecAttrAccessibleWhenUnlocked\b" --include="*.swift" | \
350
+ grep -v "ThisDeviceOnly"
351
+ ```
352
+
353
+ ---
354
+
355
+ ## Anti-Pattern #6 — Nonce Reuse in AES-GCM
356
+
357
+ **Severity:** CRITICAL | **OWASP:** M10 (Insufficient Cryptography) | **Fix effort:** Medium
358
+
359
+ Reusing a nonce with the same key in AES-GCM is a complete cryptographic break. Identical nonces produce identical keystreams, enabling plaintext recovery via `C1 ⊕ C2 = P1 ⊕ P2` and authentication key recovery via polynomial factorization ("forbidden attack," Joux 2006). CryptoKit's `AES.GCM.seal` has a safe default: omitting the `nonce` parameter auto-generates a random 12-byte nonce. Danger occurs when AI explicitly constructs nonces. Full patterns: see `cryptokit-symmetric.md`.
360
+
361
+ **❌ Insecure — AI-generated patterns:**
362
+
363
+ ```swift
364
+ import CryptoKit
365
+
366
+ // Hardcoded nonce — identical keystream every encryption
367
+ let fixedNonce = try! AES.GCM.Nonce(data: Data(repeating: 0x00, count: 12))
368
+
369
+ func encrypt(_ plaintext: Data, using key: SymmetricKey) throws -> Data {
370
+ let sealedBox = try AES.GCM.seal(
371
+ plaintext, using: key, nonce: fixedNonce) // CATASTROPHIC
372
+ return sealedBox.combined!
373
+ }
374
+ // Also dangerous: counter-based nonce that resets on app restart → collision
375
+ ```
376
+
377
+ **✅ Secure — let CryptoKit handle nonces:**
378
+
379
+ ```swift
380
+ import CryptoKit
381
+
382
+ func encrypt(_ plaintext: Data, using key: SymmetricKey) throws -> Data {
383
+ // Nonce omitted → CryptoKit generates random 12-byte nonce
384
+ let sealedBox = try AES.GCM.seal(plaintext, using: key)
385
+ return sealedBox.combined! // Contains: nonce ‖ ciphertext ‖ tag
386
+ }
387
+
388
+ func decrypt(_ combined: Data, using key: SymmetricKey) throws -> Data {
389
+ let sealedBox = try AES.GCM.SealedBox(combined: combined)
390
+ return try AES.GCM.open(sealedBox, using: key)
391
+ }
392
+
393
+ let key = SymmetricKey(size: .bits256) // AES-256 per WWDC 2025 guidance
394
+ ```
395
+
396
+ WWDC 2019-709 introduced CryptoKit with the design philosophy: "easy to use, hard to misuse."
397
+
398
+ **MASTG test:** MASTG-TEST-0317. **MASWE:** MASWE-0022. **Legacy:** MASTG-CRYPTO-4.
399
+
400
+ **Detection heuristic:**
401
+
402
+ ```bash
403
+ grep -rn "AES\.GCM\.Nonce(data:" --include="*.swift"
404
+ grep -rn "let.*nonce.*=.*AES\.GCM\.Nonce" --include="*.swift"
405
+ grep -rn "Data(repeating:.*count:\s*12)" --include="*.swift"
406
+ grep -rn "\.seal(.*nonce:" --include="*.swift"
407
+ ```
408
+
409
+ ---
410
+
411
+ ## Anti-Pattern #7 — MD5/SHA-1 for Security Purposes
412
+
413
+ **Severity:** HIGH | **OWASP:** M10 (Insufficient Cryptography) | **Fix effort:** Low
414
+
415
+ MD5 broken since Wang & Yu (2005); SHA-1 broken by SHAttered (2017). CISA January 2025 lists both as insecure. Apple signals this via CryptoKit's `Insecure.MD5` and `Insecure.SHA1` namespacing.
416
+
417
+ **❌ Insecure — AI-generated pattern:**
418
+
419
+ ```swift
420
+ import CryptoKit
421
+ func hashPassword(_ password: String) -> String {
422
+ let hash = Insecure.MD5.hash(data: password.data(using: .utf8)!)
423
+ return hash.map { String(format: "%02x", $0) }.joined()
424
+ }
425
+ // Also: CC_MD5, CC_SHA1 from CommonCrypto
426
+ ```
427
+
428
+ **✅ Secure — SHA-256 minimum, KDF for passwords:**
429
+
430
+ ```swift
431
+ import CryptoKit
432
+
433
+ // Integrity verification
434
+ func hashData(_ data: Data) -> String {
435
+ let hash = SHA256.hash(data: data)
436
+ return hash.map { String(format: "%02x", $0) }.joined()
437
+ }
438
+
439
+ // HMAC for message authentication
440
+ func authenticate(_ data: Data, key: SymmetricKey) -> Data {
441
+ Data(HMAC<SHA256>.authenticationCode(for: data, using: key))
442
+ }
443
+
444
+ // Password storage — NEVER raw hashes. Use a KDF:
445
+ // Server-side: Argon2id, bcrypt, or scrypt
446
+ // On-device: PBKDF2 with ≥600,000 iterations (OWASP 2023 minimum for HMAC-SHA256)
447
+ // See cryptokit-symmetric.md for full PBKDF2 implementation
448
+ ```
449
+
450
+ iOS 18 adds SHA-3 family (`SHA3_256`, `SHA3_384`, `SHA3_512`) in CryptoKit. WWDC 2025-314 covers post-quantum additions (ML-KEM, ML-DSA), not SHA-3.
451
+
452
+ **MASTG test:** MASTG-TEST-0211. **MASTG demos:** MASTG-DEMO-0015, MASTG-DEMO-0016. **Legacy:** MSTG-CRYPTO-1.
453
+
454
+ **Detection heuristic:**
455
+
456
+ ```bash
457
+ grep -rn "Insecure\.\(MD5\|SHA1\)" --include="*.swift"
458
+ grep -rn "CC_MD5\|CC_SHA1\|CC_MD5_DIGEST_LENGTH\|CC_SHA1_DIGEST_LENGTH" \
459
+ --include="*.swift" --include="*.m"
460
+ ```
461
+
462
+ ---
463
+
464
+ ## Anti-Pattern #8 — Logging Sensitive Data
465
+
466
+ **Severity:** HIGH | **OWASP:** M9 (Insecure Data Storage) | **Fix effort:** Low
467
+
468
+ `print()`, `NSLog()`, and `os_log()` with sensitive values persist in device logs — accessible via Xcode Console, `idevicesyslog`, and `log collect --device`. On jailbroken devices, any process reads log storage. Apple's `OSLogPrivacy` (iOS 14+): `.private` redacts in production; `.sensitive` (iOS 15+) always redacted.
469
+
470
+ **❌ Insecure — AI-generated pattern:**
471
+
472
+ ```swift
473
+ func login(username: String, password: String) async throws {
474
+ print("Logging in with password: \(password)") // In device logs!
475
+ let token = try await authService.authenticate(username, password)
476
+ print("Got auth token: \(token)") // In device logs!
477
+ os_log("API key loaded: %{public}@", apiKey) // Explicitly public!
478
+ }
479
+ ```
480
+
481
+ **✅ Secure — OSLogPrivacy with redaction:**
482
+
483
+ ```swift
484
+ import os
485
+
486
+ let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "auth")
487
+
488
+ func login(username: String, password: String) async throws {
489
+ // Log events, not values — .private(mask: .hash) enables correlation
490
+ logger.info("Login attempt: \(username, privacy: .private(mask: .hash))")
491
+ let token = try await authService.authenticate(username, password)
492
+ logger.info("Authentication succeeded") // No token value
493
+ }
494
+
495
+ // Legacy os_log
496
+ os_log("Account: %{private}@", log: .default, type: .info, accountNumber)
497
+
498
+ // Strip debug logging in release builds
499
+ #if DEBUG
500
+ print("Debug: \(sensitiveValue)")
501
+ #endif
502
+ ```
503
+
504
+ **MASTG tests:** MASTG-TEST-0296, MASTG-TEST-0297. **MASWE:** MASWE-0001. **Legacy:** MSTG-STORAGE-3.
505
+
506
+ **Detection heuristic:**
507
+
508
+ ```bash
509
+ grep -rn "print(.*\\\(" --include="*.swift" | \
510
+ grep -iE "password|token|secret|key|credential|ssn|credit"
511
+ grep -rn "NSLog(.*%@" --include="*.swift" --include="*.m" | \
512
+ grep -iE "password|token|secret|key"
513
+ grep -rn 'os_log.*%{public}' --include="*.swift" | \
514
+ grep -iE "password|token|secret|key"
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Anti-Pattern #9 — Not Clearing Keychain on First Launch
520
+
521
+ **Severity:** MEDIUM | **OWASP:** M9 (Insecure Data Storage) | **Fix effort:** Low
522
+
523
+ Keychain items persist in a system-wide encrypted database managed by `securityd`, outside the app sandbox. App deletion removes the sandbox but keychain items survive. Apple DTS engineer Quinn "The Eskimo!" confirmed this as "currently expected behaviour despite being an obvious privacy concern." Consequences: stale credentials on reinstall, cross-user data leakage on device resale, and Firebase SDK authentication errors on reinstall. Full migration patterns: see `migration-legacy-stores.md`.
524
+
525
+ **❌ The missing pattern — AI never generates this:**
526
+
527
+ ```swift
528
+ @main
529
+ struct MyApp: App {
530
+ var body: some Scene {
531
+ WindowGroup { ContentView() }
532
+ }
533
+ // Stale keychain items from previous install persist silently
534
+ }
535
+ ```
536
+
537
+ **✅ Secure — first-launch keychain cleanup:**
538
+
539
+ ```swift
540
+ @main
541
+ struct MyApp: App {
542
+ init() { clearKeychainIfFirstLaunch() }
543
+
544
+ var body: some Scene {
545
+ WindowGroup { ContentView() }
546
+ }
547
+
548
+ private func clearKeychainIfFirstLaunch() {
549
+ let defaults = UserDefaults.standard
550
+ guard !defaults.bool(forKey: "hasLaunchedBefore") else { return }
551
+
552
+ // UserDefaults was cleared on uninstall → this is first launch
553
+ for secClass in [kSecClassGenericPassword, kSecClassInternetPassword,
554
+ kSecClassCertificate, kSecClassKey, kSecClassIdentity] {
555
+ SecItemDelete([
556
+ kSecClass: secClass,
557
+ kSecAttrSynchronizable: kSecAttrSynchronizableAny
558
+ ] as NSDictionary)
559
+ }
560
+ defaults.set(true, forKey: "hasLaunchedBefore")
561
+ }
562
+ }
563
+ ```
564
+
565
+ Place this before initializing any SDKs (Firebase, analytics) that read from Keychain. Including `kSecAttrSynchronizableAny` ensures iCloud Keychain items are also cleared.
566
+
567
+ **MASTG tests:** MASTG-TEST-0300, MASTG-TEST-0301. **Legacy:** MSTG-STORAGE-11.
568
+
569
+ **Detection heuristic:**
570
+
571
+ ```bash
572
+ grep -rn "SecItemAdd\|SecItemCopyMatching" --include="*.swift" -l | \
573
+ xargs grep -L "hasLaunchedBefore\|isFirstLaunch\|firstRun"
574
+ grep -rn "SecItemDelete" --include="*.swift" -l | \
575
+ xargs grep "hasLaunchedBefore\|isFirstLaunch"
576
+ ```
577
+
578
+ ---
579
+
580
+ ## Anti-Pattern #10 — Non-Cryptographic RNG for Security Operations
581
+
582
+ **Severity:** HIGH | **OWASP:** M10 (Insufficient Cryptography) | **Fix effort:** Low
583
+
584
+ `arc4random()` returns only 32-bit `UInt32` — insufficient for cryptographic purposes requiring 128–256 bits. Character-by-character token construction introduces bias. Truly non-cryptographic alternatives (`rand()`, `drand48()`, GameplayKit RNG) must never be used for security operations.
585
+
586
+ **❌ Insecure — AI-generated patterns:**
587
+
588
+ ```swift
589
+ func generateToken() -> String {
590
+ return String(arc4random_uniform(999_999)) // ~20 bits of entropy
591
+ }
592
+
593
+ func generateSessionId(length: Int = 16) -> String {
594
+ let chars = "abcdefghijklmnopqrstuvwxyz0123456789"
595
+ return String((0..<length).map { _ in chars.randomElement()! }) // Bias
596
+ }
597
+ // Also dangerous: srand48/drand48, rand(), GameplayKit RNG
598
+ ```
599
+
600
+ **✅ Secure — SecRandomCopyBytes / CryptoKit:**
601
+
602
+ ```swift
603
+ import Security
604
+ import CryptoKit
605
+
606
+ // SecRandomCopyBytes — canonical iOS crypto RNG
607
+ func generateSecureToken(byteCount: Int = 32) throws -> String {
608
+ var bytes = [UInt8](repeating: 0, count: byteCount)
609
+ let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
610
+ guard status == errSecSuccess else {
611
+ throw CryptoError.randomGenerationFailed(status)
612
+ }
613
+ return bytes.map { String(format: "%02x", $0) }.joined()
614
+ }
615
+
616
+ // CryptoKit key generation (secure RNG internally)
617
+ let encryptionKey = SymmetricKey(size: .bits256)
618
+ ```
619
+
620
+ `SecRandomCopyBytes` sources entropy from the Secure Enclave's hardware TRNG via corecrypto's `ccrng_generate`. It reports errors via return status — unlike `arc4random`, which silently cannot fail.
621
+
622
+ **MASTG test:** MASTG-TEST-0311. **MASTG demos:** MASTG-DEMO-0073, MASTG-DEMO-0074. **Legacy:** MSTG-CRYPTO-6.
623
+
624
+ **Detection heuristic:**
625
+
626
+ ```bash
627
+ grep -rn "arc4random\|arc4random_uniform\|arc4random_buf" --include="*.swift" | \
628
+ grep -iE "token|nonce|salt|key|secret|session|iv"
629
+ grep -rn "\bsrand\b\|\brand()\|\brandom()\|\bdrand48\b" --include="*.swift"
630
+ grep -rn "GKARC4RandomSource\|GKMersenneTwisterRandomSource" --include="*.swift"
631
+ ```
632
+
633
+ ---
634
+
635
+ ## Quick Reference Matrix
636
+
637
+ | # | Anti-Pattern | OWASP 2024 | MASTG Test | Dangerous API | Secure API | Fix Effort |
638
+ | --- | ------------------------ | ---------- | --------------- | -------------------------- | ----------------------------------- | ---------- |
639
+ | 1 | UserDefaults secrets | M9 | MASTG-TEST-0302 | `UserDefaults.set` | `SecItemAdd` + Keychain | Medium |
640
+ | 2 | Hardcoded API keys | M1 | MASTG-TEST-0213 | String literals | Server proxy + Keychain cache | High |
641
+ | 3 | LAContext-only biometric | M3 | MASTG-TEST-0266 | `evaluatePolicy` | `SecAccessControlCreateWithFlags` | Medium |
642
+ | 4 | Ignored SecItem errors | M8 | MASTG-TEST-0300 | Unchecked `SecItemAdd` | OSStatus switch + `SecItemUpdate` | Low |
643
+ | 5 | Wrong data protection | M9 | MASTG-TEST-0299 | `kSecAttrAccessibleAlways` | `WhenUnlockedThisDeviceOnly` | Low |
644
+ | 6 | Nonce reuse AES-GCM | M10 | MASTG-TEST-0317 | `AES.GCM.Nonce(data:)` | Omit nonce (auto-random) | Medium |
645
+ | 7 | MD5/SHA-1 for security | M10 | MASTG-TEST-0211 | `Insecure.MD5/.SHA1` | `SHA256`+ / KDF for passwords | Low |
646
+ | 8 | Logging sensitive data | M9 | MASTG-TEST-0297 | `print(token)` | `Logger` + `.private` | Low |
647
+ | 9 | No keychain cleanup | M9 | MASTG-TEST-0300 | Missing cleanup | UserDefaults flag + `SecItemDelete` | Low |
648
+ | 10 | Non-crypto RNG | M10 | MASTG-TEST-0311 | `arc4random()` | `SecRandomCopyBytes` | Low |
649
+
650
+ ---
651
+
652
+ ## CI/CD Detection Strategy
653
+
654
+ **Semgrep** (pre-commit/PR gate): Fast structural pattern matching for `UserDefaults` misuse, missing `errSecDuplicateItem`, `LAContext` booleans. Limited data-flow analysis.
655
+
656
+ **CodeQL** (nightly/PR gate): Deep semantic taint tracking — catches tokens assigned to variables then logged. Slower execution.
657
+
658
+ **Binary scanning** (post-build): `strings`/`class-dump` on compiled binary catches hardcoded keys surviving source-level obfuscation.
659
+
660
+ Recommended: Semgrep on every PR + post-build binary scanning. CodeQL nightly for deep analysis.
661
+
662
+ ---
663
+
664
+ ## iOS 26 / WWDC 2025 Implications
665
+
666
+ WWDC 2025-314 introduced the most significant CryptoKit expansion since 2019:
667
+
668
+ - **Symmetric keys:** `.bits256` recommended over `.bits128` for quantum resistance (anti-patterns #6, #10)
669
+ - **Hashing:** SHA-3 family (`SHA3_256/384/512`) in CryptoKit on iOS 18+ (anti-pattern #7)
670
+ - **Post-quantum:** ML-KEM 768/1024, ML-DSA 65/87, X-Wing — all with Secure Enclave support
671
+ - **TLS:** `X25519MLKEM768` enabled by default for `URLSession` in iOS 26
672
+ - **Secure Enclave:** Hardware post-quantum key creation strengthens anti-patterns #3 and #5 fixes
673
+
674
+ ---
675
+
676
+ ## Summary Checklist
677
+
678
+ When reviewing iOS code for security anti-patterns, verify each item:
679
+
680
+ 1. **No secrets in UserDefaults** — tokens, passwords, API keys, JWTs use Keychain with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` or stricter
681
+ 1. **No hardcoded keys in source** — API keys fetched at runtime via server proxy or authenticated endpoint; no high-entropy string literals, no secrets in `.xcconfig` or `Info.plist`
682
+ 1. **Biometrics bound to Keychain** — `evaluatePolicy` is never used alone to gate sensitive actions; `SecAccessControlCreateWithFlags` with `.biometryCurrentSet` protects keychain items
683
+ 1. **All SecItem calls checked** — `SecItemAdd` handles `errSecDuplicateItem` with `SecItemUpdate` fallback; `SecItemCopyMatching` handles `errSecItemNotFound`; no discarded `OSStatus` return values
684
+ 1. **Explicit data protection class** — every `SecItemAdd` includes `kSecAttrAccessible` or `kSecAttrAccessControl`; no `kSecAttrAccessibleAlways`; `ThisDeviceOnly` variants used for non-syncing items
685
+ 1. **No nonce reuse** — `AES.GCM.seal` called without explicit `nonce:` parameter (auto-random); no stored/global/counter-based nonce variables
686
+ 1. **No broken hashes** — no `Insecure.MD5`, `Insecure.SHA1`, `CC_MD5`, `CC_SHA1` for security purposes; passwords use KDF (Argon2id, bcrypt, PBKDF2 with ≥310,000 iterations)
687
+ 1. **No sensitive data in logs** — `print()` and `NSLog()` never contain tokens, keys, or credentials; `os_log` uses `%{private}@`; `Logger` uses `.private` or `.private(mask: .hash)`
688
+ 1. **First-launch keychain cleanup** — `UserDefaults` flag + `SecItemDelete` for all classes runs before SDK initialization at app startup
689
+ 1. **Cryptographic RNG only** — `SecRandomCopyBytes` or CryptoKit APIs for tokens, nonces, salts, keys; no `arc4random` / `rand()` / `drand48()` / GameplayKit RNG in security contexts
690
+ 1. **iOS 26 readiness** — symmetric keys use `.bits256`; no deprecated algorithms; aware of post-quantum CryptoKit APIs for forward-looking implementations