@wazir-dev/cli 1.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/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
# Mobile iOS Security — Expertise Module
|
|
2
|
+
|
|
3
|
+
> This module equips AI agents with deep knowledge of iOS application security:
|
|
4
|
+
> threat landscape, platform security model, secure implementation patterns,
|
|
5
|
+
> vulnerability catalog, tooling, and compliance standards. All guidance targets
|
|
6
|
+
> iOS 16+ / Swift 5.9+ and reflects OWASP MASVS v2, OWASP Mobile Top 10 (2024),
|
|
7
|
+
> and Apple Platform Security Guide (January 2026 edition).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Threat Landscape
|
|
12
|
+
|
|
13
|
+
### 1.1 iOS-Specific Attack Vectors
|
|
14
|
+
|
|
15
|
+
**Jailbreak Exploitation**
|
|
16
|
+
Jailbreaking removes iOS sandbox restrictions, granting root access and enabling
|
|
17
|
+
unsigned code execution. Tools like unc0ver, checkra1n, and Dopamine exploit
|
|
18
|
+
kernel vulnerabilities to disable code signing enforcement. On a jailbroken
|
|
19
|
+
device, attackers can inspect Keychain contents, hook Objective-C methods via
|
|
20
|
+
Cydia Substrate, bypass SSL pinning, and extract decrypted IPA binaries.
|
|
21
|
+
|
|
22
|
+
**Insecure Data Storage**
|
|
23
|
+
Data stored in UserDefaults, plist files, SQLite databases, or the app's
|
|
24
|
+
Documents directory without Data Protection is accessible via device backups,
|
|
25
|
+
forensic tools, or jailbroken file managers. Sensitive data in Core Data stores
|
|
26
|
+
without NSFileProtectionComplete can be read when the device is locked.
|
|
27
|
+
|
|
28
|
+
**IPC Attacks (URL Scheme Hijacking)**
|
|
29
|
+
Custom URL schemes lack ownership verification. Any app can register the same
|
|
30
|
+
scheme, leading to URL scheme hijacking where a malicious app intercepts
|
|
31
|
+
deep links meant for a legitimate app. Since Apple documentation states "if
|
|
32
|
+
more than one third-party app registers to handle the same URL scheme, there
|
|
33
|
+
is currently no process for determining which app will be given that scheme,"
|
|
34
|
+
this creates a race condition exploitable for credential theft and session
|
|
35
|
+
hijacking.
|
|
36
|
+
|
|
37
|
+
**Reverse Engineering**
|
|
38
|
+
Despite Mach-O binary compilation, iOS apps can be decrypted (using tools like
|
|
39
|
+
Clutch or frida-ios-dump), decompiled (Hopper, IDA Pro, Ghidra), and analyzed.
|
|
40
|
+
Hardcoded API keys, encryption keys, and business logic become exposed.
|
|
41
|
+
Objective-C runtime metadata makes method swizzling trivial; pure Swift is
|
|
42
|
+
harder to instrument but not immune.
|
|
43
|
+
|
|
44
|
+
**Side-Loading Risks (EU DMA)**
|
|
45
|
+
Since iOS 17.4 (March 2024), the EU Digital Markets Act requires Apple to allow
|
|
46
|
+
alternative app marketplaces and sideloading. Apps distributed outside the App
|
|
47
|
+
Store undergo Apple Notarization (a baseline integrity check) but bypass the
|
|
48
|
+
full App Review process. This opens vectors for:
|
|
49
|
+
- Malware distribution through less-vetted marketplaces
|
|
50
|
+
- Modified apps with injected payloads
|
|
51
|
+
- Phishing apps mimicking legitimate services
|
|
52
|
+
- Reduced user trust signals compared to App Store verification
|
|
53
|
+
|
|
54
|
+
Source: Apple Newsroom, "Apple announces changes to iOS in the European Union"
|
|
55
|
+
(January 2024); Apple, "Complying with the DMA" developer documentation.
|
|
56
|
+
|
|
57
|
+
### 1.2 Real-World Incidents
|
|
58
|
+
|
|
59
|
+
**NSO Pegasus / FORCEDENTRY (2021-2025)**
|
|
60
|
+
The FORCEDENTRY exploit chain (CVE-2021-30860) used a zero-click iMessage
|
|
61
|
+
vulnerability to deploy NSO Group's Pegasus spyware. It bypassed Apple's
|
|
62
|
+
BlastDoor sandbox by exploiting an integer overflow in CoreGraphics PDF
|
|
63
|
+
parsing. Google Project Zero called it "one of the most technically
|
|
64
|
+
sophisticated exploits ever seen." The exploit required zero user interaction —
|
|
65
|
+
receiving a crafted iMessage was sufficient for full device compromise.
|
|
66
|
+
|
|
67
|
+
In December 2024, a US court ruled NSO Group liable for hacking 1,400 WhatsApp
|
|
68
|
+
users. In May 2025, NSO was ordered to pay $167 million in damages.
|
|
69
|
+
|
|
70
|
+
Source: Citizen Lab, "BLASTPASS" (September 2023); Google Project Zero deep
|
|
71
|
+
dive (December 2021).
|
|
72
|
+
|
|
73
|
+
**BLASTPASS (September 2023)**
|
|
74
|
+
Another NSO exploit chain (CVE-2023-41064, CVE-2023-41061) used PassKit
|
|
75
|
+
attachments with malicious images to achieve zero-click code execution on
|
|
76
|
+
iOS 16.6. Apple patched in iOS 16.6.1 and introduced Lockdown Mode hardening.
|
|
77
|
+
|
|
78
|
+
**XcodeGhost Supply Chain Attack (2015)**
|
|
79
|
+
A trojanized Xcode IDE distributed through Chinese cloud services injected
|
|
80
|
+
malicious code into 2,500+ apps, affecting 128 million users. The malware
|
|
81
|
+
collected device info, prompted fake authentication dialogs, and hijacked URL
|
|
82
|
+
schemes. This incident led to OWASP Mobile Top 10 2024 elevating "Inadequate
|
|
83
|
+
Supply Chain Security" to M2.
|
|
84
|
+
|
|
85
|
+
Source: Palo Alto Networks Unit 42; Apple Epic v. Apple trial documents (2021).
|
|
86
|
+
|
|
87
|
+
**App Store Malware (Ongoing)**
|
|
88
|
+
Despite App Review, malware periodically reaches the App Store:
|
|
89
|
+
- 2024: Cryptocurrency drainer apps mimicking legitimate wallets
|
|
90
|
+
- 2023: VPN apps exfiltrating user data to unauthorized servers
|
|
91
|
+
- 2022: Fleeceware apps using misleading subscriptions
|
|
92
|
+
- Clipboard-reading apps exploiting pre-iOS 14 lack of paste notifications
|
|
93
|
+
|
|
94
|
+
Source: Kaspersky Mobile Threat Report 2024; Apple Transparency Report.
|
|
95
|
+
|
|
96
|
+
**Operation Triangulation (2023)**
|
|
97
|
+
Kaspersky discovered an advanced iOS attack chain exploiting four zero-day
|
|
98
|
+
vulnerabilities (including CVE-2023-41990 in the TrueType font processor).
|
|
99
|
+
The attack used invisible iMessage attachments to achieve code execution,
|
|
100
|
+
escalate privileges, and deploy spyware — all without user interaction.
|
|
101
|
+
|
|
102
|
+
Source: Kaspersky, "Operation Triangulation" (December 2023).
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 2. Core Security Principles
|
|
107
|
+
|
|
108
|
+
### 2.1 iOS Security Model
|
|
109
|
+
|
|
110
|
+
**App Sandbox**
|
|
111
|
+
Every third-party iOS app runs in a sandboxed process with a unique filesystem
|
|
112
|
+
container. Apps cannot access other apps' data, system files, or hardware
|
|
113
|
+
directly. The sandbox enforces:
|
|
114
|
+
- Filesystem isolation (each app has private Documents, Library, tmp)
|
|
115
|
+
- IPC restrictions (only via system-mediated mechanisms)
|
|
116
|
+
- Hardware access gating (camera, microphone, location require entitlements)
|
|
117
|
+
- Network socket restrictions
|
|
118
|
+
|
|
119
|
+
**Code Signing**
|
|
120
|
+
All executable code on iOS must be signed by a trusted certificate:
|
|
121
|
+
- App Store apps: signed by Apple after review
|
|
122
|
+
- Enterprise apps: signed with enterprise distribution certificate
|
|
123
|
+
- Development: signed with developer certificate + provisioning profile
|
|
124
|
+
Runtime enforcement (via AMFI — Apple Mobile File Integrity) kills processes
|
|
125
|
+
with invalid signatures. This prevents code injection and unauthorized
|
|
126
|
+
binary modification at the kernel level.
|
|
127
|
+
|
|
128
|
+
**Entitlements**
|
|
129
|
+
Entitlements are key-value pairs embedded in the code signature that declare
|
|
130
|
+
app capabilities (push notifications, Keychain access groups, HealthKit, etc.).
|
|
131
|
+
Since they are cryptographically signed, they cannot be modified post-signing.
|
|
132
|
+
The kernel and system daemons check entitlements before granting access to
|
|
133
|
+
protected resources.
|
|
134
|
+
|
|
135
|
+
**Address Space Layout Randomization (ASLR)**
|
|
136
|
+
iOS randomizes the memory layout of processes at launch, making exploitation
|
|
137
|
+
of memory corruption vulnerabilities significantly harder. Combined with
|
|
138
|
+
Pointer Authentication Codes (PAC) on A12+ chips, this provides strong
|
|
139
|
+
runtime exploit mitigation.
|
|
140
|
+
|
|
141
|
+
### 2.2 Data Protection API
|
|
142
|
+
|
|
143
|
+
iOS Data Protection encrypts files using per-file keys derived from the
|
|
144
|
+
device passcode and hardware UID. Four protection classes exist:
|
|
145
|
+
|
|
146
|
+
| Class | Constant | Availability | Use Case |
|
|
147
|
+
|-------|----------|--------------|----------|
|
|
148
|
+
| Complete Protection | `NSFileProtectionComplete` | Only when unlocked | Highly sensitive data (health, financial) |
|
|
149
|
+
| Complete Unless Open | `NSFileProtectionCompleteUnlessOpen` | Open files accessible while locked | Background file transfers |
|
|
150
|
+
| Until First Auth | `NSFileProtectionCompleteUntilFirstUserAuthentication` | After first unlock (default) | Data needed by background services |
|
|
151
|
+
| No Protection | `NSFileProtectionNone` | Always accessible | Non-sensitive cached data only |
|
|
152
|
+
|
|
153
|
+
**Best practice:** Set `NSFileProtectionComplete` as the default for the entire
|
|
154
|
+
app via the entitlements file, then downgrade individual files only when
|
|
155
|
+
background access is genuinely required.
|
|
156
|
+
|
|
157
|
+
### 2.3 Keychain Services
|
|
158
|
+
|
|
159
|
+
The iOS Keychain provides hardware-backed encrypted storage for small secrets
|
|
160
|
+
(passwords, tokens, keys). Items are encrypted using AES-256-GCM with keys
|
|
161
|
+
derived from the Secure Enclave.
|
|
162
|
+
|
|
163
|
+
**Keychain Accessibility Classes:**
|
|
164
|
+
|
|
165
|
+
| Class | Constant | Security Level |
|
|
166
|
+
|-------|----------|----------------|
|
|
167
|
+
| When Passcode Set, This Device | `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` | Highest — requires passcode; no backup migration |
|
|
168
|
+
| When Unlocked, This Device | `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` | High — no backup migration |
|
|
169
|
+
| When Unlocked | `kSecAttrAccessibleWhenUnlocked` | Medium — migrates to new device via backup |
|
|
170
|
+
| After First Unlock, This Device | `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` | Lower — accessible while locked after boot |
|
|
171
|
+
| After First Unlock | `kSecAttrAccessibleAfterFirstUnlock` | Lowest recommended — background access + migration |
|
|
172
|
+
|
|
173
|
+
**Best practice:** Use `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` for
|
|
174
|
+
authentication tokens and sensitive credentials. This ensures the item is
|
|
175
|
+
only accessible when the device has a passcode set, is unlocked, and cannot
|
|
176
|
+
be extracted via backup or device migration.
|
|
177
|
+
|
|
178
|
+
### 2.4 App Transport Security (ATS)
|
|
179
|
+
|
|
180
|
+
ATS enforces HTTPS for all network connections made via URLSession. Since
|
|
181
|
+
iOS 9, ATS is enabled by default and requires:
|
|
182
|
+
- TLS 1.2 or later
|
|
183
|
+
- Forward secrecy cipher suites (ECDHE)
|
|
184
|
+
- Certificates with SHA-256+ signatures and RSA 2048+ / ECC 256+ keys
|
|
185
|
+
|
|
186
|
+
ATS exceptions must be declared in Info.plist and are scrutinized during
|
|
187
|
+
App Review. Apple rejects apps with `NSAllowsArbitraryLoads = YES` without
|
|
188
|
+
valid justification.
|
|
189
|
+
|
|
190
|
+
### 2.5 Certificate Pinning
|
|
191
|
+
|
|
192
|
+
Certificate pinning restricts which TLS certificates the app trusts beyond
|
|
193
|
+
the system trust store. Two approaches:
|
|
194
|
+
|
|
195
|
+
- **Public key pinning:** Pin the Subject Public Key Info (SPKI) hash.
|
|
196
|
+
Survives certificate rotation if the same key pair is reused.
|
|
197
|
+
- **Certificate pinning:** Pin the full leaf or intermediate certificate.
|
|
198
|
+
Requires app update when certificates rotate.
|
|
199
|
+
|
|
200
|
+
Apple's recommendation: Pin CA public keys rather than leaf certificates, and
|
|
201
|
+
always include a backup pin. iOS requires at least two SPKI hashes when using
|
|
202
|
+
the Info.plist-based `NSPinnedDomains` configuration.
|
|
203
|
+
|
|
204
|
+
### 2.6 Biometric Authentication
|
|
205
|
+
|
|
206
|
+
Face ID and Touch ID are mediated by the Secure Enclave, which stores
|
|
207
|
+
biometric templates in encrypted form inaccessible to the application
|
|
208
|
+
processor. The LocalAuthentication framework provides:
|
|
209
|
+
|
|
210
|
+
- `LAContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics)` —
|
|
211
|
+
biometric-only verification
|
|
212
|
+
- `LAContext.evaluatePolicy(.deviceOwnerAuthentication)` — biometric with
|
|
213
|
+
device passcode fallback
|
|
214
|
+
|
|
215
|
+
**Critical principle:** Biometric checks alone are a UX convenience, not a
|
|
216
|
+
security boundary. True security requires binding Keychain items to biometric
|
|
217
|
+
access controls so the Secure Enclave gates key release:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
Access control flag: .biometryCurrentSet + .privateKeyUsage
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
This ensures that if biometric enrollment changes (e.g., new fingerprint
|
|
224
|
+
added), the Keychain item becomes inaccessible, forcing re-authentication.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 3. Implementation Patterns
|
|
229
|
+
|
|
230
|
+
### 3.1 Secure Keychain Wrapper (Swift)
|
|
231
|
+
|
|
232
|
+
```swift
|
|
233
|
+
import Security
|
|
234
|
+
import Foundation
|
|
235
|
+
|
|
236
|
+
enum KeychainError: Error {
|
|
237
|
+
case duplicateItem
|
|
238
|
+
case itemNotFound
|
|
239
|
+
case unexpectedStatus(OSStatus)
|
|
240
|
+
case encodingError
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
struct KeychainManager {
|
|
244
|
+
|
|
245
|
+
// MARK: - Save
|
|
246
|
+
|
|
247
|
+
static func save(
|
|
248
|
+
key: String,
|
|
249
|
+
data: Data,
|
|
250
|
+
accessibility: CFString = kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
|
|
251
|
+
) throws {
|
|
252
|
+
let query: [String: Any] = [
|
|
253
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
254
|
+
kSecAttrAccount as String: key,
|
|
255
|
+
kSecValueData as String: data,
|
|
256
|
+
kSecAttrAccessible as String: accessibility
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
let status = SecItemAdd(query as CFDictionary, nil)
|
|
260
|
+
|
|
261
|
+
switch status {
|
|
262
|
+
case errSecSuccess:
|
|
263
|
+
return
|
|
264
|
+
case errSecDuplicateItem:
|
|
265
|
+
// Update existing item
|
|
266
|
+
let updateQuery: [String: Any] = [
|
|
267
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
268
|
+
kSecAttrAccount as String: key
|
|
269
|
+
]
|
|
270
|
+
let updateAttributes: [String: Any] = [
|
|
271
|
+
kSecValueData as String: data,
|
|
272
|
+
kSecAttrAccessible as String: accessibility
|
|
273
|
+
]
|
|
274
|
+
let updateStatus = SecItemUpdate(
|
|
275
|
+
updateQuery as CFDictionary,
|
|
276
|
+
updateAttributes as CFDictionary
|
|
277
|
+
)
|
|
278
|
+
guard updateStatus == errSecSuccess else {
|
|
279
|
+
throw KeychainError.unexpectedStatus(updateStatus)
|
|
280
|
+
}
|
|
281
|
+
default:
|
|
282
|
+
throw KeychainError.unexpectedStatus(status)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// MARK: - Retrieve
|
|
287
|
+
|
|
288
|
+
static func retrieve(key: String) throws -> Data {
|
|
289
|
+
let query: [String: Any] = [
|
|
290
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
291
|
+
kSecAttrAccount as String: key,
|
|
292
|
+
kSecReturnData as String: true,
|
|
293
|
+
kSecMatchLimit as String: kSecMatchLimitOne
|
|
294
|
+
]
|
|
295
|
+
|
|
296
|
+
var result: AnyObject?
|
|
297
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
298
|
+
|
|
299
|
+
guard status == errSecSuccess, let data = result as? Data else {
|
|
300
|
+
if status == errSecItemNotFound {
|
|
301
|
+
throw KeychainError.itemNotFound
|
|
302
|
+
}
|
|
303
|
+
throw KeychainError.unexpectedStatus(status)
|
|
304
|
+
}
|
|
305
|
+
return data
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// MARK: - Delete
|
|
309
|
+
|
|
310
|
+
static func delete(key: String) throws {
|
|
311
|
+
let query: [String: Any] = [
|
|
312
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
313
|
+
kSecAttrAccount as String: key
|
|
314
|
+
]
|
|
315
|
+
let status = SecItemDelete(query as CFDictionary)
|
|
316
|
+
guard status == errSecSuccess || status == errSecItemNotFound else {
|
|
317
|
+
throw KeychainError.unexpectedStatus(status)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 3.2 Biometric-Protected Keychain Item
|
|
324
|
+
|
|
325
|
+
```swift
|
|
326
|
+
import Security
|
|
327
|
+
import LocalAuthentication
|
|
328
|
+
|
|
329
|
+
func saveBiometricProtectedToken(_ token: Data, account: String) throws {
|
|
330
|
+
let access = SecAccessControlCreateWithFlags(
|
|
331
|
+
kCFAllocatorDefault,
|
|
332
|
+
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
|
|
333
|
+
[.biometryCurrentSet, .privateKeyUsage],
|
|
334
|
+
nil
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
guard let accessControl = access else {
|
|
338
|
+
throw KeychainError.unexpectedStatus(errSecParam)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let context = LAContext()
|
|
342
|
+
context.touchIDAuthenticationAllowableReuseDuration = 0 // Force fresh auth
|
|
343
|
+
|
|
344
|
+
let query: [String: Any] = [
|
|
345
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
346
|
+
kSecAttrAccount as String: account,
|
|
347
|
+
kSecValueData as String: token,
|
|
348
|
+
kSecAttrAccessControl as String: accessControl,
|
|
349
|
+
kSecUseAuthenticationContext as String: context
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
let status = SecItemAdd(query as CFDictionary, nil)
|
|
353
|
+
guard status == errSecSuccess else {
|
|
354
|
+
throw KeychainError.unexpectedStatus(status)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
func retrieveBiometricProtectedToken(account: String) throws -> Data {
|
|
359
|
+
let context = LAContext()
|
|
360
|
+
context.localizedReason = "Authenticate to access your secure token"
|
|
361
|
+
|
|
362
|
+
let query: [String: Any] = [
|
|
363
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
364
|
+
kSecAttrAccount as String: account,
|
|
365
|
+
kSecReturnData as String: true,
|
|
366
|
+
kSecMatchLimit as String: kSecMatchLimitOne,
|
|
367
|
+
kSecUseAuthenticationContext as String: context
|
|
368
|
+
]
|
|
369
|
+
|
|
370
|
+
var result: AnyObject?
|
|
371
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
372
|
+
|
|
373
|
+
guard status == errSecSuccess, let data = result as? Data else {
|
|
374
|
+
throw KeychainError.unexpectedStatus(status)
|
|
375
|
+
}
|
|
376
|
+
return data
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 3.3 Certificate Pinning (URLSession)
|
|
381
|
+
|
|
382
|
+
```swift
|
|
383
|
+
import Foundation
|
|
384
|
+
import CryptoKit
|
|
385
|
+
|
|
386
|
+
class PinnedSessionDelegate: NSObject, URLSessionDelegate {
|
|
387
|
+
|
|
388
|
+
// SHA-256 hashes of Subject Public Key Info (SPKI)
|
|
389
|
+
private let pinnedHashes: Set<String> = [
|
|
390
|
+
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Primary
|
|
391
|
+
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=" // Backup
|
|
392
|
+
]
|
|
393
|
+
|
|
394
|
+
func urlSession(
|
|
395
|
+
_ session: URLSession,
|
|
396
|
+
didReceive challenge: URLAuthenticationChallenge,
|
|
397
|
+
completionHandler: @escaping (
|
|
398
|
+
URLSession.AuthChallengeDisposition, URLCredential?
|
|
399
|
+
) -> Void
|
|
400
|
+
) {
|
|
401
|
+
guard challenge.protectionSpace.authenticationMethod
|
|
402
|
+
== NSURLAuthenticationMethodServerTrust,
|
|
403
|
+
let serverTrust = challenge.protectionSpace.serverTrust
|
|
404
|
+
else {
|
|
405
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
406
|
+
return
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Evaluate standard trust
|
|
410
|
+
var error: CFError?
|
|
411
|
+
guard SecTrustEvaluateWithError(serverTrust, &error) else {
|
|
412
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Extract and verify public key hash
|
|
417
|
+
guard let serverCertificate = SecTrustGetCertificateAtIndex(
|
|
418
|
+
serverTrust, 0
|
|
419
|
+
) else {
|
|
420
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
guard let serverPublicKey = SecCertificateCopyKey(serverCertificate),
|
|
425
|
+
let serverPublicKeyData = SecKeyCopyExternalRepresentation(
|
|
426
|
+
serverPublicKey, nil
|
|
427
|
+
) as Data?
|
|
428
|
+
else {
|
|
429
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
430
|
+
return
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let hash = SHA256.hash(data: serverPublicKeyData)
|
|
434
|
+
let hashBase64 = Data(hash).base64EncodedString()
|
|
435
|
+
|
|
436
|
+
if pinnedHashes.contains(hashBase64) {
|
|
437
|
+
completionHandler(
|
|
438
|
+
.useCredential,
|
|
439
|
+
URLCredential(trust: serverTrust)
|
|
440
|
+
)
|
|
441
|
+
} else {
|
|
442
|
+
completionHandler(.cancelAuthenticationChallenge, nil)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Usage
|
|
448
|
+
let session = URLSession(
|
|
449
|
+
configuration: .default,
|
|
450
|
+
delegate: PinnedSessionDelegate(),
|
|
451
|
+
delegateQueue: nil
|
|
452
|
+
)
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**Info.plist Alternative (NSPinnedDomains — iOS 14+):**
|
|
456
|
+
```xml
|
|
457
|
+
<key>NSAppTransportSecurity</key>
|
|
458
|
+
<dict>
|
|
459
|
+
<key>NSPinnedDomains</key>
|
|
460
|
+
<dict>
|
|
461
|
+
<key>api.example.com</key>
|
|
462
|
+
<dict>
|
|
463
|
+
<key>NSIncludesSubdomains</key>
|
|
464
|
+
<true/>
|
|
465
|
+
<key>NSPinnedCAIdentity</key>
|
|
466
|
+
<array>
|
|
467
|
+
<dict>
|
|
468
|
+
<key>SPKI-SHA256-BASE64</key>
|
|
469
|
+
<string>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</string>
|
|
470
|
+
</dict>
|
|
471
|
+
<dict>
|
|
472
|
+
<key>SPKI-SHA256-BASE64</key>
|
|
473
|
+
<string>BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</string>
|
|
474
|
+
</dict>
|
|
475
|
+
</array>
|
|
476
|
+
</dict>
|
|
477
|
+
</dict>
|
|
478
|
+
</dict>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### 3.4 Secure WKWebView Configuration
|
|
482
|
+
|
|
483
|
+
```swift
|
|
484
|
+
import WebKit
|
|
485
|
+
|
|
486
|
+
func createSecureWebView() -> WKWebView {
|
|
487
|
+
let config = WKWebViewConfiguration()
|
|
488
|
+
let prefs = WKWebpagePreferences()
|
|
489
|
+
|
|
490
|
+
// Disable JavaScript unless explicitly needed
|
|
491
|
+
prefs.allowsContentJavaScript = false
|
|
492
|
+
config.defaultWebpagePreferences = prefs
|
|
493
|
+
|
|
494
|
+
// Prevent file:// access
|
|
495
|
+
config.preferences.setValue(false, forKey: "allowFileAccessFromFileURLs")
|
|
496
|
+
|
|
497
|
+
// Disable universal file access
|
|
498
|
+
config.preferences.setValue(
|
|
499
|
+
false, forKey: "allowUniversalAccessFromFileURLs"
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
// Restrict media playback
|
|
503
|
+
config.allowsInlineMediaPlayback = false
|
|
504
|
+
config.mediaTypesRequiringUserActionForPlayback = .all
|
|
505
|
+
|
|
506
|
+
let webView = WKWebView(frame: .zero, configuration: config)
|
|
507
|
+
|
|
508
|
+
// Set navigation delegate for URL validation
|
|
509
|
+
webView.navigationDelegate = self
|
|
510
|
+
return webView
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// MARK: - WKNavigationDelegate — URL allowlisting
|
|
514
|
+
|
|
515
|
+
extension SecureWebViewController: WKNavigationDelegate {
|
|
516
|
+
func webView(
|
|
517
|
+
_ webView: WKWebView,
|
|
518
|
+
decidePolicyFor navigationAction: WKNavigationAction,
|
|
519
|
+
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
|
|
520
|
+
) {
|
|
521
|
+
guard let url = navigationAction.request.url,
|
|
522
|
+
let scheme = url.scheme,
|
|
523
|
+
["https"].contains(scheme),
|
|
524
|
+
allowedHosts.contains(url.host ?? "")
|
|
525
|
+
else {
|
|
526
|
+
decisionHandler(.cancel)
|
|
527
|
+
return
|
|
528
|
+
}
|
|
529
|
+
decisionHandler(.allow)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### 3.5 Jailbreak Detection
|
|
535
|
+
|
|
536
|
+
```swift
|
|
537
|
+
import Foundation
|
|
538
|
+
import UIKit
|
|
539
|
+
import Darwin
|
|
540
|
+
|
|
541
|
+
struct JailbreakDetector {
|
|
542
|
+
|
|
543
|
+
static func isJailbroken() -> Bool {
|
|
544
|
+
#if targetEnvironment(simulator)
|
|
545
|
+
return false
|
|
546
|
+
#else
|
|
547
|
+
return checkSuspiciousPaths()
|
|
548
|
+
|| checkSuspiciousURLSchemes()
|
|
549
|
+
|| checkWriteOutsideSandbox()
|
|
550
|
+
|| checkFork()
|
|
551
|
+
|| checkDylibs()
|
|
552
|
+
#endif
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// MARK: - Check 1: Suspicious file paths
|
|
556
|
+
|
|
557
|
+
private static func checkSuspiciousPaths() -> Bool {
|
|
558
|
+
let paths = [
|
|
559
|
+
"/Applications/Cydia.app",
|
|
560
|
+
"/Applications/Sileo.app",
|
|
561
|
+
"/Applications/Zebra.app",
|
|
562
|
+
"/Library/MobileSubstrate/MobileSubstrate.dylib",
|
|
563
|
+
"/usr/sbin/sshd",
|
|
564
|
+
"/usr/bin/ssh",
|
|
565
|
+
"/etc/apt",
|
|
566
|
+
"/var/lib/cydia",
|
|
567
|
+
"/private/var/stash",
|
|
568
|
+
"/usr/libexec/cydia",
|
|
569
|
+
"/var/jb" // rootless jailbreak path
|
|
570
|
+
]
|
|
571
|
+
return paths.contains { FileManager.default.fileExists(atPath: $0) }
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// MARK: - Check 2: URL schemes for jailbreak tools
|
|
575
|
+
|
|
576
|
+
private static func checkSuspiciousURLSchemes() -> Bool {
|
|
577
|
+
let schemes = [
|
|
578
|
+
"cydia://package/com.example",
|
|
579
|
+
"sileo://package/com.example",
|
|
580
|
+
"zbra://packages/com.example",
|
|
581
|
+
"filza://view"
|
|
582
|
+
]
|
|
583
|
+
return schemes.contains { urlString in
|
|
584
|
+
guard let url = URL(string: urlString) else { return false }
|
|
585
|
+
return UIApplication.shared.canOpenURL(url)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// MARK: - Check 3: Write outside sandbox
|
|
590
|
+
|
|
591
|
+
private static func checkWriteOutsideSandbox() -> Bool {
|
|
592
|
+
let testPath = "/private/jailbreak_test_\(UUID().uuidString)"
|
|
593
|
+
do {
|
|
594
|
+
try "test".write(
|
|
595
|
+
toFile: testPath,
|
|
596
|
+
atomically: true,
|
|
597
|
+
encoding: .utf8
|
|
598
|
+
)
|
|
599
|
+
try FileManager.default.removeItem(atPath: testPath)
|
|
600
|
+
return true // Should not succeed on non-jailbroken device
|
|
601
|
+
} catch {
|
|
602
|
+
return false
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// MARK: - Check 4: Fork availability
|
|
607
|
+
|
|
608
|
+
private static func checkFork() -> Bool {
|
|
609
|
+
let pid = fork()
|
|
610
|
+
if pid >= 0 {
|
|
611
|
+
// fork succeeded — jailbroken (sandbox should prevent fork)
|
|
612
|
+
if pid > 0 { kill(pid, SIGTERM) }
|
|
613
|
+
return true
|
|
614
|
+
}
|
|
615
|
+
return false
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// MARK: - Check 5: Suspicious dynamic libraries
|
|
619
|
+
|
|
620
|
+
private static func checkDylibs() -> Bool {
|
|
621
|
+
let suspiciousLibs = [
|
|
622
|
+
"SubstrateLoader", "SSLKillSwitch", "MobileSubstrate",
|
|
623
|
+
"TweakInject", "CydiaSubstrate", "FridaGadget",
|
|
624
|
+
"libcycript", "frida-agent"
|
|
625
|
+
]
|
|
626
|
+
let count = _dyld_image_count()
|
|
627
|
+
for i in 0..<count {
|
|
628
|
+
guard let name = _dyld_get_image_name(i) else { continue }
|
|
629
|
+
let imageName = String(cString: name)
|
|
630
|
+
if suspiciousLibs.contains(where: { imageName.contains($0) }) {
|
|
631
|
+
return true
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return false
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
**Important caveat:** All client-side jailbreak detection can be bypassed by a
|
|
640
|
+
determined attacker (e.g., using Liberty Lite, Shadow, or A-Bypass). Jailbreak
|
|
641
|
+
detection raises the cost of attack but is not a security guarantee. Critical
|
|
642
|
+
security decisions should be validated server-side.
|
|
643
|
+
|
|
644
|
+
### 3.6 App Attest / DeviceCheck
|
|
645
|
+
|
|
646
|
+
```swift
|
|
647
|
+
import DeviceCheck
|
|
648
|
+
|
|
649
|
+
// MARK: - App Attest (iOS 14+)
|
|
650
|
+
|
|
651
|
+
func attestKey() async throws -> String {
|
|
652
|
+
let service = DCAppAttestService.shared
|
|
653
|
+
|
|
654
|
+
guard service.isSupported else {
|
|
655
|
+
throw AppAttestError.unsupported
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Generate a new cryptographic key
|
|
659
|
+
let keyId = try await service.generateKey()
|
|
660
|
+
|
|
661
|
+
// Create attestation with server-generated challenge
|
|
662
|
+
let challenge = try await fetchChallengeFromServer()
|
|
663
|
+
let challengeHash = Data(SHA256.hash(data: challenge))
|
|
664
|
+
let attestation = try await service.attestKey(
|
|
665
|
+
keyId, clientDataHash: challengeHash
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
// Send attestation to server for verification
|
|
669
|
+
try await sendAttestationToServer(
|
|
670
|
+
keyId: keyId,
|
|
671
|
+
attestation: attestation
|
|
672
|
+
)
|
|
673
|
+
return keyId
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// MARK: - Assert Request Integrity
|
|
677
|
+
|
|
678
|
+
func assertRequest(
|
|
679
|
+
keyId: String, requestData: Data
|
|
680
|
+
) async throws -> Data {
|
|
681
|
+
let service = DCAppAttestService.shared
|
|
682
|
+
let requestHash = Data(SHA256.hash(data: requestData))
|
|
683
|
+
let assertion = try await service.generateAssertion(
|
|
684
|
+
keyId, clientDataHash: requestHash
|
|
685
|
+
)
|
|
686
|
+
return assertion
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// MARK: - DeviceCheck (per-device flags)
|
|
690
|
+
|
|
691
|
+
func setDeviceFlag() async throws {
|
|
692
|
+
let device = DCDevice.current
|
|
693
|
+
|
|
694
|
+
guard device.isSupported else { return }
|
|
695
|
+
|
|
696
|
+
let token = try await device.generateToken()
|
|
697
|
+
// Send token to your server; server calls Apple's API
|
|
698
|
+
// to query/update two bits of per-device state
|
|
699
|
+
try await sendDeviceTokenToServer(token)
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### 3.7 Secure Enclave Key Generation
|
|
704
|
+
|
|
705
|
+
```swift
|
|
706
|
+
import Security
|
|
707
|
+
import CryptoKit
|
|
708
|
+
|
|
709
|
+
func generateSecureEnclaveKey(tag: String) throws -> SecKey {
|
|
710
|
+
let access = SecAccessControlCreateWithFlags(
|
|
711
|
+
kCFAllocatorDefault,
|
|
712
|
+
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
713
|
+
[.privateKeyUsage, .biometryCurrentSet],
|
|
714
|
+
nil
|
|
715
|
+
)!
|
|
716
|
+
|
|
717
|
+
let attributes: [String: Any] = [
|
|
718
|
+
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
|
|
719
|
+
kSecAttrKeySizeInBits as String: 256,
|
|
720
|
+
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
|
|
721
|
+
kSecPrivateKeyAttrs as String: [
|
|
722
|
+
kSecAttrIsPermanent as String: true,
|
|
723
|
+
kSecAttrApplicationTag as String: tag.data(using: .utf8)!,
|
|
724
|
+
kSecAttrAccessControl as String: access
|
|
725
|
+
]
|
|
726
|
+
]
|
|
727
|
+
|
|
728
|
+
var error: Unmanaged<CFError>?
|
|
729
|
+
guard let privateKey = SecKeyCreateRandomKey(
|
|
730
|
+
attributes as CFDictionary, &error
|
|
731
|
+
) else {
|
|
732
|
+
throw error!.takeRetainedValue() as Error
|
|
733
|
+
}
|
|
734
|
+
return privateKey
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Sign data with Secure Enclave key
|
|
738
|
+
func signWithSecureEnclave(key: SecKey, data: Data) throws -> Data {
|
|
739
|
+
var error: Unmanaged<CFError>?
|
|
740
|
+
guard let signature = SecKeyCreateSignature(
|
|
741
|
+
key,
|
|
742
|
+
.ecdsaSignatureMessageX962SHA256,
|
|
743
|
+
data as CFData,
|
|
744
|
+
&error
|
|
745
|
+
) as Data? else {
|
|
746
|
+
throw error!.takeRetainedValue() as Error
|
|
747
|
+
}
|
|
748
|
+
return signature
|
|
749
|
+
}
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
## 4. Vulnerability Catalog
|
|
755
|
+
|
|
756
|
+
### V01: Sensitive Data in UserDefaults
|
|
757
|
+
- **Risk:** UserDefaults stores data in plaintext plist files within the app
|
|
758
|
+
sandbox. Accessible via device backups, forensic extraction, or jailbreak.
|
|
759
|
+
- **Impact:** Exposure of tokens, PII, session identifiers.
|
|
760
|
+
- **Fix:** Store secrets in Keychain with appropriate accessibility class.
|
|
761
|
+
- **MASVS:** MASVS-STORAGE
|
|
762
|
+
|
|
763
|
+
### V02: Screenshots Exposing Sensitive Data
|
|
764
|
+
- **Risk:** iOS captures a screenshot when the app enters background
|
|
765
|
+
(for the app switcher). Sensitive screens (banking, health data) are
|
|
766
|
+
stored as images in the app sandbox.
|
|
767
|
+
- **Impact:** Visual exposure of confidential information.
|
|
768
|
+
- **Fix:** Implement `applicationWillResignActive` to overlay a blur or
|
|
769
|
+
placeholder view. Use `UITextField.isSecureTextEntry` for sensitive fields.
|
|
770
|
+
- **MASVS:** MASVS-STORAGE
|
|
771
|
+
|
|
772
|
+
### V03: Pasteboard / Clipboard Leaks
|
|
773
|
+
- **Risk:** Data copied to `UIPasteboard.general` is accessible to all apps.
|
|
774
|
+
Pre-iOS 14, clipboard reads were silent. Research by Mysk (2020) documented
|
|
775
|
+
dozens of popular apps silently reading clipboard contents.
|
|
776
|
+
- **Impact:** Credential theft, location tracking via photo GPS metadata.
|
|
777
|
+
- **Fix:** Use app-specific `UIPasteboard(name:create:)` for internal copy.
|
|
778
|
+
Mark items as `localOnly` and set expiration. For sensitive fields, disable
|
|
779
|
+
copy via `UIMenuController` customization.
|
|
780
|
+
- **MASVS:** MASVS-STORAGE, MASVS-PRIVACY
|
|
781
|
+
|
|
782
|
+
### V04: Insecure IPC via Custom URL Schemes
|
|
783
|
+
- **Risk:** URL scheme hijacking (see Section 1.1). Sensitive data passed
|
|
784
|
+
via URL query parameters is logged in system logs and accessible to
|
|
785
|
+
intercepting apps.
|
|
786
|
+
- **Impact:** Authentication token theft, session hijacking.
|
|
787
|
+
- **Fix:** Migrate to Universal Links (HTTPS-based, verified via
|
|
788
|
+
apple-app-site-association file). If URL schemes are required, validate
|
|
789
|
+
the `sourceApplication` parameter and never pass secrets in URLs.
|
|
790
|
+
- **MASVS:** MASVS-PLATFORM
|
|
791
|
+
|
|
792
|
+
### V05: Missing or Misconfigured ATS
|
|
793
|
+
- **Risk:** `NSAllowsArbitraryLoads = YES` disables ATS globally,
|
|
794
|
+
permitting plaintext HTTP. Per-domain exceptions
|
|
795
|
+
(`NSExceptionAllowsInsecureHTTPLoads`) weaken specific connections.
|
|
796
|
+
- **Impact:** Man-in-the-middle attacks, credential interception.
|
|
797
|
+
- **Fix:** Remove all ATS exceptions. If exceptions are required (e.g.,
|
|
798
|
+
third-party CDN), use the narrowest scope (`NSExceptionDomains` with
|
|
799
|
+
specific domains). Apple App Review scrutinizes blanket ATS exemptions.
|
|
800
|
+
- **MASVS:** MASVS-NETWORK
|
|
801
|
+
|
|
802
|
+
### V06: Weak Keychain Accessibility
|
|
803
|
+
- **Risk:** Using `kSecAttrAccessibleAlways` (deprecated) or
|
|
804
|
+
`kSecAttrAccessibleAfterFirstUnlock` makes Keychain items accessible
|
|
805
|
+
when the device is locked and extractable via backups.
|
|
806
|
+
- **Impact:** Token theft from device backups or locked-device attacks.
|
|
807
|
+
- **Fix:** Use `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` for
|
|
808
|
+
credentials. Add `kSecAttrAccessControl` with `.biometryCurrentSet`
|
|
809
|
+
for high-value secrets.
|
|
810
|
+
- **MASVS:** MASVS-STORAGE
|
|
811
|
+
|
|
812
|
+
### V07: Missing Jailbreak Detection
|
|
813
|
+
- **Risk:** Jailbroken devices bypass sandbox, code signing, and Data
|
|
814
|
+
Protection. Apps running on jailbroken devices are vulnerable to
|
|
815
|
+
runtime manipulation, Keychain dumping, and method hooking.
|
|
816
|
+
- **Impact:** Complete compromise of client-side security controls.
|
|
817
|
+
- **Fix:** Implement layered jailbreak detection (Section 3.5). Combine
|
|
818
|
+
with server-side risk scoring. For high-security apps, refuse to
|
|
819
|
+
operate on jailbroken devices.
|
|
820
|
+
- **MASVS:** MASVS-RESILIENCE
|
|
821
|
+
|
|
822
|
+
### V08: Debug Logging in Production
|
|
823
|
+
- **Risk:** `NSLog`, `print`, and `os_log` output is readable via
|
|
824
|
+
Console.app on connected Mac or via syslog on jailbroken devices.
|
|
825
|
+
Sensitive data (tokens, user input, API responses) in logs leaks
|
|
826
|
+
to anyone with device access.
|
|
827
|
+
- **Impact:** Credential exposure, PII leakage.
|
|
828
|
+
- **Fix:** Use a logging framework with log levels (os_log with
|
|
829
|
+
`.private` redaction). Wrap sensitive logging in `#if DEBUG`.
|
|
830
|
+
Strip NSLog calls in release builds via compiler flags.
|
|
831
|
+
- **MASVS:** MASVS-STORAGE
|
|
832
|
+
|
|
833
|
+
### V09: Hardcoded Secrets and API Keys
|
|
834
|
+
- **Risk:** API keys, encryption keys, and credentials embedded in
|
|
835
|
+
source code are extractable via binary analysis (strings, class-dump,
|
|
836
|
+
Hopper). Even obfuscation only slows extraction.
|
|
837
|
+
- **Impact:** Backend compromise, unauthorized API access.
|
|
838
|
+
- **Fix:** Fetch secrets from a secure backend at runtime. Use
|
|
839
|
+
configuration endpoints with App Attest. For required embedded keys,
|
|
840
|
+
use xcconfig files excluded from VCS and obfuscation.
|
|
841
|
+
- **MASVS:** MASVS-CRYPTO, MASVS-RESILIENCE
|
|
842
|
+
|
|
843
|
+
### V10: Insecure WebView Configuration
|
|
844
|
+
- **Risk:** WKWebView with JavaScript enabled and permissive file access
|
|
845
|
+
can be exploited for cross-site scripting, local file exfiltration,
|
|
846
|
+
or JavaScript bridge abuse.
|
|
847
|
+
- **Impact:** Data theft, phishing within app context.
|
|
848
|
+
- **Fix:** Follow Section 3.4 pattern. Restrict to HTTPS URLs.
|
|
849
|
+
Implement Content Security Policy headers. Use
|
|
850
|
+
`WKScriptMessageHandler` with strict input validation for
|
|
851
|
+
JavaScript-to-native bridges.
|
|
852
|
+
- **MASVS:** MASVS-PLATFORM
|
|
853
|
+
|
|
854
|
+
### V11: Insufficient Certificate Validation
|
|
855
|
+
- **Risk:** Not implementing certificate pinning allows MITM attacks
|
|
856
|
+
with rogue certificates (e.g., corporate proxy, compromised CA).
|
|
857
|
+
Trusting self-signed certificates in production bypasses the entire
|
|
858
|
+
TLS trust model.
|
|
859
|
+
- **Impact:** Complete interception of API traffic.
|
|
860
|
+
- **Fix:** Implement SPKI pinning (Section 3.3) or use NSPinnedDomains.
|
|
861
|
+
Never override `URLAuthenticationChallenge` to blindly trust all
|
|
862
|
+
certificates in production builds.
|
|
863
|
+
- **MASVS:** MASVS-NETWORK
|
|
864
|
+
|
|
865
|
+
### V12: Unprotected Biometric Authentication
|
|
866
|
+
- **Risk:** Using `evaluatePolicy` alone without Keychain access
|
|
867
|
+
control provides only a boolean result that can be bypassed via
|
|
868
|
+
method swizzling or Frida hooking (`LAContext.evaluatePolicy` → true).
|
|
869
|
+
- **Impact:** Authentication bypass on jailbroken or instrumented devices.
|
|
870
|
+
- **Fix:** Bind Keychain items to biometric access control (Section 3.2)
|
|
871
|
+
so the Secure Enclave gates key release. Never rely solely on the
|
|
872
|
+
boolean result of `evaluatePolicy`.
|
|
873
|
+
- **MASVS:** MASVS-AUTH
|
|
874
|
+
|
|
875
|
+
### V13: Improper Credential Storage
|
|
876
|
+
- **Risk:** Storing OAuth tokens in app files, Core Data, or Realm
|
|
877
|
+
without encryption. Using symmetric encryption with hardcoded keys.
|
|
878
|
+
- **Impact:** Token theft, account takeover.
|
|
879
|
+
- **Fix:** Store all credentials in Keychain. Use refresh token rotation.
|
|
880
|
+
Implement token binding to device identity via App Attest.
|
|
881
|
+
- **MASVS:** MASVS-STORAGE, MASVS-AUTH
|
|
882
|
+
|
|
883
|
+
### V14: Missing Binary Protections
|
|
884
|
+
- **Risk:** Without obfuscation, stripped symbols, and anti-tampering
|
|
885
|
+
checks, the app binary is easily reverse-engineered. Attackers can
|
|
886
|
+
patch logic, disable security checks, or clone the app.
|
|
887
|
+
- **Impact:** IP theft, circumvention of licensing/payment logic.
|
|
888
|
+
- **Fix:** Enable bitcode. Strip debug symbols in release. Use
|
|
889
|
+
commercial obfuscators (SwiftShield, iXGuard). Implement integrity
|
|
890
|
+
checks on binary hash at runtime. Use App Attest for server-side
|
|
891
|
+
app integrity verification.
|
|
892
|
+
- **MASVS:** MASVS-RESILIENCE
|
|
893
|
+
|
|
894
|
+
### V15: Insecure Backup Data
|
|
895
|
+
- **Risk:** iTunes/Finder backups include app sandbox contents.
|
|
896
|
+
Unencrypted backups on a computer expose all files without Data
|
|
897
|
+
Protection. iCloud backup may include Keychain items with permissive
|
|
898
|
+
accessibility.
|
|
899
|
+
- **Impact:** Offline extraction of app data and credentials.
|
|
900
|
+
- **Fix:** Exclude sensitive files with
|
|
901
|
+
`URLResourceValues.isExcludedFromBackup = true`. Use
|
|
902
|
+
`ThisDeviceOnly` Keychain accessibility. Apply NSFileProtectionComplete
|
|
903
|
+
to sensitive files.
|
|
904
|
+
- **MASVS:** MASVS-STORAGE
|
|
905
|
+
|
|
906
|
+
---
|
|
907
|
+
|
|
908
|
+
## 5. Security Checklist
|
|
909
|
+
|
|
910
|
+
### Data Protection
|
|
911
|
+
- [ ] All secrets stored in Keychain (never UserDefaults, plist, or files)
|
|
912
|
+
- [ ] Keychain items use `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly`
|
|
913
|
+
- [ ] NSFileProtectionComplete set as default file protection level
|
|
914
|
+
- [ ] Sensitive files excluded from device backups
|
|
915
|
+
- [ ] Background screenshot protection implemented (blur/placeholder)
|
|
916
|
+
- [ ] Pasteboard usage restricted (local-only, expiring, or disabled)
|
|
917
|
+
- [ ] No sensitive data in debug/production logs
|
|
918
|
+
|
|
919
|
+
### Network Security
|
|
920
|
+
- [ ] ATS enabled with no blanket exceptions
|
|
921
|
+
- [ ] Certificate pinning implemented for critical API endpoints
|
|
922
|
+
- [ ] At least two SPKI pins configured (primary + backup)
|
|
923
|
+
- [ ] Certificate rotation plan documented and tested
|
|
924
|
+
- [ ] No hardcoded API endpoints — use server-driven configuration
|
|
925
|
+
|
|
926
|
+
### Authentication
|
|
927
|
+
- [ ] Biometric auth bound to Keychain access controls (not boolean-only)
|
|
928
|
+
- [ ] Session tokens use short expiry with refresh token rotation
|
|
929
|
+
- [ ] OAuth PKCE flow for authorization code exchange
|
|
930
|
+
- [ ] Re-authentication required for sensitive operations
|
|
931
|
+
- [ ] Face ID usage description present in Info.plist
|
|
932
|
+
|
|
933
|
+
### Platform Security
|
|
934
|
+
- [ ] Universal Links preferred over custom URL schemes
|
|
935
|
+
- [ ] URL scheme handlers validate `sourceApplication`
|
|
936
|
+
- [ ] WKWebView configured with minimal privileges (JS disabled if unused)
|
|
937
|
+
- [ ] WebView navigation restricted to allowlisted HTTPS domains
|
|
938
|
+
- [ ] Inter-app data sharing uses App Groups with minimal scope
|
|
939
|
+
|
|
940
|
+
### Binary Protection
|
|
941
|
+
- [ ] Jailbreak detection implemented with multiple checks
|
|
942
|
+
- [ ] Debug detection (ptrace anti-attach) in release builds
|
|
943
|
+
- [ ] App Attest / DeviceCheck integrated for server-side integrity
|
|
944
|
+
- [ ] Debug symbols stripped from release binaries
|
|
945
|
+
- [ ] No hardcoded secrets in source code or binaries
|
|
946
|
+
|
|
947
|
+
### Build & Release
|
|
948
|
+
- [ ] Xcode "Validate App" run before every submission
|
|
949
|
+
- [ ] Compiler stack protector enabled (`-fstack-protector-all`)
|
|
950
|
+
- [ ] Position Independent Executable (PIE) enabled (default for iOS)
|
|
951
|
+
- [ ] Automatic Reference Counting (ARC) enabled (prevents use-after-free)
|
|
952
|
+
- [ ] Third-party dependencies audited and version-pinned
|
|
953
|
+
- [ ] SBOM generated for supply chain tracking
|
|
954
|
+
|
|
955
|
+
---
|
|
956
|
+
|
|
957
|
+
## 6. Tools & Automation
|
|
958
|
+
|
|
959
|
+
### Static Analysis
|
|
960
|
+
|
|
961
|
+
| Tool | Purpose | Integration |
|
|
962
|
+
|------|---------|-------------|
|
|
963
|
+
| **Semgrep** (with Swift rules) | SAST for Swift — detects insecure storage, missing pinning, hardcoded secrets | CI/CD pipeline, pre-commit hooks |
|
|
964
|
+
| **SwiftLint** | Code style + custom security rules | Xcode build phase, CI |
|
|
965
|
+
| **DeepSource** | Automated code review with Swift security analyzers | GitHub/GitLab integration |
|
|
966
|
+
| **Snyk** | Dependency vulnerability scanning (CocoaPods, SPM) | CI/CD, IDE plugin |
|
|
967
|
+
| **Xcode Analyzer** | Static analysis built into Xcode (Cmd+Shift+B with analysis) | IDE-integrated |
|
|
968
|
+
|
|
969
|
+
### Dynamic Analysis
|
|
970
|
+
|
|
971
|
+
| Tool | Purpose | Requirement |
|
|
972
|
+
|------|---------|-------------|
|
|
973
|
+
| **MobSF** | Automated static + dynamic analysis of IPA files | Docker or local install |
|
|
974
|
+
| **Frida** | Dynamic instrumentation — hook functions, bypass checks, inspect runtime | Jailbroken device or Frida gadget injection |
|
|
975
|
+
| **Objection** | Runtime exploration powered by Frida — Keychain dump, SSL bypass, jailbreak bypass | Jailbroken device recommended |
|
|
976
|
+
| **Burp Suite** | HTTP/S traffic interception and manipulation | Proxy configuration |
|
|
977
|
+
| **Charles Proxy** | HTTPS debugging proxy with SSL proxying | Certificate trust on device |
|
|
978
|
+
|
|
979
|
+
### Apple-Provided Tools
|
|
980
|
+
|
|
981
|
+
| Tool | Purpose |
|
|
982
|
+
|------|---------|
|
|
983
|
+
| **Xcode Instruments** | Memory leak detection, performance profiling, network inspection |
|
|
984
|
+
| **App Attest** | Cryptographic device/app integrity verification |
|
|
985
|
+
| **DeviceCheck** | Per-device state flags (2 bits) persisted by Apple |
|
|
986
|
+
| **Xcode Organizer** | Crash reports, energy diagnostics, disk usage |
|
|
987
|
+
| **Managed App Configuration** | MDM-based secure configuration delivery |
|
|
988
|
+
|
|
989
|
+
### Recommended CI/CD Security Pipeline
|
|
990
|
+
|
|
991
|
+
```
|
|
992
|
+
Build → SwiftLint → Semgrep SAST → Snyk SCA → Unit Tests
|
|
993
|
+
→ Archive IPA → MobSF Static Scan → App Attest Integration Test
|
|
994
|
+
→ TestFlight / App Store Connect
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
## 7. Platform-Specific Guidance
|
|
1000
|
+
|
|
1001
|
+
### 7.1 Swift Security Patterns
|
|
1002
|
+
|
|
1003
|
+
**Value Types Over Reference Types:**
|
|
1004
|
+
Prefer structs for sensitive data models. Value types are stack-allocated
|
|
1005
|
+
(short-lived in memory) and copied on assignment, reducing the window for
|
|
1006
|
+
memory inspection attacks compared to heap-allocated class instances.
|
|
1007
|
+
|
|
1008
|
+
**Access Control:**
|
|
1009
|
+
Use `private` and `fileprivate` aggressively for security-sensitive
|
|
1010
|
+
properties. Mark security classes as `final` to prevent subclass overrides.
|
|
1011
|
+
|
|
1012
|
+
**Secure String Handling:**
|
|
1013
|
+
```swift
|
|
1014
|
+
// INSECURE: String persists in memory
|
|
1015
|
+
let password = "secret123"
|
|
1016
|
+
|
|
1017
|
+
// SECURE: Use Data, zero out after use
|
|
1018
|
+
var sensitiveData = "secret123".data(using: .utf8)!
|
|
1019
|
+
defer {
|
|
1020
|
+
sensitiveData.resetBytes(in: 0..<sensitiveData.count)
|
|
1021
|
+
}
|
|
1022
|
+
// Use sensitiveData for authentication, then it is zeroed
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
**Result Type for Security Operations:**
|
|
1026
|
+
```swift
|
|
1027
|
+
enum AuthResult {
|
|
1028
|
+
case authenticated(token: String)
|
|
1029
|
+
case biometricFailed(LAError)
|
|
1030
|
+
case keychainError(OSStatus)
|
|
1031
|
+
case jailbreakDetected
|
|
1032
|
+
}
|
|
1033
|
+
// Exhaustive switch forces handling all security-relevant outcomes
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
### 7.2 SwiftUI Security
|
|
1037
|
+
|
|
1038
|
+
**Redacted Content in App Switcher:**
|
|
1039
|
+
```swift
|
|
1040
|
+
struct ContentView: View {
|
|
1041
|
+
@Environment(\.scenePhase) var scenePhase
|
|
1042
|
+
@State private var isBlurred = false
|
|
1043
|
+
|
|
1044
|
+
var body: some View {
|
|
1045
|
+
SensitiveContentView()
|
|
1046
|
+
.blur(radius: isBlurred ? 30 : 0)
|
|
1047
|
+
.onChange(of: scenePhase) { _, newPhase in
|
|
1048
|
+
isBlurred = (newPhase != .active)
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
**Secure Text Input:**
|
|
1055
|
+
```swift
|
|
1056
|
+
SecureField("Password", text: $password)
|
|
1057
|
+
.textContentType(.password)
|
|
1058
|
+
.autocorrectionDisabled()
|
|
1059
|
+
.textInputAutocapitalization(.never)
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
**Preventing Screenshot Capture (iOS 17+):**
|
|
1063
|
+
```swift
|
|
1064
|
+
// Leverage UITextField.isSecureTextEntry behavior
|
|
1065
|
+
// by embedding a secure text field technique in SwiftUI
|
|
1066
|
+
struct ScreenshotProtectedView<Content: View>: UIViewRepresentable {
|
|
1067
|
+
let content: Content
|
|
1068
|
+
|
|
1069
|
+
func makeUIView(context: Context) -> UIView {
|
|
1070
|
+
let secureField = UITextField()
|
|
1071
|
+
secureField.isSecureTextEntry = true
|
|
1072
|
+
// The secure field's layer prevents screenshots
|
|
1073
|
+
let containerView = secureField.layer.sublayers?.first?.delegate
|
|
1074
|
+
as? UIView ?? UIView()
|
|
1075
|
+
return containerView
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
### 7.3 UIKit Security
|
|
1081
|
+
|
|
1082
|
+
**Secure View Controller Lifecycle:**
|
|
1083
|
+
```swift
|
|
1084
|
+
class SecureViewController: UIViewController {
|
|
1085
|
+
private let blurView = UIVisualEffectView(
|
|
1086
|
+
effect: UIBlurEffect(style: .systemUltraThinMaterial)
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
override func viewDidLoad() {
|
|
1090
|
+
super.viewDidLoad()
|
|
1091
|
+
NotificationCenter.default.addObserver(
|
|
1092
|
+
self,
|
|
1093
|
+
selector: #selector(willResignActive),
|
|
1094
|
+
name: UIApplication.willResignActiveNotification,
|
|
1095
|
+
object: nil
|
|
1096
|
+
)
|
|
1097
|
+
NotificationCenter.default.addObserver(
|
|
1098
|
+
self,
|
|
1099
|
+
selector: #selector(didBecomeActive),
|
|
1100
|
+
name: UIApplication.didBecomeActiveNotification,
|
|
1101
|
+
object: nil
|
|
1102
|
+
)
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
@objc private func willResignActive() {
|
|
1106
|
+
blurView.frame = view.bounds
|
|
1107
|
+
view.addSubview(blurView)
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
@objc private func didBecomeActive() {
|
|
1111
|
+
blurView.removeFromSuperview()
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
### 7.4 Xcode Build Settings for Security
|
|
1117
|
+
|
|
1118
|
+
| Setting | Value | Purpose |
|
|
1119
|
+
|---------|-------|---------|
|
|
1120
|
+
| `ENABLE_BITCODE` | `YES` (deprecated iOS 16+) | App thinning, additional optimization |
|
|
1121
|
+
| `GCC_GENERATE_DEBUGGING_SYMBOLS` | `NO` (Release) | Strip debug symbols |
|
|
1122
|
+
| `STRIP_INSTALLED_PRODUCT` | `YES` | Remove symbol tables from release binary |
|
|
1123
|
+
| `DEPLOYMENT_POSTPROCESSING` | `YES` | Enable post-processing including stripping |
|
|
1124
|
+
| `DEBUG_INFORMATION_FORMAT` | `dwarf` (Release) | No dSYM in final binary |
|
|
1125
|
+
| `GCC_PREPROCESSOR_DEFINITIONS` | `NDEBUG=1` (Release) | Disable assert() in release |
|
|
1126
|
+
| `SWIFT_OPTIMIZATION_LEVEL` | `-O` or `-Osize` | Optimized code, harder to reverse-engineer |
|
|
1127
|
+
| `ENABLE_TESTABILITY` | `NO` (Release) | Prevent internal access from test bundles |
|
|
1128
|
+
| `OTHER_LDFLAGS` | `-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null` | Mark binary as restricted (anti-DYLD_INSERT) |
|
|
1129
|
+
|
|
1130
|
+
### 7.5 App Store Review Security Requirements
|
|
1131
|
+
|
|
1132
|
+
Apple App Review enforces these security requirements:
|
|
1133
|
+
- **ATS compliance:** Justified exceptions only; blanket disabling rejected
|
|
1134
|
+
- **Privacy manifest:** Required since Spring 2024 for apps using tracking
|
|
1135
|
+
APIs, required reason APIs (UserDefaults timestamps, file attributes, etc.)
|
|
1136
|
+
- **Privacy nutrition labels:** Accurate data collection declarations
|
|
1137
|
+
- **Login with Apple:** Required if app offers third-party social login
|
|
1138
|
+
- **Minimum permissions:** Camera, location, contacts — must justify each
|
|
1139
|
+
- **Data deletion:** Apps collecting user data must offer account deletion
|
|
1140
|
+
- **Encryption declarations:** Export compliance documentation for custom crypto
|
|
1141
|
+
|
|
1142
|
+
---
|
|
1143
|
+
|
|
1144
|
+
## 8. Incident Patterns
|
|
1145
|
+
|
|
1146
|
+
### 8.1 Data Leak Detection
|
|
1147
|
+
|
|
1148
|
+
**Indicators of data exposure:**
|
|
1149
|
+
- Unexplained increase in account takeover attempts
|
|
1150
|
+
- User reports of personalized phishing targeting app-specific data
|
|
1151
|
+
- API traffic from unexpected geographic locations or device profiles
|
|
1152
|
+
- Anomalous Keychain access patterns in security logs
|
|
1153
|
+
- Backup files appearing on unauthorized services
|
|
1154
|
+
|
|
1155
|
+
**Investigation steps:**
|
|
1156
|
+
1. Enable App Attest to verify app integrity on API requests
|
|
1157
|
+
2. Check server logs for requests missing valid attestation assertions
|
|
1158
|
+
3. Audit Keychain accessibility classes in production builds
|
|
1159
|
+
4. Review Data Protection levels on all files in app sandbox
|
|
1160
|
+
5. Scan for sensitive data in device backups using iMazing or
|
|
1161
|
+
libimobiledevice tools
|
|
1162
|
+
6. Check if debug logging was inadvertently enabled in release
|
|
1163
|
+
|
|
1164
|
+
### 8.2 Unauthorized Access via IPC
|
|
1165
|
+
|
|
1166
|
+
**Attack scenario:** Malicious app registers the same URL scheme as the
|
|
1167
|
+
target app, intercepting OAuth callbacks containing authorization codes.
|
|
1168
|
+
|
|
1169
|
+
**Detection:**
|
|
1170
|
+
- Monitor for duplicate URL scheme registrations on user devices
|
|
1171
|
+
- Server-side correlation of authorization code redemption with expected
|
|
1172
|
+
redirect URIs (Universal Links eliminate this attack)
|
|
1173
|
+
- App Attest-based client identity verification
|
|
1174
|
+
|
|
1175
|
+
**Response:**
|
|
1176
|
+
1. Immediately migrate from custom URL schemes to Universal Links
|
|
1177
|
+
2. Invalidate all active sessions / tokens issued during compromise window
|
|
1178
|
+
3. Implement PKCE (which binds the authorization code to the requesting app)
|
|
1179
|
+
4. Deploy App Attest for API authentication
|
|
1180
|
+
5. Notify affected users and require password reset
|
|
1181
|
+
|
|
1182
|
+
### 8.3 Jailbreak-Based Exploitation Response
|
|
1183
|
+
|
|
1184
|
+
**Detection signals:**
|
|
1185
|
+
- Server receives requests from apps with tampered signatures
|
|
1186
|
+
- App Attest assertions fail validation
|
|
1187
|
+
- Unusual API call patterns (automated scraping, rate limit violations)
|
|
1188
|
+
|
|
1189
|
+
**Response:**
|
|
1190
|
+
1. Flag device via DeviceCheck (set fraud bit)
|
|
1191
|
+
2. Restrict access from flagged devices (read-only, degraded functionality)
|
|
1192
|
+
3. Require step-up authentication for sensitive operations
|
|
1193
|
+
4. Log and analyze the attack patterns for future detection rules
|
|
1194
|
+
5. Consider reporting to Apple if it involves App Store policy violations
|
|
1195
|
+
|
|
1196
|
+
---
|
|
1197
|
+
|
|
1198
|
+
## 9. Compliance & Standards
|
|
1199
|
+
|
|
1200
|
+
### 9.1 OWASP MASVS v2 (2023-2024)
|
|
1201
|
+
|
|
1202
|
+
The Mobile Application Security Verification Standard defines eight control
|
|
1203
|
+
categories. Key requirements for iOS:
|
|
1204
|
+
|
|
1205
|
+
| Category | Key Controls |
|
|
1206
|
+
|----------|--------------|
|
|
1207
|
+
| **MASVS-STORAGE** | No sensitive data in logs, backups, clipboard; Keychain for secrets; Data Protection on files |
|
|
1208
|
+
| **MASVS-CRYPTO** | No hardcoded keys; use platform crypto (CryptoKit, Security.framework); minimum AES-256 |
|
|
1209
|
+
| **MASVS-AUTH** | Biometric bound to Keychain; session management server-side; re-auth for sensitive ops |
|
|
1210
|
+
| **MASVS-NETWORK** | TLS 1.2+; certificate pinning; no cleartext HTTP; ATS enforced |
|
|
1211
|
+
| **MASVS-PLATFORM** | Universal Links over URL schemes; secure WebView; input validation on IPC |
|
|
1212
|
+
| **MASVS-CODE** | Dependency management; no debug code in release; stack canaries; binary integrity |
|
|
1213
|
+
| **MASVS-RESILIENCE** | Jailbreak detection; anti-debugging; obfuscation; App Attest |
|
|
1214
|
+
| **MASVS-PRIVACY** | Privacy manifest; minimal data collection; consent management; data deletion |
|
|
1215
|
+
|
|
1216
|
+
Source: https://mas.owasp.org/MASVS/
|
|
1217
|
+
|
|
1218
|
+
### 9.2 OWASP Mobile Top 10 (2024)
|
|
1219
|
+
|
|
1220
|
+
| # | Risk | iOS Relevance |
|
|
1221
|
+
|---|------|---------------|
|
|
1222
|
+
| M1 | Improper Credential Usage | Hardcoded keys, insecure Keychain accessibility |
|
|
1223
|
+
| M2 | Inadequate Supply Chain Security | Untrusted CocoaPods/SPM dependencies, XcodeGhost-type attacks |
|
|
1224
|
+
| M3 | Insecure Authentication/Authorization | Boolean-only biometric checks, missing server-side validation |
|
|
1225
|
+
| M4 | Insufficient Input/Output Validation | WebView JavaScript injection, URL scheme parameter injection |
|
|
1226
|
+
| M5 | Insecure Communication | Missing ATS, no certificate pinning, cleartext fallback |
|
|
1227
|
+
| M6 | Inadequate Privacy Controls | Excessive permissions, missing privacy manifest, tracking without consent |
|
|
1228
|
+
| M7 | Insufficient Binary Protections | No obfuscation, debug symbols in release, no integrity checks |
|
|
1229
|
+
| M8 | Security Misconfiguration | ATS exceptions, permissive entitlements, debug flags |
|
|
1230
|
+
| M9 | Insecure Data Storage | UserDefaults for secrets, unprotected SQLite, background screenshots |
|
|
1231
|
+
| M10 | Insufficient Cryptography | Custom crypto implementations, weak algorithms, hardcoded IVs |
|
|
1232
|
+
|
|
1233
|
+
Source: https://owasp.org/www-project-mobile-top-10/
|
|
1234
|
+
|
|
1235
|
+
### 9.3 Apple Platform Security Guide
|
|
1236
|
+
|
|
1237
|
+
Apple publishes the Apple Platform Security guide (updated January 2026),
|
|
1238
|
+
covering:
|
|
1239
|
+
- Hardware security (Secure Enclave, Secure Boot, hardware key storage)
|
|
1240
|
+
- System security (kernel integrity protection, signed system volume)
|
|
1241
|
+
- Data protection (per-file encryption, Keychain architecture)
|
|
1242
|
+
- App security (code signing, sandboxing, entitlements)
|
|
1243
|
+
- Network security (ATS, VPN, Wi-Fi security)
|
|
1244
|
+
- Biometric security (Face ID / Touch ID architecture)
|
|
1245
|
+
|
|
1246
|
+
Source: https://support.apple.com/guide/security/welcome/web
|
|
1247
|
+
|
|
1248
|
+
### 9.4 Regulatory Compliance
|
|
1249
|
+
|
|
1250
|
+
| Regulation | iOS Requirements |
|
|
1251
|
+
|-----------|------------------|
|
|
1252
|
+
| **GDPR** | Privacy manifest, data minimization, right to deletion, consent before tracking |
|
|
1253
|
+
| **CCPA/CPRA** | Opt-out of data sale, data access/deletion API, privacy policy |
|
|
1254
|
+
| **HIPAA** | NSFileProtectionComplete, encrypted Keychain, audit logging, BAA with Apple |
|
|
1255
|
+
| **PCI DSS** | No card data in logs/screenshots, encrypted storage, certificate pinning |
|
|
1256
|
+
| **SOC 2** | Access controls, encryption at rest and in transit, incident response |
|
|
1257
|
+
|
|
1258
|
+
---
|
|
1259
|
+
|
|
1260
|
+
## 10. Insecure vs. Secure Code Examples
|
|
1261
|
+
|
|
1262
|
+
### Example 1: Token Storage
|
|
1263
|
+
|
|
1264
|
+
```swift
|
|
1265
|
+
// INSECURE — token in UserDefaults (plaintext plist)
|
|
1266
|
+
UserDefaults.standard.set(authToken, forKey: "auth_token")
|
|
1267
|
+
let token = UserDefaults.standard.string(forKey: "auth_token")
|
|
1268
|
+
|
|
1269
|
+
// SECURE — token in Keychain with strong protection
|
|
1270
|
+
try KeychainManager.save(
|
|
1271
|
+
key: "auth_token",
|
|
1272
|
+
data: authToken.data(using: .utf8)!,
|
|
1273
|
+
accessibility: kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
|
|
1274
|
+
)
|
|
1275
|
+
let tokenData = try KeychainManager.retrieve(key: "auth_token")
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
### Example 2: Network Request
|
|
1279
|
+
|
|
1280
|
+
```swift
|
|
1281
|
+
// INSECURE — HTTP, no pinning, ATS disabled
|
|
1282
|
+
// Info.plist: NSAllowsArbitraryLoads = YES
|
|
1283
|
+
let url = URL(string: "http://api.example.com/login")!
|
|
1284
|
+
URLSession.shared.dataTask(with: url) { data, _, _ in
|
|
1285
|
+
// Process response
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// SECURE — HTTPS, pinned session, ATS enforced
|
|
1289
|
+
let url = URL(string: "https://api.example.com/login")!
|
|
1290
|
+
let session = URLSession(
|
|
1291
|
+
configuration: .default,
|
|
1292
|
+
delegate: PinnedSessionDelegate(),
|
|
1293
|
+
delegateQueue: nil
|
|
1294
|
+
)
|
|
1295
|
+
session.dataTask(with: url) { data, response, error in
|
|
1296
|
+
guard let httpResponse = response as? HTTPURLResponse,
|
|
1297
|
+
(200...299).contains(httpResponse.statusCode)
|
|
1298
|
+
else { return }
|
|
1299
|
+
// Process response
|
|
1300
|
+
}
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
### Example 3: Biometric Authentication
|
|
1304
|
+
|
|
1305
|
+
```swift
|
|
1306
|
+
// INSECURE — boolean-only check, bypassable via Frida
|
|
1307
|
+
let context = LAContext()
|
|
1308
|
+
context.evaluatePolicy(
|
|
1309
|
+
.deviceOwnerAuthenticationWithBiometrics,
|
|
1310
|
+
localizedReason: "Authenticate"
|
|
1311
|
+
) { success, error in
|
|
1312
|
+
if success {
|
|
1313
|
+
self.grantAccess() // Attacker hooks this to always execute
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// SECURE — biometric gates Keychain key release via Secure Enclave
|
|
1318
|
+
do {
|
|
1319
|
+
let tokenData = try retrieveBiometricProtectedToken(
|
|
1320
|
+
account: "session_token"
|
|
1321
|
+
)
|
|
1322
|
+
// Token only available after successful biometric verification
|
|
1323
|
+
// by the Secure Enclave — cannot be bypassed by hooking
|
|
1324
|
+
grantAccess(with: tokenData)
|
|
1325
|
+
} catch {
|
|
1326
|
+
handleAuthenticationFailure(error)
|
|
1327
|
+
}
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
### Example 4: Logging
|
|
1331
|
+
|
|
1332
|
+
```swift
|
|
1333
|
+
// INSECURE — sensitive data in production logs
|
|
1334
|
+
NSLog("User login: email=\(email), token=\(authToken)")
|
|
1335
|
+
print("API Response: \(responseBody)")
|
|
1336
|
+
|
|
1337
|
+
// SECURE — private redaction, debug-only logging
|
|
1338
|
+
import os.log
|
|
1339
|
+
|
|
1340
|
+
private let logger = Logger(
|
|
1341
|
+
subsystem: Bundle.main.bundleIdentifier!,
|
|
1342
|
+
category: "auth"
|
|
1343
|
+
)
|
|
1344
|
+
|
|
1345
|
+
logger.info("User login: email=\(email, privacy: .private)")
|
|
1346
|
+
#if DEBUG
|
|
1347
|
+
logger.debug("API Response: \(responseBody, privacy: .private)")
|
|
1348
|
+
#endif
|
|
1349
|
+
```
|
|
1350
|
+
|
|
1351
|
+
### Example 5: Deep Linking
|
|
1352
|
+
|
|
1353
|
+
```swift
|
|
1354
|
+
// INSECURE — custom URL scheme with sensitive data
|
|
1355
|
+
// myapp://auth?token=eyJhbGciOiJIUzI1NiIs...
|
|
1356
|
+
func application(
|
|
1357
|
+
_ app: UIApplication,
|
|
1358
|
+
open url: URL,
|
|
1359
|
+
options: [UIApplication.OpenURLOptionsKey: Any]
|
|
1360
|
+
) -> Bool {
|
|
1361
|
+
let token = url.queryParameters["token"] // Token exposed in URL
|
|
1362
|
+
authenticate(with: token)
|
|
1363
|
+
return true
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// SECURE — Universal Links with server-side validation
|
|
1367
|
+
// https://example.com/auth/callback?code=TEMP_CODE
|
|
1368
|
+
func application(
|
|
1369
|
+
_ application: UIApplication,
|
|
1370
|
+
continue userActivity: NSUserActivity,
|
|
1371
|
+
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
|
1372
|
+
) -> Bool {
|
|
1373
|
+
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
|
|
1374
|
+
let url = userActivity.webpageURL,
|
|
1375
|
+
url.host == "example.com"
|
|
1376
|
+
else { return false }
|
|
1377
|
+
|
|
1378
|
+
// Exchange temporary code for token server-side (PKCE)
|
|
1379
|
+
let code = url.queryParameters["code"]
|
|
1380
|
+
exchangeCodeForToken(code, codeVerifier: storedCodeVerifier)
|
|
1381
|
+
return true
|
|
1382
|
+
}
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1385
|
+
---
|
|
1386
|
+
|
|
1387
|
+
## References
|
|
1388
|
+
|
|
1389
|
+
- OWASP Mobile Top 10 (2024): https://owasp.org/www-project-mobile-top-10/
|
|
1390
|
+
- OWASP MASVS v2: https://mas.owasp.org/MASVS/
|
|
1391
|
+
- OWASP MASTG: https://mas.owasp.org/MASTG/
|
|
1392
|
+
- Apple Platform Security Guide: https://support.apple.com/guide/security/welcome/web
|
|
1393
|
+
- Apple Developer — App Attest: https://developer.apple.com/documentation/devicecheck
|
|
1394
|
+
- Apple Developer — Keychain Services: https://developer.apple.com/documentation/security/keychain_services
|
|
1395
|
+
- Apple Developer — LocalAuthentication: https://developer.apple.com/documentation/localauthentication
|
|
1396
|
+
- Apple Developer — Certificate Pinning: https://developer.apple.com/news/?id=g9ejcf8y
|
|
1397
|
+
- Google Project Zero — NSO Zero-Click: https://projectzero.google/2021/12/a-deep-dive-into-nso-zero-click.html
|
|
1398
|
+
- Citizen Lab — BLASTPASS: https://citizenlab.ca/2023/09/blastpass-nso-group-iphone-zero-click-zero-day-exploit-captured-in-the-wild/
|
|
1399
|
+
- NowSecure — Keychain Best Practices: https://books.nowsecure.com/secure-mobile-development/en/ios/use-the-keychain-carefully.html
|
|
1400
|
+
- HackTricks — iOS Pentesting: https://book.hacktricks.xyz/mobile-pentesting/ios-pentesting
|
|
1401
|
+
- MobSF: https://github.com/MobSF/Mobile-Security-Framework-MobSF
|