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