@mmerterden/multi-agent-pipeline 20.7.0 → 20.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 (264) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +266 -267
  5. package/package.json +2 -2
  6. package/pipeline/scripts/_notices.mjs +1 -1
  7. package/pipeline/skills/.skill-manifest.json +68 -68
  8. package/pipeline/skills/shared/README.md +70 -70
  9. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  10. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  11. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  12. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  13. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  14. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  15. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  16. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  17. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  18. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  19. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  20. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  21. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  22. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  23. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  24. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  25. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  26. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  27. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  28. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  29. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  30. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  31. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  32. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  34. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  35. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  36. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  37. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  38. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  39. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  40. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  41. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  42. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  43. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  44. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  45. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  46. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  47. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  48. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  49. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  50. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  51. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  52. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  53. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  54. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  55. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  56. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  57. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  58. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  59. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  60. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  61. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  62. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  63. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  64. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  65. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  66. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  67. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  68. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  69. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  70. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  71. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  72. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  73. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  74. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  75. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  76. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  77. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  78. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  79. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  80. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  81. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  82. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  83. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  84. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  85. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  86. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  87. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  88. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  89. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  90. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  91. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  92. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  93. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  94. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  95. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  96. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  97. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  98. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  99. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  100. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  101. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  102. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  103. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  104. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  105. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  106. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  107. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  108. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  109. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  111. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  112. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  113. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  114. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  115. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  116. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  117. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  118. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  119. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  120. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  121. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  122. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  123. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  124. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  125. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  126. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  127. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  128. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  129. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  130. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  131. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  132. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  133. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  134. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  135. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  136. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  137. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  138. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  139. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  140. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  141. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  142. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  143. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  144. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  145. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  146. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  147. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  148. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  149. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  150. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  151. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  152. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  153. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  154. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  155. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  156. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  157. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  158. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  159. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  160. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  161. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  162. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  163. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  164. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  165. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  166. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  167. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  168. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  169. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  170. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  171. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  172. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  173. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  174. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  175. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  176. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  177. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  178. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  179. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  180. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  181. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  182. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  183. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  184. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  185. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  186. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  187. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  188. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  189. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  190. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  191. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  192. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  193. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  194. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  195. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  196. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  197. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  198. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  199. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  200. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  201. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  202. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  203. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  204. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  205. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  206. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  207. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  208. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  209. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  210. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  211. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  212. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  213. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  214. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  215. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  216. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  217. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  218. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  219. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  220. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  221. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  222. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  223. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  224. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  225. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  226. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  227. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  228. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  229. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  230. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  231. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  232. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  233. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  234. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  235. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  236. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  237. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  238. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  239. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  240. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  241. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  242. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  243. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  244. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  245. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  246. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  247. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  248. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  249. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  250. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  251. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  252. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  253. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  254. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  255. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  256. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  257. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  258. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  259. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  260. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  261. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  262. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  263. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  264. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -1,573 +1,366 @@
1
- # Compliance & OWASP Mapping Reference
1
+ # OWASP Mobile Compliance for Apple Platforms
2
2
 
3
- > Scope: Maps Apple-platform client security patterns to OWASP Mobile Top 10 (2024), MASVS, and MASTG controls for audit and remediation workflows.
3
+ This file connects everyday iOS security code with three OWASP documents as they stand for 2024 to 2026: the 2024 Mobile Top 10, the MASVS at version 2.1.0, and the MASTG. It concentrates on the four categories this skill owns: M1, M3, M9 and M10.
4
4
 
5
- **Most AI code generators still cite the 2016 OWASP Mobile Top 10 numbering - "M2: Insecure Data Storage," "M5: Insufficient Cryptography" - which was completely replaced in 2024.** This reference maps current iOS security practices to the OWASP Mobile Top 10 (2024), MASVS v2.1.0, and MASTG test cases for the 2024-2026 compliance window. It covers the four categories most relevant to Keychain & Security work: M1 (Improper Credential Usage), M3 (Insecure Authentication/Authorization), M9 (Insecure Data Storage), and M10 (Insufficient Cryptography). Cybernews analysis of 156,080 iOS apps (March 2025) found 71% leak at least one hardcoded secret - CISA/FBI jointly classified hardcoded credentials as a "dangerous" bad practice (CWE-798) in January 2025.
5
+ Generated code and older audit templates still quote the 2016 list, for example calling data storage "M2" or cryptography "M5". The 2024 list replaced that numbering completely.
6
6
 
7
- ---
7
+ The stakes are well documented. A March 2025 scan of 156,080 iOS apps found that 71% leaked at least one hardcoded secret, and the January 2025 joint CISA and FBI guidance on bad product-security practices calls hardcoded credentials (CWE-798) dangerous.
8
8
 
9
- ## Contents
10
-
11
- - [What Changed: 2016 → 2024 OWASP Mobile Top 10](#what-changed-2016-2024-owasp-mobile-top-10)
12
- - [Master Traceability Matrix](#master-traceability-matrix)
13
- - [M1 - Improper Credential Usage](#m1-improper-credential-usage)
14
- - [MASTG Test Cases](#mastg-test-cases)
15
- - [Compliant: Keychain credential storage](#compliant-keychain-credential-storage)
16
- - [Anti-pattern: common AI-generated credential storage](#anti-pattern-common-ai-generated-credential-storage)
17
- - [M3 - Insecure Authentication/Authorization](#m3-insecure-authenticationauthorization)
18
- - [MASTG Test Cases](#mastg-test-cases)
19
- - [The LAContext Vulnerability](#the-lacontext-vulnerability)
20
- - [`.biometryCurrentSet` vs `.biometryAny`](#biometrycurrentset-vs-biometryany)
21
- - [Compliant: hardware-bound biometric authentication](#compliant-hardware-bound-biometric-authentication)
22
- - [Anti-pattern: LAContext-only authentication](#anti-pattern-lacontext-only-authentication)
23
- - [M9 - Insecure Data Storage](#m9-insecure-data-storage)
24
- - [iOS Storage Security Properties](#ios-storage-security-properties)
25
- - [MASTG Test Cases](#mastg-test-cases)
26
- - [NSFileProtection Classes](#nsfileprotection-classes)
27
- - [Compliant: file storage with Data Protection](#compliant-file-storage-with-data-protection)
28
- - [Anti-pattern: insecure data storage](#anti-pattern-insecure-data-storage)
29
- - [M10 - Insufficient Cryptography](#m10-insufficient-cryptography)
30
- - [Deprecated vs. Approved Algorithms](#deprecated-vs-approved-algorithms)
31
- - [MASTG Test Cases](#mastg-test-cases)
32
- - [Compliant: CryptoKit encryption](#compliant-cryptokit-encryption)
33
- - [Anti-pattern: insecure cryptography](#anti-pattern-insecure-cryptography)
34
- - [kSecAttrAccessible Selection Guide](#ksecattraccessible-selection-guide)
35
- - [Enterprise Audit Workflow](#enterprise-audit-workflow)
36
- - [How Security Teams Evaluate iOS Apps](#how-security-teams-evaluate-ios-apps)
37
- - [Top 10 Audit Findings](#top-10-audit-findings)
38
- - [Evidence Kit (5 Artifacts)](#evidence-kit-5-artifacts)
39
- - [Jailbreak-Era Testing (2025-2026)](#jailbreak-era-testing-20252026)
40
- - [Post-Quantum Cryptography Roadmap](#post-quantum-cryptography-roadmap)
41
- - [Cross-Reference Index](#cross-reference-index)
42
- - [Conclusion](#conclusion)
43
- - [Summary Checklist](#summary-checklist)
44
-
45
- ## What Changed: 2016 → 2024 OWASP Mobile Top 10
46
-
47
- The 2024 edition is a complete overhaul. Four categories are entirely new, two pairs were merged, and everything was renumbered. Any code comment or documentation citing the 2016 numbering is outdated.
48
-
49
- | 2024 Category | Status | 2016 Predecessor |
50
- | --------------------------------------------- | ---------- | ------------------ |
51
- | **M1: Improper Credential Usage** | New | None |
52
- | **M2: Inadequate Supply Chain Security** | New | None |
53
- | **M3: Insecure Authentication/Authorization** | Merged | 2016 M4 + M6 |
54
- | **M4: Insufficient Input/Output Validation** | New | None |
55
- | **M5: Insecure Communication** | Renumbered | 2016 M3 |
56
- | **M6: Inadequate Privacy Controls** | New | None |
57
- | **M7: Insufficient Binary Protections** | Merged | 2016 M8 + M9 |
58
- | **M8: Security Misconfiguration** | Expanded | 2016 M10 (partial) |
59
- | **M9: Insecure Data Storage** | Renumbered | 2016 M2 |
60
- | **M10: Insufficient Cryptography** | Renumbered | 2016 M5 |
61
-
62
- **MASVS v2.1.0** (January 18, 2024) reorganized into 8 control groups with concise, testable controls. The old L1/L2/R verification levels became **MAS Testing Profiles** within the MASTG, aligned with NIST OSCAL. Legacy MSTG-\* test IDs (e.g., MSTG-STORAGE-1) were deprecated in favor of new MASTG-TEST-02xx/03xx identifiers with granular, tool-specific test procedures.
63
-
64
- ---
65
-
66
- ## Master Traceability Matrix
67
-
68
- This matrix links each OWASP 2024 category to its MASVS controls, MASTG test cases, iOS APIs, and required audit evidence.
69
-
70
- | OWASP 2024 | MASVS v2 Controls | Key MASTG Tests (New IDs) | iOS APIs / Flags | Required Evidence |
71
- | --------------------------------- | --------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
72
- | **M1** Improper Credential Usage | MASVS-STORAGE-1, MASVS-AUTH-1, MASVS-CRYPTO-2 | 0213, 0214, 0299, 0300, 0302 | Keychain + `SecAccessControl`; App Attest | Static scan (no literals); keychain dump with ACL; attestation logs |
73
- | **M3** Insecure Auth/AuthZ | MASVS-AUTH-1, MASVS-AUTH-2, MASVS-AUTH-3 | 0266, 0267, 0268, 0269, 0270, 0271 | `SecAccessControlCreateWithFlags` + `.biometryCurrentSet`; `ASWebAuthenticationSession` | Auth flow diagrams; biometric bypass test results; token TTL policy |
74
- | **M9** Insecure Data Storage | MASVS-STORAGE-1, MASVS-STORAGE-2 | 0296, 0297, 0299, 0300, 0301, 0302, 0303, 0215, 0298, 0313, 0314 | Keychain accessibility flags; `NSFileProtectionComplete`; `isExcludedFromBackup` | `xattr` listings; backup extraction; keychain dump |
75
- | **M10** Insufficient Cryptography | MASVS-CRYPTO-1, MASVS-CRYPTO-2 | 0209, 0210, 0211, 0213, 0214, 0311, 0317 | CryptoKit `AES.GCM`/`ChaChaPoly`; `SecRandomCopyBytes`; Secure Enclave keys | Crypto inventory; algorithm audit; unit tests |
76
-
77
- > **Cross-reference note:** MASVS-STORAGE-1 and MASTG-TEST-0299/0302 appear under both M1 and M9. This is intentional - keychain configuration simultaneously addresses credential storage and data-at-rest protection. See `keychain-access-control.md` for detailed accessibility flag guidance.
78
-
79
- ---
80
-
81
- ## M1 - Improper Credential Usage
82
-
83
- **Scope:** Hardcoded credentials in source/config, insecure credential transmission, insecure on-device storage, weak auth protocols. Attack vectors: EASY. Impact: SEVERE. Entirely new in 2024 - no 2016 predecessor.
84
-
85
- **Cybernews 2025 data:** 815,000+ hardcoded secrets across 156,080 iOS apps (average 5.2 per app), including 19 Stripe secret keys, 836 unprotected cloud endpoints exposing 406TB, and 2,218 misconfigured Firebase endpoints leaking 19.8M records. Secrets found in plaintext IPA files without decompilation.
86
-
87
- ### MASTG Test Cases
88
-
89
- | Test ID | Legacy ID | Verifies | Profile |
90
- | --------------- | -------------- | ----------------------------------------------------- | ------- |
91
- | MASTG-TEST-0213 | MSTG-CRYPTO-1 | No hardcoded cryptographic keys in source/binary | L1, L2 |
92
- | MASTG-TEST-0214 | MSTG-CRYPTO-5 | No cryptographic keys in bundle files (plist, config) | L1, L2 |
93
- | MASTG-TEST-0299 | MSTG-STORAGE-1 | Files use appropriate Data Protection classes | L1 |
94
- | MASTG-TEST-0300 | MSTG-STORAGE-1 | Static: references to APIs storing unencrypted data | L2 |
95
- | MASTG-TEST-0302 | MSTG-STORAGE-2 | Sensitive data unencrypted in private storage | L2 |
96
-
97
- **Testing procedure:** Use radare2 for static analysis - search for `SecKeyCreateWithData` with hardcoded key data or CryptoKit key initialization with inline bytes. Use objection (`ios keychain dump`, `ios nsuserdefaults get`) and filesystem grep at runtime. Check `.xcconfig`, `Info.plist`, and embedded resources for API keys.
98
-
99
- **App Attest (iOS 14+):** Closes the secret provisioning gap by verifying device integrity before the server issues credentials. This avoids hardcoded secrets entirely - the server provisions secrets only to attested, genuine app instances. See `credential-storage-patterns.md` for implementation details.
100
-
101
- ### Compliant: Keychain credential storage
102
-
103
- ```swift
104
- import Security
105
-
106
- /// Stores a credential securely in the iOS Keychain.
107
- /// Compliance: OWASP M1 (Improper Credential Usage), MASVS-STORAGE-1
108
- /// Test cases: MASTG-TEST-0213, MASTG-TEST-0299
109
- /// iOS 8.0+ (SecAccessControlCreateWithFlags), iOS 11.3+ (.biometryCurrentSet)
110
- func storeCredential(account: String, secret: Data, service: String) throws {
111
- // ✅ CORRECT - secrets are persisted in Keychain with explicit access control
112
- // Delete existing item first to avoid errSecDuplicateItem
113
- let deleteQuery: [String: Any] = [
114
- kSecClass as String: kSecClassGenericPassword,
115
- kSecAttrAccount as String: account,
116
- kSecAttrService as String: service
117
- ]
118
- SecItemDelete(deleteQuery as CFDictionary)
119
-
120
- var error: Unmanaged<CFError>?
121
- guard let accessControl = SecAccessControlCreateWithFlags(
122
- kCFAllocatorDefault,
123
- kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
124
- .biometryCurrentSet,
125
- &error
126
- ) else {
127
- throw error!.takeRetainedValue() as Error
128
- }
129
-
130
- let query: [String: Any] = [
131
- kSecClass as String: kSecClassGenericPassword,
132
- kSecAttrAccount as String: account,
133
- kSecAttrService as String: service,
134
- kSecAttrAccessControl as String: accessControl,
135
- kSecValueData as String: secret
136
- ]
137
-
138
- let status = SecItemAdd(query as CFDictionary, nil)
139
- guard status == errSecSuccess else {
140
- throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
141
- }
142
- }
143
- ```
144
-
145
- ### Anti-pattern: common AI-generated credential storage
146
-
147
- ```swift
148
- // ❌ WRONG - UserDefaults writes to UNENCRYPTED plist at:
149
- // <AppSandbox>/Library/Preferences/<BundleID>.plist
150
- // Extractable via iTunes backup, iMazing, or objection
151
- UserDefaults.standard.set(apiToken, forKey: "auth_token")
9
+ Related: [keychain-access-control.md](keychain-access-control.md), [credential-storage-patterns.md](credential-storage-patterns.md), [biometric-authentication.md](biometric-authentication.md), [cryptokit-symmetric.md](cryptokit-symmetric.md), [cryptokit-public-key.md](cryptokit-public-key.md), [common-anti-patterns.md](common-anti-patterns.md).
152
10
 
153
- // ❌ WRONG - Hardcoded API key in source (found in 71% of iOS apps)
154
- let stripeKey = "sk_live_EXAMPLE_KEY_DO_NOT_USE"
155
-
156
- // ❌ WRONG - Secret in Info.plist (plaintext in IPA archive)
157
- // <key>API_SECRET</key><string>my-secret-key-12345</string>
158
-
159
- // ❌ WRONG - NSKeyedArchiver to Documents directory (no encryption)
160
- let data = try NSKeyedArchiver.archivedData(
161
- withRootObject: credentials, requiringSecureCoding: true)
162
- try data.write(to: documentsURL.appendingPathComponent("creds.dat"))
163
- ```
164
-
165
- **Why these fail audits:** objection `ios nsuserdefaults get` reveals UserDefaults instantly. MobSF flags hardcoded key patterns. Backup extraction exposes Documents directory. All fail MASTG-TEST-0213 and MASTG-TEST-0302.
166
-
167
- ---
11
+ ## Contents
168
12
 
169
- ## M3 - Insecure Authentication/Authorization
13
+ - [2016 to 2024: what moved](#2016-to-2024-what-moved)
14
+ - [Traceability matrix](#traceability-matrix)
15
+ - [M1 Improper Credential Usage](#m1-improper-credential-usage)
16
+ - [M3 Insecure Authentication/Authorization](#m3-insecure-authenticationauthorization)
17
+ - [M9 Insecure Data Storage](#m9-insecure-data-storage)
18
+ - [M10 Insufficient Cryptography](#m10-insufficient-cryptography)
19
+ - [Choosing kSecAttrAccessible](#choosing-ksecattraccessible)
20
+ - [Running an enterprise audit](#running-an-enterprise-audit)
21
+ - [Post-quantum roadmap](#post-quantum-roadmap)
22
+ - [Cross-reference index](#cross-reference-index)
23
+ - [Takeaways](#takeaways)
24
+ - [Checklist](#checklist)
170
25
 
171
- **Scope:** Merges 2016 M4 + M6. Covers remote server-side auth, local biometric auth, and client-only authorization. Attack vectors: EASY. Impact: SEVERE. Critical iOS risk: LAContext-only biometric auth is bypassable via Frida in under 10 seconds.
26
+ ## 2016 to 2024: what moved
172
27
 
173
- ### MASTG Test Cases
28
+ | 2024 category | Origin |
29
+ | --- | --- |
30
+ | M1 Improper Credential Usage | Added in 2024 |
31
+ | M2 Inadequate Supply Chain Security | Added in 2024 |
32
+ | M3 Insecure Authentication/Authorization | Merges 2016 M4 and M6 |
33
+ | M4 Insufficient Input/Output Validation | Added in 2024 |
34
+ | M5 Insecure Communication | Was 2016 M3 |
35
+ | M6 Inadequate Privacy Controls | Added in 2024 |
36
+ | M7 Insufficient Binary Protections | Merges 2016 M8 and M9 |
37
+ | M8 Security Misconfiguration | Broadens part of 2016 M10 |
38
+ | M9 Insecure Data Storage | Was 2016 M2 |
39
+ | M10 Insufficient Cryptography | Was 2016 M5 |
174
40
 
175
- | Test ID | Legacy ID | Verifies | Profile |
176
- | --------------- | ----------- | ------------------------------------------------------------- | ------- |
177
- | MASTG-TEST-0266 | MSTG-AUTH-8 | Static: references to `LAContext.evaluatePolicy` | L2 |
178
- | MASTG-TEST-0267 | MSTG-AUTH-8 | Dynamic: runtime event-based biometric auth (bypassable) | L2 |
179
- | MASTG-TEST-0268 | MSTG-AUTH-8 | Static: APIs allowing fallback to non-biometric auth | L2 |
180
- | MASTG-TEST-0269 | MSTG-AUTH-8 | Dynamic: runtime fallback to non-biometric auth | L2 |
181
- | MASTG-TEST-0270 | MSTG-AUTH-8 | Static: `.biometryCurrentSet` for enrollment change detection | L2 |
182
- | MASTG-TEST-0271 | MSTG-AUTH-8 | Dynamic: enrollment change detection enforced at runtime | L2 |
41
+ MASVS v2.1.0, published 18 January 2024, has eight control groups. The old L1, L2 and R verification levels moved into the MASTG as MAS Testing Profiles aligned with NIST OSCAL, and the legacy `MSTG-*` test identifiers are deprecated in favour of `MASTG-TEST-02xx` and `MASTG-TEST-03xx`.
183
42
 
184
- ### The LAContext Vulnerability
43
+ ## Traceability matrix
185
44
 
186
- `LAContext.evaluatePolicy` performs a software-only biometric check returning a Boolean in the completion handler. This Boolean executes in user space and is hookable by Frida to always return `true`. The Secure Enclave performs the biometric match, but the result is a plain callback with no cryptographic proof of authentication.
45
+ | Category | Apple APIs | Evidence | MASTG tests | MASVS controls |
46
+ | --- | --- | --- | --- | --- |
47
+ | M1 | Keychain with `SecAccessControl`, App Attest | Static scan with no secret literals, keychain dump showing the ACL, attestation logs | 0213, 0214, 0299, 0300, 0302 | STORAGE-1, AUTH-1, CRYPTO-2 |
48
+ | M3 | `SecAccessControlCreateWithFlags` with `.biometryCurrentSet`, `ASWebAuthenticationSession` | Auth flow diagrams, biometric bypass results, token lifetime policy | 0266 to 0271 | AUTH-1, AUTH-2, AUTH-3 |
49
+ | M9 | Keychain accessibility classes, `NSFileProtectionComplete`, `isExcludedFromBackup` | `xattr` listings, backup extraction, keychain dump | 0215, 0296, 0297, 0298, 0299, 0300, 0301, 0302, 0303, 0313, 0314 | STORAGE-1, STORAGE-2 |
50
+ | M10 | `AES.GCM`, `ChaChaPoly`, `SecRandomCopyBytes`, Secure Enclave keys | Crypto inventory, algorithm audit, unit tests | 0209, 0210, 0211, 0213, 0214, 0311, 0317 | CRYPTO-1, CRYPTO-2 |
187
51
 
188
- **Frida bypass (< 10 lines):**
52
+ Some identifiers are listed twice deliberately. MASVS-STORAGE-1 plus tests 0299 and 0302 belong to M1 as well as M9, because a credential sitting in plaintext is a credential problem and a storage problem at once.
189
53
 
190
- ```javascript
191
- // Forces LAContext.evaluatePolicy to always succeed
192
- if (ObjC.available) {
193
- var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
194
- Interceptor.attach(hook.implementation, {
195
- onEnter: function (args) {
196
- var block = new ObjC.Block(args[4]);
197
- const appCallback = block.implementation;
198
- block.implementation = function (error, value) {
199
- return appCallback(1, null); // Force success=true
200
- };
201
- },
202
- });
203
- }
204
- ```
54
+ ## M1 Improper Credential Usage
205
55
 
206
- **objection one-liner:** `ios ui biometrics_bypass` - hooks `evaluatePolicy` to return `true`.
56
+ Covers credentials hardcoded in source or configuration, sent insecurely, stored insecurely on the device, or used with weak authentication protocols. Exploitability: easy. Impact: severe. There is no 2016 predecessor.
207
57
 
208
- The correct pattern ties secrets to Keychain items protected by `SecAccessControlCreateWithFlags`. The Secure Enclave holds the decryption key and will not release it without valid biometric authentication. There is no Boolean to hook - failed biometrics means the data is cryptographically inaccessible.
58
+ The same 2025 scan counted more than 815,000 secrets (an average above five per app). Among them were 19 live payment-provider secret keys, 836 unprotected cloud storage endpoints exposing about 406 TB, and 2,218 misconfigured backend-as-a-service database endpoints leaking 19.8 million records. All of it was read straight out of IPA files.
209
59
 
210
- ### `.biometryCurrentSet` vs `.biometryAny`
60
+ | Profile | MASTG test | What it checks | Legacy ID |
61
+ | --- | --- | --- | --- |
62
+ | L1, L2 | 0213 | Keys embedded in source or the binary | MSTG-CRYPTO-1 |
63
+ | L1, L2 | 0214 | Keys in bundled resource files | MSTG-CRYPTO-5 |
64
+ | L1 | 0299 | Which protection class each item uses | MSTG-STORAGE-1 |
65
+ | L2 | 0300 | Code that calls plaintext storage APIs | MSTG-STORAGE-1 |
66
+ | L2 | 0302 | Secrets found in clear in the private container | MSTG-STORAGE-2 |
211
67
 
212
- | Flag | Behavior | Security | iOS |
213
- | --------------------- | ----------------------------------------------------- | ---------------------------------------------------- | ----- |
214
- | `.biometryCurrentSet` | Item invalidated if new biometric enrolled | **Recommended** - prevents enrollment-change attacks | 11.3+ |
215
- | `.biometryAny` | Accessible with any enrolled biometric, even new ones | Lower - attacker can add their own fingerprint | 11.3+ |
216
- | `.userPresence` | Biometry OR passcode (system chooses) | Allows passcode fallback | 8.0+ |
217
- | `.devicePasscode` | Passcode only | No biometric option | 9.0+ |
68
+ How testers look: radare2 over the binary for `SecKeyCreateWithData` fed with constant bytes or CryptoKit keys built from inline data; `objection` with `ios keychain dump` and `ios nsuserdefaults get`; a grep of the extracted file system; and a read through `.xcconfig` files, `Info.plist` and every embedded resource.
218
69
 
219
- For high-security items, always use `.biometryCurrentSet`. If an attacker adds their fingerprint to a stolen device, `.biometryAny` items become accessible; `.biometryCurrentSet` items are permanently invalidated. See `biometric-authentication.md` for full implementation patterns.
70
+ App Attest (iOS 14 and later) closes the provisioning gap: the server hands secrets only to instances it has attested as genuine (see the `device-integrity` skill).
220
71
 
221
- ### Compliant: hardware-bound biometric authentication
72
+ Compliant storage of a credential:
222
73
 
223
74
  ```swift
224
- import LocalAuthentication
225
75
  import Security
226
76
 
227
- /// Hardware-bound biometric auth using Keychain + Secure Enclave.
228
- /// Compliance: OWASP M3 (Insecure Auth), MASVS-AUTH-2
229
- /// Test cases: MASTG-TEST-0266, MASTG-TEST-0270
230
- /// iOS 11.3+ (.biometryCurrentSet)
231
- /// Canonical pattern with full error handling: biometric-authentication.md section The Secure Pattern - Hardware-Bound Secrets
232
-
233
- // STEP 1: Store secret with biometric protection
234
- func storeBiometricProtectedSecret(account: String, secret: Data) throws {
235
- // ✅ CORRECT - Secure Enclave gates secret release through keychain ACLs
236
- var error: Unmanaged<CFError>?
237
- guard let accessControl = SecAccessControlCreateWithFlags(
238
- kCFAllocatorDefault,
239
- kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
240
- .biometryCurrentSet,
241
- &error
242
- ) else {
243
- throw error!.takeRetainedValue() as Error
77
+ // MASTG-TEST-0299, 0300 | MASVS-AUTH-1, MASVS-STORAGE-1 | OWASP M1, M9
78
+ // SecAccessControlCreateWithFlags: iOS 8.0+, .biometryCurrentSet: iOS 11.3+
79
+ func storeSigningSecret(_ secret: Data, account: String) throws {
80
+ guard let acl = SecAccessControlCreateWithFlags(nil,
81
+ kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .biometryCurrentSet, nil) else {
82
+ throw KeychainFailure(errSecParam)
244
83
  }
245
-
246
- let query: [String: Any] = [
247
- kSecClass as String: kSecClassGenericPassword,
248
- kSecAttrAccount as String: account,
249
- kSecAttrService as String: "com.app.biometric-auth",
250
- kSecAttrAccessControl as String: accessControl,
251
- kSecValueData as String: secret
252
- ]
253
-
254
- let status = SecItemAdd(query as CFDictionary, nil)
255
- guard status == errSecSuccess else {
256
- throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
84
+ let identity: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
85
+ kSecAttrService: "com.example.treasury",
86
+ kSecAttrAccount: account]
87
+ var insert = identity
88
+ insert[kSecValueData] = secret
89
+ insert[kSecAttrAccessControl] = acl
90
+ var status = SecItemAdd(insert as CFDictionary, nil)
91
+ if status == errSecDuplicateItem {
92
+ status = SecItemUpdate(identity as CFDictionary, [kSecValueData: secret] as CFDictionary)
257
93
  }
258
- }
259
-
260
- // STEP 2: Retrieve - Secure Enclave enforces biometric check
261
- func retrieveBiometricProtectedSecret(account: String) throws -> Data? {
262
- let context = LAContext()
263
- context.localizedReason = "Authenticate to access your account"
264
-
265
- let query: [String: Any] = [
266
- kSecClass as String: kSecClassGenericPassword,
267
- kSecAttrAccount as String: account,
268
- kSecAttrService as String: "com.app.biometric-auth",
269
- kSecUseAuthenticationContext as String: context,
270
- kSecReturnData as String: true
271
- ]
272
-
273
- var result: AnyObject?
274
- let status = SecItemCopyMatching(query as CFDictionary, &result)
275
- guard status == errSecSuccess else { return nil }
276
- return result as? Data
94
+ guard status == errSecSuccess else { throw KeychainFailure(status) }
277
95
  }
278
96
  ```
279
97
 
280
- ### Anti-pattern: LAContext-only authentication
281
-
282
- ```swift
283
- // ❌ WRONG - #2 most common iOS audit finding
284
- // Bypassable: objection -g com.app explore -> ios ui biometrics_bypass
285
- let context = LAContext()
286
- context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
287
- localizedReason: "Log in") { success, error in
288
- if success {
289
- // ❌ This Boolean is hookable - no cryptographic proof
290
- self.showMainScreen() // Attacker gains full access
291
- }
292
- }
293
- ```
98
+ `KeychainFailure` is a small error type wrapping the `OSStatus`. Add first and update on `errSecDuplicateItem`. Deleting and re-adding is reserved for changing the access control of an existing item.
294
99
 
295
- ---
100
+ Findings that fail MASTG-TEST-0213 and 0302:
296
101
 
297
- ## M9 - Insecure Data Storage
102
+ - A token in `UserDefaults`, which lands in `<AppSandbox>/Library/Preferences/<BundleID>.plist`.
103
+ - A live payment secret (`sk_live_...`) as a Swift literal.
104
+ - A secret stored as an `Info.plist` value.
105
+ - Credentials written with `NSKeyedArchiver` into `Documents`, which backups include.
298
106
 
299
- **Scope:** All vulnerabilities in how apps store sensitive data: weak/no encryption, accessible locations, insufficient access controls, unintentional leakage (logs, caches, backups). Was M2 in 2016 - renumbered to M9 (priority shift, not diminished importance).
107
+ `objection` and MobSF find all four within minutes, and a plain backup extraction exposes everything in `Documents`.
300
108
 
301
- ### iOS Storage Security Properties
109
+ ## M3 Insecure Authentication/Authorization
302
110
 
303
- | Storage Location | Encrypted | In Backups | Accessible w/o Jailbreak | Verdict |
304
- | ------------------------------------------ | ------------------ | -------------- | ------------------------ | ------------------------------- |
305
- | Keychain (`WhenPasscodeSetThisDeviceOnly`) | ✅ AES-256-GCM | ❌ | ❌ | ✅ Use for secrets |
306
- | Keychain (`AfterFirstUnlock`) | ✅ | ✅ (encrypted) | ❌ | ⚠️ Acceptable for L1 |
307
- | `NSFileProtectionComplete` files | ✅ (when locked) | ✅ | ❌ | ✅ Use for sensitive files |
308
- | UserDefaults | ❌ Plaintext plist | ✅ | ✅ (via backup) | ❌ Never for secrets |
309
- | Documents/ (default protection) | ✅ (Class C) | ✅ | ✅ (via backup) | ❌ Not without extra encryption |
310
- | SQLite/CoreData (no SQLCipher) | ❌ | ✅ | ✅ (via backup) | ❌ Not for secrets |
311
- | NSLog output | ❌ | N/A | ✅ (Console.app) | ❌ Never log secrets |
111
+ Merges 2016 M4 and M6: remote authentication, local biometric authentication, and authorization decided only on the client. Exploitability: easy. Impact: severe. A biometric check done with `LAContext` alone falls to Frida in under ten seconds.
312
112
 
313
- **Keychain persistence note:** Keychain items survive app uninstall and persist across install/uninstall cycles (confirmed since iOS 10.3). Only factory reset clears them. Exception: `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` items are deleted when the passcode is removed.
113
+ | MASTG test | Type | Checks |
114
+ | --- | --- | --- |
115
+ | 0266 | Static | References to `evaluatePolicy` used as the gate |
116
+ | 0267 | Dynamic | Event-based authentication that can be bypassed |
117
+ | 0268 | Static | APIs that fall back to non-biometric authentication |
118
+ | 0269 | Dynamic | Fallback behaviour at runtime |
119
+ | 0270 | Static | Use of `.biometryCurrentSet` |
120
+ | 0271 | Dynamic | Enforcement after an enrolment change |
314
121
 
315
- ### MASTG Test Cases
122
+ All six replace legacy MSTG-AUTH-8 and belong to the L2 profile.
316
123
 
317
- | Test ID | Legacy ID | Verifies | Profile |
318
- | --------------- | -------------- | ------------------------------------------------- | ------- |
319
- | MASTG-TEST-0299 | MSTG-STORAGE-1 | Data Protection classes for private storage files | L1 |
320
- | MASTG-TEST-0300 | MSTG-STORAGE-1 | Static: references to unencrypted storage APIs | L2 |
321
- | MASTG-TEST-0301 | MSTG-STORAGE-1 | Dynamic: runtime use of unencrypted storage | L2 |
322
- | MASTG-TEST-0302 | MSTG-STORAGE-2 | Sensitive data unencrypted in private storage | L2 |
323
- | MASTG-TEST-0296 | MSTG-STORAGE-3 | Sensitive data in logs | L1, L2 |
324
- | MASTG-TEST-0297 | MSTG-STORAGE-3 | Insertion of sensitive data into log statements | L1, L2 |
325
- | MASTG-TEST-0215 | MSTG-STORAGE-8 | Sensitive data not excluded from backup | L1, L2 |
326
- | MASTG-TEST-0313 | MSTG-STORAGE-5 | APIs preventing keyboard caching | L1, L2 |
124
+ The match itself happens in the Secure Enclave, but `evaluatePolicy` returns a plain `Bool` to user space with no cryptographic proof attached, so a Frida script of fewer than ten lines, or `objection`'s `ios ui biometrics_bypass`, changes the answer.
327
125
 
328
- ### NSFileProtection Classes
126
+ | Flag | Behaviour | Recommendation | Available from |
127
+ | --- | --- | --- | --- |
128
+ | `.biometryCurrentSet` | Invalidated when enrolment changes | Recommended | iOS 11.3 |
129
+ | `.biometryAny` | Any enrolled biometric, including ones added later | Weaker | iOS 11.3 |
130
+ | `.userPresence` | Biometrics or device passcode | General use | iOS 8.0 |
131
+ | `.devicePasscode` | Passcode only | Passcode-only flows | iOS 9.0 |
329
132
 
330
- | Class | Constant | Accessible When | Default? |
331
- | ------------------- | ------------------------------------------------------ | ------------------------------------------------- | -------- |
332
- | A: Complete | `NSFileProtectionComplete` | Only when unlocked; key discarded ~10s after lock | No |
333
- | B: Unless Open | `NSFileProtectionCompleteUnlessOpen` | Already-open files remain accessible when locked | No |
334
- | C: Until First Auth | `NSFileProtectionCompleteUntilFirstUserAuthentication` | After first unlock, even when locked | **Yes** |
335
- | D: None | `NSFileProtectionNone` | Always; protected only by device UID | No |
133
+ Use `.biometryCurrentSet` for anything valuable. With `.biometryAny`, a thief who knows the passcode can enrol their own finger or face and then unlock the item.
336
134
 
337
- ### Compliant: file storage with Data Protection
135
+ Compliant pattern: store as in the M1 example, then read with an `LAContext` passed through `kSecUseAuthenticationContext`. A short compliance snippet may simply return nil on any failure; the full read with status handling is in [biometric-authentication.md](biometric-authentication.md).
338
136
 
339
137
  ```swift
340
- import Foundation
341
-
342
- /// Writes sensitive data with Complete file protection.
343
- /// Compliance: OWASP M9 (Insecure Data Storage), MASVS-STORAGE-1
344
- /// Test cases: MASTG-TEST-0299
345
- /// iOS 9.0+ (.completeFileProtection option)
346
- func writeProtectedFile(data: Data, to url: URL) throws {
347
- try data.write(to: url, options: [.atomic, .completeFileProtection])
348
- }
138
+ import LocalAuthentication
349
139
 
350
- /// Excludes a file from device backups.
351
- /// Compliance: MASVS-STORAGE-2, MASTG-TEST-0215
352
- /// iOS 5.1+
353
- func excludeFromBackup(url: URL) throws {
354
- var resourceURL = url
355
- var resourceValues = URLResourceValues()
356
- resourceValues.isExcludedFromBackup = true
357
- try resourceURL.setResourceValues(resourceValues)
140
+ // OWASP M3 | MASVS-AUTH-2 | MASTG-TEST-0266, 0270
141
+ func readSigningSecret(account: String, reason: String) -> Data? {
142
+ let context = LAContext()
143
+ context.localizedReason = reason
144
+ let lookup: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
145
+ kSecAttrService: "com.example.treasury",
146
+ kSecAttrAccount: account,
147
+ kSecReturnData: true,
148
+ kSecUseAuthenticationContext: context]
149
+ var out: CFTypeRef?
150
+ return SecItemCopyMatching(lookup as CFDictionary, &out) == errSecSuccess ? out as? Data : nil
358
151
  }
359
152
  ```
360
153
 
361
- ### Anti-pattern: insecure data storage
362
-
363
- ```swift
364
- // ❌ WRONG - Unencrypted plist
365
- UserDefaults.standard.set("Bearer eyJhbGciOiJSUzI1NiIs...", forKey: "authToken")
366
-
367
- // ❌ WRONG - Default file protection (Class C) for sensitive file
368
- try sensitiveData.write(to: documentsURL.appendingPathComponent("profile.dat"))
369
-
370
- // ❌ WRONG - Logging sensitive data (Console.app / idevicesyslog)
371
- NSLog("User token: %@", authToken)
372
- print("Password entered: \(password)")
373
-
374
- // ❌ WRONG - Not excluding sensitive files from backup
375
- // Files in Documents/ are in iTunes/Finder backups by default
376
- // Extractable with iMazing on non-jailbroken devices
377
- ```
378
-
379
- ---
154
+ The anti-pattern, showing the main screen when `evaluatePolicy` succeeds, is the second most common finding in iOS audits.
380
155
 
381
- ## M10 - Insufficient Cryptography
156
+ ## M9 Insecure Data Storage
382
157
 
383
- **Scope:** Weak algorithms, insufficient key lengths, poor key management, insecure RNG, deprecated hashes. Attack vectors: AVERAGE. Impact: SEVERE. Was M5 in 2016.
158
+ Covers missing or weak encryption, data in reachable locations, weak access control, and leaks through logs, caches and backups. It was M2 in 2016.
384
159
 
385
- ### Deprecated vs. Approved Algorithms
160
+ | Storage | Protection | Verdict |
161
+ | --- | --- | --- |
162
+ | Keychain, `WhenPasscodeSetThisDeviceOnly` | Encrypted, never in backups | Use for secrets |
163
+ | Keychain, `AfterFirstUnlock` | Encrypted, included in encrypted backups | Acceptable at L1 |
164
+ | File with `NSFileProtectionComplete` | Encrypted while locked | Use for sensitive files |
165
+ | `UserDefaults` | Plaintext | Never for secrets |
166
+ | `Documents` with the default class | Class C | Not without extra encryption |
167
+ | SQLite or Core Data without SQLCipher | Plaintext database | Not for secrets |
168
+ | `NSLog` output | Visible in Console.app | Never log secrets |
386
169
 
387
- | Category | ❌ Deprecated/Broken | ✅ Approved (CryptoKit, iOS 13+) |
388
- | -------------- | --------------------------------- | -------------------------------------------------------------- |
389
- | Hashing | MD5, SHA-1 (for security) | SHA256, SHA384, SHA512; SHA3 (iOS 26+) |
390
- | Symmetric | DES, 3DES, RC4, Blowfish, AES-ECB | AES.GCM (AES-256-GCM), ChaChaPoly |
391
- | Asymmetric | RSA < 2048 bits | P256, P384, P521, Curve25519, Ed25519 |
392
- | Key derivation | Simple SHA hash of password | HKDF; Argon2/bcrypt/scrypt server-side |
393
- | RNG | `rand()`, `random()`, `srand()` | `SecRandomCopyBytes` (iOS 2+), CryptoKit auto-nonces (iOS 13+) |
394
- | Post-quantum | All classical PKC (by 2030) | ML-KEM, ML-DSA, X-Wing (iOS 26+) |
170
+ Keychain items outlive the app: that has been the confirmed behaviour since iOS 10.3, and only erasing the device clears them. One exception: items with `WhenPasscodeSetThisDeviceOnly` are deleted when the user removes the device passcode.
395
171
 
396
- **`arc4random()` nuance:** On modern Apple platforms, `arc4random()` uses a CSPRNG internally (not broken RC4). It is technically secure on iOS. However, `SecRandomCopyBytes` remains recommended for explicit cryptographic use - its security guarantees are documented and cross-platform portable. See `cryptokit-symmetric.md` for detailed algorithm guidance.
172
+ | Profile | MASTG test | What it checks | Legacy ID |
173
+ | --- | --- | --- | --- |
174
+ | L1 | 0299 | Protection class per item | MSTG-STORAGE-1 |
175
+ | L2 | 0300, 0301 | Plaintext storage APIs, statically and at runtime | MSTG-STORAGE-1 |
176
+ | L2 | 0302 | Secrets left readable in the private container | MSTG-STORAGE-2 |
177
+ | L1, L2 | 0296, 0297 | Secrets written to logs | MSTG-STORAGE-3 |
178
+ | L1, L2 | 0215 | Sensitive files that backups still include | MSTG-STORAGE-8 |
179
+ | L1, L2 | 0313 | Keyboard cache | MSTG-STORAGE-5 |
397
180
 
398
- **AES-GCM nonce reuse is catastrophic:** A single reuse with the same key destroys both confidentiality (XOR of ciphertexts reveals XOR of plaintexts) and authentication (leaks GHASH key `H`, enabling arbitrary forgery). CryptoKit mitigates this by auto-generating random nonces when `AES.GCM.seal()` is called without an explicit nonce.
181
+ File protection classes:
399
182
 
400
- ### MASTG Test Cases
183
+ | Class | Constant | Behaviour |
184
+ | --- | --- | --- |
185
+ | A | `NSFileProtectionComplete` | Readable only while unlocked; the key is discarded about 10 seconds after locking |
186
+ | B | `NSFileProtectionCompleteUnlessOpen` | Files already open stay readable after locking |
187
+ | C | `NSFileProtectionCompleteUntilFirstUserAuthentication` | Default; readable from first unlock until reboot |
188
+ | D | `NSFileProtectionNone` | Protected only by the device UID key |
401
189
 
402
- | Test ID | Legacy ID | Verifies | Profile |
403
- | --------------- | ------------- | ----------------------------------------------- | ------- |
404
- | MASTG-TEST-0209 | MSTG-CRYPTO-2 | Key size meets minimum requirements | L1, L2 |
405
- | MASTG-TEST-0210 | MSTG-CRYPTO-2 | No broken symmetric algorithms (DES, 3DES, RC4) | L1, L2 |
406
- | MASTG-TEST-0211 | MSTG-CRYPTO-3 | No broken hashes (MD5, SHA-1 for security) | L1, L2 |
407
- | MASTG-TEST-0317 | MSTG-CRYPTO-3 | No broken encryption modes (ECB) | L1, L2 |
408
- | MASTG-TEST-0311 | MSTG-CRYPTO-6 | CSPRNG used (not `rand`/`random`) | L1, L2 |
409
- | MASTG-TEST-0213 | MSTG-CRYPTO-1 | No hardcoded cryptographic keys in code | L1, L2 |
410
- | MASTG-TEST-0214 | MSTG-CRYPTO-5 | No hardcoded cryptographic keys in files | L1, L2 |
411
-
412
- **iOS-specific testing:** Use radare2 to find references to `kCCAlgorithmDES`, `kCCAlgorithm3DES`, `kCCAlgorithmRC4`, `kCCOptionECBMode` in CommonCrypto calls. Search for `CC_MD5`, `CC_SHA1` or CryptoKit `Insecure.MD5`/`Insecure.SHA1`. MASTG demos: MASTG-DEMO-0015 (CommonCrypto broken hash), MASTG-DEMO-0016 (CryptoKit broken hash), MASTG-DEMO-0018 (broken encryption).
413
-
414
- ### Compliant: CryptoKit encryption
415
-
416
- Canonical full round-trip patterns are in `cryptokit-symmetric.md` and anti-pattern #6 in `common-anti-patterns.md`. This compliance snippet stays minimal to avoid duplicating canonical crypto guidance.
190
+ Compliant file handling:
417
191
 
418
192
  ```swift
419
- import CryptoKit
420
-
421
- enum CryptoError: Error { case invalidCiphertext }
422
-
423
- /// Compliance: OWASP M10 (Insufficient Cryptography), MASVS-CRYPTO-1.
424
- /// Test cases: MASTG-TEST-0210, MASTG-TEST-0317. iOS 13.0+.
425
- func sealForStorage(_ plaintext: Data, using key: SymmetricKey) throws -> Data {
426
- let sealedBox = try AES.GCM.seal(plaintext, using: key)
427
- guard let combined = sealedBox.combined else { throw CryptoError.invalidCiphertext }
428
- return combined
193
+ // OWASP M9 | MASVS-STORAGE-1 | MASTG-TEST-0299, 0215
194
+ func persistStatement(_ pdf: Data, to url: URL) throws {
195
+ try pdf.write(to: url, options: [.completeFileProtection, .atomic])
196
+ var flags = URLResourceValues()
197
+ flags.isExcludedFromBackup = true
198
+ var target = url
199
+ try target.setResourceValues(flags)
429
200
  }
430
-
431
- // Compliance: MASVS-CRYPTO-2, MASTG-TEST-0213
432
- let encryptionKey = SymmetricKey(size: .bits256) // 256-bit from CSPRNG
433
201
  ```
434
202
 
435
- ### Anti-pattern: insecure cryptography
203
+ `Data.WritingOptions.completeFileProtection` is available from iOS 9.0 and `isExcludedFromBackup` from iOS 5.1.
436
204
 
437
- ```swift
438
- // ❌ WRONG - MD5 (collisions trivially constructable) - fails MASTG-TEST-0211
439
- var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
440
- CC_MD5(data.bytes, CC_LONG(data.count), &digest)
441
-
442
- // ❌ WRONG - ECB mode - fails MASTG-TEST-0317
443
- CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES),
444
- CCOptions(kCCOptionECBMode), key, keyLength, nil, plaintext, ...)
205
+ Anti-patterns: a bearer token in `UserDefaults`; a sensitive file left at the default class C; `NSLog` or `print` of a token or password; sensitive files in `Documents` that end up in backups, where desktop backup tools extract them.
445
206
 
446
- // ❌ WRONG - Insecure RNG - fails MASTG-TEST-0311
447
- let seed = srand(UInt32(time(nil))) // Predictable seed
207
+ ## M10 Insufficient Cryptography
448
208
 
449
- // ❌ WRONG - Hardcoded key - fails MASTG-TEST-0213
450
- let key = SymmetricKey(data: "my-secret-key-1234567890123456".data(using: .utf8)!)
209
+ Covers weak algorithms, short keys, poor key management, weak randomness and deprecated hashes. Exploitability: average. Impact: severe. It was M5 in 2016.
451
210
 
452
- // ❌ WRONG - Static nonce (catastrophic if reused)
453
- let nonce = try AES.GCM.Nonce(data: Data(repeating: 0, count: 12))
454
- let sealed = try AES.GCM.seal(data, using: key, nonce: nonce)
455
- ```
211
+ | Area | Deprecated | Use instead |
212
+ | --- | --- | --- |
213
+ | Hashing | MD5, SHA-1 | SHA-256, SHA-384, SHA-512; SHA-3 from iOS 26 |
214
+ | Symmetric | Blowfish, RC4, DES and 3DES, or AES in ECB mode | AES-256-GCM, ChaChaPoly |
215
+ | Asymmetric | RSA below 2048 bits | NIST curves (P-256 up to P-521), Curve25519, Ed25519 |
216
+ | Key derivation | A plain SHA hash of a password | HKDF for keys; Argon2, bcrypt or scrypt on the server for passwords |
217
+ | Randomness | `srand()` seeding, `rand()`, `random()` | `SecRandomCopyBytes` (iOS 2 and later), CryptoKit's automatic nonces (iOS 13 and later) |
218
+ | Post-quantum | Classical public-key crypto alone, deprecated by NIST from 2030 | From iOS 26: ML-KEM, ML-DSA and the X-Wing hybrid |
456
219
 
457
- ---
220
+ On `arc4random`: on current Apple platforms it is backed by the system cryptographic generator, not RC4, so it is not a finding by itself. What auditors flag is too little entropy (a 32-bit result, a six-digit code) or modulo bias from `% n`. For keys, nonces, salts and tokens, `SecRandomCopyBytes` remains the recommendation because it is documented for cryptographic use, reports failure and makes the intent obvious; [common-anti-patterns.md](common-anti-patterns.md) entry 10 applies the same rule.
458
221
 
459
- ## kSecAttrAccessible Selection Guide
222
+ Reusing an AES-GCM nonce destroys confidentiality for the affected messages and leaks the GHASH key H, which enables forgeries. CryptoKit's automatic nonce removes the risk as long as code does not supply its own.
460
223
 
461
- > Complete selection criteria and data protection tier mapping: `keychain-access-control.md` section The "When" Layer: Seven Accessibility Constants. The guidance below is a compliance-focused quick-reference for audit contexts.
224
+ | What it checks | MASTG test | Legacy ID |
225
+ | --- | --- | --- |
226
+ | Key lengths | 0209 | MSTG-CRYPTO-2 |
227
+ | DES, 3DES and RC4 absent | 0210 | MSTG-CRYPTO-2 |
228
+ | Broken hash functions absent | 0211 | MSTG-CRYPTO-3 |
229
+ | ECB mode absent | 0317 | MSTG-CRYPTO-3 |
230
+ | Randomness from a CSPRNG | 0311 | MSTG-CRYPTO-6 |
231
+ | Keys absent from code | 0213 | MSTG-CRYPTO-1 |
232
+ | Keys absent from files | 0214 | MSTG-CRYPTO-5 |
462
233
 
463
- Keychain accessibility is the single most important iOS security decision - it simultaneously addresses M1, M3, M9, and M10 requirements.
234
+ All belong to both L1 and L2.
464
235
 
465
- | Constant | Backup | iCloud | Passcode Required | Use For |
466
- | -------------------------------------------------- | ------ | ------ | ----------------------- | ----------------------------------------------------- |
467
- | `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` | ❌ | ❌ | ✅ (deleted if removed) | **Highest-sensitivity: auth tokens, encryption keys** |
468
- | `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` | ❌ | ❌ | ❌ | Sensitive data, device-specific |
469
- | `kSecAttrAccessibleWhenUnlocked` (default) | ✅ | ✅ | ❌ | General credentials needing sync |
470
- | `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` | ❌ | ❌ | ❌ | Background-accessible, device-specific |
471
- | `kSecAttrAccessibleAfterFirstUnlock` | ✅ | ✅ | ❌ | Background tasks (e.g., push notification keys) |
472
- | `kSecAttrAccessibleAlways` | ✅ | ✅ | ❌ | **❌ DEPRECATED (iOS 12) - never use** |
236
+ Testing on iOS: radare2 searches for `kCCAlgorithmDES`, `kCCAlgorithm3DES`, `kCCAlgorithmRC4` and `kCCOptionECBMode`, plus `CC_MD5`, `CC_SHA1`, `Insecure.MD5` and `Insecure.SHA1`. MASTG-DEMO-0015 (CommonCrypto hashing), MASTG-DEMO-0016 (CryptoKit hashing) and MASTG-DEMO-0018 (broken encryption) show the expected findings.
473
237
 
474
- **Critical:** `kSecAttrAccessible` and `kSecAttrAccessControl` are mutually exclusive. When using `SecAccessControlCreateWithFlags`, the accessibility level is the function's first parameter - do not also set `kSecAttrAccessible` in the query dictionary, or you get `errSecParam (-50)`. See `keychain-access-control.md`.
238
+ ```swift
239
+ import CryptoKit
475
240
 
476
- `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` is the strictest common choice for high-security credentials because items become unavailable when the passcode is removed. `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` is a safer general default when passcode-removal data loss would surprise users. Choose based on the threat model and recovery plan.
241
+ // MASTG-TEST-0210, 0213, 0317 | MASVS-CRYPTO-1 and -2 | OWASP M10
242
+ let statementKey = SymmetricKey(size: .bits256)
477
243
 
478
- ---
244
+ func sealStatement(_ plaintext: Data) throws -> Data {
245
+ let sealed = try AES.GCM.seal(plaintext, using: statementKey)
246
+ guard let packed = sealed.combined else { throw CryptoKitError.incorrectParameterSize }
247
+ return packed
248
+ }
249
+ ```
479
250
 
480
- ## Enterprise Audit Workflow
251
+ The key itself is stored in the keychain, never compiled in.
481
252
 
482
- ### How Security Teams Evaluate iOS Apps
253
+ Anti-patterns: `CC_MD5`; `CCCrypt` with `kCCOptionECBMode`; a generator seeded with `srand(time(nil))`; `SymmetricKey(data:)` built from a string literal; a static all-zero nonce.
483
254
 
484
- Auditors evaluate against MAS Testing Profiles: **L1 (standard)** for low-risk apps, **L2 (defense-in-depth)** for apps handling financial, health, or highly sensitive data - requiring Keychain-managed encryption, hardware-bound biometrics, and certificate pinning.
255
+ ## Choosing kSecAttrAccessible
485
256
 
486
- **Audit tool workflow:** (1) Static analysis - MobSF for automated scanning; radare2 for targeted API analysis. (2) Dynamic analysis - objection for keychain dumping (`ios keychain dump`), file protection verification, UserDefaults inspection (`ios nsuserdefaults get`), biometric bypass (`ios ui biometrics_bypass`). (3) Network - Burp Suite with objection SSL pinning bypass. (4) Binary - class-dump/dsdump for method enumeration.
257
+ One accessibility decision touches M1, M3, M9 and M10 at once.
487
258
 
488
- **Key filesystem paths auditors target:** `<Sandbox>/Library/Preferences/<BundleID>.plist` (UserDefaults), `<Sandbox>/Documents/` (databases), `<Sandbox>/Library/Caches/` (web cache), `<Sandbox>/Library/SplashBoard/Snapshots/` (screenshot cache), `<Sandbox>/tmp/` (temporary files with uncleared data).
259
+ | Class | Backup | iCloud Keychain | Condition | Typical use |
260
+ | --- | --- | --- | --- | --- |
261
+ | `WhenPasscodeSetThisDeviceOnly` | No | No | Passcode must be set; item deleted if it is removed | The most sensitive values, such as session tokens and data keys |
262
+ | `WhenUnlockedThisDeviceOnly` | No | No | Device unlocked | Sensitive, device-specific data |
263
+ | `WhenUnlocked` (default) | Yes | Eligible | Device unlocked | General credentials that should sync |
264
+ | `AfterFirstUnlockThisDeviceOnly` | No | No | After first unlock | Background access, device-specific |
265
+ | `AfterFirstUnlock` | Yes | Eligible | After first unlock | Background work such as push-related keys |
266
+ | `Always` | | | Deprecated in iOS 12 | Never |
489
267
 
490
- ### Top 10 Audit Findings
268
+ `ThisDeviceOnly` and synchronization exclude each other. Such an item never migrates to another device and never syncs; adding one with `kSecAttrSynchronizable: true` fails with `errSecParam` rather than quietly staying local.
491
269
 
492
- | Rank | Finding | OWASP | MASVS | MASTG Tests | Severity |
493
- | ---- | ------------------------------- | ------ | ------------ | ----------- | -------- |
494
- | 1 | Secrets in UserDefaults/plists | M1, M9 | STORAGE-1 | 0299, 0302 | Critical |
495
- | 2 | LAContext-only biometric auth | M3 | AUTH-2 | 0266, 0267 | High |
496
- | 3 | Missing certificate pinning | M5 | NETWORK-2 | 0244 | High |
497
- | 4 | Hardcoded API keys in binary | M1 | CRYPTO-2 | 0213, 0214 | Critical |
498
- | 5 | Deprecated crypto (MD5, DES) | M10 | CRYPTO-1 | 0210, 0211 | High |
499
- | 6 | Insecure keychain accessibility | M9 | STORAGE-1 | 0299 | Medium |
500
- | 7 | Sensitive data in logs | M9 | STORAGE-2 | 0296, 0297 | Medium |
501
- | 8 | Missing jailbreak detection | M7 | RESILIENCE-1 | - | Low |
502
- | 9 | Unencrypted SQLite/Realm | M9 | STORAGE-1 | 0302 | High |
503
- | 10 | ATS exceptions allowing HTTP | M5 | NETWORK-1 | - | Medium |
270
+ `kSecAttrAccessible` and `kSecAttrAccessControl` are also mutually exclusive in one dictionary. The accessibility value goes into `SecAccessControlCreateWithFlags` as its protection argument, and supplying both attributes returns `errSecParam` (-50).
504
271
 
505
- ### Evidence Kit (5 Artifacts)
272
+ Between the two strictest foreground classes, decide by threat model and recovery plan. `WhenPasscodeSetThisDeviceOnly` is stricter; `WhenUnlockedThisDeviceOnly` is the safer default when losing the item on passcode removal would surprise users and there is no way to restore it.
506
273
 
507
- | Artifact | Proves | OWASP/MASVS |
508
- | ---------------------- | --------------------------------------------- | ----------- |
509
- | Static analysis report | No hardcoded secrets or weak crypto | M1, M10 |
510
- | Filesystem/xattr log | `NSFileProtectionComplete` applied | M9 |
511
- | Keychain dump | `ThisDeviceOnly` + `SecAccessControl` present | M1, M9 |
512
- | Backup extraction | No sensitive data migrated | M9 |
513
- | Code snippets | Correct APIs and flags used | All |
274
+ ## Running an enterprise audit
514
275
 
515
- ### Jailbreak-Era Testing (2025-2026)
276
+ MAS Testing Profiles: L1 is the baseline for low-risk apps. L2 adds defence in depth for financial, health and other highly sensitive apps, and there auditors expect encryption keys held in the keychain, biometrics tied to hardware, and pinned certificates.
516
277
 
517
- As of iOS 26, zero jailbreakable devices exist for current versions. Auditors use non-jailbreak techniques: objection with Frida Gadget injection into repackaged IPAs, Corellium virtual devices, or iMazing for backup extraction. This makes automated static analysis (MobSF, semgrep) and Frida Gadget-based dynamic testing the primary assessment paths.
278
+ Tooling:
518
279
 
519
- ---
280
+ | Phase | Tools |
281
+ | --- | --- |
282
+ | Static | MobSF, radare2 |
283
+ | Dynamic | `objection`: `ios keychain dump`, file protection checks, `ios nsuserdefaults get`, `ios ui biometrics_bypass` |
284
+ | Network | An intercepting HTTPS proxy, with `objection`'s SSL-pinning bypass to test pinning |
285
+ | Binary | class-dump, dsdump |
520
286
 
521
- ## Post-Quantum Cryptography Roadmap
287
+ Paths auditors always open:
522
288
 
523
- Apple announced PQC support at WWDC 2025 (Session 314: "Get ahead with quantum-secure cryptography"). The threat model: "harvest now, decrypt later" - adversaries collecting encrypted traffic today for future quantum decryption.
289
+ - `<Sandbox>/Library/Preferences/<BundleID>.plist`
290
+ - `<Sandbox>/Documents/`
291
+ - `<Sandbox>/Library/Caches/`
292
+ - `<Sandbox>/Library/SplashBoard/Snapshots/`
293
+ - `<Sandbox>/tmp/`
524
294
 
525
- | Date | Milestone |
526
- | ------------------------ | ------------------------------------------------------------------------------------- |
527
- | February 2024 (iOS 17.4) | iMessage PQ3 - first quantum-secure messaging at scale |
528
- | August 2024 | NIST finalizes FIPS 203/204/205 |
529
- | January 2025 | CISA adds insecure crypto algorithms to bad practices list |
530
- | June 2025 (WWDC) | CryptoKit PQC APIs announced for iOS 26 |
531
- | September 2025 (iOS 26) | ML-KEM-768/1024, ML-DSA-65/87, X-Wing KEM in CryptoKit; quantum-secure TLS by default |
532
- | 2030 (NIST target) | Classical public-key crypto deprecated |
533
- | 2035 (CNSA 2.0) | Classical algorithms disallowed for National Security Systems |
295
+ Most frequent findings:
534
296
 
535
- Apple uses hybrid cryptography - combining post-quantum and classical algorithms so updates never reduce security below the classical baseline. Build crypto agility now: abstract cryptographic interfaces behind protocols to allow configuration-level switches when PQC adoption becomes mandatory. See `cryptokit-public-key.md` for ML-KEM/ML-DSA implementation details.
297
+ | # | Severity | Finding | MASTG | OWASP | MASVS |
298
+ | --- | --- | --- | --- | --- | --- |
299
+ | 1 | Critical | Secrets in `UserDefaults` or plists | 0299, 0302 | M1, M9 | STORAGE-1 |
300
+ | 2 | High | Biometrics via `LAContext` only | 0266, 0267 | M3 | AUTH-2 |
301
+ | 3 | High | No certificate pinning | 0244 | M5 | NETWORK-2 |
302
+ | 4 | Critical | Hardcoded keys | 0213, 0214 | M1 | CRYPTO-2 |
303
+ | 5 | High | Deprecated crypto | 0210, 0211 | M10 | CRYPTO-1 |
304
+ | 6 | Medium | Weak keychain accessibility | 0299 | M9 | STORAGE-1 |
305
+ | 7 | Medium | Sensitive data in logs | 0296, 0297 | M9 | STORAGE-2 |
306
+ | 8 | Low | No jailbreak detection | none listed | M7 | RESILIENCE-1 |
307
+ | 9 | High | Unencrypted SQLite or other embedded database | 0302 | M9 | STORAGE-1 |
308
+ | 10 | Medium | ATS exceptions allowing HTTP | none listed | M5 | NETWORK-1 |
536
309
 
537
- ---
310
+ Evidence to hand over:
538
311
 
539
- ## Cross-Reference Index
312
+ | Artefact | Covers |
313
+ | --- | --- |
314
+ | Static analysis report | M1, M10 |
315
+ | File system or `xattr` listing proving `NSFileProtectionComplete` | M9 |
316
+ | Keychain dump showing `ThisDeviceOnly` classes and `SecAccessControl` | M1, M9 |
317
+ | Backup extraction containing no sensitive data | M9 |
318
+ | Code excerpts showing the APIs and flags in use | All |
540
319
 
541
- | iOS Practice | M1 | M3 | M9 | M10 | Primary Reference |
542
- | --------------------------------------------------------- | --- | --- | --- | ---------------- | -------------------------------- |
543
- | Keychain + `WhenPasscodeSetThisDeviceOnly` | ✅ | - | ✅ | ✅ (key storage) | `keychain-access-control.md` |
544
- | `SecAccessControlCreateWithFlags` + `.biometryCurrentSet` | ✅ | ✅ | ✅ | - | `biometric-authentication.md` |
545
- | CryptoKit AES.GCM with auto-nonce | - | - | ✅ | ✅ | `cryptokit-symmetric.md` |
546
- | `NSFileProtectionComplete` | - | - | ✅ | - | `keychain-access-control.md` |
547
- | `SecRandomCopyBytes` for key/token generation | ✅ | ✅ | - | ✅ | `cryptokit-symmetric.md` |
548
- | App Attest for credential provisioning | ✅ | ✅ | - | - | `credential-storage-patterns.md` |
549
- | ML-KEM/ML-DSA (iOS 26+) | - | - | - | ✅ | `cryptokit-public-key.md` |
320
+ Current iOS 26 releases have no public jailbreak. Dynamic testing therefore relies on Frida Gadget injected into a re-signed IPA together with `objection`, on virtualized iOS devices, or on backup extraction. Static analysis with MobSF or semgrep plus Gadget-based dynamic testing is now the primary approach.
550
321
 
551
- ---
322
+ ## Post-quantum roadmap
552
323
 
553
- ## Conclusion
324
+ WWDC 2025 session 314 introduced CryptoKit's post-quantum APIs. The driving threat is "harvest now, decrypt later": traffic recorded today is decrypted once a large quantum computer exists.
554
325
 
555
- Three patterns emerge from this mapping. First, the Keychain is the universal compliance mechanism on iOS - a single correctly configured `SecItemAdd` with `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` and `.biometryCurrentSet` simultaneously satisfies M1, M3, and M9. Second, any reference to "M2: Insecure Data Storage" or "M5: Insufficient Cryptography" flags outdated 2016 guidance. Third, the MASTG transition to new test IDs (MASTG-TEST-02xx/03xx) means legacy MSTG-\* references in code comments should be updated.
326
+ | Date | Event |
327
+ | --- | --- |
328
+ | February 2024 | iOS 17.4 ships PQ3 for iMessage |
329
+ | August 2024 | NIST finalises FIPS 203, 204 and 205 |
330
+ | January 2025 | CISA lists insecure cryptography as a bad practice |
331
+ | June 2025 | CryptoKit post-quantum APIs announced |
332
+ | September 2025 | iOS 26 ships ML-KEM-768 and ML-KEM-1024, ML-DSA-65 and ML-DSA-87, X-Wing, and post-quantum TLS by default |
333
+ | 2030 | NIST deprecates classical public-key algorithms |
334
+ | 2035 | CNSA 2.0 disallows classical algorithms for National Security Systems |
556
335
 
557
- For 2025-2026, the most consequential change is post-quantum cryptography reaching production iOS. With NIST targeting 2030 for classical PKC deprecation and Apple shipping ML-KEM/ML-DSA in iOS 26 with quantum-secure TLS enabled by default, compliance programs should evaluate hybrid cryptographic strategies now.
336
+ Apple deploys hybrid schemes, so security never falls below today's classical level even if a new algorithm is weakened. Apps should build crypto agility the same way: keep algorithm choices behind a protocol so ML-KEM or ML-DSA can replace a classical primitive without touching callers.
558
337
 
559
- ---
338
+ ## Cross-reference index
560
339
 
561
- ## Summary Checklist
340
+ | Control | Categories | Details in |
341
+ | --- | --- | --- |
342
+ | Keychain with `WhenPasscodeSetThisDeviceOnly` | M1, M9, and M10 for stored keys | [keychain-access-control.md](keychain-access-control.md) |
343
+ | `SecAccessControlCreateWithFlags` with `.biometryCurrentSet` | M1, M3, M9 | [biometric-authentication.md](biometric-authentication.md) |
344
+ | `AES.GCM` with automatic nonces | M9, M10 | [cryptokit-symmetric.md](cryptokit-symmetric.md) |
345
+ | `NSFileProtectionComplete` | M9 | This file |
346
+ | `SecRandomCopyBytes` | M1, M3, M10 | [common-anti-patterns.md](common-anti-patterns.md) |
347
+ | App Attest before provisioning secrets | M1, M3 | [credential-storage-patterns.md](credential-storage-patterns.md) |
348
+ | ML-KEM, ML-DSA | M10 | [cryptokit-public-key.md](cryptokit-public-key.md) |
562
349
 
563
- 1. **OWASP 2024 numbering** - All references use 2024 numbering (M1/M3/M9/M10), not 2016 (M2/M5/M4+M6)
564
- 2. **MASTG test IDs** - References use new MASTG-TEST-02xx/03xx IDs (not legacy MSTG-\* only)
565
- 3. **Keychain-only credential storage** - Credentials stored in Keychain with `ThisDeviceOnly` accessibility, never in UserDefaults/plists/files
566
- 4. **Keychain-bound biometrics** - Authentication uses `SecAccessControlCreateWithFlags` + `.biometryCurrentSet`, not LAContext-only
567
- 5. **No dual access control** - `kSecAttrAccessible` and `kSecAttrAccessControl` are never set simultaneously in the same query
568
- 6. **CryptoKit algorithms** - All cryptographic operations use CryptoKit (iOS 13+) or SecKey - no CommonCrypto deprecated algorithms (MD5, DES, 3DES, RC4, ECB)
569
- 7. **Automatic nonces** - AES-GCM encryption relies on CryptoKit auto-nonces; no manual nonce construction without a documented rotation strategy
570
- 8. **File protection** - Sensitive files use `NSFileProtectionComplete` and are excluded from backup via `isExcludedFromBackup`
571
- 9. **No sensitive logging** - No sensitive data appears in `NSLog`/`print` statements or keyboard caches (`.autocorrectionType = .no`, `.isSecureTextEntry = true`)
572
- 10. **Compliance annotations** - Code comments include OWASP category, MASVS control, and MASTG test case IDs
573
- 11. **Post-quantum readiness** - Cryptographic interfaces are abstracted behind protocols enabling future ML-KEM/ML-DSA adoption
350
+ ## Takeaways
351
+
352
+ A single well-formed keychain write, with `WhenPasscodeSetThisDeviceOnly` and `.biometryCurrentSet`, satisfies M1, M3 and M9 together. Guidance that files storage under M2 or cryptography under M5 was written for the 2016 list, and code comments citing `MSTG-*` identifiers should move to `MASTG-TEST` identifiers. With NIST's 2030 date and post-quantum TLS on by default in iOS 26, compliance programmes should assess hybrid post-quantum strategies now.
353
+
354
+ ## Checklist
355
+
356
+ - [ ] Findings and comments use 2024 category numbers only.
357
+ - [ ] Test references use `MASTG-TEST-02xx` and `03xx` identifiers.
358
+ - [ ] Credentials live only in the keychain, with a `ThisDeviceOnly` class unless they are meant to sync.
359
+ - [ ] Biometrics unlock keychain items protected with `.biometryCurrentSet`.
360
+ - [ ] No dictionary sets both `kSecAttrAccessible` and `kSecAttrAccessControl`.
361
+ - [ ] Cryptography uses CryptoKit or `SecKey`; no MD5, DES, 3DES, RC4 or ECB.
362
+ - [ ] Nonces come from CryptoKit; any manual nonce has a documented uniqueness scheme.
363
+ - [ ] Sensitive files use `NSFileProtectionComplete` and `isExcludedFromBackup`.
364
+ - [ ] No sensitive values in logs; secure fields set `.autocorrectionType = .no` and `.isSecureTextEntry = true` to keep them out of the keyboard cache.
365
+ - [ ] Comments on security-relevant code name the MASTG test, the MASVS control and the OWASP category they satisfy.
366
+ - [ ] Crypto sits behind protocols so ML-KEM and ML-DSA can be adopted without rewriting callers.