@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.1

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 (284) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +285 -285
  5. package/package.json +3 -3
  6. package/pipeline/lib/redact.mjs +3 -2
  7. package/pipeline/scripts/_notices.mjs +1 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +13 -1
  9. package/pipeline/scripts/pre-commit-check.sh +4 -0
  10. package/pipeline/skills/.skill-manifest.json +69 -69
  11. package/pipeline/skills/shared/README.md +70 -70
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  16. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  17. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  18. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  19. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  20. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  21. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  22. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  23. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +345 -277
  24. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  25. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +107 -121
  26. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +145 -165
  27. package/pipeline/skills/shared/external/app-store-review/SKILL.md +306 -326
  28. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  29. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  30. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  31. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +335 -360
  32. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  33. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  34. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  35. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  37. package/pipeline/skills/shared/external/authentication/SKILL.md +277 -381
  38. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  39. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +135 -178
  40. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  41. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  42. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  43. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  44. package/pipeline/skills/shared/external/background-processing/SKILL.md +274 -384
  45. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  46. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +173 -321
  47. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  48. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  49. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  50. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  51. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  52. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  53. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  54. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  55. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  56. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +228 -376
  57. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  58. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  59. package/pipeline/skills/shared/external/core-data/SKILL.md +302 -368
  60. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  61. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  62. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  63. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  64. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  65. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  66. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  67. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  68. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  69. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  70. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  71. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  72. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  73. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  74. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  75. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  76. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  77. package/pipeline/skills/shared/external/device-integrity/SKILL.md +236 -353
  78. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  79. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  80. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  81. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  82. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  83. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  84. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  85. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  86. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  87. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  88. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  89. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  90. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  91. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  92. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  93. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  94. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  95. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  96. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  97. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  98. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  99. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  100. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  101. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  102. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  103. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  104. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  105. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  106. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  107. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  108. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  109. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  110. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  111. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  112. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  113. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  114. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +3 -3
  115. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +1 -1
  116. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +8 -7
  117. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +2 -2
  118. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +5 -2
  119. package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +100 -0
  120. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +45 -26
  121. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
  122. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +12 -5
  123. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +2 -1
  124. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +6 -5
  125. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +44 -18
  126. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +5 -2
  127. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +10 -11
  128. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +4 -33
  129. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +12 -59
  130. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +297 -267
  131. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  132. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  133. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  134. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  135. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  136. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  137. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  138. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  139. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  140. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  141. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  142. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  143. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  144. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  145. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  146. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  147. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  148. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  149. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  150. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  151. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  152. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  153. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  154. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  155. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  156. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  157. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  158. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  159. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  160. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  161. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  162. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  163. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  164. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  165. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  166. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  167. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  168. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  169. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  170. package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
  171. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  172. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  173. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  174. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  175. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  176. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  177. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +302 -241
  178. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  179. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  180. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  181. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  182. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  183. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  184. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  185. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  186. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  187. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  188. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  189. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  190. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  191. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  192. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +304 -351
  193. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  194. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  195. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  196. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  197. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  198. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  199. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  200. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  201. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  202. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  203. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  204. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  205. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  206. package/pipeline/skills/shared/external/swift-security/SKILL.md +183 -162
  207. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  208. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  209. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +411 -476
  210. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  211. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  212. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  213. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  214. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  215. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  216. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +375 -491
  217. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  218. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  219. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  220. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +397 -457
  221. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  222. package/pipeline/skills/shared/external/swift-testing/SKILL.md +191 -175
  223. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  224. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +81 -84
  225. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  226. package/pipeline/skills/shared/external/swiftdata/SKILL.md +394 -256
  227. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  228. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  229. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  230. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  231. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  232. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  233. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  234. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  235. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  236. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  237. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  238. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  239. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  240. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  241. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  242. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  243. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  244. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  245. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  246. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  247. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  248. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  249. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  250. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  251. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +201 -168
  252. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  253. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +134 -133
  254. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  255. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +111 -138
  256. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  257. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  258. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  259. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  260. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  261. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  262. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  263. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  264. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  265. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  266. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  267. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  268. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  269. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  270. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  271. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  272. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  273. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  274. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  275. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  276. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  277. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  278. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  279. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  280. package/pipeline/skills/shared/external/weatherkit/SKILL.md +160 -315
  281. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  282. package/pipeline/skills/shared/external/widgetkit/SKILL.md +224 -288
  283. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +416 -719
  284. 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.