Series · 4 partsmacOS Security BoundariesPart 2 · You are here
- 1The App Was Sandboxed. The XPC Boundary Still Needed Authorization.
- 2The Permission Was Granted. The Data Use Still Needed a Policy.You are here
- 3The Helper Was Registered. Its Lifetime Outlived the App.
- 4The Event Was Visible. The Detection Still Needed Context.
The macOS consent boundary in 60 seconds
When a macOS application asks for the camera, microphone, contacts, calendars, automation, screen recording, accessibility, or Full Disk Access, the platform may place a user or administrator decision between the process and the protected resource. This family of privacy controls is commonly described as TCC: Transparency, Consent, and Control.
That decision is important, but its meaning is narrow. It says that a particular responsible code identity may access a class of protected resource under the platform’s current rules. It does not say that every feature in the application may use every returned record, that a plug-in may inherit the same purpose, or that an upload, model prompt, export, or background workflow is authorized.
The testing mistake is to stop at the system prompt:
The person approved resource access. The application must still authorize each data use.
A defensible review therefore follows the complete chain: declared purpose → requesting binary → system decision → resource handle → data transformation → external effect → retention → revocation. The interesting question is not only whether the prompt appears. It is whether the resulting capability remains bound to the feature, account, destination, duration, and explanation the person understood.
Five controls, five different decisions
TCC is easiest to reason about when it is separated from adjacent controls.
| Control | Decision it makes | Decision it does not make |
|---|---|---|
| Code signing | Which code identity is executing? | Is this feature allowed to process this data? |
| App Sandbox | Which resources can this executable reach? | Is every reachable object valid for the current workflow? |
| Entitlements | Which protected capability may the executable request? | Did the person grant it, and is the later use appropriate? |
| TCC and consent | Did the person or administrator allow this protected-resource class? | May the app retain, combine, transmit, or repurpose the result? |
| Application policy | Which feature, account, destination, and state may consume the data? | Can it bypass the operating system’s privacy enforcement? |
Apple’s guidance for requesting access to protected resources requires an accurate purpose string and describes access as denied by default until the person makes a decision. Some capabilities also require a signed entitlement. Those are complementary gates, not interchangeable ones.
For files, App Sandbox adds another vocabulary. A user-selected file or a security-scoped bookmark can extend a sandboxed process’s access to a specific resource. Apple’s sandbox file-access documentation also notes that other access-control layers can still reject the operation. A reachable URL is a capability. It is not proof that a parser, uploader, helper, or account transition will use it safely.
Build the privacy surface before touching a setting
On a personal or customer Mac, do not begin by dumping privacy databases or resetting decisions. Start with the application artifact and product documentation. The goal is to understand what the product declares without collecting the person’s actual grants.
For every executable in the supplied app bundle, record:
- the exact code-signing identifier and designated requirement;
- sandbox and privacy-sensitive entitlements;
- usage-description keys and their localized purpose strings;
- linked frameworks and code paths that request protected resources;
- XPC services, login items, plug-ins, and extensions involved in the workflow;
- the feature state in which the request should occur;
- local consumers, remote destinations, retention rules, and deletion behavior.
Read-only artifact inspection can answer many of these questions:
codesign -d --entitlements :- CustomerApp.app
plutil -p CustomerApp.app/Contents/Info.plist
find CustomerApp.app/Contents -maxdepth 4 -type d -name '*.xpc' -print
These examples intentionally point to a customer-supplied test artifact. They do not justify enumerating the user’s installed applications or opening the per-user TCC database. A personal Mac contains sensitive evidence about the owner’s applications, habits, accounts, and prior decisions; that information is not needed to explain this methodology.
Turn the static inventory into a feature matrix:
| Feature | Responsible executable | Protected class | Prompt trigger | Purpose shown | Returned capability | Downstream effect |
|---|---|---|---|---|---|---|
| Voice note | exact client identity | microphone | explicit record action | record this note | audio stream | local draft |
| Import | main app | selected files | open panel | user selection | scoped URL | local parser |
| Automation | XPC client and target | Apple Events | explicit integration setup | control named app | event channel | bounded command |
| Support capture | system extension or app | screen recording | explicit support session | capture visible screen | frame stream | approved case upload |
The last column prevents a shallow review. Two products can request the same resource and create very different risk: one transforms a local selection into an ephemeral preview; another sends continuous data to a remote processor and retains it after logout.
Test state transitions, not only the happy prompt
A useful privacy test has controlled states. Use a dedicated test account or disposable test Mac with synthetic contacts, calendars, files, audio, and screen content. Never use the assessor’s personal records as convenient fixtures.
At minimum, exercise:
- Undetermined: the feature should explain the action before the operating-system prompt and avoid access before a deliberate gesture.
- Denied: the feature should fail closed, preserve unrelated functions, and offer a truthful recovery path without repeatedly coercing the person.
- Granted: only the responsible component and requested workflow should receive the capability.
- Changed while running: revocation should stop or bound active capture and produce an observable state change.
- Account transition: logout, tenant switch, and role reduction should invalidate application authorization even if the system-level grant remains.
- Feature disabled: local queues, bookmarks, caches, background jobs, and helper state should no longer continue the data use.
Apple documents tccutil reset for development testing, but that is a state-changing operation. Use
it only on the named test identity with an agreed recovery plan—not on a personal laptop merely to
make a prompt reappear. Record the exact precondition and expected state; a reset is setup, not proof
of a vulnerability.
The dangerous gap is after access succeeds
Once an API returns data, platform consent may no longer distinguish the good and bad application paths. The assessment should follow the object into parsers, shared containers, databases, logs, analytics, XPC calls, cloud requests, exports, and model inputs.
Look for broken bindings:
- data approved for local preview becomes an automatic remote upload;
- a security-scoped bookmark remains usable after the project or account is removed;
- a helper with a broader grant accepts a path or operation from a weaker client;
- an extension places protected data in a shared container with unnecessary consumers;
- a denied or revoked state changes UI text but does not stop the active stream;
- a purpose string describes one feature while a background component performs another;
- diagnostic logs include filenames, contact values, message bodies, screen text, or stable user IDs.
The correct report does not say “TCC bypass” unless platform enforcement was actually bypassed. Usually the finding is an application authorization, minimization, retention, or transparency failure after legitimate access. Naming the failed boundary accurately makes the remediation both smaller and stronger.
Evidence matrix
| Claim | Positive evidence | Negative control | What remains unproven |
|---|---|---|---|
| The prompt belongs to the intended feature | Synthetic fixture, deliberate gesture, matching purpose string, requesting binary identified | Invoke unrelated feature; no prompt or access | Every localization and legacy OS version |
| Denial fails closed | Protected API returns denied and no data-dependent effect occurs | Repeat action and restart helper | Third-party SDK behavior not instrumented |
| Grant is bound to the active account | Intended account produces one expected local or remote effect | Switch account while platform grant remains; effect denied | Server-side retention outside test access |
| Revocation stops use | Active stream or bookmark access ends and UI state changes | Reopen stale object or restart background component | Future OS implementation changes |
| Logs preserve privacy | Synthetic marker correlates events while content remains redacted | Search approved diagnostic export for raw fixture value | Logs outside the agreed collection window |
| Cleanup is complete | Test records, bookmarks, caches, queues, and helpers are removed | Relaunch and confirm fixture is not resumed | Personal-device privacy state, deliberately not collected |
The matrix keeps observation and interpretation separate. A visible prompt proves that a request occurred; it does not prove why a framework requested it. A successful API call proves reachability; it does not prove appropriate retention. A missing log line proves neither absence nor denial unless the expected telemetry path was independently validated.
Remediation should preserve the platform boundary
Good fixes rarely involve weakening macOS privacy controls. They make the application use narrower and more explainable:
- request access at the moment a person starts the named feature;
- make every purpose string specific, localized, and consistent with actual downstream use;
- keep protected processing in the least-privileged component that needs it;
- pass narrow results across XPC instead of raw data or reusable paths;
- bind operations to trusted account and feature state, not client-supplied identifiers;
- discard stale security-scoped bookmarks, queues, and caches on logout or revocation;
- make background capture visible and independently stoppable;
- log decisions and effect classes while keeping content and personal identifiers private;
- document the test state that proves denied and revoked paths remain safe.
TCC is not an application policy engine, and it should not be asked to become one. Its strength is that it places the person and the operating system at the resource boundary. The product’s strength must come from preserving that intent after the resource crosses into application code.
Sources
- Apple Developer Documentation, Requesting access to protected resources
- Apple Developer Documentation, Resetting access to protected resources in macOS
- Apple Developer Documentation, Accessing files from the macOS App Sandbox
- Apple Developer Documentation, Protecting user data with App Sandbox
- Apple Support, Change Privacy & Security settings on Mac
This is a source-reviewed assessment methodology. It does not claim a TCC bypass, inspect a personal TCC database, or present synthetic controls as findings from a production application.
How current is this note?
The latest source-review, content-update, or publication date is shown.
Primary public records were checked. Environment-specific behavior remains outside the claim unless separately reproduced.
