Claude Skill

audit-xcode-security-settings

Audit and enable security-oriented Xcode build settings. Progressively enables compiler warnings, static analyzer checkers, and Enhanced Security features. Use when: user wants to secure their Xcode project, audit security settings, enable hardening, review security posture of bu

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download artemnovichkov-xcode-skills-skills_audit-xcode-security-settings-aa5c1cb.zip · 54 KB
Part of artemnovichkov/xcode-skills — 15 skills

Install

skills CLI npx skills add https://github.com/artemnovichkov/xcode-skills/tree/main/skills/audit-xcode-security-settings
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install artemnovichkov-xcode-skills@llmmart
Git git clone https://github.com/artemnovichkov/xcode-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole artemnovichkov/xcode-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Audit Xcode Security Settings

Assess an Xcode project's security posture and progressively enable security build settings and entitlements — from broadly applicable warnings through Enhanced Security hardening.

Tool Preferences

When XcodeGlob, XcodeGrep, XcodeRead, XcodeLS, and XcodeUpdate tools are available, ALWAYS use them. Do not fall back to Bash filesystem tools (ls, find, cat, grep) to learn about the project. They trigger extra permission prompts and bypass project scoping.

Tool names may carry an MCP server prefix. These tools are hosted by an MCP server whose name varies by environment (xcode-mcp, xcode-tools, xcode, etc.), so their fully qualified names look like mcp__<server>__XcodeGlob. Some harnesses register short aliases (just XcodeGlob); others only expose the prefixed form. Do not hardcode a specific server name. On the first call, use whichever form the available-tool registry advertises — look up the prefix once, then reuse it for the rest of the session. If a short-name call fails with an unknown-tool error, do not guess at the prefix: look it up in the registry and retry with the full name.

  • XcodeGlob for file discovery — find is forbidden for files inside the project.
  • XcodeGrep for content search — grep/rg is forbidden for files inside the project.
  • XcodeRead for file contents — cat/Read is forbidden for files registered in the project.
  • XcodeLS for directory listing — ls is forbidden for any path inside the project.
  • XcodeUpdate for in-place edits of project-registered text files (xcconfig files, source files) — same filePath / oldString / newString (+ optional replaceAll) signature as the built-in Edit tool, but accepts Xcode workspace-relative paths. Edit is forbidden for files registered in the project. Do not use XcodeUpdate / Edit / plutil to add or update .entitlements keys — use AddEntitlement.
  • AddEntitlement for adding or updating a target's entitlements — pass targetName, entitlementKey, entitlementValueType (bool / string / int / stringArray / dictionary), and the value. Always prefer it for entitlement changes; it adds or updates only and cannot remove keys.
  • XcodeListTargets for enumerating targets — do not parse project.pbxproj manually. Returns each target's PRODUCT_TYPE_IDENTIFIER and role flags (IS_AGGREGATE, IS_TEST_TARGET, IS_APP_EXTENSION, SUPPORTS_HOSTING_TESTS) directly.

Project root and name are already in the system prompt context. Do NOT run ls to "verify" the project layout before starting. The system prompt already tells you the working directory and the project structure.

Empty XcodeGlob results are not a failure. The .xcodeproj and .xcworkspace are not indexed as files inside the Xcode workspace — XcodeGlob "**/*.xcodeproj" correctly returns 0 matches. Use the project name from system-prompt context instead. Do not fall back to filesystem ls/find.

All Xcode* tools take Xcode workspace-relative paths. XcodeGlob, XcodeGrep, XcodeRead, XcodeLS, XcodeUpdate, XcodeWrite, and XcodeRM interpret their path arguments — and return paths — relative to the Xcode workspace root (what you see at the top of the Project Navigator). Not the git repository root; not the .xcodeproj bundle. Anything the user sees in Xcode (entitlements, xcconfig, plan and decision documents, source files) is reachable via its workspace-relative path; pass that path through these tools as-is, and don't construct absolute filesystem paths for it.

To read or edit a specific file:

  • Prefer XcodeRead / XcodeUpdate with the workspace-relative path. XcodeRead reads .entitlements plists too — they're project-registered files, navigable just like any source file — so read them this way. To add or update an entitlement, use AddEntitlement, not XcodeUpdate.

For entitlements files, never derive the path by hand. Each target's authoritative entitlements path is the evaluated value of its CODE_SIGN_ENTITLEMENTS build setting — get it from GetTargetBuildSettings and use it as-is. Do not parse project.pbxproj to reconstruct the path, and do not glob **/*.entitlements: orphaned .entitlements files may exist on disk that aren't referenced by any target. One entitlements file can be referenced by multiple targets.

Fall back to Bash only for operations the Xcode tools cannot do (e.g., git operations).

Bundled Reference Documents

All reference material lives under references/ next to this file.

  • references/security-settings-reference.md — the canonical list of security build settings and entitlements this skill tracks, with hardened values, CLI flags, and language scope.
  • references/reading-build-settings.md — GetTargetBuildSettings schema, the filter script recipe, the audit-table construction, and the "already hardened" / "deliberately disabled" predicates.
  • references/enhanced-security.md — the Enhanced Security capability: build settings, entitlements, supported product types.
  • references/pointer-authentication.md — arm64e pointer signing: supported platforms, consumer-side compatibility notes.
  • references/universal-binaries-for-libraries.md — universal-binary guidance for library/framework targets (pointer authentication adds the arm64e slice automatically), qualifying product types, XCFramework guidance.
  • references/security-compiler-warnings.md — the security-focused compiler warnings and settings enabled by Enhanced Security.
  • references/cpp-hardening.md — C++ stdlib hardening (CLANG_CXX_STANDARD_LIBRARY_HARDENING) and bounds-safe buffers (ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS).
  • references/typed-allocators.md — type-aware allocator support and the hardened-heap sub-option.
  • references/stack-zero-init.md — automatic stack-variable zero-initialization at runtime.
  • references/readonly-platform-memory.md — read-only protection of dyld state.
  • references/runtime-restrictions.md — dylib and Mach-message platform restrictions.
  • references/hardware-memory-tagging.md — MTE entitlements and supported hardware.
  • references/checked-pointer-arithmetic.md — Checked Pointer Arithmetic (CPA2).
  • references/additional-settings.md — opt-in diagnostic settings beyond the defaults (may have more false positives).
  • references/adoption-strategy.md — recommended ordering for validating Enhanced Security features (lowest-risk to highest-effort).
  • references/decision-document.md — how to maintain the persistent xcode-security-settings.md decision document.

The skill ships one helper script:

  • scripts/filter_build_settings.py — filters GetTargetBuildSettings JSON to the macros tracked in security-settings-reference.md. See references/reading-build-settings.md for usage.

Common Failure Modes

Symptom Cause Correct Response
Tool call fails with "unknown tool" / "tool not found" for XcodeGlob etc. The harness registers these tools only under their full MCP-prefixed name (mcp__<server>__XcodeGlob) in this environment Look up the prefix in the available-tool registry, retry once with the full name, then use the full name for the rest of the session.
XcodeGlob "**/*.xcodeproj" returns 0 matches The .xcodeproj itself isn't a project-indexed file Use the project name from system context; do not fall back to find or ls
XcodeRead <workspace-relative-path> fails for a file truly inside the .xcodeproj / .xcworkspace bundle (e.g. WorkspaceSettings.xcsettings) That file isn't a project-navigator member Translate to filesystem absolute path using the project root from system context, then use Read / Edit. (Does not apply to .entitlements files — those are navigable.)
Read on an entitlements path you derived by hand returns File does not exist The path was reconstructed from project.pbxproj group nesting or guessed by globbing **/*.entitlements. Xcode's authoritative path for a target's entitlements is the evaluated value of CODE_SIGN_ENTITLEMENTS, not whatever the navigator shows. Look up CODE_SIGN_ENTITLEMENTS for the target via GetTargetBuildSettings (or read it from the audit table) and use its evaluated value as the path.

Workflow

Phase 1: Briefing

Before doing any work, tell the user — in two or three sentences — what this skill is, what it will do, and roughly how much of their time and attention to expect:

  • What it is. An audit of the project's Xcode security build settings and entitlements (compiler warnings, Enhanced Security entitlements, pointer authentication, checked pointer arithmetic, universal binaries for libraries, etc.).
  • What happens. The skill runs in two parts of roughly equal length. First, planning: I analyze the project and write an editable plan file at the project root for you to review. Then, execution: once you pick Run, I apply only the changes you approved. Nothing is modified until you pick Run.
  • Time commitment. Planning is a few minutes of my analysis (longer on projects with many targets — I'll narrate progress) plus your review of the plan file, which can be quick or thorough — your call. Execution takes about as long: applying the approved changes, with two things that can pause for your input — the inquiry step (if there are deliberately-disabled settings whose rationale isn't documented), and a final yes/no on whether to keep the plan file in your project as a record. This all usually takes about 15-30 minutes, split roughly evenly between the two parts, depending on the number of build targets and how long it takes for you to review and approve the plan.

Keep it tight — the user already invoked the skill knowing they wanted an audit. The briefing exists so they have realistic expectations.

Then check for source control. The project has source control if either:

  • The Environment block's Is a git repository field is true, or
  • A single filesystem check at the project root finds any of .git, .hg, .svn, .bzr, .fslckout, _FOSSIL_, CVS.

Otherwise the project has no source control. Record this state — Phase 4 Step 3 uses it to decide whether to include the ⚠️ blockquote in the plan file.

After delivering the briefing, pause via AskUserQuestion. If the project has source control:

  • Begin audit — proceed to Phase 2.
  • Cancel — exit with "Cancelled — no changes applied."

If the project has no source control, tell the user first: "It is strongly recommended setting up source control before continuing. This skill modifies build settings and entitlements; without something like Git, rollback requires manual undo and you won't have a clean way to review the differences. Xcode has built-in support for Source control management" Then ask:

  • Set up source control first (Recommended) — exit with "Set up source control and re-run the skill."
  • Proceed without source control — proceed to Phase 2; Phase 4 Step 3 will surface the no-source-control reminder again in the plan file.
  • Cancel — exit with "Cancelled — no changes applied."

The pause exists so the briefing stays on screen long enough to read; Discovery and Analysis output would otherwise scroll it away. Failing early when there's no source control avoids spending minutes on discovery and analysis only for the user to bail at plan-approval time.

Phase 2: Discovery

Read the Environment block in the system prompt. Relevant fields:

  • Primary working directory — the project root (the project name is the basename).
  • Is a git repository — whether the project is git-tracked (used by the source-control check in Phase 1).

Track Progress

Every per-target / per-setting action that needs to happen must have its own task for transparency.

  • Phase 1 (Briefing) is one task that completes when the user picks Begin audit / Cancel.
  • Phase 3 creates one task per target (Audit <target>); the task closes once Phase 3 has produced both the per-target audit-table rows and (for supported product types) the Enhanced-Security category for that target. Phase 3 stores all per-target state in the task's description field (see Phase 3 Step 4 for the format) so later phases can read it back via TaskGet. Phases 4–7 read these task descriptions.
  • Phase 4 (Plan & Approve) is one task that completes when the user picks Run/Cancel.
  • On Run, Phase 4 step 5 parses the plan and creates fine-grained tasks. For each apply task it embeds that target's delta (extracted from the corresponding Audit <target> task's description) into the apply task's own description so Phase 5 doesn't have to look it up again.
    • For each Enhanced Security sub-item that's checked:
      • Enable Enhanced Security: Enable Enhanced Security at project level (one task). On pbxproj-only projects, this task encapsulates the guide-and-verify flow described in Phase 5 Step 1a.
      • Update entitlements: one Apply Enhanced Security entitlements to <target> per target needing changes.
      • Hardware memory tagging: Apply Hardware Memory Tagging (one task; walks supported targets internally).
      • Checked pointer arithmetic: Apply Checked Pointer Arithmetic (one task; walks supported targets internally).
    • For each Warnings sub-item that's checked:
      • Apply Compiler Warnings if that sub-item is checked.
      • Apply Static Analyzer Warnings if that sub-item is checked.
      • Apply Clang-Tidy Warnings if that sub-item is checked.
    • Apply Additional Diagnostic Settings if checked.
    • Emit Bounds Safety Adoption guidance if checked.
    • One Inquire about <MACRO> on <target> per Phase-6 candidate (only if "Inquire about disabled settings" is checked).
    • Report and update decision document.
    • Prompt to remove plan file — always last; also fires on error paths.

When entering each phase or sub-step:

  • Print one line naming the phase or sub-step in plain English — never the phase number. Use the phase's name (e.g., "▶ Briefing", "▶ Analyzing project", "▶ Plan & Approve", "▶ Applying settings"); for sub-steps, name what's being done (e.g., "▶ Detecting languages", "▶ Building the audit table").
  • Update the task to in_progress.

When finishing each phase or sub-step:

  • Print one line: "✓
  • Update the task to completed.

Apply steps may record what they did in their own task's description before completing it, one line per target. Phase 7 reads those lines instead of re-deriving state or scraping earlier output.

Phase 3: Analyze Project and Settings

No user interaction. Gather facts in the background.

Step 1: Locate the existing decision document

XcodeGlob '**/xcode-security-settings.md'. If found, XcodeRead it and extract languages + prior setting decisions with their statuses and rationale. This informs subsequent phases.

Step 2: Detect languages

One XcodeGlob per language. Empty result is not a failure — record the language as absent.

  • **/*.c → C
  • **/*.cpp, **/*.cxx, **/*.cc → C++
  • **/*.m → Objective-C
  • **/*.mm → Objective-C++
  • **/*.swift → Swift

Objective-C++ implies C++ is present. .mm files contain C++ source, so any audit gated on "C++ present" (C++ stdlib hardening, bounds-safe-buffers guidance, CLANG_ANALYZER_OSOBJECT_C_STYLE_CAST, etc.) must fire when Objective-C++ is detected, even when no .cpp/.cxx/.cc files exist.

Filename extension is not authoritative. An Xcode project can override a file's compiled language via explicitFileType / lastKnownFileType in project.pbxproj — most commonly a .m file marked sourcecode.cpp.objcpp (compiled as Objective-C++), or a .h marked sourcecode.c.h / sourcecode.cpp.h. To catch these overrides, grep -E 'sourcecode\.cpp\.[a-zA-Z0-9]+' <project-root>/<ProjectName>.xcodeproj/project.pbxproj via Bash. project.pbxproj is Xcode's project description file inside the .xcodeproj bundle; read it directly. Treat any sourcecode.cpp.objcpp match as both Objective-C++ and C++; treat any other sourcecode.cpp.* match as C++.

Step 3: Build the audit table

See references/reading-build-settings.md for column definitions, the construction recipe, and the canonical predicates ("already hardened", "at default OFF", "deliberately disabled"). At a glance:

  1. Call XcodeListTargets to enumerate targets. Skip entries with IS_AGGREGATE = true (they have no product type). Record TARGET_NAME, CONTAINING_PROJECT, and PRODUCT_TYPE_IDENTIFIER for each remaining target — Step 4 categorizes targets by PRODUCT_TYPE_IDENTIFIER directly (no inference).
  2. For each target: TaskCreate "Audit <target>", set in_progress. Call GetTargetBuildSettings, run scripts/filter_build_settings.py over the resulting JSON, and record evaluatedValue and setAtTargetLevel (yes if targetValue is present in the JSON) per tracked macro. Hold these rows ready to write into the task's description in Step 4 (along with the category). Leave the task in_progress — Step 4 closes it.
  3. Scan for explicit settings in two passes with the filter regex: XcodeGrep over *.xcconfig, and grep -nE '<filter regex>' <project-root>/<ProjectName>.xcodeproj/project.pbxproj via Bash. project.pbxproj is Xcode's project description file inside the .xcodeproj bundle; read it directly. Record per-macro numMatchesInXCConfigs, numMatchesInPbxproj, and the file:line citations.
  4. The audit table is the joined view: one row per (target, tracked macro). Phases 4, 5, and 6 all consume this table; nothing else is re-fetched.

This step scales with target count: each GetTargetBuildSettings call takes several seconds, and there is one per target. On projects with roughly ten or more targets it can take a few minutes.

Step 4: Per-target Enhanced-Security state

Route each target into one of three categories by the PRODUCT_TYPE_IDENTIFIER recorded in Step 3:

  • Entitlements-supported — product type is in the "Supported Product Types" list of references/enhanced-security.md (applications, XPC services, system extensions, driver extensions [build settings only], tools). Read the entitlements plist at the path stored in this target's CODE_SIGN_ENTITLEMENTS build setting and classify the target as Up-to-date, Partial, Off, or No-entitlements-file. Multiple targets can share the same CODE_SIGN_ENTITLEMENTS path; classify each target independently.
  • Library/framework — product type is in the qualifying set listed in references/universal-binaries-for-libraries.md (frameworks, static frameworks, static libraries, dynamic libraries). No entitlements read. Phase 5 will check the universal-binary configuration for these.
  • Skipped — anything else (test bundles, app extensions, etc.).

Now write everything Phase 3 has learned about this target into the Audit <target> task's description via TaskUpdate, then set it completed. The description holds the entire per-target state Phases 4–6 need to consult later. Format:

Category: <category> [/ <sub-state>]      # e.g. "Entitlements-supported / Partial", "Library/framework", "Skipped"
Entitlements path: <evaluated CODE_SIGN_ENTITLEMENTS>     # omit for Library/framework and Skipped
SDKROOT: <value>
SUPPORTED_PLATFORMS: <value>
Missing entitlements: <comma-separated short names>      # Entitlements-supported only; required and default-ON keys the target lacks; omit if empty
Checked pointer arithmetic: <eligible-entitlement | eligible-slice-only | enabled | not-eligible: <reason>>   # Entitlements-supported and Library/framework targets
Deliberately-disabled: <MACRO>=<value> (<source>[+<source>...]), ...   # one per disabled row; sources ⊆ {target-level, xcconfig, pbxproj} joined with '+' when more than one applies; omit the line entirely if none

Audit table:
  <MACRO>=<value> setAtTargetLevel=<yes|no> numMatchesInXCConfigs=<n> numMatchesInPbxproj=<n> matchLocations=<citations>
  ...

The Category line is first so any client that surfaces a snippet shows something meaningful. The Audit-table block is the per-(target, tracked macro) rows from Step 3 in key=value form — one line per tracked macro, using the canonical column names defined in references/reading-build-settings.md. matchLocations carries the file:line citations in the same <source>:<file>:<line>[,<line>...] format used throughout. Skipped targets get this Category line, the platform fields, and the Audit-table block. Library/framework targets get those three plus the Checked pointer arithmetic: line. Both complete immediately (no entitlements read).

Checked pointer arithmetic is the single source of truth for this feature. Compute it once, here, and record one of four values. Every later phase reads this line and applies no test of its own.

  • enabled — nothing to do for this target. For an Entitlements-supported target: the entitlements file carries com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow and the evaluated ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE is YES. For a Library/framework target: the evaluated ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE is YES — there is no entitlement to check.
  • not-eligible: <reason> — one of: platform, when SUPPORTED_PLATFORMS / SDKROOT matches neither iphoneos nor watchos; opted out, when ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE is deliberately disabled for the target; no arm64e, when ENABLE_POINTER_AUTHENTICATION is deliberately disabled; or outside the capability, when ENABLE_ENHANCED_SECURITY is deliberately disabled. A macro that is merely at default OFF is not a reason — enabling Enhanced Security lifts it. outside the capability applies to Entitlements-supported targets only: the capability supplies the entitlement, and a library takes none.
  • eligible-entitlement — an Entitlements-supported target that can take checked pointer arithmetic and is not yet fully configured for it: it is missing the arm64e.x1 slice, the checked-pointer-arithmetic entitlement, or both. Step 4 applies whichever is missing.
  • eligible-slice-only — a Library/framework target that can take checked pointer arithmetic and does not have the build setting. There is no entitlement half for these targets: entitlements are granted per process from the main executable, so the library builds the slice and the consuming app's entitlement is what enforces the checks. Step 4 applies the build setting only.

The key is never listed under Missing entitlements, which stays required and default-ON keys only, so it cannot make a target Partial and cannot reach Step 1b.

On large projects this iterates over many .entitlements plists — if Step 3 took noticeable time, this one will too.

Phase 4: Plan & Approve

This phase produces a tailored, editable plan file that the user reviews before any changes happen. Once approved, Phases 5–7 run end-to-end with no further prompts.

Step 1: Source-control state

Source control was checked in Phase 1, and the user already accepted any no-source-control state at that point. Phase 4 Step 3 uses the recorded state to decide whether to include the ⚠️ blockquote in the plan file.

Step 2: Skip if everything is already configured

TaskList the Audit <target> tasks and TaskGet each. Early-exit if all default-checked plan items are already at their target state:

  • Every Enhanced-Security category (from each task's Category: line) is Up-to-date or Skipped.
  • No task's Checked pointer arithmetic: line reads eligible-entitlement or eligible-slice-only.
  • Every relevant Warnings setting (compiler, static analyzer, and clang-tidy) is already hardened on every applicable target (per each task's Audit-table block).
  • No task's Deliberately-disabled: line yields a row (after the Phase-6 exclusions below).

Optional follow-ups (Additional diagnostic settings, Bounds safety adoption) do not block early-exit. Report "Everything in scope is already configured" and exit; do not write a plan file.

Step 3: Write the plan file

Create xcode-security-audit-plan.md at the root of the Xcode workspace via XcodeWrite (path: xcode-security-audit-plan.md, no parent group). XcodeWrite both writes the file to disk under <project-root>/ and registers it in the project so the user can open it directly from Xcode's Project Navigator.

Include only items that apply to the project (see omission rules below). Use this template — substitute the placeholders in <…>:

# Xcode Security Audit — Plan
**Project:** <name> · <N> targets · languages: <list>
**Generated:** <YYYY-MM-DD>
> ⚠️ **No source control detected.** This skill modifies build settings and entitlements.
> Without source control (e.g., Git), rollback requires manual undo. Consider [setting up source control](doc://com.apple.documentation/documentation/xcode/configuring-your-xcode-project-to-use-source-control) before picking **Run**.
Edit the items below — set what steps to perform now, or leave them unchecked to defer them. Questions about any item, or want a more detailed plan? Just ask — I'll answer, and can expand this plan on the points you care about before you decide.
## Phases
- **Enhanced Security** — the project's runtime-protection bundle. Apply to: <target list>. (Group — check the sub-items below.)
  - [x] **[Enable Enhanced Security](doc://com.apple.documentation/documentation/Xcode/enabling-enhanced-security-for-your-app)** — sets `ENABLE_ENHANCED_SECURITY=YES` at the project level. (Your project doesn't use a project-level xcconfig — I'll walk you through enabling it in Xcode's Build Settings UI yourself, then verify by reading project file.)
  - [x] **[Update entitlements](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process)** — adds the hardened-process entitlement family per target (Memory Safety, Runtime Protections).
  - [x] **[Hardware memory tagging](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations)** — adds the hardware memory tagging entitlement, in soft mode, on supported platforms (<target list filtered to MTE-supported platforms>).
  - [x] **[Checked pointer arithmetic](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow)** — adds the arm64e.x1 slice and the entitlement to enforce pointer-arithmetic overflow checking (<target list filtered to arm64e.x1-supported platforms>). Run time enforcement requires hardware memory tagging enabled. Latent pointer-arithmetic bugs will terminate the app on capable hardware.
- **[Warnings](doc://com.apple.documentation/documentation/Xcode/build-settings-reference)** — additional diagnostics on all C/C++/ObjC targets. (Group — check the sub-items below.)
  - [x] **Compiler warnings** — <N> settings promoting security-relevant compiler diagnostics (fire on every build).
  - [x] **Static analyzer warnings** — <N> security checkers (run during Build and analyze).
  - [x] **Clang-tidy warnings** — <N> clang-tidy-integrated checks (run during Build and analyze).
- [x] **Inquire about disabled settings** — <M> found (e.g., `<setting>=NO` on `<target>`). May trigger follow-up questions if no rationale is documented.
- [ ] **Additional diagnostic settings** — extra opt-in warnings/checkers beyond the defaults. Off by default: they surface more findings to review and can be noisier (more false positives).
- [ ] **[Bounds safety adoption](https://clang.llvm.org/docs/BoundsSafetyAdoptionGuide.html)** — pointer to a separate skill. No changes applied here.
## Decision document
The skill creates or updates `xcode-security-settings.md` to record every setting decision (kept, deferred, disabled, with rationale). Edit the path to relocate.
- Path: `xcode-security-settings.md`

Include the ⚠️ blockquote only when the project has no source control; omit it otherwise.

Include the trailing parenthetical on the Enable Enhanced Security sub-item only when the project is pbxproj-only (no *.xcconfig files surfaced by Phase 3's project-wide scan); omit it otherwise.

The decision document should live in the same directory as the rest of the documentation, or at the project level.

Item omission rules

A plan item is omitted entirely when it doesn't apply:

  • Enhanced Security — omit (along with all sub-items) only if every supported-product-type category from Phase 3 step 4 is Up-to-date or Skipped, and no task's Checked pointer arithmetic: line reads eligible-entitlement or eligible-slice-only. Enhanced Security must be enabled otherwise.
  • Enable Enhanced Security (sub-item) — never omitted when Enhanced Security is shown; the trailing pbxproj-only parenthetical is the only conditional part.
  • Update entitlements (sub-item) — never omitted when Enhanced Security is shown.
  • Hardware memory tagging (sub-item) — omit if no target's SUPPORTED_PLATFORMS / SDKROOT matches macosx, iphoneos, iphonesimulator, watchos, xros, or xrsimulator.
  • Checked pointer arithmetic (sub-item) — omit if no task's Checked pointer arithmetic: line reads eligible-entitlement or eligible-slice-only.
  • Warnings — omit the parent (and all three sub-items) if pure-Swift, or if every setting across all three groups is already hardened on every applicable target. Otherwise omit an individual sub-item — Compiler warnings, Static analyzer warnings, or Clang-tidy warnings — when every setting in that group is already hardened on every applicable target, or the group has no applicable settings for the detected languages.
  • Inquire about disabled settings — omit if the deliberately disabled predicate yields no rows.
  • Additional diagnostic settings — never omitted; always offered.
  • Bounds safety adoption — omit if Phase 3 step 2 detected no C, C++, or Objective-C++ (counting sourcecode.cpp.* overrides as C++).
Default check state

Group headings carry no checkbox. The parent lines that have sub-items — Enhanced Security and Warnings — are plain bold group labels, not checkable items; their sub-items carry the checkboxes. This avoids the ambiguity of a checked parent whose sub-items are all unchecked. Every other item (including leaf items with no sub-items, like Inquire about disabled settings, Additional diagnostic settings, Bounds safety adoption) is checkable.

The user can flip items and sub-items under Phases by editing the plan file before picking Run.

Step 4: Ask for approval

Tell the user:

"Plan written to xcode-security-audit-plan.md and added to the Xcode project — open it to review. Edit it as needed — uncheck or delete items to skip them; edit the decision document path to relocate. When ready, pick Run. Pick Cancel to abort without changes. Nothing is modified until you pick Run."

Then ask via AskUserQuestion with single-select options:

  • Run — proceed to "Phase 5"
  • Cancel — abort

Step 5: Handle the response

If the user asks a question or requests more detail instead of picking Run/Cancel: answer it, consulting the relevant doc from Bundled Reference Documents (e.g. references/additional-settings.md for the additional diagnostic settings). If they want that detail captured, update xcode-security-audit-plan.md via XcodeUpdate to elaborate on those points. Then re-present the Step 4 approval prompt — nothing is applied until the user picks Run.

If Cancel: run the final cleanup task (Prompt to remove plan file, see "Phase 7: Report and Decision Document" below). The keep-or-remove prompt is offered on Cancel too, so the user's choice to abandon the audit doesn't silently differ from a normal completion. Report "Cancelled — no changes applied," and exit the skill.

If the plan file is missing at re-read time (the user deleted it from disk before responding), treat it as a Cancel — and skip the Prompt to remove plan file task (there's nothing to remove).

If Run: XcodeRead xcode-security-audit-plan.md. Parse:

  • Each - [x] or - [X] bullet is a checked item; the item name is the bold portion (between **…**).
  • A bold bullet with no checkbox (e.g. - **Enhanced Security** …, - **Warnings** …) is a group heading, not a checkable item. It creates no task of its own — its checked sub-items drive the work. Do not treat it as checked or unchecked.
  • Items written as - [ ] and items deleted from the file are skipped — both produce identical skip behavior.
  • Under the "Decision document" heading, the value after Path: is the decision document location.

Create the fine-grained tasks listed in Track Progress:

  • For each Apply Enhanced Security entitlements to <target> task, copy the per-target delta from the corresponding Audit <target> task's description (Category:, Entitlements path:, Missing entitlements:) into the apply task's own description so Phase 5 reads from one place.
  • The Warnings parent line is a heading, not a task — it produces no task of its own. Each checked Warnings sub-item creates its corresponding apply task: Compiler warnings → Apply Compiler Warnings, Static analyzer warnings → Apply Static Analyzer Warnings, Clang-tidy warnings → Apply Clang-Tidy Warnings. This mirrors how the Enhanced Security parent maps to its sub-item tasks.
  • To create the Inquire about <MACRO> on <target> tasks (only when Inquire about disabled settings is checked), TaskList the Audit <target> tasks and TaskGet each; the Deliberately-disabled: line of each description lists that target's candidate rows. Apply the Phase-6 exclusions documented below when filtering.
  • When creating the Report and update decision document task, put the parsed decision-document path in its description so Phase 7 reads it from there.

If the parsed plan has zero checked items, run the final cleanup task immediately and report "Plan was empty — nothing to do."

Phase 5: Apply Settings

Read build-setting state from each Audit <target> task's description (the Audit-table block) when needed; per-target apply state comes from each apply task's own description.

How to apply build settings:

  • Project uses .xcconfig files — edit the xcconfig directly. Supports both project-level and target-level settings.
  • Project uses .pbxproj only — use UpdateTargetBuildSetting for target-level settings. Ask the user to enable project-level settings. Once the user responds that it was set, verify that it was set correctly using grep on the project file.
  • Mixed — if a target has an .xcconfig file, edit the xcconfig. Otherwise, use the Xcode build setting tools. Never introduce a new configuration method.

ENABLE_ENHANCED_SECURITY must be set at project level such that any existing and future build targets inherit this setting. This setting should be disabled only after serious consideration and with strong justification.

Step 1: Enhanced Security

1a. Enable Enhanced Security at the project level. Walk the Enable Enhanced Security at project level task. Two paths inside it:

  • Project uses a project-level xcconfig — write ENABLE_ENHANCED_SECURITY = YES to the xcconfig via XcodeUpdate. Mark the task completed.
  • Project is pbxproj-only — no MCP tool can write a project-level pbxproj setting directly, so the user has to set it in Xcode. Give these exact steps (repeat them verbatim whenever you re-show them): "Open the project in Xcode. Select the project in the Project Navigator (the top entry, not a target). Go to Build Settings, switch the scope to All / Combined, search for ENABLE_ENHANCED_SECURITY, and set the project-level column (left of the target columns) to YES. Save." Then AskUserQuestion with two options: I've enabled it and Show me the steps again. On I've enabled it, verify with Bash: grep -E 'ENABLE_ENHANCED_SECURITY *= *YES' <project-root>/<ProjectName>.xcodeproj/project.pbxproj. If a match is found, mark the task completed. If not, do not move on: the confirmation was most likely accepted without the change actually being made — an accidental Enter, or Save was missed. Say that plainly, re-show the steps verbatim, and ask again. Loop — re-run the grep after each confirmation and re-show the steps every time it still isn't found — until the grep finds ENABLE_ENHANCED_SECURITY = YES.

1b. Update Enhanced Security entitlements. The fine-grained Apply Enhanced Security entitlements to <target> tasks created in Phase 4 step 5 already enumerate the targets needing changes (the Partial, Off, and No-entitlements-file categories — Up-to-date and Skipped are excluded). Walk those tasks.

Read references/enhanced-security.md for the full key list, defaults, and the supported product-type list. For details on individual sub-options, see:

  • references/pointer-authentication.md — arm64e pointer signing
  • references/typed-allocators.md — type-aware memory allocation
  • references/stack-zero-init.md — automatic stack variable zeroing
  • references/readonly-platform-memory.md — dyld state protection
  • references/runtime-restrictions.md — dylib and Mach message restrictions
  • references/security-compiler-warnings.md — security-focused compiler warnings
  • references/cpp-hardening.md — C++ stdlib hardening and bounds checking
  • references/hardware-memory-tagging.md — ARM MTE
  • references/checked-pointer-arithmetic.md — checked pointer arithmetic (CPA2)

Pointer authentication and binary dependencies. Enhanced Security is a bundle of independent protections; only pointer authentication cascades to arm64e. Always recommend ENABLE_ENHANCED_SECURITY = YES at the project level. If the project has a binary Swift Package, xcframework, or prebuilt framework that does not ship arm64e, the right mitigation is to override ENABLE_POINTER_AUTHENTICATION = NO at the target level on every target that links the dependency — not to skip Enhanced Security. List the offending dependencies in the report so the user can ask the vendor for arm64e support and lift the override later.

arm64e.x1 is a pointer-authentication slice, so it should not be built where pointer authentication is off. A binary dependency that ships no arm64e slice will likely not ship arm64e.x1 either. On every target that gets a target-level ENABLE_POINTER_AUTHENTICATION = NO, also set a target-level ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = NO. Step 4 skips these targets, so the audit never adds the checked pointer arithmetic entitlement there. If a target already carries com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow from an earlier configuration, report it — the build will warn that it has no effect without arm64e.x1.

Producer side — universal binary on library/framework targets. Pointer authentication is highly recommended on library and framework targets too — do not skip it on the grounds that the universal binary is a larger on-disk artifact (RAM footprint and execution cost are unchanged; dyld loads only one slice). Enabling pointer authentication already builds both the arm64 and arm64e slices automatically, so no explicit ARCHS is needed. The same argument extends to checked pointer arithmetic, which requires the arm64e.x1 slice appended: a consumer building for arm64e.x1 gets checked arithmetic over the library's code only if the library ships that slice — the consumer app must meet other requisites as well for run time enforcement. Step 4 applies the build setting to these targets. See references/universal-binaries-for-libraries.md and references/checked-pointer-arithmetic.md.

For each task:

  1. Compose the change set from this apply task's description (the Category: / Missing entitlements: lines copied in from the audit task).

    • Entitlements-supported categories (Partial / Off / No-entitlements-file): add/update entitlements via AddEntitlement; create .entitlements if missing and wire CODE_SIGN_ENTITLEMENTS. DriverKit targets are supported for build settings only — skip entitlement changes for them.
    • Library/framework category: no entitlements work, and no build-setting change either — pointer authentication already emits both slices. The only thing to do is the distribution check in item 2 below.
  2. Per-target build settings. ENABLE_ENHANCED_SECURITY = YES is already set at the project level (Step 1a above), so it cascades ENABLE_POINTER_AUTHENTICATION = YES to every target. Simulator builds need no override — the build system drops arm64e for simulator SDKs automatically. The only per-target override: for each target that links a binary dependency that doesn't ship arm64e, set an unconditional target-level ENABLE_POINTER_AUTHENTICATION = NO (that dependency can't be linked as arm64e on any platform). Skip targets that already have an explicit target-level value (per the Audit-table block in their Audit <target> task).

    For each Library/framework-category target where pointer authentication will end up enabled (the target's platform supports arm64e and there is no existing target-level ENABLE_POINTER_AUTHENTICATION = NO), no build-setting change is needed — pointer authentication appends the arm64e slice automatically. Only check that the distributed build emits both the arm64 and arm64e slices: if the target sets ONLY_ACTIVE_ARCH = YES in its Release/distribution configuration, warn in the report that consumers get a single-architecture artifact.

    Do not auto-enable default-OFF sub-options. Hardware memory tagging belongs to Step 3, checked pointer arithmetic to Step 4.

  3. Apply the change set per target: add or update entitlements with AddEntitlement (creating the .entitlements file and wiring CODE_SIGN_ENTITLEMENTS when the target has none); and apply build-setting changes.

After all targets are processed, report: "Enabled Enhanced Security on N target(s). Added a target-level ENABLE_POINTER_AUTHENTICATION = NO on T target(s) that link arm64e-less binary dependencies. Universal arm64/arm64e binary on U library/framework target(s)." If the project is pbxproj-only and Verify Enhanced Security at project level succeeded, append: "Enhanced Security is enabled at the project level (you set it in Xcode)." If the user skipped the guide step, append: "Project-level ENABLE_ENHANCED_SECURITY was not enabled this run — re-run the skill after enabling it in Xcode."

The user already approved this in "Phase 4" — no further prompt is needed.

The per-target Apply Enhanced Security entitlements tasks dominate Phase-5 wall time on multi-target projects. Each one edits the target's .entitlements plist.

Step 2: Warnings

If pure Swift, skip the whole step. This step covers three groups, each gated on its own plan sub-item — Compiler warnings, Static analyzer warnings, and Clang-tidy warnings. Skip any group whose sub-item was unchecked or deleted. For every setting, consult that target's Audit <target> task description (the Audit-table block) and skip individual settings whose row is already hardened. Otherwise apply target-level (see "How to apply build settings").

Compiler warnings (fire on every build):

  • GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR — non-void function returning without a value is undefined behavior; callers read whatever happened to be in the return register. Promoting to error catches this at compile time. YES_ERROR is the documented Xcode value for "treat this specific warning as an error" — it does not flip every warning into an error.
  • GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE — reading uninitialized stack values leaks prior frame contents and lets attackers control flow with stale data. Aggressive mode warns on more cases (e.g., conditional initialization paths).
  • CLANG_WARN_IMPLICIT_FALLTHROUGH = YES — implicit switch fallthrough is one of the most common sources of branching bugs; the warning forces an explicit [[fallthrough]] / __attribute__((fallthrough)) whenever intentional.
  • GCC_WARN_64_TO_32_BIT_CONVERSION = YES — silent narrowing of size_t/pointers to int is a classic source of integer-truncation vulnerabilities (length checks pass on the wide value, then fail open on the narrow one).
  • GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES (C/ObjC only) — implicit declarations were removed in C99 and produce wrong calling conventions and wrong return-type assumptions in modern C. Always an error.

The two YES_ERROR / … ERRORS = YES settings are scoped: they only promote their own specific warning to an error, not all warnings in the project.

Static analyzer warnings (run during Build and analyze, not regular builds):

  • CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES — floating-point loop counters can stall or overshoot due to rounding; the analyzer flags loops where this can become a security-relevant bug.
  • CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES — rand() / random() are predictable PRNGs unsuitable for any security purpose; analyzer flags their use so callers switch to arc4random_uniform or SecRandomCopyBytes.
  • CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES — flags strcpy, strcat, and friends that are inherently unsafe; callers should switch to size-bounded variants (strlcpy, strlcat, snprintf).

Clang-tidy warnings (clang-tidy-integrated checks that are part of the clang static analyzer; they fire only during Build and analyze / clang --analyze, never on normal builds, so there is no build-break risk and adopters need to install nothing extra):

  • CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES — flags a branch condition that is redundant with an enclosing condition, a common sign of a copy-paste or logic error.

Report briefly per group, e.g.: "Enabled compiler warnings, static analyzer warnings, and clang-tidy warnings." — naming only the groups actually applied.

Step 3: Hardware Memory Tagging

If the Hardware memory tagging sub-item (under Enhanced Security) was unchecked or deleted, skip this step.

Hardware memory tagging is supported only for targets whose SUPPORTED_PLATFORMS (or SDKROOT) is macosx, iphoneos / iphonesimulator, watchos, or xros / xrsimulator. Hardware backing requires an iPhone or iPad with an A19 chip or later, a Mac or Apple Vision Pro with an M5 chip or later, or an Apple Watch with an S11 chip or later.

Read references/hardware-memory-tagging.md and apply both keys to every supported target: com.apple.security.hardened-process.checked-allocations, and its soft-mode sub-option for a non-fatal rollout. Soft mode alone does nothing — it modifies the parent key rather than replacing it. The user already approved this in "Phase 4" — no further prompt is needed.

Step 4: Checked Pointer Arithmetic

Run this step after Step 1 and Step 3, whichever of them run: it reads settings Step 1 can change and the entitlements Step 3 can add. Checked pointer arithmetic requires the arm64e.x1 slice, and this step enables that slice only on a target already building the arm64e slice with pointer authentication. Run time enforcement additionally requires hardware memory tagging on the same target.

Skip this step if the Checked pointer arithmetic sub-item was unchecked or deleted.

Apply to every target whose Checked pointer arithmetic: line reads eligible-entitlement or eligible-slice-only; skip the rest. That line is computed in Phase 3 step 4 and is the only eligibility test — do not re-derive it here.

Then check the conditions below per target, reading each value fresh: Step 1 may have changed the build settings, and Step 3 may have added the entitlement. Skip a target and report it when any condition it is subject to fails.

  • ENABLE_ENHANCED_SECURITY evaluates to YES — eligible-entitlement targets only, since the entitlement needs the capability.
  • ENABLE_POINTER_AUTHENTICATION evaluates to YES — both kinds of target, since arm64e.x1 is a pointer-authentication slice.
  • com.apple.security.hardened-process.checked-allocations is in the entitlements file — eligible-entitlement targets only, since run time enforcement depends on hardware memory tagging. The key is absent when Step 3 did not run, skipped this target, or the Hardware memory tagging sub-item was unchecked.

Run time enforcement requires a device running iOS with an A20 Pro chip or later, or a device running watchOS with an S11 chip or later.

Read references/checked-pointer-arithmetic.md and apply per target. For an eligible-entitlement target, apply both halves: set ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = YES at target level (the target's xcconfig, otherwise UpdateTargetBuildSetting), and add com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow with AddEntitlement. Both halves are required because the slice alone does not enforce checked pointer arithmetic, and Xcode warns at build time if the entitlement is set while the target is not building arm64e.x1. For an eligible-slice-only target, apply the build setting only.

Record the outcome for every target in the Apply Checked Pointer Arithmetic task's description via TaskUpdate, one line per target, so Phase 7 (Report and Decision Document) reads it from one place:

<target>: applied | skipped: <reason>

Use skipped: not eligible — <reason from the target's Checked pointer arithmetic: line> for a target that was never eligible, and skipped: ENABLE_ENHANCED_SECURITY is <value>, skipped: ENABLE_POINTER_AUTHENTICATION is <value>, or skipped: no hardware memory tagging entitlement for one that was eligible but failed the re-read above. The user already approved this in "Phase 4" — no further prompt is needed.

Step 5: Additional Diagnostic Settings

If the Additional diagnostic settings plan item was unchecked or deleted, skip this step.

Read references/additional-settings.md and follow it. The user already approved this in "Phase 4" — no further prompt is needed.

Step 6: Bounds Safety Adoption

If the Bounds safety adoption plan item was unchecked or deleted, skip this step.

This step does not apply changes — it emits guidance only.

For C projects (C present per Phase 3 step 2), print:

"To adopt ENABLE_C_BOUNDS_SAFETY (annotation-based bounds safety for C), invoke the adopt-c-bounds-safety skill."

For C++ projects (C++ or Objective-C++ present per Phase 3 step 2 — including any sourcecode.cpp.* override on files with other extensions), print:

"To adopt ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS (C++ bounds-safe buffer patterns), read the documentation at https://clang.llvm.org/docs/SafeBuffers.html"

Phase 6: Inquire about Disabled Settings

If the Inquire about disabled settings plan item was unchecked or deleted, skip this phase.

This phase pauses for one user response per deliberately-disabled setting that lacks a documented rationale. If the candidate list is long, surface the count up front so the user knows what to expect ("I found 7 deliberately-disabled settings; let me ask about each").

A row is a candidate when the deliberately disabled predicate (defined in references/reading-build-settings.md) holds. TaskList the Audit <target> tasks and TaskGet each; the Deliberately-disabled: line of each description lists that target's candidate rows. Flag an unconditional ENABLE_POINTER_AUTHENTICATION = NO, since that disables pointer authentication on device builds. Flag ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = NO on a target whose Checked pointer arithmetic: line reads not-eligible: opted out — that reason means the opt-out is the only thing standing between the target and the arm64e.x1 slice. Do not flag it for the other not-eligible reasons, where the slice could not be built anyway. Restrict to settings whose Scope (in references/security-settings-reference.md) covers a language detected in Phase 3 step 2; both settings above have no Scope and are flagged regardless.

For each candidate, walk the corresponding Inquire about <MACRO> on <target> task created in Phase 4 step 5:

  • If the decision document has an entry with status Disabled and a rationale → note it in the report and move on.
  • Otherwise → AskUserQuestion: "I found <MACRO> explicitly set to NO with no explanation. Is there a reason for this?" Double-check that the macro is deliberately disabled and not merely at Xcode's default OFF — only call out explicit overrides. Record the rationale (or recommend re-enabling if none).

Same flow applies to ENABLE_ENHANCED_SECURITY = NO if it appears on any task's Deliberately-disabled: line.

Phase 7: Report and Decision Document

Produce a lean summary:

  1. Enabled — project-wide settings that were enabled.
  2. Enhanced Security per target — one line per target: name, final status (up-to-date / applied / skipped-by-user), terse delta (entitlements added, whether an entitlements file was created, which slices the target now builds, whether checked pointer arithmetic was applied). Roll up Skipped targets into one line. For checked pointer arithmetic, TaskGet the Apply Checked Pointer Arithmetic task and use its per-target outcome lines, including the reason for each skip.
  3. Already active — settings already configured correctly.
  4. Inquired — settings found disabled and the outcome of the inquiry.
  5. Test your app — action item for the user: test on real hardware (not the simulator) that supports every enabled hardening, watch for protections firing, and fix the crashes and simulated crash reports that surface. Ship to customers only once the hardened app is adequately tested — otherwise it may crash or run slowly in production. For hardware memory tagging specifically, fix the simulated crash reports soft mode produces before disabling soft mode for enforcement. Checked pointer arithmetic has no soft mode and memory tagging's does not cover it, so test on capable hardware before shipping: a latent pointer-arithmetic bug terminates the app.

Decision document. TaskGet the Report and update decision document task to read the decision-document path. Then read references/decision-document.md and follow it to create or update the document at that path.

After Phase 7 — and on any error path during Phases 5–7 — this final task runs:

  1. Prompt to remove plan file — ask the user via AskUserQuestion: "The audit is complete. Remove the plan file xcode-security-audit-plan.md from your project?"
    • Yes, remove it (Recommended) → XcodeRM xcode-security-audit-plan.md deleteFiles:true
    • No, keep it → leave it in place; it stays in the Project Navigator as a record of what was approved. The user can delete it later from Xcode or Finder.

If removal fails, warn the user but do not block exit.

User-Facing Interaction Guidelines

  • Keep replies lean. Short sentences.
  • Speak in complete sentences. No fragments. Don't emit telegraphic noun phrases like "No existing decision document." — write a full sentence ("I didn't find an existing decision document — I'll create one at the end.").
  • Phases are internal. Never reference phase numbers or step numbers in user-facing prose. Describe outcomes plainly: say "I won't need to ask you about disabled settings" instead of "there will be no Phase 6 inquiry questions". This applies to narration, status lines, and any AskUserQuestion text.
  • No skill-internal jargon. Don't use words like "catalog", "audit table" in user-facing prose — those are internal to the skill. Describe what's happening in everyday Xcode terms: "checking known security build settings", "the list of targets", "the analysis I just ran".
  • Keep user questions minimal. Three scheduled questions: the briefing-acknowledgment prompt (Begin audit / Cancel) at the end of "Phase 1", the plan approval prompt (Run / Cancel) at the end of "Phase 4", and the keep-or-remove-plan-file prompt at the end of "Phase 7". Other questions are situational: inquiries about deliberately-disabled settings during "Phase 6" (only when an explicit = NO lacks a documented rationale), and the Enable Enhanced Security at project level confirmation prompt (only for pbxproj-only projects when that sub-item is checked).
  • Report progress so the user can track: "Enabling...", "Evaluating...", "Keeping/Reverting..."
  • Use AskUserQuestion for the briefing acknowledgment (Begin audit / Cancel), for the plan approval (Run / Cancel), for inquiring about disabled settings during "Phase 6", for the Enable Enhanced Security at project level confirmation in Phase 5 Step 1a (pbxproj-only), and for the keep-or-remove-plan-file prompt at the end of "Phase 7".
  • When asking a question provide context the user needs to answer the question. For example, describe the benefit of the security protection before asking whether to enable it. Describe it in terms of the protection it provides, not how it is enabled.
  • When emitting lists of Xcode build settings, use bullet lists Don't use comma-separated lists.
Files (xcode-skills)
  • references
    • additional-settings.md 1016 B
      # Additional Settings
      
      Additional diagnostic settings that can find more issues but may also produce false positives. These are applied only when the user opts in after the main audit.
      [Read the build settings reference](doc://com.apple.documentation/documentation/Xcode/build-settings-reference) for the complete list of available settings.
      
      ## Settings
      
      - `CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES`
      - `CLANG_ANALYZER_SECURITY_BUFFER_OVERFLOW_EXPERIMENTAL = YES`
      - `CLANG_WARN_ASSIGN_ENUM = YES`
      - `GCC_WARN_SIGN_COMPARE = YES`
      
      **C++ / DriverKit / IOKit (only if C++ present):**
      
      - `CLANG_ANALYZER_OSOBJECT_C_STYLE_CAST = YES`
      
      **Blocks (only if ObjC, ObjC++, or C with -fblocks present):**
      
      - `CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES`
      
      **ObjC-specific (only if ObjC/ObjC++ present):**
      
      - `CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES`
      - `CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES`
      
      ## Procedure
      
      Enable relevant settings based on languages used in the project. Record decisions in the decision document.
      
    • adoption-strategy.md 5.6 KB
      # Adoption Strategy
      
      A recommended order for validating and addressing Xcode Enhanced Security features, from lowest risk and effort to highest.
      
      Adding the Enhanced Security capability enables all cascaded settings at once. The phases below represent the order in which to **validate and fix issues** — not separate enablement steps. Phase 1 features are zero-cost (nothing to fix for well-behaved code), Phase 2 may need minor code changes, and Phase 3 requires active annotation or rewriting.
      
      ## Phase 1: Zero-Cost, No Code Changes
      
      Start here. These features have no runtime cost and require no source code changes for well-behaved code.
      
      | Feature | Why first | Reference |
      |---------|----------|-----------|
      | **Security Compiler Warnings** | Compile-time only. Zero runtime cost. Identifies real bugs. | `security-compiler-warnings.md` |
      | **Stack Zero Initialization** | Transparent. Cannot cause crashes. Prevents info leaks. | `stack-zero-init.md` |
      | **Read-Only Platform Memory** | No impact on well-behaved code. Blocks post-exploitation. | `readonly-platform-memory.md` |
      
      **Action:** After enabling Enhanced Security, build and fix any new warnings. These features won't cause runtime issues.
      
      ## Phase 2: Low-Effort Runtime Protections
      
      Next, validate runtime protections that require minimal or no code changes for most apps.
      
      | Feature | Effort | Reference |
      |---------|--------|-----------|
      | **Runtime Restrictions** | No changes if using XPC or no IPC. Review needed only for raw Mach IPC. | `runtime-restrictions.md` |
      | **Typed Allocators** | No changes for standard `malloc`/`free`. Update custom allocator wrappers if present. | `typed-allocators.md` |
      
      **Action:** Test thoroughly. If you use raw Mach IPC, read the Mach IPC conformance guide.
      
      ## Phase 3: Annotation and Code Hardening
      
      These features require active code changes — annotations, pointer type updates, or fixing unsafe patterns.
      
      | Feature | Effort | Reference |
      |---------|--------|-----------|
      | **Pointer Authentication** | Add `__ptrauth` qualifiers to security-critical function/data pointers. Review pointer casts. | `pointer-authentication.md` |
      | **C++ Stdlib Hardening** | Fix out-of-bounds container access and unsafe buffer operations. | `cpp-hardening.md` |
      
      **Action:** Prioritize security-critical code paths first (parsers, network handlers, IPC).
      
      Additionally, consider adopting **C Bounds Safety** (`-fbounds-safety`) as a complementary feature for C codebases — see the `adopt-c-bounds-safety` skill.
      
      ## Phase 4: Hardware-Dependent Protections
      
      These require specific hardware and OS versions.
      
      | Feature | Requirement | Reference |
      |---------|------------|-----------|
      | **Hardware Memory Tagging** | iPhone/iPad with an A19 chip or later; Mac/Vision Pro with an M5 chip or later; Apple Watch with an S11 chip or later | `hardware-memory-tagging.md` |
      | **Checked Pointer Arithmetic** | device running iOS with an A20 Pro chip or later; device running watchOS with an S11 chip or later. | `checked-pointer-arithmetic.md` |
      
      **Action for Hardware Memory Tagging:**
      1. Enable with soft mode first — this generates simulated crash reports without terminating the app
      2. Deploy soft mode to internal testers
      3. Review simulated crash reports and fix memory bugs
      4. Disable soft mode for production enforcement
      
      **Action for Checked Pointer Arithmetic:** enable hardware memory tagging first — run time enforcement requires it — and finish that rollout before adding this. Then build the `arm64e.x1` slice, add the enforcement entitlement, and test on capable hardware. There is no soft mode here, and memory tagging's soft mode does not cover these faults: a latent pointer-arithmetic bug terminates the app. Read `checked-pointer-arithmetic.md` for instructions on how to enable checked pointer arithmetic and additional notes about adoption.
      
      ## Decision Matrix
      
      Use this to decide which features to prioritize based on your codebase:
      
      | If your app... | Prioritize |
      |---|---|
      | Is pure Swift | Phase 1 + Runtime Restrictions + Read-Only Memory |
      | Has C code | All of Phase 1-3, plus consider C Bounds Safety (separate skill) |
      | Has C++ code | All of Phase 1-3, especially C++ Hardening |
      | Processes untrusted input | All features, prioritize bounds checking, memory tagging, and checked pointer arithmetic |
      | Uses Mach IPC | Review runtime restrictions carefully before enabling |
      | Targets MTE-capable hardware (iPhone/iPad with chip A19 or later, Mac/Vision Pro with chip M5 or later, Apple Watch with chip S11 or later) | Consider hardware memory tagging (start with soft mode) |
      | Runs on devices running iOS with an A20 Pro chip or later, or devices running watchOS with an S11 chip or later | Consider checked pointer arithmetic — it requires hardware memory tagging for run time enforcement |
      | Is a DriverKit extension | All applicable features — elevated privilege means higher stakes |
      
      ## General Principles
      
      1. **Enable Enhanced Security as a capability first** — this turns on all cascaded features at once
      2. **Fix warnings before testing runtime protections** — compiler warnings often reveal the same bugs that runtime protections would crash on
      3. **Fix undefined behavior in pointer arithmetic** — most checked pointer arithmetic failures are a consequence of undefined behavior, such as subtracting pointers into different objects
      4. **Test in soft mode before hard mode** — applies to hardware memory tagging
      5. **Prioritize security-critical code** — parsers, network handlers, IPC, auth logic
      6. **Don't skip testing** — Enhanced Security features turn latent bugs into crashes, which is the point, but you want to find them before your users do
      
    • checked-pointer-arithmetic.md 13.7 KB
      # Checked Pointer Arithmetic
      
      Checked pointer arithmetic makes hardware supporting the `FEAT_CPA2` extension detect when a pointer computation overflows out of the address bits into the upper bits of the pointer. Those upper bits hold the Memory Tagging Extension (MTE) tag, when such protection is enabled. Overflowing into them is what lets arithmetic walk from one object into another while still presenting a tag the hardware accepts — without this check, that overflow is how an attacker would defeat tagging.
      
      Detection happens in two places: explicit arithmetic poisons its result, and every load and store checks the addition it performs as part of its addressing mode.
      
      The dependency runs one way: checked pointer arithmetic needs hardware memory tagging enabled on the same target for its run time enforcement, while memory tagging works on its own. Checked pointer arithmetic also requires its own entitlement and the `arm64e.x1` slice.
      
      > **Apple developer documentation:** entitlement reference for [`com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow), and [Enabling Enhanced Security for your app](doc://com.apple.documentation/documentation/Xcode/enabling-enhanced-security-for-your-app) for the capability that provisions it. See `hardware-memory-tagging.md` for the memory-tagging protection this one defends.
      
      ## What It Does
      
      Checked pointer arithmetic requires the **`arm64e.x1`** slice (Mach-O cpusubtype 12, `CPU_SUBTYPE_ARM64E_X1`) to be built, and that slice is where the compiler emits checked pointer arithmetic instructions: explicit pointer arithmetic becomes `ADDPT` / `SUBPT` / `MADDPT` / `MSUBPT` instead of `ADD` / `SUB`.
      
      Those instructions are evaluated for overflow only when the application has the entitlements that enforce checked pointer arithmetic and runs on capable hardware. The same evaluation covers every load and store that computes its effective address by addition, whatever the addressing mode. For example, immediate-offset forms such as `LDR [Xn, #imm]`, or scaled register-offset forms such as `LDR [Xn, Xm, LSL #3]`.
      
      The check compares the result's top byte, bits [63:56], against the **base operand's** top byte. That byte carries the 4-bit Memory Tagging Extension (MTE) tag in bits [59:56] when MTE is enabled. When the two differ, the arithmetic has overflowed into the top byte and the result is **poisoned**: bits [63:55] are copied from the base and bit [54] is set to the inverse of bit [55].
      
      A poisoned pointer is deliberately non-canonical, so the next dereference takes a level-0 translation fault, delivered as `EXC_ARM_CPA_FAIL` (`0x108`) with ESR `0x92000004` (read) or `0x92000044` (write). A poisoned value used as a length or an offset instead of an address may present as `EXC_ARM_MTE_TAGCHECK_FAIL` instead. Poison also survives further arithmetic, so a poisoned value that is passed around and used later still faults at the point of use rather than being silently laundered.
      
      Requiring the result's top byte to equal the base's is what confines pointer arithmetic to a single tagged region when tagging is enabled: a neighbouring allocation carries a different tag, so walking into it poisons the result instead of letting the access through.
      
      ## What Memory-safety Issues It Mitigates
      
      - **Out-of-bounds access through an oversized or attacker-influenced offset** — with tagging enabled, an index or length large enough to leave the allocation changes the tag, so the derived pointer faults instead of reading or writing a neighbour
      - **Tag forging against hardware memory tagging** — arithmetic can no longer be used to manufacture a pointer whose tag matches a different allocation, closing the bypass that would otherwise weaken MTE
      - **Cross-allocation pointer deltas** — a difference between pointers into two different allocations (the classic post-`realloc` rebase of internal object pointers) carries non-zero high bytes, and adding it to a base is caught
      - **Pointer/integer type confusion in arithmetic** — expressions that put an integer in the pointer position, or subtract a pointer stored as `uintptr_t`, produce a tag mismatch and fault at once
      - **Dereference of a NULL or corrupted base** — a negative immediate offset applied to a NULL base pointer wraps the top byte from `0x00` to `0xFF` and is caught at the faulting instruction
      
      These are ordinary memory-safety and correctness bugs, most of them undefined behaviour that the hardware turns into an immediate, localized fault instead of a silent corruption exploitable later.
      
      ## How to Enable
      
      Four things must be enabled on an app target. Miss one and there is no protection.
      
      | # | Set | Where | Xcode UI | Gives you |
      |---|---|---|---|---|
      | 1 | `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = YES` | build setting on the project or the target (`project.pbxproj` or an `.xcconfig`) | Build Settings > Security > "Enable Hardware-Checked Pointer Arithmetic Slice" | the `arm64e.x1` slice, which carries the checked instructions but is not enough for run time enforcement |
      | 2 | `com.apple.security.hardened-process = <true/>` | the target's `.entitlements` file | Signing & Capabilities > + Capability > Enhanced Security | the Enhanced Security entitlement, which run time enforcement requires |
      | 3 | `com.apple.security.hardened-process.checked-allocations = <true/>` | the target's `.entitlements` file | Signing & Capabilities > Enhanced Security > Memory Safety > "Enable Hardware Memory Tagging" | hardware memory tagging, which run time enforcement requires |
      | 4 | `com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow = <true/>` | the target's `.entitlements` file | Signing & Capabilities > Enhanced Security > Memory Safety > "Enforce Checking for Overflow of Pointer Arithmetic" | run time enforcement entitlement |
      
      Row 4 is a sub-option of row 3, and row 3 of row 2. Row 2 also needs `com.apple.security.hardened-process.enhanced-security-version-string = 2`; Xcode writes that key when you add the capability, so write it yourself if you edit the entitlements file directly. See `enhanced-security.md` for the rest of that capability.
      
      Hardware memory tagging is **required** for run time enforcement, which is why row 3 is in the table: the checked-pointer-arithmetic entitlement is a sub-option of `checked-allocations` and is not honoured without it. The two protections also reinforce each other — tagging is what gives the top byte a value worth comparing, and checked arithmetic in turn closes the tag-forging bypass against tagging.
      
      Library and framework targets take row 1 only. Entitlements are granted per process from the main executable, so a library builds the slice but it is the consuming app's entitlements that decide whether checked pointer arithmetic is enforced.
      
      `ENABLE_POINTER_AUTHENTICATION = YES` is recommended alongside row 1, though not strictly required for checked pointer arithmetic. The recommendation runs the other way too: once a target builds the `arm64e` slice, build the `arm64e.x1` slice as well and enable run time enforcement of checked pointer arithmetic on top of it.
      
      Xcode warns at build time if row 4 is set while the target is not building `arm64e.x1`. Full enforcement requires the `arm64e.x1` slice and the entitlements. The warning is the only signal that the configuration is incomplete.
      
      ### What the build setting does
      
      The `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` build setting appends `arm64e.x1` to `ARCHS_STANDARD`. That slice is a pre-requisite for run time enforcement of checked pointer arithmetic. The setting has no effect if `ARCHS` is overridden to something not based on `ARCHS_STANDARD`.
      
      Measured on an iOS target:
      
      | `ENABLE_POINTER_AUTHENTICATION` | `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` | Resulting `ARCHS_STANDARD` |
      |---|---|---|
      | NO | NO | `arm64` |
      | YES | NO | `arm64 arm64e` |
      | NO | YES | `arm64 arm64e.x1` |
      | YES | YES | `arm64 arm64e arm64e.x1` |
      
      Use the combination in the last row. With the slice enabled but pointer authentication off, the binary ships no `arm64e` slice, so devices without `FEAT_CPA2` fall back to `arm64` and lose pointer authentication on capable hardware. With both enabled, every device is covered: `arm64e.x1` where the hardware supports it, `arm64e` everywhere else where pointer authentication is supported.
      
      Xcode's Validate Settings offers this setting as an upgrade task, "Enable Hardware Checked Pointer Arithmetic".
      
      ### Verifying the slice
      
      Use `lipo -archs`:
      
      ```bash
      lipo -archs MyApp.app/MyApp        # expect: arm64 arm64e arm64e.x1
      ```
      
      ## Code Changes Required
      
      Generally none. The compiler emits the checked instructions in the `arm64e.x1` slice, and the hardware enforces them once the entitlements in "How to Enable" are in place.
      
      Two kinds of code base do need changes, though. Code that relies on undefined behaviour in pointer arithmetic — a difference between pointers into two different allocations, an offset carried past the end of an object, arithmetic on a NULL base — has to be corrected, because that is precisely what the check detects. Less commonly, code that mixes pointer and integer types in one expression may need changes too: subtracting a pointer stored as `uintptr_t`, or putting an integer in the position where the compiler expects the base pointer, produces checked arithmetic on operands that were never meant to be an address and a displacement.
      
      Expect the fault to be far from the poisoning: the instruction that poisons a value and the one that dereferences it may be in different functions, files, or libraries, with the value sitting in a struct field or global in between.
      
      `__arm64e_x1__` is a predefined macro, for code that must be compiled differently for the `arm64e.x1` slice.
      
      ## How to Disable
      
      | # | Set | Where | Xcode UI | Takes away |
      |---|---|---|---|---|
      | 1 | `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = NO` | build setting on the project or the target (`project.pbxproj` or an `.xcconfig`) | Build Settings > Security > "Enable Hardware-Checked Pointer Arithmetic Slice" | the `arm64e.x1` slice |
      | 4 | remove `com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow` | the target's `.entitlements` file | Signing & Capabilities > Enhanced Security > Memory Safety > uncheck "Enforce Checking for Overflow of Pointer Arithmetic" | enforcement at run time |
      
      The row numbers in this table come from the table in "How to Enable".
      
      To disable run time enforcement of checked pointer arithmetic in an app target, only the entitlement removal (row 4) is required. Whether or not the `arm64e.x1` slice should be removed (row 1) depends on evaluating its benefits beyond checked pointer arithmetic. Read `pointer-authentication.md` for more information.
      
      If the only reason for building the `arm64e.x1` slice was to enable run time enforcement of checked pointer arithmetic by adding its entitlement to the app target, the recommendation is to undo both rows. Removing the slice only leaves an entitlement Xcode warns about.
      
      A library or framework target has only row 1 to undo, since it never took the entitlement. Removing the `arm64e.x1` slice leaves the library without checked pointer arithmetic instructions. However, if the library still builds the `arm64e` slice and is loaded by an application enforcing checked pointer arithmetic at run time (i.e., an app that meets the criteria in section "How to Enable" and runs on capable hardware), load/store instructions in the library will still be checked.
      
      Leave `com.apple.security.hardened-process` — row 2 in "How to Enable" — in place. It is the Enhanced Security capability itself, and clearing it disables far more than checked pointer arithmetic.
      
      ## Platform Availability
      
      - **Platforms:** checked pointer arithmetic requires **iOS on a device with an A20 Pro chip or later** or **watchOS on a device with an S11 chip or later**. Both chips support `FEAT_CPA2`, which the `arm64e.x1` slice targets.
      - **Simulator:** no action required. Simulator SDKs define no `arm64e.x1` architecture, so the build system drops it from a simulator build's effective architectures exactly as it does `arm64e`.
      
      ## Performance and Stability Impact
      
      - **Performance:** low overhead — the check is part of the arithmetic and address generation the CPU already performs, with no extra instructions. The cost is binary size: a third slice.
      - **Stability:** code with latent pointer-arithmetic bugs **will crash**, and undefined behaviour that has been benign for years is exactly what this catches. Expect faults in raw-pointer-heavy C/C++, in code that stores pointers as `uintptr_t`, and in code that rebases internal pointers in an object after a reallocation.
      - **Adoption path:** enable pointer authentication and hardware memory tagging first. `arm64e.x1` is a pointer-authentication slice, so the target should already be building and shipping `arm64e` cleanly before a third slice is added, and tagging is what run time enforcement requires. Then build the `arm64e.x1` slice and add the enforcement entitlement, run your test suite and internal builds on hardware that implements `FEAT_CPA2`, and diagnose and fix each fault. An app ships with the entitlement enabled; a library or framework ships the slice alone, and the consuming app's entitlement is what enforces the checks. Checked pointer arithmetic has no soft mode: there is no setting that reports a fault without terminating the app, and hardware memory tagging's `soft-mode` sub-option does not cover these faults — it applies to tag-check failures, while a poisoned-pointer dereference is a translation fault. Plan for crashes during validation and fix them before shipping.
      
    • cpp-hardening.md 3.5 KB
      # C++ Standard Library Hardening and Bounds Checking
      
      Enables safety checks in the C++ standard library and compiler-enforced bounds checking for unsafe buffer operations.
      
      ## What It Does
      
      Two protections in one setting:
      
      ### 1. C++ Standard Library Hardening (Fast Mode)
      
      Enables assertion checks in standard library container types:
      
      - **Valid element access** — checks that elements exist before accessing them (applies to all containers including `std::function` and `std::optional`)
      - **Valid input range** — checks that ranges passed to standard algorithms are valid (begin iterator can reach the sentinel)
      
      These checks run in constant time. If an assertion fails, the system crashes the app.
      
      ### 2. Unsafe Buffer Usage Warnings (as Errors)
      
      The compiler reports errors when it detects:
      - Indexing an array, performing pointer arithmetic, or using unsafe C stdlib functions on raw pointers
      - Calling `operator[]()` on a smart pointer referring to a list of objects
      - Constructing `std::span` with a two-argument (pointer + size) constructor
      
      ## What Vulnerabilities It Mitigates
      
      - **Out-of-bounds container access** — accessing elements beyond container size
      - **Iterator invalidation** — using invalid or dangling iterators
      - **Unsafe buffer access** — raw pointer arithmetic and indexing without bounds
      - **Span construction errors** — creating spans with incorrect size parameters
      
      ## How to Enable
      
      **Build setting:** `ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = Yes`
      
      This enables both protections described above (hardened libc++ and unsafe buffer usage warnings).
      
      **Relationship to Enhanced Security:** `ENABLE_ENHANCED_SECURITY = YES` cascades the hardened libc++ portion only (via `CLANG_CXX_STANDARD_LIBRARY_HARDENING`). It does NOT enable unsafe buffer usage warnings. `ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS` is the superset — it enables both the hardened libc++ and the compiler warnings — and must be enabled separately if you want both.
      
      ## Hardening Modes
      
      You can override the mode per-file by defining `_LIBCPP_HARDENING_MODE` **before** any standard library includes:
      
      | Macro Value | Mode | Checks |
      |---|---|---|
      | `_LIBCPP_HARDENING_MODE_NONE` | None | No checks |
      | `_LIBCPP_HARDENING_MODE_FAST` | Fast (default) | Constant-time checks only |
      | `_LIBCPP_HARDENING_MODE_EXTENSIVE` | Extensive | Additional non-constant-time checks |
      | `_LIBCPP_HARDENING_MODE_DEBUG` | Debug | All checks including debug-only assertions |
      
      ```cpp
      // At the very top of the file, before any includes
      #define _LIBCPP_HARDENING_MODE _LIBCPP_HARDENING_MODE_EXTENSIVE
      #include <vector>
      ```
      
      For more information, see [Hardening Modes](https://libcxx.llvm.org/Hardening.html) in the LLVM documentation.
      
      ## Code Changes Required
      
      - Fix hardening assertion failures (e.g., accessing `std::vector` out of bounds, using invalidated iterators)
      - Replace unsafe raw pointer operations with safe alternatives (e.g., use `std::span` with range constructors, `std::array`, or iterator-based access)
      - Fix `std::span` construction to use safe constructors
      
      ## How to Disable
      
      **Build setting:** `ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS = No`
      
      ## Platform Availability
      
      - iOS, iPadOS, macOS, visionOS
      - Available on all supported hardware
      
      ## Performance and Stability Impact
      
      - **Performance:** Low. Fast mode checks are constant-time. The overhead is typically negligible for most applications.
      - **Stability:** Code with latent out-of-bounds access bugs will crash. Test with the Debug hardening mode during development to catch issues early.
      
    • decision-document.md 3.3 KB
      # Decision Document
      
      Maintain a persistent `xcode-security-settings.md` that records every setting considered, its status, and the rationale.
      This file is under source control and serves as the single source of truth for security build setting decisions.
      All settings must be recorded in the decision document.
      
      ## Step 1: Locate or Create the File
      
      The decision document path comes from the plan file approved in Phase 4 (the `Path:` value under the "Decision document" heading). Use `XcodeRead` / `XcodeGlob` to locate; use `XcodeWrite` (new file) or `XcodeUpdate` (existing file) to write.
      
      1. If a file at the planned path exists, use it. Skip to Step 2.
      2. If it doesn't, create the file at the planned path with the initial structure (see Document Structure below) via `XcodeWrite`. `XcodeWrite` both writes to disk and registers the file in the project, so the new file appears in the Project Navigator without a separate add-to-project step.
      
      ## Step 2: Merge Decisions
      
      If an existing document was found, its content is already known. Preserve all user-added content, custom notes, and section organization.
      
      For each setting considered in this run:
      
      - **New entry** (setting not in document) — add to the appropriate section.
      - **Status unchanged** — leave the entry untouched.
      - **Status changed** (e.g., moved from Deferred to Enabled) — move the entry to the correct section. Preserve the old rationale as context (e.g., "Previously deferred because too noisy. Now enabled after codebase cleanup.").
      
      Never remove entries. The document is append/update only.
      
      Sections:
      - **Enabled settings** — settings that are active.
      - **Disabled settings** — settings the team decided not to adopt. Always include rationale explaining why.
      - **Deferred** — settings considered but not yet enabled. Always include rationale explaining what would need to change.
      
      ## Step 3: Write the File
      
      Write the merged document via `XcodeUpdate` if you opened an existing file in Step 1, or `XcodeWrite` if you're creating it. Report the path: "Decision document updated at `<path>`."
      
      ## Document Structure
      
      Use this layout for new files. If the file already exists, follow its existing style.
      
      ```markdown
      # Xcode Security Settings
      
      Security build settings decisions for [ProjectName].
      
      ## Enabled settings
      
      - `GCC_WARN_ABOUT_RETURN_TYPE` to `YES_ERROR`
      - `GCC_WARN_UNINITIALIZED_AUTOS` to `YES_AGGRESSIVE`
      - `ENABLE_ENHANCED_SECURITY`
      
      ## Disabled settings
      
      - `GCC_WARN_SIGN_COMPARE`: A lot of `for` loops trigger this.
        The team decided to not adopt this warning because it would involve too many changes.
      
      ## Deferred
      
      Settings considered but not yet enabled. Revisit them later.
      
      - `CLANG_WARN_ASSIGN_ENUM`: The findings seem relevant.
      - `CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION`:
        Too noisy with current generated code.
        Revisit after generated code is excluded from analysis.
      - `ENABLE_C_BOUNDS_SAFETY`:
        Requires annotation-based programming model.
        It needs careful adoption planning.
      ```
      
      Entry format: "- `SETTING_NAME` [to `VALUE`]: Rationale"
      
      Omit the `to VALUE` part for settings that are enabled, unless we have some relevant rationale to state.
      For example, if the setting was disabled in the past, we can mention that and why it was enabled now.
      Usually, disabled settings or deferred settings need explanation.
      
    • enhanced-security.md 9.2 KB
      # Enhanced Security
      
      Enhanced Security is an Xcode capability, not just a build setting. Enabling it fully touches **two places per target**:
      
      1. Build settings (in pbxproj or xcconfig) — `ENABLE_ENHANCED_SECURITY` + pointer authentication.
      2. Entitlements (in the target's `.entitlements` file) — the runtime-protection keys.
      
      `ENABLE_ENHANCED_SECURITY = YES` is the build setting that turns on the compiler-driven pieces. The **Enhanced Security entitlements** (the `com.apple.security.hardened-process` key family) turn on the runtime-driven pieces and are what actually provisions the capability.
      
      ## Apple developer documentation
      
      - [Enabling Enhanced Security for your app](doc://com.apple.documentation/documentation/Xcode/enabling-enhanced-security-for-your-app) — the canonical how-to.
      - [Creating enhanced security helper extensions](doc://com.apple.documentation/documentation/Xcode/creating-enhanced-security-helper-extensions) — for XPC services / system extensions / driver extensions called from a hardened host.
      - [Entitlements](doc://com.apple.documentation/documentation/BundleResources/Entitlements) — overview of every entitlement, including the `com.apple.security.hardened-process` family used below.
      
      ## Supported Product Types
      
      Enhanced Security only applies on iOS, macOS, visionOS, and DriverKit, to these product types. Skip any target whose product type isn't in this list (frameworks, test bundles, app extensions other than those below, etc.) or whose platform isn't one of those four.
      
      - `com.apple.product-type.application`
      - `com.apple.product-type.application.on-demand-install-capable`
      - `com.apple.product-type.xpc-service`
      - `com.apple.product-type.driver-extension` (**build settings only** — entitlements do not apply to DriverKit)
      - `com.apple.product-type.system-extension`
      - `com.apple.product-type.tool`
      
      ## Libraries and Frameworks
      
      Library and framework targets (frameworks, static frameworks, static libraries, dynamic libraries) are deliberately absent from the supported product-type list above — the Enhanced Security entitlements (the `com.apple.security.hardened-process` key family) apply only to executable targets that run directly on the OS, not to code linked into someone else's executable. The audit therefore skips entitlement edits on these targets.
      
      The build settings cascaded by `ENABLE_ENHANCED_SECURITY = YES`, however, do still benefit library/framework targets — pointer authentication, security compiler warnings, typed allocator support, and C++ stdlib hardening all apply at compile time. **Enable pointer authentication on these targets** (`ENABLE_POINTER_AUTHENTICATION = YES`): the setting appends `arm64e` to the architecture list when `arm64` is present, so enabling it is exactly what produces the **universal `arm64`/`arm64e` binary** — consumers then pick the slice that matches their architecture. Do not skip pointer authentication on a library to avoid the larger artifact: the extra `arm64e` slice is the accepted tradeoff for control-flow integrity in shipped library code, and only one slice is loaded at runtime. See `universal-binaries-for-libraries.md` for the qualifying product types, the distribution check, and XCFramework guidance.
      
      ## Part A — Build Settings
      
      One setting the audit needs to resolve to `YES` on every supported target:
      
      - `ENABLE_ENHANCED_SECURITY = YES` — listed in the capability's `requiredValues`. Cascades automatically to pointer authentication, stack zero init, security compiler warnings, typed allocators, and C++ stdlib hardening (the audit does not manipulate these cascaded settings directly).
      
      The apply path:
      
      1. Set `ENABLE_ENHANCED_SECURITY = YES` at the project level so every target inherits it. If the project uses a project-level xcconfig, write it there. If the project is pbxproj-only, no MCP tool can write a project-level pbxproj setting — `SKILL.md` Phase 5 Step 1a guides the user through Xcode's Build Settings UI and then verifies via grep on `project.pbxproj`.
      2. No simulator handling is required: the build system automatically drops `arm64e` from a simulator SDK's effective architectures (simulator SDKs define no `arm64e`), so simulator builds keep working with `arm64` and need no `ENABLE_POINTER_AUTHENTICATION = NO` override. Only override `ENABLE_POINTER_AUTHENTICATION = NO` (at the target level via `UpdateTargetBuildSetting` or the target's xcconfig) on a target that links a binary dependency not shipping `arm64e` — that dependency can't be linked as `arm64e` on any platform. See `pointer-authentication.md` for the platform / `arm64e` details. Skip if the target already has an explicit value — respect existing user intent.
      
      A second build setting is relevant but outside the cascade: `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = YES` adds the `arm64e.x1` slice, which is a pre-requisite for run time enforcement of checked pointer arithmetic. It defaults to `NO`, is never set implicitly, and applies per target. See `checked-pointer-arithmetic.md`.
      
      ## Part B — Entitlements
      
      All keys live in the target's `.entitlements` file. Each supported target has its own; the audit walks every one.
      
      Required when the capability is enabled:
      
      - [`com.apple.security.hardened-process`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process) `= <true/>` — the main toggle. Without this, the runtime protections below are inert.
      - [`com.apple.security.hardened-process.enhanced-security-version-string`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.enhanced-security-version-string) `= "2"` — selects v2 protections.
      
      Default-ON sub-options (the audit adds these when missing):
      
      - [`com.apple.security.hardened-process.hardened-heap`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.hardened-heap) — Memory Safety category. Adds extra type-isolation buckets to the allocator at runtime, regardless of compiler settings. Most effective in combination with the cascaded `CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT` / `CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT` build settings, which communicate type information from the compiler to the allocator.
      - [`com.apple.security.hardened-process.dyld-ro`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.dyld-ro) — Runtime Protections. Marks dyld state read-only.
      - [`com.apple.security.hardened-process.platform-restrictions-string`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.platform-restrictions-string) `= "2"` — Runtime Protections. Dyld + Mach messaging restrictions.
      
      Default-OFF sub-options (audit reports state, does **not** auto-enable):
      
      - [`com.apple.security.hardened-process.checked-allocations`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations) and its related keys — Hardware Memory Tagging (MTE). See `hardware-memory-tagging.md` for supported hardware. Recommend soft-mode rollout when reporting state.
      - [`com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow) — Checked Pointer Arithmetic (CPA2). Also needs the `arm64e.x1` slice from `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` for run time enforcement. See `checked-pointer-arithmetic.md`.
      
      ## Settings implied by Enhanced Security
      
      These are automatically configured when `ENABLE_ENHANCED_SECURITY = YES` and do not need to be set explicitly:
      
      - `GCC_WARN_SHADOW` — `-Wshadow`, detects variable declarations that shadow other variables.
      - `CLANG_WARN_EMPTY_BODY` — `-Wempty-body`, detects empty bodies in control flow statements.
      - `ENABLE_SECURITY_COMPILER_WARNINGS` — enables additional security-focused warnings (`-Wbuiltin-memcpy-chk-size`, `-Wformat-nonliteral`, `-Warray-bounds`, etc.). See `security-compiler-warnings.md`.
      - `CLANG_CXX_STANDARD_LIBRARY_HARDENING` — set to `fast` in Release builds and `debug` in Debug builds (the cascade handles per-configuration differentiation automatically). This enables the hardened libc++ runtime checks only. It does NOT enable unsafe buffer usage warnings — that requires `ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS` separately (see `cpp-hardening.md`).
      - `CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT` — communicates type information from the compiler to the allocator for C code. Works in combination with the `hardened-heap` sub-option of Enhanced Security (see below).
      - `CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT` — same, for C++ code.
      
      ## Settings NOT covered by Enhanced Security
      
      These must be set independently and are out of scope for this reference:
      
      - All `CLANG_ANALYZER_SECURITY_*` checkers
      - Additional `CLANG_WARN_*` / `GCC_WARN_*` diagnostics not flipped by Enhanced Security (e.g. `CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION`, `GCC_WARN_ABOUT_RETURN_TYPE`)
      - `GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS`, `CLANG_TIDY_*`
      - `ENABLE_C_BOUNDS_SAFETY` / `ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS` (defensive programming models, separate adoption)
      
    • hardware-memory-tagging.md 4.1 KB
      # Hardware Memory Tagging
      
      Hardware memory tagging (Memory Integrity Enforcement) uses ARM Memory Tagging Extension (MTE) to detect use-after-free and out-of-bounds memory access at runtime.
      
      > **Apple developer documentation:** entitlement reference for [`com.apple.security.hardened-process.checked-allocations`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations) (and its sub-options [`soft-mode`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.soft-mode), [`enable-pure-data`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.enable-pure-data), [`no-tagged-receive`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.no-tagged-receive)).
      
      ## What It Does
      
      Each memory allocation and pointer receives an embedded **tag** value. When your app accesses memory through a pointer, the hardware checks that the pointer's tag matches the allocation's tag. If the tags don't match — because of a use-after-free, buffer overflow, or other memory corruption — the app crashes instead of performing the unsafe access.
      
      Checked pointer arithmetic is the companion protection on platforms that support it: it stops pointer arithmetic from overflowing into the tag in the first place. See `checked-pointer-arithmetic.md`.
      
      ## What Vulnerabilities It Mitigates
      
      - **Use-after-free** — accessing memory after it has been freed (the freed memory gets a new tag)
      - **Heap buffer overflow** — accessing memory beyond the allocated region (adjacent allocations have different tags)
      - **Out-of-bounds access** — reading or writing past array boundaries
      - **Double-free** — freeing memory that has already been freed
      
      ## How to Enable
      
      **Xcode UI:** Signing & Capabilities > Enhanced Security > Memory Safety > click "Enable Hardware Memory Tagging"
      
      **Entitlement:** `com.apple.security.hardened-process.checked-allocations`
      
      ### Soft Mode.
      
      Soft mode produces **simulated crashes** (crash reports) instead of actually terminating the app. Use this to find memory bugs without impacting users.
      
      **Entitlement:** `com.apple.security.hardened-process.checked-allocations.soft-mode`
      
      Soft mode is enabled by default when you first enable hardware memory tagging. After reviewing crash reports and fixing issues, disable soft mode for enforcement.
      
      **Xcode UI:** Under Memory Safety, deselect "Enable Soft Mode for Memory Tagging"
      
      ### Debugging Diagnostics
      
      For detailed diagnostics during development, navigate to Scheme Editor > Run > Diagnostics > enable "Hardware Memory Tagging".
      
      ### Additional Entitlements
      
      - `com.apple.security.hardened-process.checked-allocations.enable-pure-data` — extends tagging to pure data allocations
      - `com.apple.security.hardened-process.checked-allocations.no-tagged-receive` — prevents receiving tagged pointers from other processes
      
      ## Code Changes Required
      
      None for basic adoption. Hardware memory tagging is a runtime enforcement mechanism — no source code annotations are needed. However, code with latent memory bugs will safely abort (or produce simulated crash reports in soft mode).
      
      ## How to Disable
      
      **Xcode UI:** Under Memory Safety, deselect "Enable Hardware Memory Tagging"
      
      Remove the `com.apple.security.hardened-process.checked-allocations` entitlement.
      
      ## Platform Availability
      
      - **Hardware:** Available on iPhone and iPad with an A19 chip or later, Mac and Apple Vision Pro with an M5 chip or later, and Apple Watch with an S11 chip or later. (The iPhone 17 family is the first A19 generation.)
      
      ## Performance and Stability Impact
      
      - **Performance:** Moderate overhead due to hardware tag checking on every memory access. Profile your app.
      - **Stability:** Code with latent memory bugs **will crash**. Use soft mode first to identify and fix issues before enforcing.
      - **Adoption path:** Enable soft mode > review simulated crash reports > fix memory bugs > disable soft mode for production.
      
    • pointer-authentication.md 6.8 KB
      # Pointer Authentication
      
      Pointer authentication protects against control-flow hijacking attacks by signing pointers with cryptographic metadata and verifying the signatures before use.
      
      > **Apple developer documentation:** [Preparing your app to work with pointer authentication](doc://com.apple.documentation/documentation/Security/preparing-your-app-to-work-with-pointer-authentication).
      
      ## What It Does
      
      When enabled, the build system adds an **arm64e** slice — it appends `arm64e` to `ARCHS_STANDARD` alongside the existing `arm64`, so the target builds both slices — and arm64e enables pointer authentication. The system:
      
      1. Generates signature metadata for pointers your app creates (memory allocation, C++ object construction)
      2. Validates that signatures are unchanged when your app accesses memory through those pointers
      3. Crashes your app if a pointer's signature is invalid
      
      This prevents an attacker from overwriting function pointers or return addresses to redirect your app's control flow.
      
      A second slice builds on this one: `arm64e.x1` adds other features on top of pointer authentication. It also raises the pointer-authentication baseline itself, because the compiler targets two features that plain `arm64e` does not:
      
      - **FPAC** — a failed authentication faults at the authenticating instruction, instead of producing a pointer that faults later when it is used.
      - **PAC with LR diversity** (`pauth-lr`) — return-address signing mixes in the address of the signing instruction, so a signed return address cannot be replayed at a different call site.
      
      ## What Vulnerabilities It Mitigates
      
      - **Control-flow hijacking** — overwriting function pointers, vtable pointers, or return addresses
      - **ROP/JOP attacks** — chaining existing code gadgets by corrupting pointer values
      - **Code injection via pointer corruption** — modifying data pointers to point to attacker-controlled memory
      
      ## How to Enable
      
      **Xcode UI:** Signing & Capabilities > Enhanced Security > check "Authenticate Pointers"
      
      **Build setting:** `ENABLE_POINTER_AUTHENTICATION = Yes`
      
      This is enabled by default when you add the Enhanced Security capability.
      
      For detailed usage, see [Improving control flow integrity with pointer authentication](https://developer.apple.com/documentation/Apple-Silicon/improving-control-flow-integrity-with-pointer-authentication).
      
      ## How to Disable
      
      **Xcode UI:** Uncheck "Authenticate Pointers" in the Enhanced Security capability
      
      **Build setting:** `ENABLE_POINTER_AUTHENTICATION = No`
      
      ## Swift Package Manager Support
      
      Swift Package dependencies are not automatically built for arm64e when the main project enables pointer authentication. To build SPM packages with arm64e, set workspace-level flags in the project's embedded workspace settings.
      
      For a `.xcodeproj` (which contains an implicit workspace at `MyProject.xcodeproj/project.xcworkspace/`):
      
      ```bash
      plutil -create xml1 MyProject.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      plutil -insert iOSPackagesShouldBuildARM64e -bool YES MyProject.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      plutil -insert macOSPackagesShouldBuildARM64e -bool YES MyProject.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      plutil -insert visionOSPackagesShouldBuildARM64e -bool YES MyProject.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      ```
      
      For a standalone `.xcworkspace`:
      
      ```bash
      plutil -create xml1 MyWorkspace.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      plutil -insert iOSPackagesShouldBuildARM64e -bool YES MyWorkspace.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      plutil -insert macOSPackagesShouldBuildARM64e -bool YES MyWorkspace.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      plutil -insert visionOSPackagesShouldBuildARM64e -bool YES MyWorkspace.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
      ```
      
      Set the flags for each platform your project targets.
      
      For binary SPM dependencies (XCFrameworks), the XCFramework must include an arm64e slice. If it only contains arm64, linking will fail. Contact the dependency vendor for a universal (arm64 + arm64e) build.
      
      ## Library and Framework Authors
      
      Pointer authentication is **highly recommended** for libraries and frameworks distributed to other developers (e.g. a Swift Package, CocoaPod, or `.xcframework`). Enabling it already builds a **universal binary** — `arm64e` is appended alongside `arm64`, so the artifact contains both slices and consumers pick whichever matches their own build. For a distributed target, just make sure the shipped (Release) configuration builds the full arch list. Do not disable pointer authentication on the library to avoid the larger artifact; the size increase is the accepted tradeoff for control-flow integrity in shipped library code, and only one slice is loaded at runtime. See `universal-binaries-for-libraries.md` for the qualifying product types, the distribution check, and XCFramework guidance.
      
      ## Platform Availability
      
      **Platforms that support arm64e:**
      - iOS / iPadOS (SDKROOT: `iphoneos`)
      - macOS (SDKROOT: `macosx`)
      - visionOS (SDKROOT: `xros`)
      - DriverKit (SDKROOT: `driverkit`)
      - tvOS (SDKROOT: `appletvos`)
      - watchOS (SDKROOT: `watchos`)
      
      Every device platform defines an `arm64e` architecture and carries `arm64` in `ARCHS_STANDARD`, so enabling pointer authentication appends an `arm64e` slice on each of them — the build system treats them identically.
      
      **Platforms that do NOT support arm64e:**
      - Simulator (any `*simulator` SDKROOT) — the simulator SDKs define no `arm64e` architecture.
      
      When `ENABLE_ENHANCED_SECURITY = YES` cascades `ENABLE_POINTER_AUTHENTICATION = YES` project-wide, `arm64e` is appended to the architecture list for every destination whose `ARCHS_STANDARD` contains `arm64`. This is safe for the Simulator with **no action required**: simulator SDKs define no `arm64e` architecture, so the build system drops `arm64e` from a simulator build's effective architectures automatically. The simulator slice simply builds as `arm64` (plus `x86_64`) without pointer authentication, while device builds still get the `arm64e` slice. Do **not** add an `ENABLE_POINTER_AUTHENTICATION = NO` override for the simulator: it is unnecessary, and an unconditional one would also disable pointer authentication on device builds.
      
      ## Performance and Stability Impact
      
      - **Performance:** Low overhead. Pointer signing/verification is done in hardware.
      - **Stability:** Code that manipulates raw pointers, casts between function pointer types, or uses inline assembly with pointers may crash. Test thoroughly.
      - **Compatibility:** arm64e binaries are separate from arm64. Need to rebuild dependencies as arm64e. **If there are binary dependencies that you don't have the source code for, you will need to reach out to your dependency vendor to get a universal (arm64 and arm64e) version of the dependency.
      
    • reading-build-settings.md 5.7 KB
      # Reading Build Settings
      
      How to consume `GetTargetBuildSettings` output during a security audit, and how to assemble the audit table that Phases 2–4 of `SKILL.md` rely on.
      
      ## Schema
      
      `GetTargetBuildSettings` returns:
      
      ```json
      { "buildSettings": [ { "macroName": "...", "evaluatedValue": "...", "value": "...", "targetValue": "..." }, ... ] }
      ```
      
      Field reference:
      
      - **`macroName`** — setting name (always present).
      - **`evaluatedValue`** — fully resolved value after `$(...)` macro expansion. This is what the build actually sees. Use this for audit decisions. May be omitted when the resolved value is empty — treat its absence as an empty string.
      - **`value`** — raw, unexpanded value as written in the source (often missing).
      - **`targetValue`** — present only when the setting is explicitly set at the **target** level (vs. inherited from project level). Use this to detect per-target overrides.
      
      `value` might hold the default value of the setting — read the xcconfig and pbxproj files directly to see if the value was overridden or it's just the default.
      
      ## Filter recipes
      
      If `GetTargetBuildSettings` writes its output to a saved file due to a token limit, run `scripts/filter_build_settings.py` against that file to extract the tracked macros (security-reference macros plus `CODE_SIGN_ENTITLEMENTS`, `SDKROOT`, `SUPPORTED_PLATFORMS`). Do not read the saved file linearly.
      
      The script lives at `scripts/filter_build_settings.py` (relative to the skill root). It derives its filter regex from `references/security-settings-reference.md` at runtime, so adding settings to the reference automatically extends the filter. Override with `--regex` if you need a narrower filter.
      
      ### Compact `name=value` view
      
      ```sh
      python3 scripts/filter_build_settings.py <saved-file>
      ```
      
      ### With explicit target-override flag
      
      ```sh
      python3 scripts/filter_build_settings.py <saved-file> --show-overrides
      ```
      
      ### Show only unhardened settings
      
      ```sh
      python3 scripts/filter_build_settings.py <saved-file> --unhardened-only
      ```
      
      The `--show-overrides` and `--unhardened-only` flags can be combined.
      
      ## The audit table
      
      The audit table is a per-(target, tracked macro) view assembled by Phase 3 of `SKILL.md`. Phases 4–6 consume it; nothing else is re-fetched. Each target's rows physically live in that target's `Audit <target>` task description — see `SKILL.md` Phase 3 Step 4 for the on-task format.
      
      A *tracked macro* is either:
      
      - a **security-reference macro** (from `security-settings-reference.md`) — the build settings whose values the audit evaluates, or
      - one of three additional macros — `CODE_SIGN_ENTITLEMENTS`, `SDKROOT`, `SUPPORTED_PLATFORMS` — that downstream phases read to locate the entitlements plist and decide platform eligibility.
      
      ### Columns
      
      | Column | Meaning |
      |---|---|
      | `target` | the target name |
      | `macroName` | the setting name — a security-reference macro or one of `CODE_SIGN_ENTITLEMENTS` / `SDKROOT` / `SUPPORTED_PLATFORMS` |
      | `evaluatedValue` | what the build sees (from `GetTargetBuildSettings` JSON) |
      | `setAtTargetLevel` | `yes` if `targetValue` is present in the JSON, else `no` |
      | `numMatchesInXCConfigs` | count of `*.xcconfig` lines (under project-root) mentioning this macro |
      | `numMatchesInPbxproj` | count of `project.pbxproj` lines mentioning this macro |
      | `matchLocations` | citations from all sources, joined by `; `. Each entry is either `target` or `<source>:<file>:<line>[,<line>...]` (line numbers grouped per (source, file)). File paths are relative to `<project-root>`. |
      
      ### Construction recipe
      
      1. **Per target.** Call `GetTargetBuildSettings`, run `scripts/filter_build_settings.py` over its output, and record `evaluatedValue` and `setAtTargetLevel` per tracked macro.
      2. **Project-wide once.** Scan in two passes with the filter regex: `XcodeGrep` over `*.xcconfig`, and `grep -nE` via Bash on `<project-root>/<ProjectName>.xcodeproj/project.pbxproj` (Xcode's project description file inside the `.xcodeproj` bundle). Group hits by (source, file) and per macro count `numMatchesInXCConfigs` / `numMatchesInPbxproj`; collect the file:line citations into `matchLocations`.
      3. **Join.** For each (target, tracked macro), emit one row combining the per-target columns with the project-wide counts and citations.
      
      The filter regex comes from `references/security-settings-reference.md` (backtick-quoted macro names extracted at runtime) together with `CODE_SIGN_ENTITLEMENTS`, `SDKROOT`, and `SUPPORTED_PLATFORMS`; both the script and the project-wide grep share it, so adding a setting to the reference automatically extends both.
      
      ### Predicates
      
      Three named predicates referenced from `SKILL.md`. They apply to the security-reference macros. The other three (`CODE_SIGN_ENTITLEMENTS`, `SDKROOT`, `SUPPORTED_PLATFORMS`) are path/identifier values, not security toggles, so the YES/NO comparisons in the predicates are not meaningful for them.
      
      - **already hardened** ≡ `evaluatedValue ∈ {YES, YES_AGGRESSIVE, YES_ERROR}`
      - **at default OFF** ≡ `evaluatedValue = NO` AND `setAtTargetLevel = no` AND `numMatchesInXCConfigs = 0` AND `numMatchesInPbxproj = 0`
      - **deliberately disabled** ≡ `evaluatedValue ∉ {YES, YES_AGGRESSIVE, YES_ERROR}` AND (`setAtTargetLevel = yes` OR `numMatchesInXCConfigs > 0` OR `numMatchesInPbxproj > 0`)
      
      ## Product type
      
      The target's product type identifier comes from `XcodeListTargets` (`PRODUCT_TYPE_IDENTIFIER`). It matches the strings used in `enhanced-security.md` ("Supported Product Types") and `universal-binaries-for-libraries.md` ("Qualifying Product Types"), so phases that classify targets by capability can compare against those lists directly.
      
      Targets with `IS_AGGREGATE = true` have no product type and are skipped at enumeration time (see `SKILL.md` Phase 3 Step 3).
      
    • readonly-platform-memory.md 2.5 KB
      # Read-Only Platform Memory
      
      Marks regions of memory used by the platform for internal state (such as the dynamic loader) as read-only, preventing tampering.
      
      > **Apple developer documentation:** entitlement reference for [`com.apple.security.hardened-process.dyld-ro`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.dyld-ro).
      
      ## What It Does
      
      Informs the system to mark memory regions in your process that the platform uses for its internal state as **read-only**. This primarily protects the dynamic loader (dyld) internal data structures from being modified by an attacker who has achieved code execution in your process.
      
      ## What Vulnerabilities It Mitigates
      
      - **Dyld state tampering** — an attacker modifying the dynamic loader's internal data to redirect library loading
      - **Runtime metadata corruption** — overwriting platform-internal data structures to alter program behavior
      - **Post-exploitation persistence** — modifying loader state to maintain control after initial exploitation
      
      ## How to Enable
      
      **Xcode UI:** Signing & Capabilities > Enhanced Security > check "Enable Read-Only Platform Memory"
      
      **Entitlement:** `com.apple.security.hardened-process.dyld-ro`
      
      Enabled by default when you add the Enhanced Security capability.
      
      ## Code Changes Required
      
      **Usually none.** In most applications, this entitlement requires no code changes.
      
      The only exception: if your app **modifies data in protected memory regions** (for example, modifying the value of `const` data sections), the system will crash your app. Fix: remove the code that writes to read-only memory.
      
      ## How to Disable
      
      **Xcode UI:** Uncheck "Enable Read-Only Platform Memory" in the Enhanced Security capability
      
      ## Platform Availability
      
      - iOS, iPadOS, macOS, visionOS
      - Available on all supported hardware
      
      ## Performance and Stability Impact
      
      - **Performance:** None. Memory is marked read-only at load time; no ongoing runtime checks.
      - **Stability:** Unless your code writes to `const` data sections or platform-internal memory (which is already a bug), this has zero impact.
      
      ## Why This Feature Is Low-Risk
      
      Read-only platform memory is one of the safest Enhanced Security features:
      - No runtime cost
      - No code changes for well-behaved code
      - Only crashes code that was already doing something wrong (writing to `const` memory)
      - Provides meaningful protection against post-exploitation techniques
      
      Enable this early alongside compiler warnings and stack zero init.
      
    • runtime-restrictions.md 2.8 KB
      # Additional Run-time Restrictions
      
      Adds runtime checks on dynamic libraries your app loads and Mach messages your app receives, preventing common code injection and privilege escalation attacks.
      
      > **Apple developer documentation:** entitlement reference for [`com.apple.security.hardened-process.platform-restrictions-string`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.platform-restrictions-string).
      
      ## What It Does
      
      Informs the system to perform additional checks on:
      
      1. **Dynamic libraries** — validates libraries your app or extension loads at runtime
      2. **Mach messages** — validates Mach messages your app or extension receives from other processes
      
      Potentially insecure situations are turned into crashes rather than allowing an attacker to gain privileged access through Mach ports.
      
      ## What Vulnerabilities It Mitigates
      
      - **Dylib injection** — an attacker loading malicious dynamic libraries into your process
      - **Mach port attacks** — exploiting Mach IPC to send crafted messages to your process
      - **Privilege escalation via IPC** — using Mach messages to gain access to your app's privileges or data
      
      ## How to Enable
      
      **Xcode UI:** Signing & Capabilities > Enhanced Security > check "Enable Additional Runtime Platform Restrictions"
      
      **Entitlement:** `com.apple.security.hardened-process.platform-restrictions-string`
      
      Enabled by default when you add the Enhanced Security capability.
      
      ## Code Changes Required
      
      **If your app uses XPC for IPC** (and doesn't use raw Mach IPC traps): likely no code changes needed.
      
      **If your app uses raw Mach IPC traps:** you may need to update your code. The runtime restrictions turn potentially insecure Mach messaging patterns into crashes. For details on what patterns to fix, see [Conforming to Mach IPC security restrictions](https://developer.apple.com/documentation/xcode/conforming-to-mach-ipc-security-restrictions).
      
      **If your app has no explicit IPC mechanism:** no code changes needed.
      
      ## How to Disable
      
      **Xcode UI:** Uncheck "Enable Additional Runtime Platform Restrictions" in the Enhanced Security capability
      
      ## Platform Availability
      
      - iOS, iPadOS, macOS, visionOS
      - Available on all supported hardware
      
      ## Performance and Stability Impact
      
      - **Performance:** Negligible. The checks run at library load time and message receive time, not on every operation.
      - **Stability:** Apps using XPC or no IPC are unaffected. Apps using raw Mach IPC may crash if they use insecure messaging patterns — review and fix these before enabling.
      
      ## Decision Guide
      
      | Your IPC approach | Impact | Action needed |
      |---|---|---|
      | No IPC | None | Safe to enable |
      | XPC only | None | Safe to enable |
      | Mach IPC via higher-level APIs | Low | Test, review for issues |
      | Raw Mach IPC traps | Moderate | Read Mach IPC conformance guide, fix insecure patterns |
      
    • security-compiler-warnings.md 3.2 KB
      # Security Compiler Warnings
      
      Enhanced Security enables a set of compiler warnings that help identify potentially insecure C and C++ code patterns at build time.
      
      ## What It Does
      
      Enables two categories of compiler warnings:
      
      ### Standard Warnings (always-on with Enhanced Security)
      
      | Warning Flag | What It Detects |
      |---|---|
      | `-Wshadow` | Variable declarations that shadow other variables or type aliases |
      | `-Wempty-body` | Empty bodies in control flow statements (`if`, `for`, `while`) |
      
      ### Additional Security Warnings
      
      Enabled via the `ENABLE_SECURITY_COMPILER_WARNINGS` build setting:
      
      | Warning Flag | What It Detects |
      |---|---|
      | `-Wbuiltin-memcpy-chk-size` | `memcpy` destination buffer smaller than copy size |
      | `-Wformat-nonliteral` | `printf`-style format string that isn't a string literal |
      | `-Warray-bounds` | Array index before beginning or past end of array; array argument smaller than function expects |
      | `-Warray-bounds-pointer-arithmetic` | Pointer arithmetic resulting in out-of-bounds pointer |
      | `-Wsuspicious-memaccess` | Suspicious memory operations: acting on vtable pointers, transposed `memset` args, non-trivially-copyable objects, zero-size operations |
      | `-Wsizeof-array-div` | Incorrect `sizeof` calculation for array element count due to wrong types |
      | `-Wsizeof-pointer-div` | `sizeof` returning pointer size instead of array size |
      | `-Wreturn-stack-address` | Returning address of a local (stack) variable to the caller |
      
      ## What Vulnerabilities It Mitigates
      
      - **Buffer overflows** — `memcpy` size mismatches, array bounds violations
      - **Format string attacks** — non-literal format strings that an attacker could control
      - **Use-after-return** — returning pointers to stack-allocated data
      - **Logic bugs** — variable shadowing, empty control flow bodies, transposed arguments
      
      ## How to Enable
      
      **Build settings:**
      - `-Wshadow`: `GCC_WARN_SHADOW = Yes`
      - `-Wempty-body`: `CLANG_WARN_EMPTY_BODY = Yes`
      - Additional security warnings: `ENABLE_SECURITY_COMPILER_WARNINGS = Yes`
      
      All are cascaded automatically when `ENABLE_ENHANCED_SECURITY = YES` — no manual setup needed if Enhanced Security is enabled.
      
      ## Code Changes Required
      
      Fix the warnings. Common fixes include:
      - Rename shadowed variables
      - Add bounds checks before array access
      - Use string literals for format strings, or mark intentional non-literal formats with appropriate attributes
      - Fix `sizeof` calculations to use the correct types
      - Remove or populate empty control flow bodies
      
      ## How to Disable
      
      - `-Wshadow`: `GCC_WARN_SHADOW = No`
      - `-Wempty-body`: `CLANG_WARN_EMPTY_BODY = No`
      - Additional security warnings: `ENABLE_SECURITY_COMPILER_WARNINGS = No`
      
      ## Platform Availability
      
      - All platforms — these are compile-time checks with no runtime component
      
      ## Performance and Stability Impact
      
      - **Performance:** Zero runtime cost. These are compile-time warnings only.
      - **Stability:** No runtime behavior change. Fixing the warnings improves code correctness.
      
      ## Why This Feature Is Low-Risk
      
      Security compiler warnings are the safest Enhanced Security feature:
      - Zero runtime cost
      - No behavior changes — only build-time diagnostics
      - Warnings identify real bugs that should be fixed regardless of security posture
      
      Enable this first, before any other Enhanced Security feature.
      
    • security-settings-reference.md 10.5 KB
      # Security Settings Reference
      
      Complete reference for the security build settings and entitlements managed by this skill, organized by application order.
      
      > **Skill-internal use only.** Do not call this the "catalog" or use terms like "catalog macro" / "catalog regex" in user-facing narration — those are skill-internal jargon. In any text shown to the user, describe what's being checked plainly: "the known security build settings", "the security setting `CLANG_WARN_…`", etc.
      
      **Language relevance:** Only enable or inquire about a setting if the codebase contains code in a language the setting applies to. The Scope column indicates which languages each setting is relevant to. Do not enable clang-only settings for pure Swift codebases.
      
      **Filtering recipe.** `scripts/filter_build_settings.py` filters `GetTargetBuildSettings` output to entries in this reference; it derives its filter regex from this file at runtime by extracting backtick-quoted macro names. Adding a new setting here automatically extends the filter. See `references/reading-build-settings.md` for usage.
      
      ## Warnings — Always Enable
      
      ### Compiler Warnings
      
      Fire on every build.
      
      | Build Setting | Value | CLI Flag | Scope | Why Safe |
      |---|---|---|---|---|
      | `GCC_WARN_ABOUT_RETURN_TYPE` | `YES_ERROR` | `-Werror=return-type` | C/C++/ObjC/ObjC++ | Missing returns are always bugs |
      | `GCC_WARN_UNINITIALIZED_AUTOS` | `YES_AGGRESSIVE` | `-Wuninitialized -Wconditional-uninitialized` | C/C++/ObjC/ObjC++ | Real bugs, rarely false |
      | `CLANG_WARN_IMPLICIT_FALLTHROUGH` | `YES` | `-Wimplicit-fallthrough` | C/C++/ObjC/ObjC++ | Catches logic bugs in switch |
      | `GCC_WARN_64_TO_32_BIT_CONVERSION` | `YES` | `-Wshorten-64-to-32` | C/C++/ObjC/ObjC++ | Truncation is a real issue |
      | `GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS` | `YES` | `-Werror=implicit-function-declaration` | C/ObjC | Implicit decls cause wrong return types |
      
      ### Static Analyzer Warnings
      
      Run during *Build and analyze*, not regular builds.
      
      | Build Setting | Value | CLI Flag | Scope | Why Safe |
      |---|---|---|---|---|
      | `CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER` | `YES` | checker: `security.FloatLoopCounter` | C/C++/ObjC/ObjC++ | Low false-positive rate |
      | `CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND` | `YES` | checker: `security.insecureAPI.rand` | C/C++/ObjC/ObjC++ | Flags insecure random |
      | `CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY` | `YES` | checker: `security.insecureAPI.strcpy` | C/C++/ObjC/ObjC++ | Flags unsafe string ops |
      
      ### Clang-Tidy Warnings
      
      Clang-tidy-integrated checks that are part of the clang static analyzer; they fire only during *Build and analyze* (or `clang --analyze`), never on normal builds. There is no build-break risk from enabling them, and adopters do not need to install anything extra.
      
      | Build Setting | Value | CLI Flag | Scope | Why Safe |
      |---|---|---|---|---|
      | `CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION` | `YES` | static analyzer check (integrated from clang-tidy): `bugprone-redundant-branch-condition` | C/C++/ObjC/ObjC++ | Runs during Build and analyze, not regular builds |
      
      ## Enhanced Security — Capability
      
      ### Build Settings
      
      | Build Setting | Value | CLI Flag / Effect | Note |
      |---|---|---|---|
      | `ENABLE_ENHANCED_SECURITY` | `YES` | Enables the Enhanced Security capability (build-setting + entitlements) | See `enhanced-security.md` |
      | `ENABLE_POINTER_AUTHENTICATION` | `YES` | Appends `arm64e` to `ARCHS_STANDARD` — builds both `arm64` and `arm64e` slices | Set at project level. |
      | `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` | `YES` | Appends `arm64e.x1` to `ARCHS_STANDARD`, with checked pointer arithmetic instructions and other features. | Defaults to `NO` and is **not** cascaded by `ENABLE_ENHANCED_SECURITY` — set it explicitly, per target. |
      
      **Cascaded by `ENABLE_ENHANCED_SECURITY` (do not set manually):**
      
      | Build Setting | Value | Effect | Note |
      |---|---|---|---|
      | `GCC_WARN_SHADOW` | `YES` | `-Wshadow` — variable declarations that shadow other variables | See `security-compiler-warnings.md` |
      | `CLANG_WARN_EMPTY_BODY` | `YES` | `-Wempty-body` — empty bodies in control flow statements | See `security-compiler-warnings.md` |
      | `ENABLE_SECURITY_COMPILER_WARNINGS` | `YES` | Enables additional security warnings (`-Wformat-nonliteral`, `-Warray-bounds`, etc.) | See `security-compiler-warnings.md` |
      | `CLANG_CXX_STANDARD_LIBRARY_HARDENING` | `fast` / `debug` | Hardened libc++ runtime checks (fast in Release, debug in Debug — cascade handles per-configuration automatically) | Does not include unsafe buffer warnings — see `cpp-hardening.md` |
      | `CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT` | `YES` | Communicates type information to the allocator for C code | Most effective with the `hardened-heap` sub-option of Enhanced Security |
      | `CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT` | `YES` | Communicates type information to the allocator for C++ code | Most effective with the `hardened-heap` sub-option of Enhanced Security |
      
      ### Entitlements
      
      These are managed per-target in each target's `.entitlements` file. See `enhanced-security.md` Part B for full details.
      
      **Required (always add when enabling Enhanced Security):**
      
      - [`com.apple.security.hardened-process`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process) = `<true/>` — main toggle for runtime protections
      - [`com.apple.security.hardened-process.enhanced-security-version-string`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.enhanced-security-version-string) = `"2"` — selects v2 protections
      
      **Default-ON (add when missing):**
      
      - [`com.apple.security.hardened-process.hardened-heap`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.hardened-heap) — adds type-isolation buckets to the allocator at runtime; most effective with the cascaded `CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT` / `CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT` build settings (Memory Safety)
      - [`com.apple.security.hardened-process.dyld-ro`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.dyld-ro) — marks dyld state read-only (Runtime Protections)
      - [`com.apple.security.hardened-process.platform-restrictions-string`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.platform-restrictions-string) = `"2"` — dyld + Mach messaging restrictions (Runtime Protections)
      
      **Default-OFF (report state, do not auto-enable):**
      
      - [`com.apple.security.hardened-process.checked-allocations`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations) — hardware memory tagging (MTE)
      - [`com.apple.security.hardened-process.checked-allocations.soft-mode`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.soft-mode) — simulated crash reports without termination
      - [`com.apple.security.hardened-process.checked-allocations.enable-pure-data`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.enable-pure-data) — tag non-pointer heap allocations
      - [`com.apple.security.hardened-process.checked-allocations.no-tagged-receive`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.no-tagged-receive) — opt out of receiving tagged pointers via Mach IPC
      - [`com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow) — checked pointer arithmetic; needs the `arm64e.x1` slice from `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` and other requisites
      
      ## Additional Settings — Potentially More False Positives
      
      | Build Setting | Value | CLI Flag | Scope | Note |
      |---|---|---|---|---|
      | `CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION` | `YES` | `-Wconversion` | C/C++/ObjC/ObjC++ | May be noisy in some codebases |
      | `CLANG_ANALYZER_SECURITY_BUFFER_OVERFLOW_EXPERIMENTAL` | `YES` | checker: `security.ArrayBound` | C/C++/ObjC/ObjC++ | Higher false-positive rate |
      | `CLANG_WARN_ASSIGN_ENUM` | `YES` | `-Wassign-enum` | C/C++/ObjC/ObjC++ | Code quality |
      | `GCC_WARN_SIGN_COMPARE` | `YES` | `-Wsign-compare` | C/C++/ObjC/ObjC++ | Code quality |
      
      ### C++ / DriverKit / IOKit (only if C++ present)
      
      | Build Setting | Value | CLI Flag |
      |---|---|---|
      | `CLANG_ANALYZER_OSOBJECT_C_STYLE_CAST` | `YES` | checker: `optin.osx.OSObjectCStyleCast` |
      
      ### Blocks (only if ObjC, ObjC++, or C with -fblocks present)
      
      | Build Setting | Value | CLI Flag |
      |---|---|---|
      | `CLANG_WARN_COMPLETION_HANDLER_MISUSE` | `YES` | `-Wcompletion-handler` |
      
      ### ObjC-Specific (only if ObjC/ObjC++ present)
      
      | Build Setting | Value | CLI Flag |
      |---|---|---|
      | `CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF` | `YES` | `-Wimplicit-retain-self` |
      | `CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK` | `YES` | `-Warc-repeated-use-of-weak` |
      
      ## Not Auto-Enabled (Mentioned in Report)
      
      | Setting | User-Facing Build Setting | Why Not Auto-Enabled |
      |---|---|---|
      | C bounds safety | `ENABLE_C_BOUNDS_SAFETY` | Requires annotations, changes language semantics |
      | C++ unsafe buffer usage | `ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS` | Requires rewriting buffer patterns |
      | Hardware memory tagging | `com.apple.security.hardened-process.checked-allocations` | See `hardware-memory-tagging.md` for supported hardware |
      
      ## Default-ON Security Checkers — Audit Only
      
      These default to YES in Xcode. The skill does not actively enable them, but Phase 3 will flag them if explicitly set to NO.
      
      | Build Setting | Value | What It Checks | Scope |
      |---|---|---|---|
      | `CLANG_ANALYZER_SECURITY_KEYCHAIN_API` | `YES` | Improper Keychain API usage | C/C++/ObjC/ObjC++ |
      | `CLANG_ANALYZER_SECURITY_INSECUREAPI_UNCHECKEDRETURN` | `YES` | Unchecked return values from security APIs | C/C++/ObjC/ObjC++ |
      | `CLANG_ANALYZER_SECURITY_INSECUREAPI_GETPW_GETS` | `YES` | Use of insecure `getpw()` and `gets()` | C/C++/ObjC/ObjC++ |
      | `CLANG_ANALYZER_SECURITY_INSECUREAPI_MKSTEMP` | `YES` | Insecure use of `mkstemp()` / `mktemp()` | C/C++/ObjC/ObjC++ |
      | `CLANG_ANALYZER_SECURITY_INSECUREAPI_VFORK` | `YES` | Use of `vfork()` | C/C++/ObjC/ObjC++ |
      | `GCC_WARN_TYPECHECK_CALLS_TO_PRINTF` | `YES` | Format string type checking (`-Wformat`) | C/C++/ObjC/ObjC++ |
      
    • stack-zero-init.md 1.9 KB
      # Stack Zero Initialization
      
      Stack zero initialization automatically zeroes out stack variables when they are created, preventing information leaks from uninitialized memory.
      
      ## What It Does
      
      The compiler initializes all automatic (stack) variables in your code with zeroes. Without this, stack memory retains whatever values were left by previous function calls, which can leak sensitive data if a variable is used before explicit initialization.
      
      ## What Vulnerabilities It Mitigates
      
      - **Information disclosure via uninitialized stack variables** — reading sensitive data left on the stack from a previous function call
      - **Use-of-uninitialized-value bugs** — using a variable before assigning it a value, leading to undefined behavior
      - **Stack-based exploitation** — leveraging predictable uninitialized values to influence control flow
      
      ## How to Enable
      
      **Build setting:** `CLANG_ENABLE_STACK_ZERO_INIT = Yes`
      
      This is enabled by default when you add the Enhanced Security capability.
      
      ## Code Changes Required
      
      None. This is a transparent compiler behavior change.
      
      ## How to Disable
      
      **Build setting:** `CLANG_ENABLE_STACK_ZERO_INIT = No`
      
      ## Platform Availability
      
      - iOS, iPadOS, macOS, visionOS
      - Available on all supported hardware
      
      ## Performance and Stability Impact
      
      - **Performance:** Minimal. The compiler inserts zero-initialization instructions for stack variables. In most code paths this is negligible.
      - **Stability:** This change can only improve stability. If your code relied on reading uninitialized stack values (a bug), the behavior changes — variables will now consistently be zero instead of containing garbage.
      
      ## Why This Feature Is Low-Risk
      
      Stack zero initialization is one of the safest Enhanced Security features to adopt:
      - No source code changes required
      - No new crash scenarios (zeroing memory cannot cause crashes)
      - Minimal performance impact
      - Catches a real class of security bugs
      
      This should be one of the first features you enable.
      
    • typed-allocators.md 3.2 KB
      # Typed Allocators
      
      > **Apple developer documentation:** [Adopting type-aware memory allocation](doc://com.apple.documentation/documentation/Xcode/adopting-type-aware-memory-allocation).
      
      Typed allocator support has two complementary pieces that can be enabled separately but are most effective in combination:
      
      1. **Entitlement ([`com.apple.security.hardened-process.hardened-heap`](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.hardened-heap))** — adds extra type-isolation buckets to the allocator at runtime, regardless of compiler settings. This provides baseline type isolation.
      2. **Build settings (`CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT`, `CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT`)** — the compiler communicates type information to the allocator, allowing it to do a better job isolating different types and improving protection against use-after-free vulnerabilities.
      
      Both are enabled by default when you add the Enhanced Security capability (the entitlement as a default-ON sub-option, the build settings as cascaded settings).
      
      ## What It Does
      
      When the build settings are enabled, the compiler tracks the intended type of memory allocations. This means that `malloc`, `calloc`, and similar allocator functions produce pointers that carry type information. Combined with the `hardened-heap` sub-option's runtime type-isolation buckets, this makes it harder for an attacker to exploit type confusion vulnerabilities where memory allocated for one type is used as another.
      
      ## What Vulnerabilities It Mitigates
      
      - **Type confusion** — treating a pointer to type A as a pointer to type B after allocation
      - **Allocator-based exploitation** — abusing custom allocator wrappers to bypass type safety
      
      ## How to Enable
      
      **Xcode UI:** Signing & Capabilities > Enhanced Security > check "Enable Typed Allocators"
      
      **Build settings:**
      - C code: `CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT = Yes`
      - C++ code: `CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT = Yes`
      
      **Entitlement:** `com.apple.security.hardened-process.hardened-heap`
      
      All are enabled by default when you add the Enhanced Security capability (build settings are cascaded by `ENABLE_ENHANCED_SECURITY`; entitlement is a default-ON sub-option).
      
      ## Code Changes Required
      
      If your code uses **custom memory-allocator wrapper functions**, you may need to update them to propagate type information. Standard `malloc`/`free` usage typically requires no changes.
      
      For details on updating custom allocators, see [Adopting type-aware memory allocation](https://developer.apple.com/documentation/xcode/adopting-type-aware-memory-allocation).
      
      ## How to Disable
      
      **Build settings:**
      - C: `CLANG_ENABLE_C_TYPED_ALLOCATOR_SUPPORT = No`
      - C++: `CLANG_ENABLE_CPLUSPLUS_TYPED_ALLOCATOR_SUPPORT = No`
      
      **Xcode UI:** Uncheck "Enable Typed Allocators" in the Enhanced Security capability.
      
      ## Platform Availability
      
      - iOS, iPadOS, macOS, visionOS
      - Available on all supported hardware
      
      ## Performance and Stability Impact
      
      - **Performance:** Minimal overhead — type tracking is primarily a compile-time mechanism.
      - **Stability:** Custom allocator wrappers may need updates. Standard allocator usage is unaffected.
      
    • universal-binaries-for-libraries.md 6.1 KB
      # Universal Binaries for Libraries
      
      **Pointer authentication is highly recommended for library and framework targets.** Enabling it (`ENABLE_POINTER_AUTHENTICATION = YES`, directly or via the `ENABLE_ENHANCED_SECURITY` cascade) is by itself enough to produce a **universal binary**: the build system appends `arm64e` to `ARCHS_STANDARD` whenever `arm64` is already present, so the target builds **both** an `arm64` slice and an `arm64e` slice. This happens for any target — application or library — not just libraries; there is no setting that makes pointer authentication produce an `arm64e`-only build.
      
      Once a distributed library is being built with pointer authentication, consider `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = YES` as well. It adds a third slice, so the target builds `arm64`, `arm64e`, and `arm64e.x1`. The `arm64e.x1` slice carries security protections over your code that the `arm64e` slice does not:
      
      - **Checked pointer arithmetic** instructions, which are enforced at run time only if the consuming app's entitlements meet the requirements in `checked-pointer-arithmetic.md`. Entitlements do not apply to library and framework targets, so you ship the slice and the app must enable enforcement.
      - **FPAC** — a failed pointer authentication faults at the authenticating instruction rather than later, when the pointer is used.
      - **PAC with LR diversity** — a signed return address cannot be replayed at a different call site.
      
      The slice exists for iOS and watchOS targets only. Test on hardware that supports it before shipping: as with `arm64e`, latent pointer bugs in library code surface as crashes in the consuming app. See `checked-pointer-arithmetic.md` and `pointer-authentication.md`.
      
      For a library or framework you ship to other developers, a universal binary is exactly what you want: a Mach-O that contains `arm64`, `arm64e` and `arm64e.x1` slices. The dynamic linker (or `lipo` at the static-archive level) selects whichever slice matches the consumer's architecture, so the library author does not force an architecture choice on downstream projects — plain-`arm64` consumers keep working, and consumers who opt into arm64e get the pointer-authentication protections.
      
      The one thing to verify is that the **distributed** build actually emits every slice. `ONLY_ACTIVE_ARCH = YES` (the conventional Debug value) builds only the active development architecture; a Release/distribution configuration uses `ONLY_ACTIVE_ARCH = NO`, so the full `ARCHS` list is built. Distribute the Release artifact (or set `ONLY_ACTIVE_ARCH = NO` for whatever configuration you ship) so every slice in `ARCHS` lands in the binary.
      
      Warn when a library or framework target sets `ONLY_ACTIVE_ARCH = YES` in a Release/distribution configuration: only the active architecture gets built, which forces every consumer onto that single slice — rarely what the library author intends.
      
      Do not skip pointer authentication on the grounds that multiple slices produce a larger binary. The on-disk artifact roughly doubles for two slices, but at runtime dyld loads only the slice matching the running CPU — RAM footprint, code-page residency, and execution cost are unchanged. The alternative (leaving pointer authentication off on the library) gives up control-flow-integrity protections — ROP/JOP mitigation, vtable / function-pointer hijack defense — for every consumer of that library, with no consumer-side knob that can recover them after the fact. Ship both slices.
      
      > "Fat binary" / "fat archive" is the Mach-O-format term used by tools like `lipo` and `nm`. This is known as a **universal binary**.
      
      ## Qualifying Product Types
      
      This document's guidance applies to any target whose product type is in this set:
      
      - `com.apple.product-type.framework` (dynamic framework)
      - `com.apple.product-type.framework.static` (static framework)
      - `com.apple.product-type.library.static` (`.a` static library)
      - `com.apple.product-type.library.dynamic` (`.dylib` dynamic library)
      
      Application, XPC service, system extension, driver extension, and tool targets are out of scope for this document's extra packaging guidance. They already get the universal `arm64`+`arm64e` build from pointer authentication, and because they are not linked into anyone else's project there is no consumer-compatibility concern to manage — no special handling is needed.
      
      ## How to Check
      
      Confirm every expected slice landed in the shipped artifact:
      
      ```bash
      lipo -archs path/to/YourFramework.framework/YourFramework
      # arm64 arm64e                 — with ENABLE_POINTER_AUTHENTICATION = YES
      # arm64 arm64e arm64e.x1       — plus ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = YES
      ```
      
      ## XCFramework Distribution
      
      If you distribute via `.xcframework` (typical for binary Swift Package and CocoaPods deliveries), each per-platform slice inside the XCFramework should itself be a universal binary. Bundle them with `xcodebuild -create-xcframework -framework <ios-device-build> -framework <ios-sim-build> ...` as usual; the `-create-xcframework` step does not change architectures, it just packages already-built frameworks for multiple platforms. To ship the `arm64e.x1` slice as well, leave `ARCHS` unset and let `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = YES` append it.
      
      Note that `arm64e` exists on every device platform (iOS device, macOS, visionOS device, DriverKit, tvOS device, watchOS device) but on no Simulator SDK. Simulator slices stay `arm64` (Apple Silicon Mac) plus `x86_64` (Intel Mac) — see `pointer-authentication.md` for the full platform table. `arm64e.x1` is narrower still: it exists for iOS and watchOS device builds only, so a framework built for several platforms carries that slice on some of them and not others.
      
      ## Related References
      
      - `pointer-authentication.md` — what arm64e and pointer authentication actually do, and the consumer-side compatibility note for binary dependencies.
      - `checked-pointer-arithmetic.md` — the checked pointer arithmetic protection that builds on the `arm64e.x1` slice.
      - `enhanced-security.md` — how Enhanced Security build settings (including pointer authentication) cascade to library/framework targets even though entitlements do not apply to them.
      
  • scripts
    • filter_build_settings.py 2.4 KB
      #!/usr/bin/env python3
      """Filter GetTargetBuildSettings JSON to security-relevant entries.
      
      Usage:
          filter_build_settings.py <saved-file> [--show-overrides] [--unhardened-only] [--regex REGEX]
      """
      
      import argparse
      import json
      import re
      from pathlib import Path
      
      REFERENCE_PATH = (
          Path(__file__).resolve().parent.parent
          / "references"
          / "security-settings-reference.md"
      )
      
      # Settings the script needs that aren't documented in the security reference
      # as security settings but are required to interpret results (entitlements
      # path, SDK, supported platforms).
      EXTRA_NAMES = ("CODE_SIGN_ENTITLEMENTS", "SDKROOT", "SUPPORTED_PLATFORMS")
      
      # Tokens inside backticks that look like build-setting macro names.
      _NAME_RX = re.compile(r"`([A-Z][A-Z0-9_]{2,})`")
      
      HARDENED_VALUES = {"YES", "YES_AGGRESSIVE", "YES_ERROR"}
      
      
      def _load_reference_names(path: Path) -> list[str]:
          text = path.read_text()
          names = set(_NAME_RX.findall(text))
          names.update(EXTRA_NAMES)
          # Longest-first so prefix-like names don't get shadowed in alternation.
          return sorted(names, key=lambda n: (-len(n), n))
      
      
      def _default_regex() -> str:
          return "|".join(re.escape(n) for n in _load_reference_names(REFERENCE_PATH))
      
      
      def main():
          parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
          parser.add_argument("saved_file", help="Path to the saved GetTargetBuildSettings JSON")
          parser.add_argument("--regex", default=None,
                              help="Override the reference-derived default regex")
          parser.add_argument("--show-overrides", action="store_true",
                              help="Annotate target-level overrides with [target-override]")
          parser.add_argument("--unhardened-only", action="store_true",
                              help="Only show settings whose evaluatedValue is not YES/YES_AGGRESSIVE/YES_ERROR")
          args = parser.parse_args()
      
          rx = re.compile(args.regex if args.regex else _default_regex())
          with open(args.saved_file) as f:
              data = json.load(f)
      
          for s in data["buildSettings"]:
              name = s["macroName"]
              val = s.get("evaluatedValue", "")
              if not rx.search(name):
                  continue
              if args.unhardened_only and val in HARDENED_VALUES:
                  continue
              flag = "  [target-override]" if args.show_overrides and "targetValue" in s else ""
              print(f"{name}={val}{flag}")
      
      
      if __name__ == "__main__":
          main()
      
  • SKILL.md 57.8 KB
    ---
    name: audit-xcode-security-settings
    description: |
      Audit and enable security-oriented Xcode build settings. Progressively enables compiler warnings, static analyzer checkers, and Enhanced Security features. Use when: user wants to secure their Xcode project, audit security settings, enable hardening, review security posture of build configuration, set up security-focused static analysis, enable static analysis, improve warning coverage, harden diagnostics, or catch more bugs at compile time in C/C++/Objective-C/Swift. SKIP: network security (TLS/ATS), code signing, privacy APIs.
    ---
    # Audit Xcode Security Settings
    
    Assess an Xcode project's security posture and progressively enable security build settings and entitlements — from broadly applicable warnings through Enhanced Security hardening.
    
    ## Tool Preferences
    
    When XcodeGlob, XcodeGrep, XcodeRead, XcodeLS, and XcodeUpdate tools are available, ALWAYS use them. Do not fall back to Bash filesystem tools (`ls`, `find`, `cat`, `grep`) to learn about the project. They trigger extra permission prompts and bypass project scoping.
    
    **Tool names may carry an MCP server prefix.** These tools are hosted by an MCP server whose name varies by environment (`xcode-mcp`, `xcode-tools`, `xcode`, etc.), so their fully qualified names look like `mcp__<server>__XcodeGlob`. Some harnesses register short aliases (just `XcodeGlob`); others only expose the prefixed form. Do not hardcode a specific server name. On the first call, use whichever form the available-tool registry advertises — look up the prefix once, then reuse it for the rest of the session. If a short-name call fails with an unknown-tool error, do not guess at the prefix: look it up in the registry and retry with the full name.
    
    - **XcodeGlob** for file discovery — `find` is forbidden for files inside the project.
    - **XcodeGrep** for content search — `grep`/`rg` is forbidden for files inside the project.
    - **XcodeRead** for file contents — `cat`/`Read` is forbidden for files registered in the project.
    - **XcodeLS** for directory listing — `ls` is forbidden for any path inside the project.
    - **XcodeUpdate** for in-place edits of project-registered text files (xcconfig files, source files) — same `filePath` / `oldString` / `newString` (+ optional `replaceAll`) signature as the built-in `Edit` tool, but accepts Xcode workspace-relative paths. `Edit` is forbidden for files registered in the project. **Do not** use `XcodeUpdate` / `Edit` / `plutil` to add or update `.entitlements` keys — use `AddEntitlement`.
    - **AddEntitlement** for adding or updating a target's entitlements — pass `targetName`, `entitlementKey`, `entitlementValueType` (`bool` / `string` / `int` / `stringArray` / `dictionary`), and the value. Always prefer it for entitlement changes; it adds or updates only and cannot remove keys.
    - **XcodeListTargets** for enumerating targets — do not parse `project.pbxproj` manually. Returns each target's `PRODUCT_TYPE_IDENTIFIER` and role flags (`IS_AGGREGATE`, `IS_TEST_TARGET`, `IS_APP_EXTENSION`, `SUPPORTS_HOSTING_TESTS`) directly.
    
    **Project root and name are already in the system prompt context.** Do NOT run `ls` to "verify" the project layout before starting. The system prompt already tells you the working directory and the project structure.
    
    **Empty XcodeGlob results are not a failure.** The `.xcodeproj` and `.xcworkspace` are not indexed as files inside the Xcode workspace — `XcodeGlob "**/*.xcodeproj"` correctly returns 0 matches. Use the project name from system-prompt context instead. Do not fall back to filesystem `ls`/`find`.
    
    **All `Xcode*` tools take Xcode workspace-relative paths.** `XcodeGlob`, `XcodeGrep`, `XcodeRead`, `XcodeLS`, `XcodeUpdate`, `XcodeWrite`, and `XcodeRM` interpret their path arguments — and return paths — relative to the Xcode workspace root (what you see at the top of the Project Navigator). Not the git repository root; not the `.xcodeproj` bundle. Anything the user sees in Xcode (entitlements, xcconfig, plan and decision documents, source files) is reachable via its workspace-relative path; pass that path through these tools as-is, and don't construct absolute filesystem paths for it.
    
    To read or edit a specific file:
    - Prefer `XcodeRead` / `XcodeUpdate` with the workspace-relative path. `XcodeRead` reads `.entitlements` plists too — they're project-registered files, navigable just like any source file — so read them this way. To add or update an entitlement, use `AddEntitlement`, not `XcodeUpdate`.
    
    **For entitlements files, never derive the path by hand.** Each target's authoritative entitlements path is the evaluated value of its `CODE_SIGN_ENTITLEMENTS` build setting — get it from `GetTargetBuildSettings` and use it as-is. Do not parse `project.pbxproj` to reconstruct the path, and do not glob `**/*.entitlements`: orphaned `.entitlements` files may exist on disk that aren't referenced by any target. One entitlements file can be referenced by multiple targets.
    
    Fall back to Bash only for operations the Xcode tools cannot do (e.g., git operations).
    
    ## Bundled Reference Documents
    
    All reference material lives under `references/` next to this file.
    
    - `references/security-settings-reference.md` — the canonical list of security build settings and entitlements this skill tracks, with hardened values, CLI flags, and language scope.
    - `references/reading-build-settings.md` — `GetTargetBuildSettings` schema, the filter script recipe, the audit-table construction, and the "already hardened" / "deliberately disabled" predicates.
    - `references/enhanced-security.md` — the Enhanced Security capability: build settings, entitlements, supported product types.
    - `references/pointer-authentication.md` — arm64e pointer signing: supported platforms, consumer-side compatibility notes.
    - `references/universal-binaries-for-libraries.md` — universal-binary guidance for library/framework targets (pointer authentication adds the `arm64e` slice automatically), qualifying product types, XCFramework guidance.
    - `references/security-compiler-warnings.md` — the security-focused compiler warnings and settings enabled by Enhanced Security.
    - `references/cpp-hardening.md` — C++ stdlib hardening (`CLANG_CXX_STANDARD_LIBRARY_HARDENING`) and bounds-safe buffers (`ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS`).
    - `references/typed-allocators.md` — type-aware allocator support and the `hardened-heap` sub-option.
    - `references/stack-zero-init.md` — automatic stack-variable zero-initialization at runtime.
    - `references/readonly-platform-memory.md` — read-only protection of dyld state.
    - `references/runtime-restrictions.md` — dylib and Mach-message platform restrictions.
    - `references/hardware-memory-tagging.md` — MTE entitlements and supported hardware.
    - `references/checked-pointer-arithmetic.md` — Checked Pointer Arithmetic (CPA2).
    - `references/additional-settings.md` — opt-in diagnostic settings beyond the defaults (may have more false positives).
    - `references/adoption-strategy.md` — recommended ordering for validating Enhanced Security features (lowest-risk to highest-effort).
    - `references/decision-document.md` — how to maintain the persistent `xcode-security-settings.md` decision document.
    
    The skill ships one helper script:
    
    - `scripts/filter_build_settings.py` — filters `GetTargetBuildSettings` JSON to the macros tracked in `security-settings-reference.md`. See `references/reading-build-settings.md` for usage.
    
    ### Common Failure Modes
    
    | Symptom | Cause | Correct Response |
    |---|---|---|
    | Tool call fails with "unknown tool" / "tool not found" for `XcodeGlob` etc. | The harness registers these tools only under their full MCP-prefixed name (`mcp__<server>__XcodeGlob`) in this environment | Look up the prefix in the available-tool registry, retry once with the full name, then use the full name for the rest of the session. |
    | `XcodeGlob "**/*.xcodeproj"` returns 0 matches | The `.xcodeproj` itself isn't a project-indexed file | Use the project name from system context; do not fall back to `find` or `ls` |
    | `XcodeRead <workspace-relative-path>` fails for a file truly inside the `.xcodeproj` / `.xcworkspace` bundle (e.g. `WorkspaceSettings.xcsettings`) | That file isn't a project-navigator member | Translate to filesystem absolute path using the project root from system context, then use `Read` / `Edit`. (Does not apply to `.entitlements` files — those are navigable.) |
    | `Read` on an entitlements path you derived by hand returns *File does not exist* | The path was reconstructed from `project.pbxproj` group nesting or guessed by globbing `**/*.entitlements`. Xcode's authoritative path for a target's entitlements is the evaluated value of `CODE_SIGN_ENTITLEMENTS`, not whatever the navigator shows. | Look up `CODE_SIGN_ENTITLEMENTS` for the target via `GetTargetBuildSettings` (or read it from the audit table) and use its evaluated value as the path. |
    
    ## Workflow
    
    ## Phase 1: Briefing
    
    Before doing any work, tell the user — in two or three sentences — what this skill is, what it will do, and roughly how much of their time and attention to expect:
    
    - **What it is.** An audit of the project's Xcode security build settings and entitlements (compiler warnings, Enhanced Security entitlements, pointer authentication, checked pointer arithmetic, universal binaries for libraries, etc.).
    - **What happens.** The skill runs in two parts of roughly equal length. First, **planning**: I analyze the project and write an editable plan file at the project root for you to review. Then, **execution**: once you pick Run, I apply only the changes you approved. Nothing is modified until you pick Run.
    - **Time commitment.** *Planning* is a few minutes of my analysis (longer on projects with many targets — I'll narrate progress) plus your review of the plan file, which can be quick or thorough — your call. *Execution* takes about as long: applying the approved changes, with two things that can pause for your input — the inquiry step (if there are deliberately-disabled settings whose rationale isn't documented), and a final yes/no on whether to keep the plan file in your project as a record.
    This all usually takes about 15-30 minutes, split roughly evenly between the two parts, depending on the number of build targets and how long it takes for you to review and approve the plan.
    
    Keep it tight — the user already invoked the skill knowing they wanted an audit.
    The briefing exists so they have realistic expectations.
    
    **Then check for source control.** The project has **source control** if either:
    
    - The Environment block's `Is a git repository` field is `true`, or
    - A single filesystem check at the project root finds any of `.git`, `.hg`, `.svn`, `.bzr`, `.fslckout`, `_FOSSIL_`, `CVS`.
    
    Otherwise the project has **no source control**. Record this state — Phase 4 Step 3 uses it to decide whether to include the ⚠️ blockquote in the plan file.
    
    After delivering the briefing, pause via `AskUserQuestion`. If the project has source control:
    
    - **Begin audit** — proceed to Phase 2.
    - **Cancel** — exit with "Cancelled — no changes applied."
    
    If the project has **no source control**, tell the user first: *"It is strongly recommended setting up source control before continuing. This skill modifies build settings and entitlements; without something like Git, rollback requires manual undo and you won't have a clean way to review the differences. Xcode has built-in support for [Source control management](doc://com.apple.documentation/documentation/xcode/source-control-management)"* Then ask:
    
    - **Set up source control first (Recommended)** — exit with "[Set up source control](doc://com.apple.documentation/documentation/xcode/configuring-your-xcode-project-to-use-source-control) and re-run the skill."
    - **Proceed without source control** — proceed to Phase 2; Phase 4 Step 3 will surface the no-source-control reminder again in the plan file.
    - **Cancel** — exit with "Cancelled — no changes applied."
    
    The pause exists so the briefing stays on screen long enough to read; Discovery and Analysis output would otherwise scroll it away. Failing early when there's no source control avoids spending minutes on discovery and analysis only for the user to bail at plan-approval time.
    
    ## Phase 2: Discovery
    
    Read the Environment block in the system prompt. Relevant fields:
    - `Primary working directory` — the project root (the project name is the basename).
    - `Is a git repository` — whether the project is git-tracked (used by the source-control check in Phase 1).
    
    ## Track Progress
    
    Every per-target / per-setting action that needs to happen must have its own task for transparency.
    
    - Phase 1 (Briefing) is one task that completes when the user picks Begin audit / Cancel.
    - Phase 3 creates one task per target (`Audit <target>`); the task closes once Phase 3 has produced both the per-target audit-table rows and (for supported product types) the Enhanced-Security category for that target. Phase 3 stores all per-target state in the task's `description` field (see Phase 3 Step 4 for the format) so later phases can read it back via `TaskGet`. Phases 4–7 read these task descriptions.
    - Phase 4 (Plan & Approve) is one task that completes when the user picks Run/Cancel.
    - On Run, Phase 4 step 5 parses the plan and creates fine-grained tasks. For each apply task it embeds that target's delta (extracted from the corresponding `Audit <target>` task's description) into the apply task's own `description` so Phase 5 doesn't have to look it up again.
      - For each **Enhanced Security** sub-item that's checked:
        - **Enable Enhanced Security**: `Enable Enhanced Security at project level` (one task). On pbxproj-only projects, this task encapsulates the guide-and-verify flow described in Phase 5 Step 1a.
        - **Update entitlements**: one `Apply Enhanced Security entitlements to <target>` per target needing changes.
        - **Hardware memory tagging**: `Apply Hardware Memory Tagging` (one task; walks supported targets internally).
        - **Checked pointer arithmetic**: `Apply Checked Pointer Arithmetic` (one task; walks supported targets internally).
      - For each **Warnings** sub-item that's checked:
        - `Apply Compiler Warnings` if that sub-item is checked.
        - `Apply Static Analyzer Warnings` if that sub-item is checked.
        - `Apply Clang-Tidy Warnings` if that sub-item is checked.
      - `Apply Additional Diagnostic Settings` if checked.
      - `Emit Bounds Safety Adoption guidance` if checked.
      - One `Inquire about <MACRO> on <target>` per Phase-6 candidate (only if "Inquire about disabled settings" is checked).
      - `Report and update decision document`.
      - `Prompt to remove plan file` — always last; also fires on error paths.
    
    When entering each phase or sub-step:
    - Print one line naming the phase or sub-step in plain English — never the phase number. Use the phase's name (e.g., "▶ Briefing", "▶ Analyzing project", "▶ Plan & Approve", "▶ Applying settings"); for sub-steps, name what's being done (e.g., "▶ Detecting languages", "▶ Building the audit table").
    - Update the task to `in_progress`.
    
    When finishing each phase or sub-step:
    - Print one line: "✓ <same label>" with a brief outcome if applicable (e.g., "✓ Detecting languages: C and Swift found.").
    - Update the task to `completed`.
    
    Apply steps may record what they did in their own task's `description` before completing it, one line per target. Phase 7 reads those lines instead of re-deriving state or scraping earlier output.
    
    ### Phase 3: Analyze Project and Settings
    
    No user interaction. Gather facts in the background.
    
    #### Step 1: Locate the existing decision document
    
    `XcodeGlob '**/xcode-security-settings.md'`. If found, `XcodeRead` it and extract languages + prior setting decisions with their statuses and rationale. This informs subsequent phases.
    
    #### Step 2: Detect languages
    
    One `XcodeGlob` per language. Empty result is not a failure — record the language as absent.
    
    - `**/*.c` → C
    - `**/*.cpp`, `**/*.cxx`, `**/*.cc` → C++
    - `**/*.m` → Objective-C
    - `**/*.mm` → Objective-C++
    - `**/*.swift` → Swift
    
    **Objective-C++ implies C++ is present.** `.mm` files contain C++ source, so any audit gated on "C++ present" (C++ stdlib hardening, bounds-safe-buffers guidance, `CLANG_ANALYZER_OSOBJECT_C_STYLE_CAST`, etc.) must fire when Objective-C++ is detected, even when no `.cpp`/`.cxx`/`.cc` files exist.
    
    **Filename extension is not authoritative.** An Xcode project can override a file's compiled language via `explicitFileType` / `lastKnownFileType` in `project.pbxproj` — most commonly a `.m` file marked `sourcecode.cpp.objcpp` (compiled as Objective-C++), or a `.h` marked `sourcecode.c.h` / `sourcecode.cpp.h`. To catch these overrides, `grep -E 'sourcecode\.cpp\.[a-zA-Z0-9]+' <project-root>/<ProjectName>.xcodeproj/project.pbxproj` via Bash. `project.pbxproj` is Xcode's project description file inside the `.xcodeproj` bundle; read it directly. Treat any `sourcecode.cpp.objcpp` match as both Objective-C++ and C++; treat any other `sourcecode.cpp.*` match as C++.
    
    #### Step 3: Build the audit table
    
    See `references/reading-build-settings.md` for column definitions, the construction recipe, and the canonical predicates ("already hardened", "at default OFF", "deliberately disabled"). At a glance:
    
    1. Call `XcodeListTargets` to enumerate targets. Skip entries with `IS_AGGREGATE = true` (they have no product type). Record `TARGET_NAME`, `CONTAINING_PROJECT`, and `PRODUCT_TYPE_IDENTIFIER` for each remaining target — Step 4 categorizes targets by `PRODUCT_TYPE_IDENTIFIER` directly (no inference).
    2. For each target: `TaskCreate "Audit <target>"`, set in_progress. Call `GetTargetBuildSettings`, run `scripts/filter_build_settings.py` over the resulting JSON, and record `evaluatedValue` and `setAtTargetLevel` (`yes` if `targetValue` is present in the JSON) per tracked macro. Hold these rows ready to write into the task's `description` in Step 4 (along with the category). Leave the task in_progress — Step 4 closes it.
    3. Scan for explicit settings in two passes with the filter regex: `XcodeGrep` over `*.xcconfig`, and `grep -nE '<filter regex>' <project-root>/<ProjectName>.xcodeproj/project.pbxproj` via Bash. `project.pbxproj` is Xcode's project description file inside the `.xcodeproj` bundle; read it directly. Record per-macro `numMatchesInXCConfigs`, `numMatchesInPbxproj`, and the file:line citations.
    4. The audit table is the joined view: one row per (target, tracked macro). Phases 4, 5, and 6 all consume this table; nothing else is re-fetched.
    
    This step scales with target count: each `GetTargetBuildSettings` call takes several seconds, and there is one per target. On projects with roughly ten or more targets it can take a few minutes.
    
    #### Step 4: Per-target Enhanced-Security state
    
    Route each target into one of three categories by the `PRODUCT_TYPE_IDENTIFIER` recorded in Step 3:
    
    - **Entitlements-supported** — product type is in the "Supported Product Types" list of `references/enhanced-security.md` (applications, XPC services, system extensions, driver extensions [build settings only], tools). Read the entitlements plist at the path stored in this target's `CODE_SIGN_ENTITLEMENTS` build setting and classify the target as **Up-to-date**, **Partial**, **Off**, or **No-entitlements-file**. Multiple targets can share the same `CODE_SIGN_ENTITLEMENTS` path; classify each target independently.
    - **Library/framework** — product type is in the qualifying set listed in `references/universal-binaries-for-libraries.md` (frameworks, static frameworks, static libraries, dynamic libraries). No entitlements read. Phase 5 will check the universal-binary configuration for these.
    - **Skipped** — anything else (test bundles, app extensions, etc.).
    
    Now write everything Phase 3 has learned about this target into the `Audit <target>` task's `description` via `TaskUpdate`, then set it `completed`. The description holds the entire per-target state Phases 4–6 need to consult later. Format:
    
    ```
    Category: <category> [/ <sub-state>]      # e.g. "Entitlements-supported / Partial", "Library/framework", "Skipped"
    Entitlements path: <evaluated CODE_SIGN_ENTITLEMENTS>     # omit for Library/framework and Skipped
    SDKROOT: <value>
    SUPPORTED_PLATFORMS: <value>
    Missing entitlements: <comma-separated short names>      # Entitlements-supported only; required and default-ON keys the target lacks; omit if empty
    Checked pointer arithmetic: <eligible-entitlement | eligible-slice-only | enabled | not-eligible: <reason>>   # Entitlements-supported and Library/framework targets
    Deliberately-disabled: <MACRO>=<value> (<source>[+<source>...]), ...   # one per disabled row; sources ⊆ {target-level, xcconfig, pbxproj} joined with '+' when more than one applies; omit the line entirely if none
    
    Audit table:
      <MACRO>=<value> setAtTargetLevel=<yes|no> numMatchesInXCConfigs=<n> numMatchesInPbxproj=<n> matchLocations=<citations>
      ...
    ```
    
    The Category line is first so any client that surfaces a snippet shows something meaningful. The Audit-table block is the per-(target, tracked macro) rows from Step 3 in `key=value` form — one line per tracked macro, using the canonical column names defined in `references/reading-build-settings.md`. `matchLocations` carries the file:line citations in the same `<source>:<file>:<line>[,<line>...]` format used throughout. **Skipped** targets get this Category line, the platform fields, and the Audit-table block. **Library/framework** targets get those three plus the `Checked pointer arithmetic:` line. Both complete immediately (no entitlements read).
    
    **`Checked pointer arithmetic` is the single source of truth for this feature.** Compute it once, here, and record one of four values. Every later phase reads this line and applies no test of its own.
    
    - `enabled` — nothing to do for this target. For an Entitlements-supported target: the entitlements file carries `com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow` and the evaluated `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` is `YES`. For a Library/framework target: the evaluated `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` is `YES` — there is no entitlement to check.
    - `not-eligible: <reason>` — one of: `platform`, when `SUPPORTED_PLATFORMS` / `SDKROOT` matches neither `iphoneos` nor `watchos`; `opted out`, when `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE` is `deliberately disabled` for the target; `no arm64e`, when `ENABLE_POINTER_AUTHENTICATION` is `deliberately disabled`; or `outside the capability`, when `ENABLE_ENHANCED_SECURITY` is `deliberately disabled`. A macro that is merely `at default OFF` is not a reason — enabling Enhanced Security lifts it. `outside the capability` applies to Entitlements-supported targets only: the capability supplies the entitlement, and a library takes none.
    - `eligible-entitlement` — an Entitlements-supported target that can take checked pointer arithmetic and is not yet fully configured for it: it is missing the `arm64e.x1` slice, the checked-pointer-arithmetic entitlement, or both. Step 4 applies whichever is missing.
    - `eligible-slice-only` — a Library/framework target that can take checked pointer arithmetic and does not have the build setting. There is no entitlement half for these targets: entitlements are granted per process from the main executable, so the library builds the slice and the consuming app's entitlement is what enforces the checks. Step 4 applies the build setting only.
    
    The key is never listed under `Missing entitlements`, which stays required and default-ON keys only, so it cannot make a target **Partial** and cannot reach Step 1b.
    
    On large projects this iterates over many `.entitlements` plists — if Step 3 took noticeable time, this one will too.
    
    ### Phase 4: Plan & Approve
    
    This phase produces a tailored, editable plan file that the user reviews before any changes happen. Once approved, Phases 5–7 run end-to-end with no further prompts.
    
    #### Step 1: Source-control state
    
    Source control was checked in Phase 1, and the user already accepted any no-source-control state at that point. Phase 4 Step 3 uses the recorded state to decide whether to include the ⚠️ blockquote in the plan file.
    
    #### Step 2: Skip if everything is already configured
    
    `TaskList` the `Audit <target>` tasks and `TaskGet` each. Early-exit if **all** default-checked plan items are already at their target state:
    
    - Every Enhanced-Security category (from each task's `Category:` line) is **Up-to-date** or **Skipped**.
    - No task's `Checked pointer arithmetic:` line reads `eligible-entitlement` or `eligible-slice-only`.
    - Every relevant Warnings setting (compiler, static analyzer, and clang-tidy) is `already hardened` on every applicable target (per each task's Audit-table block).
    - No task's `Deliberately-disabled:` line yields a row (after the Phase-6 exclusions below).
    
    Optional follow-ups (Additional diagnostic settings, Bounds safety adoption) do **not** block early-exit. Report "Everything in scope is already configured" and exit; do not write a plan file.
    
    #### Step 3: Write the plan file
    
    Create `xcode-security-audit-plan.md` at the **root of the Xcode workspace** via `XcodeWrite` (path: `xcode-security-audit-plan.md`, no parent group). `XcodeWrite` both writes the file to disk under `<project-root>/` and registers it in the project so the user can open it directly from Xcode's Project Navigator.
    
    Include only items that apply to the project (see omission rules below). Use this template — substitute the placeholders in `<…>`:
    
    ````markdown
    # Xcode Security Audit — Plan
    **Project:** <name> · <N> targets · languages: <list>
    **Generated:** <YYYY-MM-DD>
    > ⚠️ **No source control detected.** This skill modifies build settings and entitlements.
    > Without source control (e.g., Git), rollback requires manual undo. Consider [setting up source control](doc://com.apple.documentation/documentation/xcode/configuring-your-xcode-project-to-use-source-control) before picking **Run**.
    Edit the items below — set what steps to perform now, or leave them unchecked to defer them. Questions about any item, or want a more detailed plan? Just ask — I'll answer, and can expand this plan on the points you care about before you decide.
    ## Phases
    - **Enhanced Security** — the project's runtime-protection bundle. Apply to: <target list>. (Group — check the sub-items below.)
      - [x] **[Enable Enhanced Security](doc://com.apple.documentation/documentation/Xcode/enabling-enhanced-security-for-your-app)** — sets `ENABLE_ENHANCED_SECURITY=YES` at the project level. (Your project doesn't use a project-level xcconfig — I'll walk you through enabling it in Xcode's Build Settings UI yourself, then verify by reading project file.)
      - [x] **[Update entitlements](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process)** — adds the hardened-process entitlement family per target (Memory Safety, Runtime Protections).
      - [x] **[Hardware memory tagging](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations)** — adds the hardware memory tagging entitlement, in soft mode, on supported platforms (<target list filtered to MTE-supported platforms>).
      - [x] **[Checked pointer arithmetic](doc://com.apple.documentation/documentation/BundleResources/Entitlements/com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow)** — adds the arm64e.x1 slice and the entitlement to enforce pointer-arithmetic overflow checking (<target list filtered to arm64e.x1-supported platforms>). Run time enforcement requires hardware memory tagging enabled. Latent pointer-arithmetic bugs will terminate the app on capable hardware.
    - **[Warnings](doc://com.apple.documentation/documentation/Xcode/build-settings-reference)** — additional diagnostics on all C/C++/ObjC targets. (Group — check the sub-items below.)
      - [x] **Compiler warnings** — <N> settings promoting security-relevant compiler diagnostics (fire on every build).
      - [x] **Static analyzer warnings** — <N> security checkers (run during Build and analyze).
      - [x] **Clang-tidy warnings** — <N> clang-tidy-integrated checks (run during Build and analyze).
    - [x] **Inquire about disabled settings** — <M> found (e.g., `<setting>=NO` on `<target>`). May trigger follow-up questions if no rationale is documented.
    - [ ] **Additional diagnostic settings** — extra opt-in warnings/checkers beyond the defaults. Off by default: they surface more findings to review and can be noisier (more false positives).
    - [ ] **[Bounds safety adoption](https://clang.llvm.org/docs/BoundsSafetyAdoptionGuide.html)** — pointer to a separate skill. No changes applied here.
    ## Decision document
    The skill creates or updates `xcode-security-settings.md` to record every setting decision (kept, deferred, disabled, with rationale). Edit the path to relocate.
    - Path: `xcode-security-settings.md`
    ````
    
    Include the ⚠️ blockquote only when the project has **no source control**; omit it otherwise.
    
    Include the trailing parenthetical on the **Enable Enhanced Security** sub-item only when the project is pbxproj-only (no `*.xcconfig` files surfaced by Phase 3's project-wide scan); omit it otherwise.
    
    The decision document should live in the same directory as the rest of the documentation, or at the project level.
    
    ##### Item omission rules
    
    A plan item is omitted entirely when it doesn't apply:
    
    - **Enhanced Security** — omit (along with all sub-items) only if every supported-product-type category from Phase 3 step 4 is **Up-to-date** or **Skipped**, and no task's `Checked pointer arithmetic:` line reads `eligible-entitlement` or `eligible-slice-only`. **Enhanced Security** must be enabled otherwise.
    - **Enable Enhanced Security** (sub-item) — never omitted when Enhanced Security is shown; the trailing pbxproj-only parenthetical is the only conditional part.
    - **Update entitlements** (sub-item) — never omitted when Enhanced Security is shown.
    - **Hardware memory tagging** (sub-item) — omit if no target's `SUPPORTED_PLATFORMS` / `SDKROOT` matches `macosx`, `iphoneos`, `iphonesimulator`, `watchos`, `xros`, or `xrsimulator`.
    - **Checked pointer arithmetic** (sub-item) — omit if no task's `Checked pointer arithmetic:` line reads `eligible-entitlement` or `eligible-slice-only`.
    - **Warnings** — omit the parent (and all three sub-items) if pure-Swift, or if every setting across all three groups is `already hardened` on every applicable target. Otherwise omit an individual sub-item — **Compiler warnings**, **Static analyzer warnings**, or **Clang-tidy warnings** — when every setting in that group is `already hardened` on every applicable target, or the group has no applicable settings for the detected languages.
    - **Inquire about disabled settings** — omit if the `deliberately disabled` predicate yields no rows.
    - **Additional diagnostic settings** — never omitted; always offered.
    - **Bounds safety adoption** — omit if Phase 3 step 2 detected no C, C++, or Objective-C++ (counting `sourcecode.cpp.*` overrides as C++).
    
    ##### Default check state
    
    **Group headings carry no checkbox.** The parent lines that have sub-items — **Enhanced Security** and **Warnings** — are plain bold group labels, not checkable items; their sub-items carry the checkboxes. This avoids the ambiguity of a checked parent whose sub-items are all unchecked. Every other item (including leaf items with no sub-items, like **Inquire about disabled settings**, **Additional diagnostic settings**, **Bounds safety adoption**) is checkable.
    
    The user can flip items and sub-items under **Phases** by editing the plan file before picking **Run**.
    
    #### Step 4: Ask for approval
    
    Tell the user:
    
    > "Plan written to `xcode-security-audit-plan.md` and added to the Xcode project — open it to review. Edit it as needed — uncheck or delete items to skip them; edit the decision document path to relocate. When ready, pick Run. Pick Cancel to abort without changes. Nothing is modified until you pick Run."
    
    Then ask via `AskUserQuestion` with single-select options:
    - **Run** — proceed to "Phase 5"
    - **Cancel** — abort
    
    #### Step 5: Handle the response
    
    If the user asks a question or requests more detail instead of picking Run/Cancel: answer it, consulting the relevant doc from **Bundled Reference Documents** (e.g. `references/additional-settings.md` for the additional diagnostic settings). If they want that detail captured, update `xcode-security-audit-plan.md` via `XcodeUpdate` to elaborate on those points. Then re-present the Step 4 approval prompt — nothing is applied until the user picks Run.
    
    If **Cancel**: run the final cleanup task (`Prompt to remove plan file`, see "Phase 7: Report and Decision Document" below). The keep-or-remove prompt is offered on Cancel too, so the user's choice to abandon the audit doesn't silently differ from a normal completion. Report "Cancelled — no changes applied," and exit the skill.
    
    If the plan file is missing at re-read time (the user deleted it from disk before responding), treat it as a Cancel — and skip the `Prompt to remove plan file` task (there's nothing to remove).
    
    If **Run**: `XcodeRead xcode-security-audit-plan.md`. Parse:
    - Each `- [x]` or `- [X]` bullet is a checked item; the item name is the bold portion (between `**…**`).
    - A bold bullet with **no** checkbox (e.g. `- **Enhanced Security** …`, `- **Warnings** …`) is a group heading, not a checkable item. It creates no task of its own — its checked sub-items drive the work. Do not treat it as checked or unchecked.
    - Items written as `- [ ]` and items deleted from the file are skipped — both produce identical skip behavior.
    - Under the "Decision document" heading, the value after `Path:` is the decision document location.
    
    Create the fine-grained tasks listed in **Track Progress**:
    
    - For each `Apply Enhanced Security entitlements to <target>` task, copy the per-target delta from the corresponding `Audit <target>` task's description (`Category:`, `Entitlements path:`, `Missing entitlements:`) into the apply task's own description so Phase 5 reads from one place.
    - The **Warnings** parent line is a heading, not a task — it produces no task of its own. Each checked **Warnings** sub-item creates its corresponding apply task: **Compiler warnings** → `Apply Compiler Warnings`, **Static analyzer warnings** → `Apply Static Analyzer Warnings`, **Clang-tidy warnings** → `Apply Clang-Tidy Warnings`. This mirrors how the **Enhanced Security** parent maps to its sub-item tasks.
    - To create the `Inquire about <MACRO> on <target>` tasks (only when **Inquire about disabled settings** is checked), `TaskList` the `Audit <target>` tasks and `TaskGet` each; the `Deliberately-disabled:` line of each description lists that target's candidate rows. Apply the Phase-6 exclusions documented below when filtering.
    - When creating the `Report and update decision document` task, put the parsed decision-document path in its description so Phase 7 reads it from there.
    
    If the parsed plan has zero checked items, run the final cleanup task immediately and report "Plan was empty — nothing to do."
    
    ### Phase 5: Apply Settings
    
    Read build-setting state from each `Audit <target>` task's description (the Audit-table block) when needed; per-target apply state comes from each apply task's own description.
    
    **How to apply build settings:**
    - **Project uses `.xcconfig` files** — edit the xcconfig directly. Supports both project-level and target-level settings.
    - **Project uses `.pbxproj` only** — use `UpdateTargetBuildSetting` for target-level settings. Ask the user to enable project-level settings. Once the user responds that it was set, verify that it was set correctly using grep on the project file.
    - **Mixed** — if a target has an `.xcconfig` file, edit the xcconfig. Otherwise, use the Xcode build setting tools. Never introduce a new configuration method.
    
    `ENABLE_ENHANCED_SECURITY` must be set at project level such that any existing and future build targets inherit this setting.
    This setting should be disabled only after serious consideration and with strong justification.
    
    #### Step 1: Enhanced Security
    
    **1a. Enable Enhanced Security at the project level.** Walk the `Enable Enhanced Security at project level` task. Two paths inside it:
    
    - **Project uses a project-level xcconfig** — write `ENABLE_ENHANCED_SECURITY = YES` to the xcconfig via `XcodeUpdate`. Mark the task completed.
    - **Project is pbxproj-only** — no MCP tool can write a project-level pbxproj setting directly, so the user has to set it in Xcode. Give these exact steps (repeat them verbatim whenever you re-show them): *"Open the project in Xcode. Select the project in the Project Navigator (the top entry, not a target). Go to **Build Settings**, switch the scope to **All / Combined**, search for `ENABLE_ENHANCED_SECURITY`, and set the **project-level** column (left of the target columns) to `YES`. Save."* Then `AskUserQuestion` with two options: **I've enabled it** and **Show me the steps again**. On **I've enabled it**, verify with Bash: `grep -E 'ENABLE_ENHANCED_SECURITY *= *YES' <project-root>/<ProjectName>.xcodeproj/project.pbxproj`. If a match is found, mark the task completed. If not, **do not move on**: the confirmation was most likely accepted without the change actually being made — an accidental Enter, or Save was missed. Say that plainly, **re-show the steps verbatim**, and ask again. Loop — re-run the grep after each confirmation and re-show the steps every time it still isn't found — until the grep finds `ENABLE_ENHANCED_SECURITY = YES`.
    
    **1b. Update Enhanced Security entitlements.** The fine-grained `Apply Enhanced Security entitlements to <target>` tasks created in Phase 4 step 5 already enumerate the targets needing changes (the **Partial**, **Off**, and **No-entitlements-file** categories — **Up-to-date** and **Skipped** are excluded). Walk those tasks.
    
    Read `references/enhanced-security.md` for the full key list, defaults, and the supported product-type list. For details on individual sub-options, see:
    - `references/pointer-authentication.md` — arm64e pointer signing
    - `references/typed-allocators.md` — type-aware memory allocation
    - `references/stack-zero-init.md` — automatic stack variable zeroing
    - `references/readonly-platform-memory.md` — dyld state protection
    - `references/runtime-restrictions.md` — dylib and Mach message restrictions
    - `references/security-compiler-warnings.md` — security-focused compiler warnings
    - `references/cpp-hardening.md` — C++ stdlib hardening and bounds checking
    - `references/hardware-memory-tagging.md` — ARM MTE
    - `references/checked-pointer-arithmetic.md` — checked pointer arithmetic (CPA2)
    
    **Pointer authentication and binary dependencies.** Enhanced Security is a bundle of independent protections; only pointer authentication cascades to `arm64e`. Always recommend `ENABLE_ENHANCED_SECURITY = YES` at the project level. If the project has a binary Swift Package, xcframework, or prebuilt framework that does not ship `arm64e`, the right mitigation is to override `ENABLE_POINTER_AUTHENTICATION = NO` at the target level on every target that links the dependency — not to skip Enhanced Security. List the offending dependencies in the report so the user can ask the vendor for `arm64e` support and lift the override later.
    
    `arm64e.x1` is a pointer-authentication slice, so it should not be built where pointer authentication is off. A binary dependency that ships no `arm64e` slice will likely not ship `arm64e.x1` either. On every target that gets a target-level `ENABLE_POINTER_AUTHENTICATION = NO`, also set a target-level `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = NO`. Step 4 skips these targets, so the audit never adds the checked pointer arithmetic entitlement there. If a target already carries `com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow` from an earlier configuration, report it — the build will warn that it has no effect without `arm64e.x1`.
    
    **Producer side — universal binary on library/framework targets.** Pointer authentication is highly recommended on library and framework targets too — do not skip it on the grounds that the universal binary is a larger on-disk artifact (RAM footprint and execution cost are unchanged; dyld loads only one slice). Enabling pointer authentication already builds both the `arm64` and `arm64e` slices automatically, so no explicit `ARCHS` is needed. The same argument extends to checked pointer arithmetic, which requires the `arm64e.x1` slice appended: a consumer building for `arm64e.x1` gets checked arithmetic over the library's code only if the library ships that slice — the consumer app must meet other requisites as well for run time enforcement. Step 4 applies the build setting to these targets. See `references/universal-binaries-for-libraries.md` and `references/checked-pointer-arithmetic.md`.
    
    For each task:
    
    1. **Compose the change set** from this apply task's description (the `Category:` / `Missing entitlements:` lines copied in from the audit task).
       - **Entitlements-supported** categories (Partial / Off / No-entitlements-file): add/update entitlements via `AddEntitlement`; create `.entitlements` if missing and wire `CODE_SIGN_ENTITLEMENTS`. DriverKit targets are supported for build settings only — skip entitlement changes for them.
       - **Library/framework** category: no entitlements work, and no build-setting change either — pointer authentication already emits both slices. The only thing to do is the distribution check in item 2 below.
    
    2. **Per-target build settings.** `ENABLE_ENHANCED_SECURITY = YES` is already set at the project level (Step 1a above), so it cascades `ENABLE_POINTER_AUTHENTICATION = YES` to every target. Simulator builds need no override — the build system drops `arm64e` for simulator SDKs automatically. The only per-target override: for each target that links a binary dependency that doesn't ship `arm64e`, set an unconditional target-level `ENABLE_POINTER_AUTHENTICATION = NO` (that dependency can't be linked as `arm64e` on any platform). Skip targets that already have an explicit target-level value (per the Audit-table block in their `Audit <target>` task).
    
       For each **Library/framework**-category target where pointer authentication will end up enabled (the target's platform supports arm64e and there is no existing target-level `ENABLE_POINTER_AUTHENTICATION = NO`), no build-setting change is needed — pointer authentication appends the `arm64e` slice automatically. Only check that the distributed build emits both the `arm64` and `arm64e` slices: if the target sets `ONLY_ACTIVE_ARCH = YES` in its Release/distribution configuration, warn in the report that consumers get a single-architecture artifact.
    
       Do not auto-enable default-OFF sub-options. Hardware memory tagging belongs to Step 3, checked pointer arithmetic to Step 4.
    
    3. **Apply** the change set per target: add or update entitlements with `AddEntitlement` (creating the `.entitlements` file and wiring `CODE_SIGN_ENTITLEMENTS` when the target has none); and apply build-setting changes.
    
    After all targets are processed, report: "Enabled Enhanced Security on N target(s). Added a target-level `ENABLE_POINTER_AUTHENTICATION = NO` on T target(s) that link arm64e-less binary dependencies. Universal `arm64`/`arm64e` binary on U library/framework target(s)." If the project is pbxproj-only and `Verify Enhanced Security at project level` succeeded, append: "Enhanced Security is enabled at the project level (you set it in Xcode)." If the user skipped the guide step, append: "Project-level `ENABLE_ENHANCED_SECURITY` was not enabled this run — re-run the skill after enabling it in Xcode."
    
    The user already approved this in "Phase 4" — no further prompt is needed.
    
    The per-target `Apply Enhanced Security entitlements` tasks dominate Phase-5 wall time on multi-target projects. Each one edits the target's `.entitlements` plist.
    
    #### Step 2: Warnings
    
    If pure Swift, skip the whole step. This step covers three groups, each gated on its own plan sub-item — **Compiler warnings**, **Static analyzer warnings**, and **Clang-tidy warnings**. Skip any group whose sub-item was unchecked or deleted. For every setting, consult that target's `Audit <target>` task description (the Audit-table block) and skip individual settings whose row is `already hardened`. Otherwise apply target-level (see "How to apply build settings").
    
    **Compiler warnings** (fire on every build):
    
    - `GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR` — non-void function returning without a value is undefined behavior; callers read whatever happened to be in the return register. Promoting to error catches this at compile time. `YES_ERROR` is the documented Xcode value for "treat this specific warning as an error" — it does not flip every warning into an error.
    - `GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE` — reading uninitialized stack values leaks prior frame contents and lets attackers control flow with stale data. Aggressive mode warns on more cases (e.g., conditional initialization paths).
    - `CLANG_WARN_IMPLICIT_FALLTHROUGH = YES` — implicit `switch` fallthrough is one of the most common sources of branching bugs; the warning forces an explicit `[[fallthrough]]` / `__attribute__((fallthrough))` whenever intentional.
    - `GCC_WARN_64_TO_32_BIT_CONVERSION = YES` — silent narrowing of `size_t`/pointers to `int` is a classic source of integer-truncation vulnerabilities (length checks pass on the wide value, then fail open on the narrow one).
    - `GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES` (C/ObjC only) — implicit declarations were removed in C99 and produce wrong calling conventions and wrong return-type assumptions in modern C. Always an error.
    
    The two `YES_ERROR` / `… ERRORS = YES` settings are scoped: they only promote *their own specific warning* to an error, not all warnings in the project.
    
    **Static analyzer warnings** (run during *Build and analyze*, not regular builds):
    
    - `CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES` — floating-point loop counters can stall or overshoot due to rounding; the analyzer flags loops where this can become a security-relevant bug.
    - `CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES` — `rand()` / `random()` are predictable PRNGs unsuitable for any security purpose; analyzer flags their use so callers switch to `arc4random_uniform` or `SecRandomCopyBytes`.
    - `CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES` — flags `strcpy`, `strcat`, and friends that are inherently unsafe; callers should switch to size-bounded variants (`strlcpy`, `strlcat`, `snprintf`).
    
    **Clang-tidy warnings** (clang-tidy-integrated checks that are part of the clang static analyzer; they fire only during *Build and analyze* / `clang --analyze`, never on normal builds, so there is no build-break risk and adopters need to install nothing extra):
    
    - `CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES` — flags a branch condition that is redundant with an enclosing condition, a common sign of a copy-paste or logic error.
    
    Report briefly per group, e.g.: "Enabled compiler warnings, static analyzer warnings, and clang-tidy warnings." — naming only the groups actually applied.
    
    #### Step 3: Hardware Memory Tagging
    
    If the **Hardware memory tagging** sub-item (under Enhanced Security) was unchecked or deleted, skip this step.
    
    Hardware memory tagging is supported only for targets whose `SUPPORTED_PLATFORMS` (or `SDKROOT`) is `macosx`, `iphoneos` / `iphonesimulator`, `watchos`, or `xros` / `xrsimulator`.
    Hardware backing requires an iPhone or iPad with an A19 chip or later, a Mac or Apple Vision Pro with an M5 chip or later, or an Apple Watch with an S11 chip or later.
    
    Read `references/hardware-memory-tagging.md` and apply both keys to every supported target: `com.apple.security.hardened-process.checked-allocations`, and its `soft-mode` sub-option for a non-fatal rollout. Soft mode alone does nothing — it modifies the parent key rather than replacing it. The user already approved this in "Phase 4" — no further prompt is needed.
    
    #### Step 4: Checked Pointer Arithmetic
    
    Run this step after Step 1 and Step 3, whichever of them run: it reads settings Step 1 can change and the entitlements Step 3 can add. Checked pointer arithmetic requires the `arm64e.x1` slice, and this step enables that slice only on a target already building the `arm64e` slice with pointer authentication. Run time enforcement additionally requires hardware memory tagging on the same target.
    
    Skip this step if the **Checked pointer arithmetic** sub-item was unchecked or deleted.
    
    Apply to every target whose `Checked pointer arithmetic:` line reads `eligible-entitlement` or `eligible-slice-only`; skip the rest. That line is computed in Phase 3 step 4 and is the only eligibility test — do not re-derive it here.
    
    Then check the conditions below per target, reading each value fresh: Step 1 may have changed the build settings, and Step 3 may have added the entitlement. Skip a target and report it when any condition it is subject to fails.
    
    - `ENABLE_ENHANCED_SECURITY` evaluates to `YES` — `eligible-entitlement` targets only, since the entitlement needs the capability.
    - `ENABLE_POINTER_AUTHENTICATION` evaluates to `YES` — both kinds of target, since `arm64e.x1` is a pointer-authentication slice.
    - `com.apple.security.hardened-process.checked-allocations` is in the entitlements file — `eligible-entitlement` targets only, since run time enforcement depends on hardware memory tagging. The key is absent when Step 3 did not run, skipped this target, or the **Hardware memory tagging** sub-item was unchecked.
    
    Run time enforcement requires a device running iOS with an A20 Pro chip or later, or a device running watchOS with an S11 chip or later.
    
    Read `references/checked-pointer-arithmetic.md` and apply per target. For an `eligible-entitlement` target, apply both halves: set `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = YES` at target level (the target's xcconfig, otherwise `UpdateTargetBuildSetting`), and add `com.apple.security.hardened-process.checked-allocations.enforce-checked-pointer-arithmetic-overflow` with `AddEntitlement`. Both halves are required because the slice alone does not enforce checked pointer arithmetic, and Xcode warns at build time if the entitlement is set while the target is not building `arm64e.x1`. For an `eligible-slice-only` target, apply the build setting only.
    
    Record the outcome for every target in the `Apply Checked Pointer Arithmetic` task's `description` via `TaskUpdate`, one line per target, so Phase 7 (Report and Decision Document) reads it from one place:
    
    ```
    <target>: applied | skipped: <reason>
    ```
    
    Use `skipped: not eligible — <reason from the target's Checked pointer arithmetic: line>` for a target that was never eligible, and `skipped: ENABLE_ENHANCED_SECURITY is <value>`, `skipped: ENABLE_POINTER_AUTHENTICATION is <value>`, or `skipped: no hardware memory tagging entitlement` for one that was eligible but failed the re-read above. The user already approved this in "Phase 4" — no further prompt is needed.
    
    #### Step 5: Additional Diagnostic Settings
    
    If the **Additional diagnostic settings** plan item was unchecked or deleted, skip this step.
    
    Read `references/additional-settings.md` and follow it. The user already approved this in "Phase 4" — no further prompt is needed.
    
    #### Step 6: Bounds Safety Adoption
    
    If the **Bounds safety adoption** plan item was unchecked or deleted, skip this step.
    
    This step does not apply changes — it emits guidance only.
    
    For C projects (C present per Phase 3 step 2), print:
    > "To adopt `ENABLE_C_BOUNDS_SAFETY` (annotation-based bounds safety for C), invoke the `adopt-c-bounds-safety` skill."
    
    For C++ projects (C++ **or** Objective-C++ present per Phase 3 step 2 — including any `sourcecode.cpp.*` override on files with other extensions), print:
    > "To adopt `ENABLE_CPLUSPLUS_BOUNDS_SAFE_BUFFERS` (C++ bounds-safe buffer patterns), read the documentation at https://clang.llvm.org/docs/SafeBuffers.html"
    
    ### Phase 6: Inquire about Disabled Settings
    
    If the **Inquire about disabled settings** plan item was unchecked or deleted, skip this phase.
    
    This phase pauses for one user response per deliberately-disabled setting that lacks a documented rationale. If the candidate list is long, surface the count up front so the user knows what to expect ("I found 7 deliberately-disabled settings; let me ask about each").
    
    A row is a candidate when the `deliberately disabled` predicate (defined in `references/reading-build-settings.md`) holds. `TaskList` the `Audit <target>` tasks and `TaskGet` each; the `Deliberately-disabled:` line of each description lists that target's candidate rows. Flag an *unconditional* `ENABLE_POINTER_AUTHENTICATION = NO`, since that disables pointer authentication on device builds. Flag `ENABLE_HARDWARE_CHECKED_POINTER_ARITHMETIC_SLICE = NO` on a target whose `Checked pointer arithmetic:` line reads `not-eligible: opted out` — that reason means the opt-out is the only thing standing between the target and the `arm64e.x1` slice. Do not flag it for the other `not-eligible` reasons, where the slice could not be built anyway. Restrict to settings whose Scope (in `references/security-settings-reference.md`) covers a language detected in Phase 3 step 2; both settings above have no Scope and are flagged regardless.
    
    For each candidate, walk the corresponding `Inquire about <MACRO> on <target>` task created in Phase 4 step 5:
    
    - If the decision document has an entry with status `Disabled` and a rationale → note it in the report and move on.
    - Otherwise → `AskUserQuestion`: "I found `<MACRO>` explicitly set to `NO` with no explanation. Is there a reason for this?" Double-check that the macro is `deliberately disabled` and not merely at Xcode's default OFF — only call out explicit overrides. Record the rationale (or recommend re-enabling if none).
    
    Same flow applies to `ENABLE_ENHANCED_SECURITY = NO` if it appears on any task's `Deliberately-disabled:` line.
    
    ### Phase 7: Report and Decision Document
    
    Produce a lean summary:
    
    1. **Enabled** — project-wide settings that were enabled.
    2. **Enhanced Security per target** — one line per target: name, final status (up-to-date / applied / skipped-by-user), terse delta (entitlements added, whether an entitlements file was created, which slices the target now builds, whether checked pointer arithmetic was applied). Roll up Skipped targets into one line. For checked pointer arithmetic, `TaskGet` the `Apply Checked Pointer Arithmetic` task and use its per-target outcome lines, including the reason for each skip.
    3. **Already active** — settings already configured correctly.
    4. **Inquired** — settings found disabled and the outcome of the inquiry.
    5. **Test your app** — action item for the user: test on real hardware (not the simulator) that supports every enabled hardening, watch for protections firing, and fix the crashes and simulated crash reports that surface. Ship to customers only once the hardened app is adequately tested — otherwise it may crash or run slowly in production. For hardware memory tagging specifically, fix the simulated crash reports soft mode produces before disabling soft mode for enforcement. Checked pointer arithmetic has no soft mode and memory tagging's does not cover it, so test on capable hardware before shipping: a latent pointer-arithmetic bug terminates the app.
    
    **Decision document.** `TaskGet` the `Report and update decision document` task to read the decision-document path. Then read `references/decision-document.md` and follow it to create or update the document at that path.
    
    After Phase 7 — and on any error path during Phases 5–7 — this final task runs:
    
    1. **`Prompt to remove plan file`** — ask the user via `AskUserQuestion`: "The audit is complete. Remove the plan file `xcode-security-audit-plan.md` from your project?"
       - **Yes, remove it (Recommended)** → `XcodeRM xcode-security-audit-plan.md deleteFiles:true`
       - **No, keep it** → leave it in place; it stays in the Project Navigator as a record of what was approved. The user can delete it later from Xcode or Finder.
    
    If removal fails, warn the user but do not block exit.
    
    ## User-Facing Interaction Guidelines
    
    - **Keep replies lean.** Short sentences.
    - **Speak in complete sentences.** No fragments. Don't emit telegraphic noun phrases like "No existing decision document." — write a full sentence ("I didn't find an existing decision document — I'll create one at the end.").
    - **Phases are internal.** Never reference phase numbers or step numbers in user-facing prose. Describe outcomes plainly: say "I won't need to ask you about disabled settings" instead of "there will be no Phase 6 inquiry questions". This applies to narration, status lines, and any AskUserQuestion text.
    - **No skill-internal jargon.** Don't use words like "catalog", "audit table" in user-facing prose — those are internal to the skill. Describe what's happening in everyday Xcode terms: "checking known security build settings", "the list of targets", "the analysis I just ran".
    - **Keep user questions minimal.** Three scheduled questions: the briefing-acknowledgment prompt (Begin audit / Cancel) at the end of "Phase 1", the plan approval prompt (Run / Cancel) at the end of "Phase 4", and the keep-or-remove-plan-file prompt at the end of "Phase 7". Other questions are situational: inquiries about deliberately-disabled settings during "Phase 6" (only when an explicit `= NO` lacks a documented rationale), and the `Enable Enhanced Security at project level` confirmation prompt (only for pbxproj-only projects when that sub-item is checked).
    - **Report progress** so the user can track: "Enabling...", "Evaluating...", "Keeping/Reverting..."
    - **Use `AskUserQuestion`** for the briefing acknowledgment (Begin audit / Cancel), for the plan approval (Run / Cancel), for inquiring about disabled settings during "Phase 6", for the `Enable Enhanced Security at project level` confirmation in Phase 5 Step 1a (pbxproj-only), and for the keep-or-remove-plan-file prompt at the end of "Phase 7".
    - **When asking a question provide context the user needs to answer the question**. For example, describe the benefit of the security protection before asking whether to enable it. Describe it in terms of the protection it provides, not how it is enabled.
    - **When emitting lists of Xcode build settings, use bullet lists** Don't use comma-separated lists.

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related