---
title: "The App Was Sandboxed. The XPC Boundary Still Needed Authorization."
description: "A macOS app can be sandboxed while a separate helper, launch agent, or launch daemon holds different authority. A defensible review maps every executable, entitlement, XPC peer, operation, and effect instead of treating the app bundle as one security boundary."
date: 2026-09-03
author: Sevban Dönmez (@jankesec)
canonical: https://jankesec.com/posts/macos-xpc-authority-boundary/
---

## The macOS boundary in 60 seconds

A macOS application is rarely one process with one set of permissions. The visible app may be
sandboxed, notarized, signed, and protected by the Hardened Runtime. Its bundle may also contain an
XPC service, login item, launch agent, launch daemon, system extension, command-line tool, or shared
container. Each component can run with a different identity, lifetime, sandbox profile, entitlement
set, and access to user or system resources.

That architecture is often good security engineering. Privileged or failure-prone work can be
moved out of the user interface process and exposed through a narrow interface. But process
separation does not create authorization by itself.

The receiving service must still decide:

- which process is connected;
- which code-signing identity that process satisfies;
- whether the caller is allowed to invoke this operation;
- whether the arguments name resources the caller is allowed to control;
- whether the requested action preserves path, ownership, state, and user-consent invariants;
- what authority and persistence remain after the visible app exits.

The core question is therefore not "is the app sandboxed?" It is:

> Where does untrusted application intent become a file, process, preference, credential, or
> system-level effect?

That conversion point is the macOS authority boundary.

## Seven protections that answer different questions

macOS layers several security mechanisms. They reinforce one another, but they are not synonyms.

| Mechanism                    | Primary question it answers                                                | What it does not prove                                                       |
| ---------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Code signing                 | Which signed code identity does this executable satisfy?                   | That the code is safe or this caller is authorized for every operation       |
| Notarization and Gatekeeper  | Did distributed software pass platform distribution and launch checks?     | That runtime business logic and local IPC authorization are correct          |
| Hardened Runtime             | Which process-integrity protections and exceptions apply?                  | That files, services, tenants, or XPC methods enforce application policy     |
| App Sandbox                  | Which resources may this executable access after compromise?               | That another component in the bundle has the same sandbox or authority       |
| TCC and user consent         | Did the user or administrator permit access to protected data or services? | That the process uses the granted access only for the intended workflow      |
| XPC and Service Management   | How are separate processes launched and connected?                         | That the listener authenticates the peer or authorizes each requested effect |
| SIP and platform protections | Which system locations and processes receive operating-system protection?  | That third-party application data, helpers, and custom protocols are correct |

Apple describes App Sandbox as a kernel-enforced access-control technology that restricts an app to
the capabilities it declares. Its purpose is containment: it reduces the harm available to
compromised code. The [App Sandbox
documentation](https://developer.apple.com/documentation/security/protecting-user-data-with-app-sandbox)
also makes clear that a sandboxed app can use separately configured tools and services when its
design requires them.

The [Hardened Runtime](https://developer.apple.com/documentation/security/hardened-runtime) protects
process integrity against classes of code injection, library loading, and memory-space tampering.
Exceptions are expressed as entitlements on executables. It is a different boundary from file,
privacy, IPC, and business authorization.

This distinction prevents a common reporting failure: listing platform protections as though their
presence closes every path. A strong review records what each control actually constrains, then
looks for the authority that crosses between components.

## The app bundle is a graph of executables

<figure class="diagram">
<svg viewBox="0 0 720 322" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="A macOS application bundle contains a sandboxed UI, XPC service, login item, launch agent, launch daemon and system extension, each with separate identity and authority leading to different system effects.">
<defs>
<marker id="mxab-a" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker>
</defs>
<rect x="14" y="22" width="152" height="50" rx="8" class="dg-box-accent" />
<text x="28" y="42" class="dg-label">visible application</text><text x="28" y="59" class="dg-accent">USER · SANDBOX</text>
<line x1="168" y1="47" x2="204" y2="47" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="208" y="22" width="150" height="50" rx="8" class="dg-box" />
<text x="222" y="42" class="dg-label">bundled XPC service</text><text x="222" y="59" class="dg-muted">SEPARATE PROCESS</text>
<line x1="360" y1="47" x2="396" y2="47" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="400" y="22" width="136" height="50" rx="8" class="dg-box" />
<text x="414" y="42" class="dg-label">launch agent</text><text x="414" y="59" class="dg-muted">USER SESSION</text>
<line x1="538" y1="47" x2="574" y2="47" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="578" y="22" width="128" height="50" rx="8" class="dg-box-accent" />
<text x="592" y="42" class="dg-label">launch daemon</text><text x="592" y="59" class="dg-accent">SYSTEM · ROOT</text>
<line x1="642" y1="74" x2="642" y2="112" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="550" y="116" width="156" height="50" rx="8" class="dg-box" />
<text x="564" y="136" class="dg-label">system extension</text><text x="564" y="153" class="dg-muted">RESTRICTED ENTITLEMENT</text>
<line x1="548" y1="141" x2="512" y2="141" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="340" y="116" width="168" height="50" rx="8" class="dg-box-accent" />
<text x="354" y="136" class="dg-label">authority inventory</text><text x="354" y="153" class="dg-accent">UID · SANDBOX · RIGHTS</text>
<line x1="338" y1="141" x2="302" y2="141" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="132" y="116" width="166" height="50" rx="8" class="dg-box-accent" />
<text x="146" y="136" class="dg-label">message boundary</text><text x="146" y="153" class="dg-accent">PEER · METHOD · INPUT</text>
<line x1="215" y1="168" x2="215" y2="208" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="132" y="212" width="166" height="50" rx="8" class="dg-box" />
<text x="146" y="232" class="dg-label">system effect</text><text x="146" y="249" class="dg-muted">FILE · PROCESS · CONFIG</text>
<line x1="300" y1="237" x2="336" y2="237" class="dg-line" marker-end="url(#mxab-a)" />
<rect x="340" y="212" width="168" height="50" rx="8" class="dg-box-accent" />
<text x="354" y="232" class="dg-label">negative control</text><text x="354" y="249" class="dg-accent">WRONG PEER · DENY</text>
<text x="14" y="300" class="dg-crit">UNIT OF REVIEW</text><text x="158" y="300" class="dg-mono">one executable, one identity, one interface, one resulting effect</text>
</svg>
<figcaption>The product name is not the security principal. Every executable and service boundary needs its own code identity, entitlements, caller policy, accepted inputs, and observable effects.</figcaption>
</figure>

Apple's current [XPC documentation](https://developer.apple.com/documentation/xpc) describes three
important service types:

- a bundled XPC service is normally one process per client and follows the client's lifetime;
- a launch agent runs for the logged-in user and can communicate within that user session;
- a launch daemon is system-wide, may start before login, and runs as root.

Those are different security propositions. Even two binaries signed by the same developer can have
different entitlements and access. A review that extracts only the main application's signature
misses the components that may create the most significant effect.

## Start with a component and authority inventory

Before exercising an interface, inventory the application bundle and every persistent component
associated with it. On a customer-supplied test Mac, read-only inspection can begin with standard
platform tools:

```bash
APP_PATH="/Applications/Example.app"

codesign -dvvv --entitlements :- "$APP_PATH"
find "$APP_PATH/Contents" -maxdepth 4 -type d \
  \( -name "XPCServices" -o -name "LoginItems" -o -name "LaunchAgents" \
  -o -name "LaunchDaemons" -o -name "SystemExtensions" \) -print
```

Repeat signature and entitlement inspection for every Mach-O executable, not just the outer bundle.
Record the designated requirement, Team ID, signing identifier, Hardened Runtime state, sandbox
entitlement, App Groups, Keychain groups, privacy-sensitive entitlements, runtime exceptions, and
restricted framework entitlements.

Then inspect the component's declared role:

```bash
plutil -p "$APP_PATH/Contents/Info.plist"
otool -L "$APP_PATH/Contents/MacOS/Example"
```

For launch items, preserve the relevant property list, executable location, label, arguments,
working directory, environment, user context, and registration state. On macOS 13 and later,
[Service Management](https://developer.apple.com/documentation/servicemanagement/) provides the
supported model for registering login items, launch agents, and launch daemons from an app bundle.
The presence of a helper in Contents/Library describes packaging; the service configuration and
runtime identity describe authority.

A useful inventory has one row per executable:

| Field                    | Question                                                                         |
| ------------------------ | -------------------------------------------------------------------------------- |
| Bundle-relative path     | Where is this executable packaged and updated?                                   |
| Code requirement         | Which signing identifier and Team ID does it satisfy?                            |
| Runtime identity         | Which UID, session, and launch domain execute it?                                |
| Sandbox and entitlements | Which platform restrictions and exceptions apply to this executable?             |
| Persistence              | Is it app-bound, login-bound, on-demand, or system-wide?                         |
| IPC names and interfaces | Which XPC, Mach, socket, Apple Event, URL, or file interfaces reach it?          |
| Accepted callers         | How does the receiver determine peer identity and authorization?                 |
| Privileged operations    | Which files, processes, settings, credentials, or devices can it affect?         |
| Input ownership          | Which paths, identifiers, bookmarks, handles, and state values come from caller? |
| Evidence and uncertainty | What proves each property, and what remains inferred?                            |

The output is an authority graph. A root process with no useful callable operation may be lower
risk than a user process with Full Disk Access and a broad message interface. UID is one dimension,
not the risk score.

<figure class="evidence">
  <picture>
    <source
      type="image/webp"
      srcset="/images/posts/responsive/macos-xpc-evidence-bundle-inventory-768.webp 768w, /images/posts/responsive/macos-xpc-evidence-bundle-inventory-1200.webp 1200w, /images/posts/macos-xpc-evidence-bundle-inventory.webp 1600w"
      sizes="(max-width: 820px) 94vw, 720px"
    />
    <img
      src="/images/posts/macos-xpc-evidence-bundle-inventory.webp"
      alt="Evidence plate showing a Photos.app bundle subset with the main application and a bundled XPC service as separate executable identities."
      loading="lazy"
      decoding="async"
      width="1600"
      height="1000"
    />
  </picture>
  <figcaption>
    <span>Evidence / 01</span>
    Two Mach-O identities inside one product bundle. Packaging is not a single security principal.
  </figcaption>
</figure>

## Entitlements are capabilities, not business authorization

Entitlements tell macOS which protected capability an executable may request or which exception
applies. They do not say that every caller of that executable should receive that capability.

Suppose a helper can update a protected configuration file. Its signature and entitlement may be
correct. The system may launch it exactly as designed. The helper must still decide:

1. Is this connection from an approved client identity?
2. Is this client allowed to request this operation now?
3. Does the user or administrator authorize the business action?
4. Are all referenced paths and objects owned by the expected principal?
5. Will the helper open and validate the final resource safely?
6. Is the resulting effect bounded, logged, and reversible?

The entitlement answers "may this executable attempt the operation?" The service method answers
"may this caller cause this particular effect?"

This is also why TCC and sandbox conclusions must remain separate. User consent can grant a process
access to protected data. App Sandbox can restrict filesystem or device access. Neither mechanism
automatically validates the semantics of an XPC request or prevents an already-authorized process
from misusing a broad operation.

<figure class="evidence">
  <picture>
    <source
      type="image/webp"
      srcset="/images/posts/responsive/macos-xpc-evidence-entitlement-diff-768.webp 768w, /images/posts/responsive/macos-xpc-evidence-entitlement-diff-1200.webp 1200w, /images/posts/macos-xpc-evidence-entitlement-diff.webp 1600w"
      sizes="(max-width: 820px) 94vw, 720px"
    />
    <img
      src="/images/posts/macos-xpc-evidence-entitlement-diff.webp"
      alt="Evidence plate comparing minimized entitlement subsets for a sandboxed main application and its bundled XPC service."
      loading="lazy"
      decoding="async"
      width="1600"
      height="1000"
    />
  </picture>
  <figcaption>
    <span>Evidence / 02</span>
    Effective entitlement subsets differ inside one bundle. The outer app is not a proxy for the service capability set.
  </figcaption>
</figure>

## Authenticate the peer, then authorize the operation

XPC gives the client and service a structured communication channel. The service still needs a
caller policy. A process name, PID, bundle path, or client-supplied identifier is not a durable code
identity.

Modern platform APIs allow an XPC listener or connection to enforce code-signing requirements.
Apple documents that
[xpc_listener_set_peer_requirement](https://developer.apple.com/documentation/xpc/xpc_listener_set_peer_requirement)
checks incoming messages against a peer code-signing requirement and drops requests that do not
satisfy it. Foundation's
[setCodeSigningRequirement](https://developer.apple.com/documentation/foundation/nsxpcconnection/setcodesigningrequirement%28_%3A%29)
provides the corresponding peer requirement control for NSXPCConnection. You can experiment with these validation primitives and observe policy verdicts in the interactive [macOS Code Requirement & Entitlement Evaluator](/tools/macos-entitlement-evaluator/).

Peer identity is the first gate, not the final policy:

<figure class="diagram">
<svg viewBox="0 0 720 316" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="An XPC request crosses peer code identity, operation authorization, argument validation, resource resolution and effect verification gates before a privileged helper performs an action.">
<defs>
<marker id="mxab-b" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L8,4 L0,8 z" class="dg-arrow" /></marker>
</defs>
<rect x="14" y="24" width="128" height="52" rx="8" class="dg-box-accent" />
<text x="28" y="44" class="dg-label">XPC request</text><text x="28" y="62" class="dg-accent">METHOD · ARGS</text>
<line x1="144" y1="50" x2="174" y2="50" class="dg-line" marker-end="url(#mxab-b)" />
<rect x="178" y="24" width="138" height="52" rx="8" class="dg-box" />
<text x="192" y="44" class="dg-label">peer identity</text><text x="192" y="62" class="dg-muted">SIGNING REQUIREMENT</text>
<line x1="318" y1="50" x2="348" y2="50" class="dg-line" marker-end="url(#mxab-b)" />
<rect x="352" y="24" width="148" height="52" rx="8" class="dg-box" />
<text x="366" y="44" class="dg-label">operation policy</text><text x="366" y="62" class="dg-muted">RIGHT · STATE · USER</text>
<line x1="502" y1="50" x2="532" y2="50" class="dg-line" marker-end="url(#mxab-b)" />
<rect x="536" y="24" width="170" height="52" rx="8" class="dg-box" />
<text x="550" y="44" class="dg-label">argument policy</text><text x="550" y="62" class="dg-muted">TYPE · RANGE · OWNER</text>
<line x1="621" y1="78" x2="621" y2="116" class="dg-line" marker-end="url(#mxab-b)" />
<rect x="536" y="120" width="170" height="52" rx="8" class="dg-box" />
<text x="550" y="140" class="dg-label">resource resolution</text><text x="550" y="158" class="dg-muted">FINAL OBJECT · HANDLE</text>
<line x1="534" y1="146" x2="504" y2="146" class="dg-line" marker-end="url(#mxab-b)" />
<rect x="346" y="120" width="154" height="52" rx="8" class="dg-box-accent" />
<text x="360" y="140" class="dg-label">bounded effect</text><text x="360" y="158" class="dg-accent">CHANGE · AUDIT</text>
<line x1="344" y1="146" x2="314" y2="146" class="dg-line" marker-end="url(#mxab-b)" />
<rect x="142" y="120" width="168" height="52" rx="8" class="dg-box-accent" />
<text x="156" y="140" class="dg-label">effect verification</text><text x="156" y="158" class="dg-accent">EXPECTED · ACTUAL</text>
<line x1="226" y1="174" x2="226" y2="212" class="dg-line" marker-end="url(#mxab-b)" />
<rect x="142" y="216" width="168" height="52" rx="8" class="dg-box" />
<text x="156" y="236" class="dg-label">negative control</text><text x="156" y="254" class="dg-muted">WRONG PEER · DENY</text>
<text x="14" y="300" class="dg-crit">TRUST IS NOT TRANSITIVE</text><text x="210" y="300" class="dg-mono">signed client ≠ authorized method ≠ safe resource effect</text>
</svg>
<figcaption>A correct signing identity admits a peer to the protocol. Method-level authorization, argument ownership, final resource validation, and effect verification still belong to the service.</figcaption>
</figure>

<figure class="evidence">
  <picture>
    <source
      type="image/webp"
      srcset="/images/posts/responsive/macos-xpc-evidence-peer-decision-768.webp 768w, /images/posts/responsive/macos-xpc-evidence-peer-decision-1200.webp 1200w, /images/posts/macos-xpc-evidence-peer-decision.webp 1600w"
      sizes="(max-width: 820px) 94vw, 720px"
    />
    <img
      src="/images/posts/macos-xpc-evidence-peer-decision.webp"
      alt="Evidence plate showing ALLOW for an approved client and DENY for a mismatched client under the same synthetic XPC request."
      loading="lazy"
      decoding="async"
      width="1600"
      height="1000"
    />
  </picture>
  <figcaption>
    <span>Evidence / 03</span>
    Same synthetic request, different peer identities: approved client reaches the method; mismatched client is denied before effect.
  </figcaption>
</figure>

A robust decision chain is:

1. **Peer requirement:** accept only the designated client identities and expected signing team.
2. **Connection context:** bind the accepted connection to an immutable server-observed identity.
3. **Method allowlist:** expose narrow operations rather than a generic command, path, or selector.
4. **Right evaluation:** decide whether this caller and user context may request the operation.
5. **Typed arguments:** reject ambiguous types, unexpected keys, excessive sizes, and invalid state.
6. **Resource binding:** resolve caller-controlled references into approved resources without
   trusting textual paths alone.
7. **Effect check:** confirm the actual file, process, preference, or system state changed exactly as
   intended.
8. **Audit and revocation:** preserve caller identity, operation, target, outcome, and policy
   decision; invalidate authority when the app, account, or configuration changes.

The security property belongs to the whole chain. Improving only one gate can leave the same effect
reachable through another interface.

## The PID check fails in a way a black-box test cannot see

The recommendation above has a measurable consequence, so it is worth measuring rather than asserting.

The lab that accompanies this post now runs two servers that expose the same method and differ in
exactly one respect. One resolves the peer with `SecCodeCopyGuestWithAttributes` and
`kSecGuestAttributePid`, taking the PID from `connection.processIdentifier`. The other calls
`setCodeSigningRequirement` and lets the platform evaluate the peer against the connection's audit token.

Under normal conditions the two are **indistinguishable from outside**. Both accept the approved client
and deny the mismatched one. Any assessment that consists of connecting with the wrong identity and
checking whether it is refused will score both implementations as sound.

The difference appears only when the caller stops being honest about which binary holds the PID. A client
signed under the denied identity puts its request in flight, then replaces its own process image with a
decoy carrying the approved identity, using `posix_spawn` with `POSIX_SPAWN_SETEXEC`. The PID does not
change. The server performs its check afterwards and resolves that PID to the decoy:

```text
scenario                     client    server   result
normal                       allowed   weak     ALLOW
normal                       denied    weak     DENY
normal                       allowed   strict   ALLOW
normal                       denied    strict   DENY
PID-reuse race               denied    weak     ALLOW   <-- bypass
PID-reuse race               denied    strict   DENY
```

The weak server reports the bypassed request in the same terms it uses for a legitimate one, and then
performs the effect for a caller whose own signature it had refused moments earlier:

```text
peer=APPROVED decision=ALLOW gate=code-requirement
effect=BOUNDED input=synthetic
```

The strict server never reaches that state. `setCodeSigningRequirement` is evaluated by the platform
against the audit token, which is a kernel-held property of the connection rather than a number the
caller can recycle.

Two conclusions follow, and the second is the one that changes how a review is run. First, a peer check
written against a PID is not a weaker version of a code-signing check; it is a different check that
happens to agree with the correct one whenever nobody is attacking it. Second, **you cannot audit this
from the outside without attempting the race or reading the implementation.** A service that refuses your
unsigned client has told you nothing about whether it would refuse a client that lies.

The bench, the racer, and the decoy are in `labs/macos-xpc-authority/`. The weak server is kept on
purpose: an instrument that claims to assess peer validation has to be shown catching an implementation
that is genuinely unsound before a clean verdict on a real service means anything.

## Path strings are requests, not resources

Privileged helpers often receive paths because the visible application selected a file, created a
backup, downloaded an update, or asked for a configuration change. The service must not treat the
string as proof that the caller owns the final object.

Between validation and use, filesystem state can change. Relative paths, symbolic links, aliases,
mount points, hard links, case behavior, and directory replacement can alter what a textual path
resolves to. A useful review asks:

- who created each path component;
- which process owns and can modify the parent directories;
- whether validation and use address the same opened object;
- whether the target may cross out of an approved container or product directory;
- whether the helper follows links or changes ownership and permissions;
- whether temporary files are created safely;
- whether archive extraction, update staging, or restore operations preserve a fixed destination;
- whether a file selected under user consent becomes a generic capability for later operations.

The safest interface carries the narrowest resource representation the operation needs. A helper
that exposes "run this command" or "write these bytes to this arbitrary path" has delegated its own
authority model to the caller.

## Assess the boundary without turning the Mac into a target

A product review should use a dedicated customer-owned test Mac, disposable test accounts, synthetic
files, and a reversible operation set. Start with static and observational evidence. Dynamic tests
should change one variable at a time.

| Test dimension     | Positive control                                              | Negative control                                                        |
| ------------------ | ------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Peer identity      | Approved signed test client connects                          | Unapproved test client or mismatched requirement is rejected            |
| Method             | Approved client invokes one documented operation              | Same client invokes an unavailable or unauthorized operation            |
| User right         | Authorized test user performs a permitted administrative task | Ordinary test user receives a denial before the effect                  |
| Resource ownership | Helper modifies a synthetic product-owned file                | Caller-controlled path outside the approved root is rejected            |
| Workflow state     | Registered component performs a current operation             | Removed, disabled, or stale component cannot retain old authority       |
| Effect             | Exactly one expected change occurs and is logged              | Denied request produces no file, process, setting, or permission change |

Stop immediately if a test touches real user data, another account, production credentials,
security settings outside the agreed lab, or a system component not owned by the product. Do not
disable SIP, bypass TCC prompts, weaken endpoint controls, or install unsigned helpers merely to
make a hypothesis easier to demonstrate. If the proof requires changing a platform protection,
reframe the test or obtain an explicit lab-specific decision.

## A fictional XPC authority review

Assume a product called HarborSync has a sandboxed graphical application and a launch daemon that
performs a small set of backup and restore operations. The daemon runs as root and receives
requests over XPC. The customer supplies a disposable Mac, two signed test clients, synthetic
backup data, and one product-owned destination directory.

1. The reviewer inventories the GUI, bundled XPC component, launch daemon, updater, and login item.
   Each executable receives its own signature, entitlement, UID, persistence, and interface record.
2. The GUI is sandboxed and uses security-scoped access for user-selected data. The launch daemon is
   not treated as inheriting the GUI's restrictions; its root authority and XPC listener are mapped
   separately.
3. A normal restore from the approved client to the synthetic product directory succeeds. The
   created file, ownership, mode, audit event, and timestamp establish the positive control.
4. A second signed test client from the same development team does not satisfy the listener's
   designated requirement. Its request is rejected before the restore method runs. Team membership
   alone is not the product's caller policy.
5. The approved client requests a restore outside the product-owned root. The daemon rejects the
   resource after resolving the final destination. No file or temporary artifact appears.
6. The customer removes the daemon's authorization in System Settings. Existing clients lose the
   operation rather than preserving a hidden long-lived capability.
7. One issue is found in the cleanup method: it accepts the correct peer but authorizes deletion
   using a caller-supplied backup identifier that is not bound to the active user's backup set. In
   the synthetic lab, User A can request deletion of User B's test backup.
8. The test stops after one reversible record proves the boundary. The finding is framed as missing
   object authorization inside an authenticated XPC method—not as a sandbox escape or a macOS
   platform failure.

This distinction matters. The application was sandboxed. The service accepted the expected signed
client. The missing decision was still local business authorization: which backup object this user
was permitted to affect.

## Evidence matrix

| Observation or result                          | What it supports                                           | What it cannot prove                                              | Required next step                                             |
| ---------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------- |
| Main app has the App Sandbox entitlement       | The main executable enters App Sandbox                     | Helper sandbox state, TCC state, XPC policy, or method safety     | Inspect every executable and runtime component                 |
| Bundle is notarized and accepted by Gatekeeper | Distribution and launch checks succeeded                   | Absence of logic, IPC, update, or authorization vulnerabilities   | Map runtime boundaries and privileged effects                  |
| Hardened Runtime is enabled                    | Default runtime integrity restrictions apply               | Which exception entitlements weaken a component                   | Inspect executable-specific flags and entitlements             |
| XPC service is packaged in the app             | A separate process and interface are part of the product   | Peer requirements, method authorization, or input ownership       | Recover listener policy and operation catalogue                |
| Launch daemon runs as root                     | The component has system-level execution context           | Reachability, exploitable operation, or caller authorization      | Map every interface and resulting effect                       |
| Listener enforces a signing requirement        | Peers must satisfy one code identity policy                | That every accepted peer may invoke every method                  | Test operation and object authorization                        |
| Client PID or process name is checked          | A transient process attribute influences policy            | Stable code identity or resistance to process lifecycle changes   | Replace with platform-enforced code requirement where possible |
| Path is canonicalized before use               | One textual normalization step occurred                    | Final-object identity, link safety, or validation/use consistency | Bind the operation to a resolved approved resource             |
| Unauthorized request returns an error          | One request path reported denial                           | Absence of side effects, partial writes, or queued work           | Verify filesystem, process, configuration, and audit state     |
| Synthetic forbidden effect does not occur      | The tested negative control preserved the stated invariant | All callers, methods, resources, versions, or lifecycle states    | Expand by authority and effect, not blind message mutation     |

## What developers and defenders should receive

The useful deliverable is not an entitlement dump. It is a component-to-effect model.

**Executable inventory.** Every app, helper, XPC service, login item, agent, daemon, extension, and
tool with code requirement, entitlements, UID, launch domain, persistence, and update relationship.

**Authority map.** Files, processes, credentials, protected data, system settings, devices, and
security events each component can affect.

**Interface catalogue.** XPC listener names, public methods, argument schemas, error behavior,
connection lifetime, peer requirements, and alternate interfaces.

**Decision matrix.** Accepted client identities, method rights, user context, resource ownership,
workflow state, expected effects, and denied controls.

**Lifecycle results.** Installation, registration, upgrade, disablement, logout, user switch,
uninstall, and stale-client behavior. Persistent authority should have a visible owner and a defined
revocation path.

**Remediation contract.** The exact receiver-side decision to add or narrow, the component owner,
the minimum code requirement, the allowed methods and resources, effect-level logging, and a retest
that proves both the intended success and intended denial.

## The macOS topic begins with authority, not checklists

macOS Security will follow a consistent route:

1. **App Sandbox and XPC authority:** where one process asks another to create a stronger effect.
2. **TCC and consent boundaries:** how user-approved access becomes persistent application
   capability.
3. **Launch services and persistence:** how login items, agents, daemons, updates, and ownership
   survive across lifecycle changes.
4. **Endpoint Security and observability:** what defenders can prove about process, file, mount, and
   authorization events without confusing telemetry with prevention.

The platform already supplies strong primitives: signing requirements, entitlements, sandboxes,
Hardened Runtime, user consent, Service Management, and structured IPC. The security review asks
whether the product composed those primitives into the intended authority model.

The app window is only the visible component. The boundary is the moment a specific caller, method,
resource, and state become a real system effect.

## MITRE ATT&CK mapping

| Tactic               | Technique ID | Technique name                            | Verification signal                                     |
| -------------------- | ------------ | ----------------------------------------- | ------------------------------------------------------- |
| Execution            | `T1559.003`  | Inter-Process Communication: XPC Services | `launchctl print gui/$UID/<service>` connection inspect |
| Privilege Escalation | `T1548`      | Abuse Elevation Control Mechanism         | Audit caller code requirement against receiving helper  |
| Defense Evasion      | `T1553.001`  | Subvert Trust Controls: Code Signing      | Check peer `codesign -dvvv` and team ID boundary        |

<div class="compact-panel lab-callout">
  <div class="lab-callout-head">
    <span class="signal-pill">Companion Lab</span>
    <strong>macOS XPC Authority Boundary Field Kit</strong>
  </div>
  <p>Minimal reproducible Swift client, privileged helper server, and signature requirement verification primitives are available in the project repository under <code>labs/macos-xpc-authority/</code>.</p>
  <a class="btn" href="https://github.com/sevbandonmez/jankesec/tree/main/labs/macos-xpc-authority" target="_blank" rel="noopener">Browse Swift Lab Source ↗</a>
</div>