buildanything 1.7.1 → 2.0.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.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +9 -3
- package/CHANGELOG.md +112 -0
- package/README.md +2 -2
- package/agents/a11y-architect.md +166 -0
- package/agents/business-model.md +80 -29
- package/agents/code-architect.md +75 -0
- package/agents/code-reviewer.md +255 -0
- package/agents/code-simplifier.md +64 -0
- package/agents/design-brand-guardian.md +293 -53
- package/agents/design-critic.md +139 -0
- package/agents/design-inclusive-visuals-specialist.md +6 -19
- package/agents/design-ui-designer.md +335 -56
- package/agents/design-ux-architect.md +403 -55
- package/agents/design-ux-researcher.md +264 -49
- package/agents/engineering-ai-engineer.md +26 -36
- package/agents/engineering-backend-architect.md +185 -36
- package/agents/engineering-data-engineer.md +225 -43
- package/agents/engineering-devops-automator.md +227 -74
- package/agents/engineering-frontend-developer.md +210 -34
- package/agents/engineering-mobile-app-builder.md +6 -1
- package/agents/engineering-rapid-prototyper.md +30 -9
- package/agents/engineering-security-engineer.md +263 -61
- package/agents/engineering-senior-developer.md +128 -19
- package/agents/engineering-sre.md +84 -0
- package/agents/engineering-technical-writer.md +285 -41
- package/agents/feature-intel.md +110 -0
- package/agents/ios-app-review-guardian.md +66 -0
- package/agents/ios-foundation-models-specialist.md +64 -0
- package/agents/ios-storekit-specialist.md +59 -0
- package/agents/ios-swift-architect.md +129 -0
- package/agents/ios-swift-search.md +137 -0
- package/agents/ios-swift-ui-design.md +136 -0
- package/agents/marketing-app-store-optimizer.md +246 -64
- package/agents/planner.md +216 -0
- package/agents/pr-test-analyzer.md +63 -0
- package/agents/product-feedback-synthesizer.md +8 -2
- package/agents/refactor-cleaner.md +102 -0
- package/agents/security-reviewer.md +128 -0
- package/agents/silent-failure-hunter.md +54 -0
- package/agents/swift-build-resolver.md +119 -0
- package/agents/swift-reviewer.md +112 -0
- package/agents/tech-feasibility.md +21 -1
- package/agents/testing-api-tester.md +236 -59
- package/agents/testing-evidence-collector.md +26 -1
- package/agents/testing-performance-benchmarker.md +21 -1
- package/agents/testing-reality-checker.md +6 -1
- package/agents/visual-research.md +116 -0
- package/bin/adapters/cycle-counter-tool.ts +155 -0
- package/bin/adapters/scribe-tool.ts +71 -0
- package/bin/adapters/state-save-tool.ts +130 -0
- package/bin/adapters/write-lease-tool.ts +127 -0
- package/bin/buildanything-runtime.js +15 -0
- package/bin/buildanything-runtime.ts +328 -0
- package/bin/setup.js +83 -8
- package/commands/add-feature.md +2 -0
- package/commands/build.md +752 -332
- package/commands/fix.md +65 -0
- package/commands/self-check.md +121 -0
- package/commands/setup.md +114 -0
- package/commands/ux-review.md +63 -0
- package/commands/verify.md +69 -0
- package/docs/migration/agents.yaml +729 -0
- package/docs/migration/phase-graph.yaml +1088 -0
- package/docs/migration/sdk-host-compat.md +18 -0
- package/hooks/compile-writer-owner-cache.ts +171 -0
- package/hooks/hooks.json +36 -0
- package/hooks/pre-tool-use +19 -0
- package/hooks/pre-tool-use.ts +776 -0
- package/hooks/record-mode-transitions.ts +178 -0
- package/hooks/session-start +89 -2
- package/hooks/subagent-start +17 -0
- package/hooks/subagent-start.ts +471 -0
- package/hooks/subagent-stop +17 -0
- package/hooks/subagent-stop.ts +153 -0
- package/package.json +28 -5
- package/protocols/architecture-schema.md +171 -0
- package/protocols/build-fix.md +52 -0
- package/protocols/cleanup.md +54 -0
- package/protocols/decision-log.md +131 -0
- package/protocols/eval-harness.md +61 -0
- package/protocols/fake-data-detector.md +64 -0
- package/protocols/ios-context.md +234 -0
- package/protocols/ios-frameworks-map.md +323 -0
- package/protocols/ios-phase-branches.md +337 -0
- package/protocols/ios-preflight.md +27 -0
- package/protocols/launch-readiness.md +258 -0
- package/protocols/metric-loop.md +153 -0
- package/protocols/smoke-test.md +118 -0
- package/protocols/state-schema.json +388 -0
- package/protocols/state-schema.md +172 -0
- package/protocols/verify.md +127 -0
- package/protocols/visual-dna.md +185 -0
- package/protocols/web-phase-branches.md +351 -0
- package/skills/ios/_VENDORED.md +62 -0
- package/skills/ios/activitykit/LICENSE +131 -0
- package/skills/ios/activitykit/SKILL.md +505 -0
- package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
- package/skills/ios/app-intents/LICENSE +131 -0
- package/skills/ios/app-intents/SKILL.md +494 -0
- package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
- package/skills/ios/app-store-connect-metadata/SKILL.md +148 -0
- package/skills/ios/apple-on-device-ai/LICENSE +131 -0
- package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
- package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
- package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/skills/ios/asc-privacy-manifest/SKILL.md +350 -0
- package/skills/ios/hig-components-content/SKILL.md +86 -0
- package/skills/ios/hig-components-content/references/activity-views.md +79 -0
- package/skills/ios/hig-components-content/references/charts.md +180 -0
- package/skills/ios/hig-components-content/references/collections.md +48 -0
- package/skills/ios/hig-components-content/references/color-wells.md +42 -0
- package/skills/ios/hig-components-content/references/image-views.md +82 -0
- package/skills/ios/hig-components-content/references/image-wells.md +34 -0
- package/skills/ios/hig-components-content/references/lockups.md +78 -0
- package/skills/ios/hig-components-content/references/web-views.md +36 -0
- package/skills/ios/hig-components-controls/SKILL.md +88 -0
- package/skills/ios/hig-components-controls/references/combo-boxes.md +40 -0
- package/skills/ios/hig-components-controls/references/controls.md +112 -0
- package/skills/ios/hig-components-controls/references/gauges.md +74 -0
- package/skills/ios/hig-components-controls/references/labels.md +92 -0
- package/skills/ios/hig-components-controls/references/pickers.md +128 -0
- package/skills/ios/hig-components-controls/references/rating-indicators.md +38 -0
- package/skills/ios/hig-components-controls/references/segmented-controls.md +94 -0
- package/skills/ios/hig-components-controls/references/sliders.md +92 -0
- package/skills/ios/hig-components-controls/references/steppers.md +40 -0
- package/skills/ios/hig-components-controls/references/text-fields.md +88 -0
- package/skills/ios/hig-components-controls/references/text-views.md +56 -0
- package/skills/ios/hig-components-controls/references/toggles.md +127 -0
- package/skills/ios/hig-components-controls/references/token-fields.md +48 -0
- package/skills/ios/hig-components-controls/references/virtual-keyboards.md +156 -0
- package/skills/ios/hig-components-dialogs/SKILL.md +76 -0
- package/skills/ios/hig-components-dialogs/references/action-sheets.md +74 -0
- package/skills/ios/hig-components-dialogs/references/alerts.md +158 -0
- package/skills/ios/hig-components-dialogs/references/digit-entry-views.md +32 -0
- package/skills/ios/hig-components-dialogs/references/popovers.md +81 -0
- package/skills/ios/hig-components-dialogs/references/sheets.md +157 -0
- package/skills/ios/hig-components-layout/SKILL.md +99 -0
- package/skills/ios/hig-components-layout/references/boxes.md +48 -0
- package/skills/ios/hig-components-layout/references/column-views.md +44 -0
- package/skills/ios/hig-components-layout/references/lists-and-tables.md +99 -0
- package/skills/ios/hig-components-layout/references/ornaments.md +56 -0
- package/skills/ios/hig-components-layout/references/outline-views.md +64 -0
- package/skills/ios/hig-components-layout/references/panels.md +75 -0
- package/skills/ios/hig-components-layout/references/scroll-views.md +123 -0
- package/skills/ios/hig-components-layout/references/sidebars.md +109 -0
- package/skills/ios/hig-components-layout/references/split-views.md +110 -0
- package/skills/ios/hig-components-layout/references/tab-bars.md +173 -0
- package/skills/ios/hig-components-layout/references/tab-views.md +68 -0
- package/skills/ios/hig-components-layout/references/windows.md +188 -0
- package/skills/ios/hig-components-menus/SKILL.md +81 -0
- package/skills/ios/hig-components-menus/references/action-button.md +61 -0
- package/skills/ios/hig-components-menus/references/buttons.md +261 -0
- package/skills/ios/hig-components-menus/references/context-menus.md +105 -0
- package/skills/ios/hig-components-menus/references/disclosure-controls.md +84 -0
- package/skills/ios/hig-components-menus/references/dock-menus.md +40 -0
- package/skills/ios/hig-components-menus/references/edit-menus.md +88 -0
- package/skills/ios/hig-components-menus/references/menus.md +171 -0
- package/skills/ios/hig-components-menus/references/pop-up-buttons.md +70 -0
- package/skills/ios/hig-components-menus/references/pull-down-buttons.md +77 -0
- package/skills/ios/hig-components-menus/references/the-menu-bar.md +303 -0
- package/skills/ios/hig-components-menus/references/toolbars.md +256 -0
- package/skills/ios/hig-components-search/SKILL.md +68 -0
- package/skills/ios/hig-components-search/references/page-controls.md +120 -0
- package/skills/ios/hig-components-search/references/path-controls.md +40 -0
- package/skills/ios/hig-components-search/references/search-fields.md +189 -0
- package/skills/ios/hig-components-status/SKILL.md +80 -0
- package/skills/ios/hig-components-status/references/activity-rings.md +105 -0
- package/skills/ios/hig-components-status/references/progress-indicators.md +116 -0
- package/skills/ios/hig-components-status/references/status-bars.md +38 -0
- package/skills/ios/hig-components-system/SKILL.md +88 -0
- package/skills/ios/hig-components-system/references/app-clips.md +387 -0
- package/skills/ios/hig-components-system/references/app-shortcuts.md +114 -0
- package/skills/ios/hig-components-system/references/complications.md +425 -0
- package/skills/ios/hig-components-system/references/home-screen-quick-actions.md +42 -0
- package/skills/ios/hig-components-system/references/live-activities.md +442 -0
- package/skills/ios/hig-components-system/references/notifications.md +153 -0
- package/skills/ios/hig-components-system/references/top-shelf.md +135 -0
- package/skills/ios/hig-components-system/references/watch-faces.md +40 -0
- package/skills/ios/hig-components-system/references/widgets.md +517 -0
- package/skills/ios/hig-foundations/SKILL.md +98 -0
- package/skills/ios/hig-foundations/references/accessibility.md +291 -0
- package/skills/ios/hig-foundations/references/app-icons.md +210 -0
- package/skills/ios/hig-foundations/references/branding.md +44 -0
- package/skills/ios/hig-foundations/references/color.md +274 -0
- package/skills/ios/hig-foundations/references/dark-mode.md +116 -0
- package/skills/ios/hig-foundations/references/icons.md +263 -0
- package/skills/ios/hig-foundations/references/images.md +176 -0
- package/skills/ios/hig-foundations/references/immersive-experiences.md +174 -0
- package/skills/ios/hig-foundations/references/inclusion.md +189 -0
- package/skills/ios/hig-foundations/references/layout.md +425 -0
- package/skills/ios/hig-foundations/references/materials.md +238 -0
- package/skills/ios/hig-foundations/references/motion.md +103 -0
- package/skills/ios/hig-foundations/references/privacy.md +231 -0
- package/skills/ios/hig-foundations/references/right-to-left.md +206 -0
- package/skills/ios/hig-foundations/references/sf-symbols.md +310 -0
- package/skills/ios/hig-foundations/references/spatial-layout.md +142 -0
- package/skills/ios/hig-foundations/references/typography.md +1146 -0
- package/skills/ios/hig-foundations/references/writing.md +91 -0
- package/skills/ios/hig-inputs/SKILL.md +94 -0
- package/skills/ios/hig-inputs/references/apple-pencil-and-scribble.md +148 -0
- package/skills/ios/hig-inputs/references/camera-control.md +107 -0
- package/skills/ios/hig-inputs/references/digital-crown.md +83 -0
- package/skills/ios/hig-inputs/references/eyes.md +120 -0
- package/skills/ios/hig-inputs/references/focus-and-selection.md +120 -0
- package/skills/ios/hig-inputs/references/game-controls.md +156 -0
- package/skills/ios/hig-inputs/references/gestures.md +208 -0
- package/skills/ios/hig-inputs/references/gyro-and-accelerometer.md +40 -0
- package/skills/ios/hig-inputs/references/keyboards.md +234 -0
- package/skills/ios/hig-inputs/references/nearby-interactions.md +70 -0
- package/skills/ios/hig-inputs/references/pointing-devices.md +237 -0
- package/skills/ios/hig-inputs/references/remotes.md +67 -0
- package/skills/ios/hig-inputs/references/spatial-interactions.md +70 -0
- package/skills/ios/hig-patterns/SKILL.md +104 -0
- package/skills/ios/hig-patterns/references/charting-data.md +81 -0
- package/skills/ios/hig-patterns/references/collaboration-and-sharing.md +86 -0
- package/skills/ios/hig-patterns/references/drag-and-drop.md +134 -0
- package/skills/ios/hig-patterns/references/entering-data.md +69 -0
- package/skills/ios/hig-patterns/references/feedback.md +67 -0
- package/skills/ios/hig-patterns/references/file-management.md +135 -0
- package/skills/ios/hig-patterns/references/going-full-screen.md +79 -0
- package/skills/ios/hig-patterns/references/launching.md +81 -0
- package/skills/ios/hig-patterns/references/live-viewing-apps.md +79 -0
- package/skills/ios/hig-patterns/references/loading.md +59 -0
- package/skills/ios/hig-patterns/references/managing-accounts.md +107 -0
- package/skills/ios/hig-patterns/references/managing-notifications.md +99 -0
- package/skills/ios/hig-patterns/references/modality.md +82 -0
- package/skills/ios/hig-patterns/references/multitasking.md +131 -0
- package/skills/ios/hig-patterns/references/offering-help.md +117 -0
- package/skills/ios/hig-patterns/references/onboarding.md +69 -0
- package/skills/ios/hig-patterns/references/playing-audio.md +124 -0
- package/skills/ios/hig-patterns/references/playing-haptics.md +280 -0
- package/skills/ios/hig-patterns/references/playing-video.md +180 -0
- package/skills/ios/hig-patterns/references/printing.md +50 -0
- package/skills/ios/hig-patterns/references/ratings-and-reviews.md +48 -0
- package/skills/ios/hig-patterns/references/searching.md +70 -0
- package/skills/ios/hig-patterns/references/settings.md +84 -0
- package/skills/ios/hig-patterns/references/undo-and-redo.md +58 -0
- package/skills/ios/hig-patterns/references/workouts.md +76 -0
- package/skills/ios/hig-platforms/SKILL.md +84 -0
- package/skills/ios/hig-platforms/references/designing-for-games.md +159 -0
- package/skills/ios/hig-platforms/references/designing-for-ios.md +66 -0
- package/skills/ios/hig-platforms/references/designing-for-ipados.md +64 -0
- package/skills/ios/hig-platforms/references/designing-for-macos.md +70 -0
- package/skills/ios/hig-platforms/references/designing-for-tvos.md +68 -0
- package/skills/ios/hig-platforms/references/designing-for-visionos.md +85 -0
- package/skills/ios/hig-platforms/references/designing-for-watchos.md +74 -0
- package/skills/ios/hig-project-context/SKILL.md +133 -0
- package/skills/ios/hig-technologies/SKILL.md +107 -0
- package/skills/ios/hig-technologies/references/airplay.md +125 -0
- package/skills/ios/hig-technologies/references/always-on.md +62 -0
- package/skills/ios/hig-technologies/references/apple-pay.md +441 -0
- package/skills/ios/hig-technologies/references/augmented-reality.md +247 -0
- package/skills/ios/hig-technologies/references/carekit.md +224 -0
- package/skills/ios/hig-technologies/references/carplay.md +119 -0
- package/skills/ios/hig-technologies/references/game-center.md +343 -0
- package/skills/ios/hig-technologies/references/generative-ai.md +110 -0
- package/skills/ios/hig-technologies/references/healthkit.md +120 -0
- package/skills/ios/hig-technologies/references/homekit.md +343 -0
- package/skills/ios/hig-technologies/references/icloud.md +52 -0
- package/skills/ios/hig-technologies/references/id-verifier.md +73 -0
- package/skills/ios/hig-technologies/references/imessage-apps-and-stickers.md +105 -0
- package/skills/ios/hig-technologies/references/in-app-purchase.md +263 -0
- package/skills/ios/hig-technologies/references/live-photos.md +54 -0
- package/skills/ios/hig-technologies/references/mac-catalyst.md +216 -0
- package/skills/ios/hig-technologies/references/machine-learning.md +394 -0
- package/skills/ios/hig-technologies/references/maps.md +221 -0
- package/skills/ios/hig-technologies/references/nfc.md +51 -0
- package/skills/ios/hig-technologies/references/photo-editing.md +40 -0
- package/skills/ios/hig-technologies/references/researchkit.md +134 -0
- package/skills/ios/hig-technologies/references/shareplay.md +142 -0
- package/skills/ios/hig-technologies/references/shazamkit.md +47 -0
- package/skills/ios/hig-technologies/references/sign-in-with-apple.md +288 -0
- package/skills/ios/hig-technologies/references/siri.md +523 -0
- package/skills/ios/hig-technologies/references/tap-to-pay-on-iphone.md +208 -0
- package/skills/ios/hig-technologies/references/voiceover.md +90 -0
- package/skills/ios/hig-technologies/references/wallet.md +420 -0
- package/skills/ios/ios-26-platform/SKILL.md +53 -0
- package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
- package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
- package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
- package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
- package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
- package/skills/ios/ios-bootstrap/SKILL.md +107 -0
- package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
- package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
- package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
- package/skills/ios/ios-debugger-agent/LICENSE +21 -0
- package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
- package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
- package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
- package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
- package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
- package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
- package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
- package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
- package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
- package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
- package/skills/ios/swift-accessibility/LICENSE +21 -0
- package/skills/ios/swift-accessibility/SKILL.md +371 -0
- package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
- package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
- package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
- package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
- package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
- package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
- package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
- package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
- package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
- package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
- package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
- package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
- package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
- package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
- package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
- package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
- package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
- package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
- package/skills/ios/swift-actor-persistence/SKILL.md +143 -0
- package/skills/ios/swift-concurrency/LICENSE +21 -0
- package/skills/ios/swift-concurrency/SKILL.md +171 -0
- package/skills/ios/swift-concurrency/references/_index.md +50 -0
- package/skills/ios/swift-concurrency/references/actors.md +660 -0
- package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
- package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
- package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
- package/skills/ios/swift-concurrency/references/core-data.md +560 -0
- package/skills/ios/swift-concurrency/references/glossary.md +135 -0
- package/skills/ios/swift-concurrency/references/linting.md +155 -0
- package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
- package/skills/ios/swift-concurrency/references/migration.md +1104 -0
- package/skills/ios/swift-concurrency/references/performance.md +593 -0
- package/skills/ios/swift-concurrency/references/sendable.md +598 -0
- package/skills/ios/swift-concurrency/references/tasks.md +636 -0
- package/skills/ios/swift-concurrency/references/testing.md +592 -0
- package/skills/ios/swift-concurrency/references/threading.md +495 -0
- package/skills/ios/swift-concurrency-6-2/SKILL.md +216 -0
- package/skills/ios/swift-protocol-di-testing/SKILL.md +190 -0
- package/skills/ios/swift-security-expert/LICENSE +21 -0
- package/skills/ios/swift-security-expert/SKILL.md +470 -0
- package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
- package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
- package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
- package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
- package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
- package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
- package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
- package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
- package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
- package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
- package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
- package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
- package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
- package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
- package/skills/ios/swift-testing-expert/LICENSE +21 -0
- package/skills/ios/swift-testing-expert/SKILL.md +79 -0
- package/skills/ios/swift-testing-expert/references/_index.md +12 -0
- package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
- package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
- package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
- package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
- package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
- package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
- package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
- package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
- package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
- package/skills/ios/swiftdata-pro/LICENSE +21 -0
- package/skills/ios/swiftdata-pro/SKILL.md +102 -0
- package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
- package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
- package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
- package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
- package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
- package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
- package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
- package/skills/ios/swiftui-design-principles/LICENSE +21 -0
- package/skills/ios/swiftui-design-principles/README.md +41 -0
- package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
- package/skills/ios/swiftui-design-principles/metadata.json +10 -0
- package/skills/ios/swiftui-design-tokens/SKILL.md +475 -0
- package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
- package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
- package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
- package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
- package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
- package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/skills/ios/swiftui-pro/LICENSE +21 -0
- package/skills/ios/swiftui-pro/SKILL.md +108 -0
- package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
- package/skills/ios/swiftui-pro/references/api.md +39 -0
- package/skills/ios/swiftui-pro/references/data.md +43 -0
- package/skills/ios/swiftui-pro/references/design.md +31 -0
- package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
- package/skills/ios/swiftui-pro/references/navigation.md +14 -0
- package/skills/ios/swiftui-pro/references/performance.md +46 -0
- package/skills/ios/swiftui-pro/references/swift.md +56 -0
- package/skills/ios/swiftui-pro/references/views.md +35 -0
- package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
- package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
- package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
- package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
- package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
- package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
- package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
- package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
- package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
- package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
- package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
- package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
- package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
- package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
- package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
- package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
- package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
- package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/skills/ios/widgetkit/LICENSE +131 -0
- package/skills/ios/widgetkit/SKILL.md +502 -0
- package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
- package/skills/ios/writing-for-interfaces/SKILL.md +75 -0
- package/skills/web/accessibility/SKILL.md +146 -0
- package/skills/web/aceternity-ui/SKILL.md +719 -0
- package/skills/web/aceternity-ui/metadata.json +10 -0
- package/skills/web/api-design/SKILL.md +523 -0
- package/skills/web/chart-accessibility/SKILL.md +332 -0
- package/skills/web/composition-patterns/AGENTS.md +946 -0
- package/skills/web/composition-patterns/README.md +60 -0
- package/skills/web/composition-patterns/SKILL.md +89 -0
- package/skills/web/composition-patterns/metadata.json +11 -0
- package/skills/web/composition-patterns/rules/_sections.md +29 -0
- package/skills/web/composition-patterns/rules/_template.md +24 -0
- package/skills/web/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/web/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/web/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/web/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/web/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/web/composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/web/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/web/composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/web/cost-aware-llm-pipeline/SKILL.md +183 -0
- package/skills/web/database-migrations/SKILL.md +429 -0
- package/skills/web/deployment-patterns/SKILL.md +427 -0
- package/skills/web/docker-patterns/SKILL.md +364 -0
- package/skills/web/e2e-testing/SKILL.md +326 -0
- package/skills/web/lighthouse-ci/SKILL.md +361 -0
- package/skills/web/mcp-server-patterns/SKILL.md +69 -0
- package/skills/web/next-best-practices/SKILL.md +153 -0
- package/skills/web/next-best-practices/async-patterns.md +87 -0
- package/skills/web/next-best-practices/bundling.md +180 -0
- package/skills/web/next-best-practices/data-patterns.md +297 -0
- package/skills/web/next-best-practices/debug-tricks.md +105 -0
- package/skills/web/next-best-practices/directives.md +73 -0
- package/skills/web/next-best-practices/error-handling.md +227 -0
- package/skills/web/next-best-practices/file-conventions.md +140 -0
- package/skills/web/next-best-practices/font.md +245 -0
- package/skills/web/next-best-practices/functions.md +108 -0
- package/skills/web/next-best-practices/hydration-error.md +91 -0
- package/skills/web/next-best-practices/image.md +173 -0
- package/skills/web/next-best-practices/metadata.md +301 -0
- package/skills/web/next-best-practices/parallel-routes.md +287 -0
- package/skills/web/next-best-practices/route-handlers.md +146 -0
- package/skills/web/next-best-practices/rsc-boundaries.md +159 -0
- package/skills/web/next-best-practices/runtime-selection.md +39 -0
- package/skills/web/next-best-practices/scripts.md +141 -0
- package/skills/web/next-best-practices/self-hosting.md +371 -0
- package/skills/web/next-best-practices/suspense-boundaries.md +67 -0
- package/skills/web/next-cache-components/SKILL.md +411 -0
- package/skills/web/postgres-best-practices/SKILL.md +14 -0
- package/skills/web/postgres-best-practices/references/schema-design.md +9 -0
- package/skills/web/react-best-practices/AGENTS.md +3810 -0
- package/skills/web/react-best-practices/README.md +123 -0
- package/skills/web/react-best-practices/SKILL.md +149 -0
- package/skills/web/react-best-practices/metadata.json +15 -0
- package/skills/web/react-best-practices/rules/_sections.md +46 -0
- package/skills/web/react-best-practices/rules/_template.md +28 -0
- package/skills/web/react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/skills/web/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/web/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/web/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/web/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/web/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/skills/web/react-best-practices/rules/async-defer-await.md +82 -0
- package/skills/web/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/web/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/web/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/web/react-best-practices/rules/bundle-analyzable-paths.md +63 -0
- package/skills/web/react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/skills/web/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/web/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/web/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/web/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/web/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/web/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/web/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/web/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/web/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/web/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/web/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/web/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/web/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/web/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/web/react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/web/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/web/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/web/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/web/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/web/react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/skills/web/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/web/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/web/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/web/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/web/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/web/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/web/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/web/react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/web/react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/web/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/web/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/web/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/web/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/web/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/web/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/web/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/web/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/web/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/web/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/web/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/web/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/web/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/web/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/web/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/web/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/web/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/web/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/web/react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/skills/web/react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/skills/web/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/web/react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/skills/web/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/web/seo/SKILL.md +154 -0
- package/skills/web/web-design-guidelines/SKILL.md +39 -0
- package/skills/web/zap-scan-config/SKILL.md +444 -0
- package/skills/web/zap-scan-config/assets/.gitkeep +9 -0
- package/skills/web/zap-scan-config/assets/github_action.yml +207 -0
- package/skills/web/zap-scan-config/assets/gitlab_ci.yml +226 -0
- package/skills/web/zap-scan-config/assets/zap_automation.yaml +196 -0
- package/skills/web/zap-scan-config/assets/zap_context.xml +192 -0
- package/skills/web/zap-scan-config/references/EXAMPLE.md +40 -0
- package/skills/web/zap-scan-config/references/api_testing_guide.md +475 -0
- package/skills/web/zap-scan-config/references/authentication_guide.md +431 -0
- package/skills/web/zap-scan-config/references/false_positive_handling.md +427 -0
- package/skills/web/zap-scan-config/references/owasp_mapping.md +255 -0
- package/src/lrr/aggregator.ts +80 -0
- package/src/orchestrator/hooks/context-header.ts +95 -0
- package/src/orchestrator/hooks/token-accounting-emitter.ts +77 -0
- package/src/orchestrator/hooks/token-accounting.ts +101 -0
- package/src/orchestrator/mcp/cycle-counter.ts +129 -0
- package/src/orchestrator/mcp/scribe.ts +283 -0
- package/src/orchestrator/mcp/state-save.ts +149 -0
- package/src/orchestrator/mcp/write-lease.ts +167 -0
- package/src/orchestrator/phase4-shared-context.ts +41 -0
- package/src/orchestrator/schemas/backward-edge.ts +46 -0
- package/agents/agentic-identity-trust.md +0 -121
- package/agents/data-consolidation-agent.md +0 -39
- package/agents/design-image-prompt-engineer.md +0 -105
- package/agents/design-visual-storyteller.md +0 -147
- package/agents/design-whimsy-injector.md +0 -89
- package/agents/engineering-autonomous-optimization-architect.md +0 -105
- package/agents/market-intel.md +0 -35
- package/agents/marketing-instagram-curator.md +0 -111
- package/agents/marketing-reddit-community-builder.md +0 -121
- package/agents/marketing-social-media-strategist.md +0 -74
- package/agents/marketing-tiktok-strategist.md +0 -123
- package/agents/marketing-twitter-engager.md +0 -124
- package/agents/marketing-wechat-official-account.md +0 -143
- package/agents/marketing-xiaohongshu-specialist.md +0 -136
- package/agents/marketing-zhihu-strategist.md +0 -160
- package/agents/product-behavioral-nudge-engine.md +0 -78
- package/agents/project-management-experiment-tracker.md +0 -102
- package/agents/report-distribution-agent.md +0 -43
- package/agents/risk-analysis.md +0 -45
- package/agents/sales-data-extraction-agent.md +0 -46
- package/agents/specialized-cultural-intelligence-strategist.md +0 -65
- package/agents/specialized-developer-advocate.md +0 -146
- package/agents/support-analytics-reporter.md +0 -133
- package/agents/support-executive-summary-generator.md +0 -64
- package/agents/support-finance-tracker.md +0 -145
- package/agents/support-legal-compliance-checker.md +0 -129
- package/agents/support-support-responder.md +0 -91
- package/agents/testing-accessibility-auditor.md +0 -110
- package/agents/testing-test-results-analyzer.md +0 -97
- package/agents/testing-tool-evaluator.md +0 -76
- package/agents/testing-workflow-optimizer.md +0 -99
- package/agents/user-research.md +0 -40
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
# Biometric Authentication
|
|
2
|
+
|
|
3
|
+
> **Domain scope:** SecAccessControl + LAContext integration, the LAContext-only bypass vulnerability, hardware-bound biometric gating, fallback behavior, UI customization, enrollment change detection, thread safety.
|
|
4
|
+
>
|
|
5
|
+
> **Risk level:** CRITICAL — #1 most dangerous AI-generated pattern. `LAContext.evaluatePolicy()` used alone is trivially bypassable at runtime.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The Boolean Gate Vulnerability
|
|
10
|
+
|
|
11
|
+
The most dangerous pattern AI coding assistants generate for iOS biometric authentication is `LAContext.evaluatePolicy()` used as a standalone authentication gate. This pattern appears in virtually every tutorial, Stack Overflow answer, and AI training corpus — and it is **trivially bypassable**.
|
|
12
|
+
|
|
13
|
+
The attack requires no exploit. An attacker uses Frida or objection to hook the Objective-C callback and force `success = true`, bypassing Face ID or Touch ID entirely. The formal weakness classification is CWE-288: Authentication Bypass Using an Alternate Path or Channel.
|
|
14
|
+
|
|
15
|
+
OWASP MASTG explicitly fails any app relying solely on `evaluatePolicy` (test MASTG-TEST-0266, requirements MSTG-AUTH-8 and MSTG-AUTH-12). The standard states: biometric authentication must not be event-bound (returning `true`/`false`); it must be based on unlocking the keychain/keystore.
|
|
16
|
+
|
|
17
|
+
### The Dangerous Pattern — Boolean Gate
|
|
18
|
+
|
|
19
|
+
```swift
|
|
20
|
+
// ❌ DANGEROUS: Trivially bypassable with Frida — do NOT use for security
|
|
21
|
+
import LocalAuthentication
|
|
22
|
+
|
|
23
|
+
func authenticateUser() {
|
|
24
|
+
let context = LAContext()
|
|
25
|
+
var error: NSError?
|
|
26
|
+
|
|
27
|
+
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
|
28
|
+
context.evaluatePolicy(
|
|
29
|
+
.deviceOwnerAuthenticationWithBiometrics,
|
|
30
|
+
localizedReason: "Authenticate to access your account"
|
|
31
|
+
) { success, authError in
|
|
32
|
+
DispatchQueue.main.async {
|
|
33
|
+
if success {
|
|
34
|
+
self.isAuthenticated = true // ← Just a boolean in hookable memory
|
|
35
|
+
self.showProtectedContent() // ← No secret unlocked, no key released
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Why this fails:** `evaluatePolicy()` asks the OS "did the user authenticate?" and receives a boolean answer in user-space. No cryptographic material is involved. No secret is decrypted. The entire security model rests on a boolean that exists in hookable memory.
|
|
44
|
+
|
|
45
|
+
### How Attackers Bypass It
|
|
46
|
+
|
|
47
|
+
The objection tool (built on Frida) provides a one-command bypass:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
objection -g "com.example.targetapp" explore
|
|
51
|
+
ios ui biometrics_bypass
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The hook listens for invocations of `-[LAContext evaluatePolicy:localizedReason:reply:]`, intercepts the reply block, and replaces the `success` boolean with `true`. The equivalent raw Frida script:
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// Frida script — forces evaluatePolicy success = true
|
|
58
|
+
if (ObjC.available) {
|
|
59
|
+
var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
|
|
60
|
+
Interceptor.attach(hook.implementation, {
|
|
61
|
+
onEnter: function (args) {
|
|
62
|
+
var block = new ObjC.Block(args[4]);
|
|
63
|
+
const callback = block.implementation;
|
|
64
|
+
block.implementation = function (error, value) {
|
|
65
|
+
const result = callback(1, null); // 1 = true, null = no error
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The objection wiki confirms the attack boundary: this bypass **does not work** against keychain items protected with access control flags like `.biometryCurrentSet` or `.biometryAny`. That boundary is the entire basis of the secure pattern.
|
|
74
|
+
|
|
75
|
+
✅ Correct pattern in this threat model: use biometrics only to unlock keychain-protected secrets (`SecAccessControl` + `SecItemCopyMatching`), never as a standalone boolean gate.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## The Secure Pattern — Hardware-Bound Secrets
|
|
80
|
+
|
|
81
|
+
The correct architecture stores a secret in the iOS keychain with biometric access control. The secret's encryption key is held by the Secure Enclave — a dedicated processor running its own microkernel (sepOS), with its own encrypted memory, completely isolated from the application processor.
|
|
82
|
+
|
|
83
|
+
When the app requests the secret, the Secure Enclave independently verifies the biometric match and only then releases the decryption key. There is no boolean to hook. The data physically cannot be read without valid biometric authentication.
|
|
84
|
+
|
|
85
|
+
WWDC 2014 Session 711 ("Keychain and Authentication with Touch ID") drew the critical distinction:
|
|
86
|
+
|
|
87
|
+
- **`evaluatePolicy`**: "Trust the OS" — vulnerable if runtime is compromised
|
|
88
|
+
- **Keychain + SecAccessControl**: "Trust the Secure Enclave" — ACLs evaluated inside hardware
|
|
89
|
+
|
|
90
|
+
### Step 1 — Create the Access Control Object
|
|
91
|
+
|
|
92
|
+
```swift
|
|
93
|
+
import LocalAuthentication
|
|
94
|
+
import Security
|
|
95
|
+
|
|
96
|
+
enum BiometricKeychainError: Error {
|
|
97
|
+
case accessControlCreationFailed
|
|
98
|
+
case keychainOperationFailed(status: OSStatus)
|
|
99
|
+
case dataConversionFailed
|
|
100
|
+
case biometryNotAvailable(reason: String)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func createBiometricAccessControl() throws -> SecAccessControl {
|
|
104
|
+
var error: Unmanaged<CFError>?
|
|
105
|
+
guard let accessControl = SecAccessControlCreateWithFlags(
|
|
106
|
+
kCFAllocatorDefault,
|
|
107
|
+
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, // Strongest: requires passcode, device-only
|
|
108
|
+
.biometryCurrentSet, // Invalidates on enrollment change
|
|
109
|
+
&error
|
|
110
|
+
) else {
|
|
111
|
+
throw BiometricKeychainError.accessControlCreationFailed
|
|
112
|
+
}
|
|
113
|
+
return accessControl
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Step 2 — Store a Secret Bound to Biometric Auth
|
|
118
|
+
|
|
119
|
+
```swift
|
|
120
|
+
// ✅ SECURE: Secret is encrypted by Secure Enclave, released only on biometric match
|
|
121
|
+
func storeSecretWithBiometric(secret: Data, account: String, service: String) throws {
|
|
122
|
+
let accessControl = try createBiometricAccessControl()
|
|
123
|
+
|
|
124
|
+
// Delete any existing item first (add-or-update pattern)
|
|
125
|
+
let deleteQuery: [String: Any] = [
|
|
126
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
127
|
+
kSecAttrAccount as String: account,
|
|
128
|
+
kSecAttrService as String: service
|
|
129
|
+
]
|
|
130
|
+
SecItemDelete(deleteQuery as CFDictionary)
|
|
131
|
+
|
|
132
|
+
let addQuery: [String: Any] = [
|
|
133
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
134
|
+
kSecAttrAccount as String: account,
|
|
135
|
+
kSecAttrService as String: service,
|
|
136
|
+
kSecValueData as String: secret,
|
|
137
|
+
kSecAttrAccessControl as String: accessControl,
|
|
138
|
+
kSecAttrSynchronizable as String: kCFBooleanFalse // Never sync biometric-gated secrets
|
|
139
|
+
// NOTE: Do NOT set kSecAttrAccessible — it conflicts with kSecAttrAccessControl
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
let status = SecItemAdd(addQuery as CFDictionary, nil)
|
|
143
|
+
guard status == errSecSuccess else {
|
|
144
|
+
throw BiometricKeychainError.keychainOperationFailed(status: status)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Critical detail:** Do NOT set both `kSecAttrAccessible` and `kSecAttrAccessControl` in the same query. They conflict — `SecAccessControl` already encodes the accessibility level. Setting both causes `errSecParam`.
|
|
150
|
+
|
|
151
|
+
**Critical detail:** Always use `ThisDeviceOnly` accessibility for biometric-gated secrets. The `ThisDeviceOnly` suffix ensures the secret is hardware-bound and excluded from iCloud backups. Syncing biometric-gated secrets across devices expands the attack surface.
|
|
152
|
+
|
|
153
|
+
### Step 3 — Retrieve the Secret (Biometric Prompt Appears Automatically)
|
|
154
|
+
|
|
155
|
+
```swift
|
|
156
|
+
// ✅ SECURE: System presents biometric prompt; Secure Enclave gates decryption
|
|
157
|
+
func retrieveSecretWithBiometric(account: String, service: String) throws -> Data {
|
|
158
|
+
let context = LAContext()
|
|
159
|
+
context.localizedReason = "Authenticate to access your credentials"
|
|
160
|
+
|
|
161
|
+
let query: [String: Any] = [
|
|
162
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
163
|
+
kSecAttrAccount as String: account,
|
|
164
|
+
kSecAttrService as String: service,
|
|
165
|
+
kSecReturnData as String: true,
|
|
166
|
+
kSecMatchLimit as String: kSecMatchLimitOne,
|
|
167
|
+
kSecUseAuthenticationContext as String: context
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
var result: AnyObject?
|
|
171
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
172
|
+
|
|
173
|
+
switch status {
|
|
174
|
+
case errSecSuccess:
|
|
175
|
+
guard let data = result as? Data else {
|
|
176
|
+
throw BiometricKeychainError.dataConversionFailed
|
|
177
|
+
}
|
|
178
|
+
return data // Secret returned ONLY after Secure Enclave validates biometric
|
|
179
|
+
case errSecItemNotFound:
|
|
180
|
+
throw BiometricKeychainError.keychainOperationFailed(status: status)
|
|
181
|
+
case errSecUserCanceled:
|
|
182
|
+
throw BiometricKeychainError.keychainOperationFailed(status: status)
|
|
183
|
+
case errSecAuthFailed:
|
|
184
|
+
throw BiometricKeychainError.keychainOperationFailed(status: status)
|
|
185
|
+
default:
|
|
186
|
+
throw BiometricKeychainError.keychainOperationFailed(status: status)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Key insight:** Authentication and data protection are the same operation, not sequential ones. When `SecItemCopyMatching` encounters an item with biometric access control, the system presents the biometric prompt automatically. The Secure Enclave verifies the match internally and only then unwraps the AES-256-GCM decryption key. There is no callback to intercept.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## `evaluatePolicy` vs `evaluateAccessControl`
|
|
196
|
+
|
|
197
|
+
These two `LAContext` methods represent the two trust models from WWDC 2014 Session 711:
|
|
198
|
+
|
|
199
|
+
**`evaluatePolicy(_:localizedReason:reply:)`** triggers biometric authentication and returns a boolean. The Secure Enclave validates the biometric correctly, but the result is communicated to user-space as `true`/`false`. No key is released. The app branches on a boolean in hookable memory. This is "trust the OS."
|
|
200
|
+
|
|
201
|
+
**`evaluateAccessControl(_:operation:localizedReason:reply:)`** evaluates a `SecAccessControl` object for a specific cryptographic operation (`.useItem`, `.useKeySign`, `.useKeyDecrypt`). When used with keychain items, the authenticated `LAContext` is passed to `SecItemCopyMatching` via `kSecUseAuthenticationContext`, and the Secure Enclave recognizes the prior authentication. This is "trust the Secure Enclave."
|
|
202
|
+
|
|
203
|
+
**In practice, you rarely call `evaluateAccessControl` directly.** The recommended flow: store data with `SecAccessControl` via `SecItemAdd`, then retrieve with `SecItemCopyMatching`. The system handles the biometric prompt automatically when the query encounters an ACL-protected item.
|
|
204
|
+
|
|
205
|
+
The only legitimate use of `evaluatePolicy` is **non-security-critical UI gating** — deciding whether to show a "Sign in with Face ID" button. It must never protect sensitive data or gate access to secrets.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Biometric Flag Selection
|
|
210
|
+
|
|
211
|
+
`SecAccessControlCreateFlags` provides three biometric-related flags. Choosing the wrong one is a common mistake even in otherwise-correct implementations.
|
|
212
|
+
|
|
213
|
+
### `.biometryCurrentSet` — Banking, Payments, Credential Storage
|
|
214
|
+
|
|
215
|
+
Ties the keychain item to the **exact biometric enrollment** at time of storage. If the user adds a fingerprint, re-enrolls Face ID, or removes a biometric entry, the item becomes **permanently inaccessible**.
|
|
216
|
+
|
|
217
|
+
```swift
|
|
218
|
+
// ✅ Strongest biometric binding — invalidates on enrollment change
|
|
219
|
+
let access = SecAccessControlCreateWithFlags(
|
|
220
|
+
nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
221
|
+
.biometryCurrentSet, nil
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Tradeoff:** Users who change biometrics must re-authenticate via your app's password flow. Detect enrollment changes via `LAContext.evaluationPolicyDomainState` (see Enrollment Change Detection below) and present graceful re-enrollment.
|
|
226
|
+
|
|
227
|
+
### `.biometryAny` — Convenience Features, Moderate Sensitivity
|
|
228
|
+
|
|
229
|
+
Survives biometric enrollment changes. An attacker who enrolls their own biometrics on a compromised device can access the data.
|
|
230
|
+
|
|
231
|
+
```swift
|
|
232
|
+
// Survives re-enrollment — better UX, weaker security
|
|
233
|
+
let access = SecAccessControlCreateWithFlags(
|
|
234
|
+
nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
235
|
+
.biometryAny, nil
|
|
236
|
+
)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Use case:** "Remember me" features, non-critical app locks, preferences that benefit from biometric convenience without protecting financial data.
|
|
240
|
+
|
|
241
|
+
### `.userPresence` — Maximum Device Compatibility
|
|
242
|
+
|
|
243
|
+
Allows passcode fallback when biometrics are unavailable. Weaker because passcodes are susceptible to shoulder-surfing.
|
|
244
|
+
|
|
245
|
+
```swift
|
|
246
|
+
// Broadest compatibility — biometric or passcode
|
|
247
|
+
let access = SecAccessControlCreateWithFlags(
|
|
248
|
+
nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
249
|
+
.userPresence, nil
|
|
250
|
+
)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Use case:** Accessibility-first apps, devices without biometric hardware, or as a `.biometryCurrentSet` degradation path.
|
|
254
|
+
|
|
255
|
+
### Combining Flags
|
|
256
|
+
|
|
257
|
+
Flags can be combined with `.or` and `.and` conjunctions:
|
|
258
|
+
|
|
259
|
+
```swift
|
|
260
|
+
// ✅ Strong biometric binding WITH passcode escape hatch
|
|
261
|
+
let access = SecAccessControlCreateWithFlags(
|
|
262
|
+
nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
263
|
+
[.biometryCurrentSet, .or, .devicePasscode], nil
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
This combination is practical for most production apps — strong biometric security with a recovery path when biometrics become unavailable.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Biometric Availability Checks and Graceful Degradation
|
|
272
|
+
|
|
273
|
+
### Incomplete Availability Check
|
|
274
|
+
|
|
275
|
+
```swift
|
|
276
|
+
// ❌ WRONG: Ignores WHY biometrics failed — user gets no guidance
|
|
277
|
+
func checkBiometrics() -> Bool {
|
|
278
|
+
let context = LAContext()
|
|
279
|
+
var error: NSError?
|
|
280
|
+
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Complete Availability Evaluation
|
|
285
|
+
|
|
286
|
+
```swift
|
|
287
|
+
// ✅ CORRECT: Evaluates every failure reason with actionable guidance
|
|
288
|
+
enum BiometricAvailability {
|
|
289
|
+
case available(type: LABiometryType)
|
|
290
|
+
case notEnrolled // Hardware exists, no biometrics registered
|
|
291
|
+
case lockedOut // Too many failed attempts — passcode required
|
|
292
|
+
case notAvailable // No hardware or restricted by MDM
|
|
293
|
+
case passcodeNotSet // No device passcode — biometrics require one
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
func evaluateBiometricAvailability() -> BiometricAvailability {
|
|
297
|
+
let context = LAContext()
|
|
298
|
+
var error: NSError?
|
|
299
|
+
|
|
300
|
+
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
|
|
301
|
+
return .available(type: context.biometryType)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
guard let laError = error as? LAError else { return .notAvailable }
|
|
305
|
+
|
|
306
|
+
switch laError.code {
|
|
307
|
+
case .biometryNotEnrolled:
|
|
308
|
+
return .notEnrolled // → "Enable Face ID in Settings"
|
|
309
|
+
case .biometryLockout:
|
|
310
|
+
return .lockedOut // → Prompt passcode to reset sensor
|
|
311
|
+
case .biometryNotAvailable:
|
|
312
|
+
return .notAvailable // → Hide biometric UI entirely
|
|
313
|
+
case .passcodeNotSet:
|
|
314
|
+
return .passcodeNotSet // → "Set a passcode to use Face ID"
|
|
315
|
+
default:
|
|
316
|
+
return .notAvailable
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Graceful Degradation Flow
|
|
322
|
+
|
|
323
|
+
```swift
|
|
324
|
+
// ✅ Degrades from biometric → passcode → password login
|
|
325
|
+
func authenticateWithGracefulDegradation() async throws -> Data {
|
|
326
|
+
let availability = evaluateBiometricAvailability()
|
|
327
|
+
|
|
328
|
+
switch availability {
|
|
329
|
+
case .available:
|
|
330
|
+
return try retrieveSecretWithBiometric(account: "user", service: "com.app.auth")
|
|
331
|
+
|
|
332
|
+
case .lockedOut:
|
|
333
|
+
// Biometrics locked — use .userPresence item for passcode fallback
|
|
334
|
+
return try retrieveSecretWithPasscodeFallback(account: "user", service: "com.app.auth")
|
|
335
|
+
|
|
336
|
+
case .notEnrolled:
|
|
337
|
+
throw BiometricKeychainError.biometryNotAvailable(
|
|
338
|
+
reason: "Please enable Face ID in Settings > Face ID & Passcode"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
case .notAvailable, .passcodeNotSet:
|
|
342
|
+
throw BiometricKeychainError.biometryNotAvailable(
|
|
343
|
+
reason: "Biometric authentication is not available on this device"
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Critical:** Failing to handle `.biometryLockout` strands users. The app cannot bypass this lockout — the user must successfully enter their device passcode to re-enable the biometric sensor. If your app has no fallback, users are permanently locked out until they leave your app and unlock with passcode.
|
|
350
|
+
|
|
351
|
+
**Important:** `canEvaluatePolicy()` is strictly for pre-flight UI decisions (showing or hiding a "Sign in with Face ID" button). It must never be used as a security control.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Enrollment Change Detection
|
|
356
|
+
|
|
357
|
+
When using `.biometryCurrentSet`, detect enrollment changes proactively so your app can guide the user through re-enrollment rather than presenting a cryptic keychain error.
|
|
358
|
+
|
|
359
|
+
```swift
|
|
360
|
+
// ✅ Detect biometric enrollment changes via domainState
|
|
361
|
+
class BiometricEnrollmentMonitor {
|
|
362
|
+
private let domainStateKey = "com.app.biometric.domainState"
|
|
363
|
+
|
|
364
|
+
/// Call after successful biometric setup to snapshot current enrollment
|
|
365
|
+
func saveCurrentEnrollment() {
|
|
366
|
+
let context = LAContext()
|
|
367
|
+
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) else { return }
|
|
368
|
+
|
|
369
|
+
// domainState changes whenever biometric enrollment changes
|
|
370
|
+
if let domainState = context.evaluatedPolicyDomainState {
|
|
371
|
+
UserDefaults.standard.set(domainState, forKey: domainStateKey)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/// Call on app launch or before biometric retrieval
|
|
376
|
+
func hasEnrollmentChanged() -> Bool {
|
|
377
|
+
let context = LAContext()
|
|
378
|
+
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) else {
|
|
379
|
+
return true // Can't evaluate — treat as changed
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
guard let currentState = context.evaluatedPolicyDomainState,
|
|
383
|
+
let savedState = UserDefaults.standard.data(forKey: domainStateKey) else {
|
|
384
|
+
return true // No saved state — first run or data cleared
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return currentState != savedState
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Note:** `evaluatedPolicyDomainState` is an opaque `Data` blob. It changes whenever biometric enrollment changes but reveals no information about the biometrics themselves. Store it in `UserDefaults` (not keychain) since it is not sensitive — it's only used for change detection.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Thread Safety and async/await
|
|
397
|
+
|
|
398
|
+
`SecItemCopyMatching` with biometric access control **blocks the calling thread** until the user completes authentication. Never run it on `@MainActor` or the main thread.
|
|
399
|
+
|
|
400
|
+
`LAContext.evaluatePolicy`'s legacy completion handler executes on a private queue in an unspecified threading context. Direct UI updates from this callback cause crashes, especially on iOS 18 where threading strictness increased.
|
|
401
|
+
|
|
402
|
+
### Actor-Isolated Biometric Keychain (iOS 15+)
|
|
403
|
+
|
|
404
|
+
```swift
|
|
405
|
+
@available(iOS 15.0, *)
|
|
406
|
+
actor BiometricKeychain {
|
|
407
|
+
|
|
408
|
+
func retrieveSecret(account: String, service: String) async throws -> Data {
|
|
409
|
+
return try await withCheckedThrowingContinuation { continuation in
|
|
410
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
411
|
+
let context = LAContext()
|
|
412
|
+
context.localizedReason = "Authenticate to access your account"
|
|
413
|
+
|
|
414
|
+
let query: [String: Any] = [
|
|
415
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
416
|
+
kSecAttrAccount as String: account,
|
|
417
|
+
kSecAttrService as String: service,
|
|
418
|
+
kSecReturnData as String: true,
|
|
419
|
+
kSecMatchLimit as String: kSecMatchLimitOne,
|
|
420
|
+
kSecUseAuthenticationContext as String: context
|
|
421
|
+
]
|
|
422
|
+
|
|
423
|
+
var result: AnyObject?
|
|
424
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
425
|
+
|
|
426
|
+
switch status {
|
|
427
|
+
case errSecSuccess:
|
|
428
|
+
if let data = result as? Data {
|
|
429
|
+
continuation.resume(returning: data)
|
|
430
|
+
} else {
|
|
431
|
+
continuation.resume(throwing: BiometricKeychainError.dataConversionFailed)
|
|
432
|
+
}
|
|
433
|
+
case errSecUserCanceled, errSecAuthFailed:
|
|
434
|
+
continuation.resume(throwing: BiometricKeychainError.keychainOperationFailed(status: status))
|
|
435
|
+
default:
|
|
436
|
+
continuation.resume(throwing: BiometricKeychainError.keychainOperationFailed(status: status))
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### SwiftUI ViewModel Integration
|
|
445
|
+
|
|
446
|
+
```swift
|
|
447
|
+
@MainActor
|
|
448
|
+
class AuthViewModel: ObservableObject {
|
|
449
|
+
@Published var isAuthenticated = false
|
|
450
|
+
@Published var errorMessage: String?
|
|
451
|
+
|
|
452
|
+
private let keychain = BiometricKeychain()
|
|
453
|
+
|
|
454
|
+
func authenticate() {
|
|
455
|
+
Task {
|
|
456
|
+
do {
|
|
457
|
+
let secret = try await keychain.retrieveSecret(
|
|
458
|
+
account: "user_token",
|
|
459
|
+
service: "com.myapp.auth"
|
|
460
|
+
)
|
|
461
|
+
self.isAuthenticated = true
|
|
462
|
+
self.processToken(secret)
|
|
463
|
+
} catch {
|
|
464
|
+
self.errorMessage = error.localizedDescription
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Note on native async:** `LAContext` gained `evaluatePolicy(_:localizedReason:) async throws -> Bool` in iOS 15. However, this is only relevant for the non-security-critical UI gating use case. For the secure keychain pattern, you wrap `SecItemCopyMatching` as shown above — there is no native async overload for SecItem\* APIs.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Secure Enclave-Backed Keys with Biometric Protection
|
|
476
|
+
|
|
477
|
+
For asymmetric key operations (signing, key agreement), combine Secure Enclave key generation with biometric access control via CryptoKit. The private key **never leaves the Secure Enclave** — all operations happen in hardware.
|
|
478
|
+
|
|
479
|
+
```swift
|
|
480
|
+
// ✅ Secure Enclave P-256 key with biometric protection (WWDC 2019-709)
|
|
481
|
+
let accessControl = SecAccessControlCreateWithFlags(
|
|
482
|
+
nil,
|
|
483
|
+
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
484
|
+
[.privateKeyUsage, .biometryCurrentSet],
|
|
485
|
+
nil
|
|
486
|
+
)!
|
|
487
|
+
|
|
488
|
+
let privateKey = try SecureEnclave.P256.Signing.PrivateKey(
|
|
489
|
+
accessControl: accessControl
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
// Signing triggers biometric prompt automatically
|
|
493
|
+
let signature = try privateKey.signature(for: dataToSign)
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Frida operates in user-space on the application processor and has zero access to the Secure Enclave's internal state. The Secure Enclave's memory is encrypted with its own AES engine. Even a kernel-level compromise cannot extract the keys.
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## SDLC Controls — Catching the Anti-Pattern in CI
|
|
501
|
+
|
|
502
|
+
Because AI coding assistants frequently generate the vulnerable `evaluatePolicy` pattern, teams should implement automated detection:
|
|
503
|
+
|
|
504
|
+
**Conceptual SAST rule (`INSECURE_BIOMETRIC_GATE`):** Identify all calls to `LAContext.evaluatePolicy`. If the `success` boolean directly gates access to a resource AND there is no corresponding `SecItemCopyMatching` using a `SecAccessControl` object in the same flow, flag the code.
|
|
505
|
+
|
|
506
|
+
**Security review sign-off criteria:**
|
|
507
|
+
|
|
508
|
+
1. Zero instances of standalone `LAContext.evaluatePolicy` gating sensitive operations
|
|
509
|
+
2. Evidence of `SecItemAdd` with `kSecAttrAccessControl` using `ThisDeviceOnly` accessibility
|
|
510
|
+
3. Documented proof that objection bypass (`ios ui biometrics_bypass`) fails to unlock protected data
|
|
511
|
+
4. All `LAError` cases handled with graceful degradation
|
|
512
|
+
5. If `.biometryCurrentSet` is used, a tested re-enrollment recovery flow exists
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Dynamic Verification — Proving Bypass Resistance
|
|
517
|
+
|
|
518
|
+
Static code review alone is insufficient. Verification requires dynamic testing:
|
|
519
|
+
|
|
520
|
+
**Test procedure:** On a jailbroken or instrumented device, inject a Frida script to hook `-[LAContext evaluatePolicy:localizedReason:reply:]` and force `success = true`.
|
|
521
|
+
|
|
522
|
+
**Pass criteria:** The app prevents access to protected data despite the manipulated callback. The secret remains locked because `SecAccessControl` + Secure Enclave enforcement is independent of the boolean.
|
|
523
|
+
|
|
524
|
+
**Fail criteria:** The app grants access after the hook forces success. This proves reliance on the vulnerable boolean gate.
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Key References
|
|
529
|
+
|
|
530
|
+
- **WWDC 2014 Session 711** — "Keychain and Authentication with Touch ID": Introduced the two trust models (evaluatePolicy vs keychain+ACL)
|
|
531
|
+
- **WWDC 2019 Session 709** — "Cryptography and Your Apps": CryptoKit + Secure Enclave key generation with access control
|
|
532
|
+
- **Apple Platform Security Guide** — Secure Enclave architecture, keychain encryption chain (metadata key + secret key), ACL evaluation in hardware
|
|
533
|
+
- **OWASP MASTG MSTG-AUTH-8** — Biometric authentication must not be event-bound
|
|
534
|
+
- **OWASP MASTG MSTG-AUTH-12** — Integrity of biometric mechanism must be verified
|
|
535
|
+
- **OWASP MASTG MASTG-TEST-0266** — Test for local authentication bypass
|
|
536
|
+
- **objection wiki** — "Understanding the iOS Biometrics Bypass": Confirms attack boundary at SecAccessControl
|
|
537
|
+
- **TN3137** — "On Mac Keychain APIs and implementations" (macOS keychain unification)
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## Cross-References
|
|
542
|
+
|
|
543
|
+
- `keychain-fundamentals.md` — SecItem CRUD patterns used by the keychain-bound biometric flow
|
|
544
|
+
- `keychain-access-control.md` — `SecAccessControlCreateWithFlags`, accessibility constants, and flag composition rules
|
|
545
|
+
- `secure-enclave.md` — Hardware-backed keys with biometric gating via `SecAccessControl`
|
|
546
|
+
- `common-anti-patterns.md` — Anti-pattern #3 (LAContext-only biometric gate)
|
|
547
|
+
- `credential-storage-patterns.md` — Biometric protection for high-value credentials (OAuth tokens, API keys)
|
|
548
|
+
- `testing-security-code.md` — Protocol-based mocking for biometric flows, LAContext test strategies
|
|
549
|
+
- `compliance-owasp-mapping.md` — M3 (Insecure Authentication/Authorization) biometric requirements
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## Summary Checklist
|
|
554
|
+
|
|
555
|
+
1. **No standalone boolean gates** — `LAContext.evaluatePolicy()` is NEVER the sole authentication mechanism for sensitive data; secrets are always bound to keychain + `SecAccessControl`
|
|
556
|
+
2. **Hardware-gated secrets** — All sensitive data protected by biometrics uses `SecAccessControlCreateWithFlags` with the Secure Enclave enforcing the ACL
|
|
557
|
+
3. **Correct flag selection** — `.biometryCurrentSet` for high-security (banking, payments); `.biometryAny` for convenience; `.userPresence` for broad compatibility or fallback
|
|
558
|
+
4. **No kSecAttrAccessible conflict** — `kSecAttrAccessible` and `kSecAttrAccessControl` are never set on the same keychain item
|
|
559
|
+
5. **ThisDeviceOnly accessibility** — Biometric-gated secrets use `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` or `WhenUnlockedThisDeviceOnly`; never syncable
|
|
560
|
+
6. **Complete error handling** — All `LAError` codes handled: `.biometryNotEnrolled`, `.biometryLockout`, `.biometryNotAvailable`, `.passcodeNotSet`, `.userCancel`, `.userFallback`
|
|
561
|
+
7. **Graceful degradation** — App provides fallback path (passcode or password) when biometrics are unavailable or locked out
|
|
562
|
+
8. **Enrollment change detection** — `evaluatedPolicyDomainState` monitored when using `.biometryCurrentSet`; re-enrollment flow implemented
|
|
563
|
+
9. **Thread safety** — `SecItemCopyMatching` with biometric ACL never runs on `@MainActor`; actor-isolated or dispatched to background queue
|
|
564
|
+
10. **Dynamic verification** — objection/Frida bypass test confirms protected data remains inaccessible when `evaluatePolicy` callback is hooked
|
|
565
|
+
11. **SAST/linting** — CI pipeline includes rule to flag standalone `evaluatePolicy` without corresponding `SecAccessControl` keychain operations
|