
Eight folders Apple decided to guard, and no key to manage them
A new file access policy ships through XProtect rather than through anything you configure. On the macOS 27 public beta the delivered file is never read: sandboxd wants a different filename, falls back to rules built into itself, and denies root.
Apple is shipping a new file access policy through XProtect. It names eight app data folders, it is already sitting in the XProtect bundle on Macs that have received the configuration data, and it arrived without an administrator control surface.
I went looking for that surface. I searched everything an administrator can configure in macOS 27, both the configuration profile schema and its declarative replacement. XProtect appears once in the whole thing: a checkbox for uploading malware samples to Apple.
Then I ran macOS 27 in a VM to find out what the policy actually does, and the answer turned out to be stranger than the missing key. The file Apple delivers is not the file macOS 27 reads. sandboxd looks for a different filename, does not find it, and falls back to a rule set compiled into its own binary. That fallback is what enforces on macOS 27, it is not identical to the file you can inspect today, and it denies root.
What follows is the long version of both searches: the measurements, the controls, and the parts I could not settle.
The file is already on your Macs
Howard Oakley found AppProtectionPolicy.plist in the XProtect bundle at version 5354, released 5 August 2026. His per-release diff of 5353 the week before documents only Yara rule changes and does not mention the file, which is what makes 5354 the introducing release.
The same eight folders were first documented by Wojciech Reguła, who extracted the rule set from /usr/libexec/sandboxd on a macOS 27 beta in July and published it on 16 July, crediting @ciphwall for the pointer. What he read out of sandboxd is a separate artifact from the file XProtect delivers, a distinction the section on what macOS 27 actually enforces returns to. He coined the name "Application Support Protection", and that name is his rather than Apple's.
The shipped file declares version: defaultRules-1 and protects eight app data folders, each keyed to the Team ID that owns it:
| App | Bundle ID | Team ID |
|---|---|---|
| Google Chrome | com.google.Chrome | EQHXZ8M8AV |
| Brave | com.brave.Browser | KL8N8XSYF4 |
| Microsoft Edge | com.microsoft.edgemac | UBF8T346G9 |
| Firefox | org.mozilla.firefox | 43AQ936H96 |
| Discord | com.hnc.Discord | 53Q6R32WPB |
| Ledger Live | com.ledger.live | X6LFS5BQKN |
| Exodus | com.electron.exodus | VK5Q293EVL |
| Wasabi Wallet | zksnacks.wasabiwallet | L233B2JQ68 |
Four browsers, three desktop wallets, and Discord. Seven of the eight paths sit under ~/Library/Application Support. Wasabi is the exception at ~/.walletwasabi, a home-directory dotfile, which is why the accurate description is "app data folders" rather than "Application Support folders."
The list is not arbitrary. LevelBlue's SpiderLabs published a report in March 2026 on the MioLab stealer family that names ~/Library/Application Support/discord/Local Storage/leveldb as the location of active session tokens, and names Wasabi Wallet (.walletwasabi/client/Wallets) as a theft target. Both are subdirectories of paths Apple now protects, in a vendor report that predates Apple's rule by five months.
I checked the file on macOS 15 and macOS 26. It is present and byte-for-byte identical on both, unchanged across XProtect 5354, 5355 and 5356, in both the system bundle and the protected copy under /var/protected/xprotect/.
The file Apple ships is not the file macOS 27 reads
Everything above describes the artifact Apple delivers. This section is about what the operating system does with it, measured on macOS 27 Golden Gate public beta 2, build 26A5388g, running as a VM guest with SIP enabled.
sandboxd on that build contains exactly one policy file path, and it is not the name Apple ships:
/Library/Apple/System/Library/CoreServices/XProtect.bundle/Contents/Resources/AppProtectionRules.plist
The literal AppProtectionPolicy.plist does not appear in the binary at all. A raw byte search returns one hit for AppProtectionRules.plist, zero hits for AppProtectionPolicy.plist, and thirty-seven hits for the control string sandboxd, so the search reaches real content.
macOS 27 states the consequence itself. With XProtect installed (the payload is unchanged across 5354, 5355 and 5356) and AppProtectionPolicy.plist present in the bundle at its shipped sha256 of 2d715412ad6611a072314bbfe04bb4446c1879b5522d19da7a989a2aad96b7a6, sandboxd logs this on every boot:
sandboxd: /Library/Apple/System/Library/CoreServices/XProtect.bundle/Contents/
Resources/AppProtectionRules.plist not found; using internal app protection policy
The internal policy it falls back to is real, and it is enforcing. Two sysctls report it:
security.mac.sandbox.appprotection.enabled: 1
security.mac.sandbox.appprotection.version: defaultRules-2
The delivered file declares defaultRules-1. The kernel reports defaultRules-2. Those are two different rule sets, and the one in force is the one compiled into sandboxd.
They are two distinct artifacts with independent counters, not one artifact at two revisions. Reguła read defaultRules-2 out of a beta 3 sandboxd; it is still defaultRules-2 in public beta 2, while XProtect delivers defaultRules-1 to every Mac on the current release.
The rule set that is actually enforcing
Extracted from the sandboxd binary as an embedded XML property list, 4,398 bytes, plutil -lint clean. It protects the same eight folders as the delivered file, keyed the same way, and every entry carries enforce set to true:
| App | Path | allowedTeamId |
|---|---|---|
| Discord | ~/Library/Application Support/discord | 53Q6R32WPB |
| Google Chrome | ~/Library/Application Support/Google/Chrome | EQHXZ8M8AV |
| Brave | ~/Library/Application Support/BraveSoftware/Brave-Browser | KL8N8XSYF4 |
| Microsoft Edge | ~/Library/Application Support/Microsoft Edge | UBF8T346G9 |
| Firefox | ~/Library/Application Support/Firefox | 43AQ936H96 |
| Ledger Live | ~/Library/Application Support/Ledger Live | X6LFS5BQKN |
| Exodus | ~/Library/Application Support/Exodus | VK5Q293EVL |
| Wasabi Wallet | ~/.walletwasabi | L233B2JQ68 |
The supporting machinery is visible in the same binary: a sandbox filter named vnode-is-in-appprotection, operations appprotection-ancestor, appprotection-id, appprotection-path and appprotection-subpath, a private entitlement com.apple.private.security.appprotection-authority that sandboxd holds itself, and a registration store at /var/db/Sandbox/appprotection whose contents are the eight paths in lowercase, each paired with its attribution bundle ID.
The exemption fields are worth reading closely, because they are all identity and none of them is privilege: processSigningId, processTeamId, responsibleSigningId and responsibleTeamId. There is no user field and no privilege field anywhere in the structure.
Where the two rule sets differ
The eight protected folders are identical. The exclusions are not.
Delivered defaultRules-1 | Enforced defaultRules-2 | |
|---|---|---|
| Exclusion count | 6 | 4 |
NativeMessagingHosts for Brave, Chrome, Edge, Mozilla | yes | yes |
Chrome/ChromeFeatureState | yes | no |
Chrome/Crashpad | yes | no |
That difference is what makes the mismatch demonstrable rather than merely textual. From a context subject to the policy, Chrome/NativeMessagingHosts is readable, because both rule sets exclude it. Chrome/Crashpad and Chrome/ChromeFeatureState are denied, although the delivered file excludes both. The rules being applied are the built-in ones.
I confirmed that across a reboot, so a stale sandboxd that had not re-read the bundle cannot explain it. On a clean boot with XProtect 5355 already installed, a fresh sandboxd logged the same "not found" line, the sysctl still reported defaultRules-2, and both Chrome subdirectories were still denied.
What this establishes, and what it doesn't. The two filenames differ on public beta 2, and the delivered file is not read on it. It does not establish Apple's intent, and it is not a claim that anyone made a mistake. The detailed rule-set specifics stay scoped to the build I measured; the filename mismatch itself I have since re-confirmed on the current public beta, build 26A5416b, where the enforced rule set has advanced to defaultRules-3, which I have not characterized. One strings command settles the filename on any macOS 27 host.
Apple names the user, not the administrator
The macOS 27 release notes carry it under System Integrity Protection, in the TCC section, radar 178668601:
"XProtect may now restrict access to app data that is commonly targeted by malicious software. Accessing files created by other developer teams' apps may be denied by default but may be managed by the user in Privacy & Security settings."Apple, macOS 27 release notes
An adjacent note, radar 161835690, says the same thing about app group containers: denied by default, "managed by the user in Privacy & Security settings."
Neither note mentions MDM, configuration profiles, or declarative management. Apple names the pane a user opens.
Note also how carefully Apple hedged it. "May now restrict," not "restricts." Nothing below is more certain than Apple's own wording.
Every surface an administrator has, and what each one carries
Apple publishes its device management schema as a public repository. It has exactly two branches: release, last touched in March for 26.4, and seed_OS_27_0, which tracks the macOS 27 betas. I fetched both and searched the whole repository including the examples.
| Surface | What it carries | Administrator key |
|---|---|---|
| PPPC profile, nine file access services | Full Disk Access down to the Downloads folder | Yes, all nine |
| PPPC profile, this new app data service | Nothing. No such service exists in the schema | No |
Declarative PermissionDefaults | Seven macOS permissions, none of them file services | No |
ConfigDataInstall | Blanket switch for configuration data | Removed in macOS 27 |
Four things are worth stating precisely.
The nine existing file services are all keyed and none are deprecated. The PPPC payload's file access services run from SystemPolicyAllFiles down to SystemPolicyDownloadsFolder, and every one of them still answers to an administrator in the macOS 27 seed. SystemPolicyAppData, introduced in macOS 14, is the closest existing neighbor and it carries no deprecation marker.
Apple's schema does not carry a key for the new service. Searching both branches for AppDataDetailed, AppProtectionRules, and AppDataDetail returns zero hits. The positive control returns exactly one hit for SystemPolicyAppData, at line 306 of the TCC profile YAML, so the search itself works.
That absence is current, not stale. Apple modified that exact TCC schema file in Seed5 on 12 August 2026, eight lines changed. The changes were an Accessibility content rewrite and three corrections of com.apple.configuration.app-settings to com.apple.configuration.app.settings. None of them added a service. Apple was editing the file and did not add a key.
The key did not move to declarative management either. The declarative configuration replacing parts of PPPC, com.apple.configuration.app.settings, carries a Privacy dictionary of PermissionDefaults. Its complete service list is OrganizationJustification, Accessibility, Bluetooth, Camera, Dictation, LocalNetwork, Location, LocationAccuracy, and Microphone. There are no file system services in it at all. This is a live schema Apple is actively extending, and it omits file services too.
The honest phrasing of all of that is the narrow one: Apple's device management schema does not carry a key for this. Not "no key exists anywhere."
The one lever, and it is blunt
There is a way to stop this policy reaching a Mac, and almost nobody should use it.
ConfigDataInstall stops configuration data installing. That is the same channel XProtect malware definitions arrive on, so turning it off to avoid one ruleset also stops the ruleset that catches actual stealers. It is the definition of a blunt instrument.
It also does not survive the upgrade. Apple marks that whole payload removed in macOS 27, and I can find no equivalent in its declarative replacement. The one lever is blunt today and gone tomorrow.
What is actually enforcing this today
Oakley found the file shipping and wrote that acting on it would probably need new code in macOS 27. He said plainly that he had not tested which versions could apply the rules. So I tested it.
On macOS 15 and macOS 26, strings against the two binaries that would have to act on the policy:
| Check | macOS 15 | macOS 26 |
|---|---|---|
tccd, the new service name | 0 | 0 |
tccd, kTCCServiceSystemPolicyAllFiles (control) | 4 | 4 |
sandboxd, AppProtection | 0 | 0 |
sandboxd, sandbox (control) | 43 | 43 |
sandboxd, walletwasabi | 0 | 0 |
The controls are the point. Both binaries still return the file access services that do exist, so the zeros are a real absence rather than a search that missed.
macOS 27 is the other end of that matrix, and there the same targets return content. These figures come from a separate pass on public beta 2, so they are reported against their own controls rather than folded into the table above:
| Check on macOS 27 | Result |
|---|---|
sandboxd, distinct strings matching appprotection | 26 |
sandboxd, raw hits for AppProtectionRules.plist | 1 |
sandboxd, raw hits for AppProtectionPolicy.plist | 0 |
sandboxd, raw hits for sandboxd (control) | 37 |
sandboxd, walletwasabi | 1 |
sandboxd, total strings extracted (control) | 7,846 |
tccd, kTCCServiceSystemPolicyAppData | present |
tccd, kTCCServiceSystemPolicyAppDataDetailed | present |
tccd, total strings extracted (control) | 15,910 |
That completes the version matrix. The policy shipped to macOS 15 and macOS 26 in August, neither of them has any code that reads it, and macOS 27 has the code but looks for a different filename.
One tooling note that cost me a false result, because it fails silently. On a stock macOS image without Xcode Command Line Tools, /usr/bin/strings, nm, otool and python3 are installer shims: they print a prompt to stderr and exit zero with empty output. My first pass on macOS 27 reported no AppProtection strings in sandboxd, which was entirely an artifact, and the only reason I caught it was a positive control that returned zero total strings from a binary that plainly has thousands. /usr/bin/perl ships with macOS proper and is not a shim, so the numbers above were taken with it. Every figure here is paired with a control for exactly this reason.
One method note, because it changed the conclusion. A find(1) sweep across the same roots returned zero hits for XProtect.yara, a file that demonstrably exists on both machines. That positive control failed, which made that sweep's negative result worthless. I threw it out and inspected the bundles directly. Three separate sweeps failed their own controls during this work, and the only reason the surviving numbers mean anything is that their controls passed.
The scope of the macOS 15 and macOS 26 measurement is userland, and the limit is worth stating: those kernels were never swept. That gap matters less now than it did, because the macOS 27 result is positive rather than negative, and a kernel sysctl reporting defaultRules-2 is direct evidence of what is loaded rather than an inference from an absence.
The exclusions are not an escape hatch
This is the part with a Monday consequence, so read the exclusion list before assuming your tooling is fine.
Six paths are carved out of the protected folders in the delivered file. Four are NativeMessagingHosts directories, for Brave, Chrome, Edge, and Mozilla. Reguła documented those four from beta 3. The delivered file adds two more, both under Chrome: ChromeFeatureState and Crashpad.
On macOS 27 it is four, not six, because the delivered file is not the one in force. Read the number off the rule set you are actually running against: six if you are describing the artifact in the XProtect bundle, four if you are describing what macOS 27 enforces.
Not one of them, on either count, is profile data. Cookies, login data, and the profile directories all sit inside the boundary.
The protection is path-scoped with punch-through subdirectories, which Reguła demonstrated directly: listing ~/Library/Application Support/Google/Chrome fails with "Operation not permitted" while listing .../Chrome/NativeMessagingHosts underneath it works normally. The exclusion list is therefore part of the security boundary, not a footnote to it.
That is true of three of the four NativeMessagingHosts exclusions. The fourth, Mozilla's, is not a punch-through at all. The exclusion sits at .../Mozilla/NativeMessagingHosts, but the folder Apple protects is .../Firefox, a sibling directory the policy never covers, so that exclusion excuses nothing. Apple named Firefox's real native-messaging location, which on macOS lives under Mozilla/ rather than under Firefox/, and the rule set carried the exclusion over without adjusting for it.
One correction to a secondary source while you are here. Oakley characterizes the two new exclusions as being there "to support Chrome's Progressive Web Apps." ChromeFeatureState is a feature-flag state store and Crashpad is a crash-reporting spool. Neither is a PWA mechanism, and the PWA framing is worth not repeating.
If you maintain anything that reads a browser profile, it is the thing that stops, not the thing that survives.
There is a second carve-out that says something about Apple's model. allowedPlatformSigningIds appears on exactly two entries, Chrome and Firefox, each with a single value: com.apple.Safari.BrowserDataImportingService. Apple wrote its own data importer into two competitors' protected profile directories as a named signing ID rather than relying on platform privilege. That is a code-identity exemption model, not a privilege one, and the Developer ID test later confirms it behaves that way: identity decides, and privilege does not enter into it.
Root is not exempt, and your management scripts are not either
This was the highest-value open question for anyone running an endpoint agent, and nobody had published an answer. Reguła's proofs all run as an unprivileged user: he describes listing his own ~/Library/Application Support "from a plain Terminal (no Full Disk Access)", and his second proof runs from an ordinary shell prompt. He never runs sudo, never grants Full Disk Access, and never mentions either. Searching Objective-See, Hacker News, r/macsysadmin, Michael Tsai's thread, Oakley's comment threads and the published guidance from Jamf, Kandji, CrowdStrike, SentinelOne and Huntress turned up nobody who had.
The file hinted at the answer. Six of the allowed signing IDs are updaters and privileged helpers that run as root, and Apple would not need to name them if root walked through the boundary unimpeded. That was reasoning rather than a result, so I ran it.
The results below come from a root LaunchDaemon, which is the important detail. A daemon does not inherit the Full Disk Access grant that a login or remote-shell session may carry, and it is also the shape real fleet automation actually takes.
| Context | Reading a protected folder |
|---|---|
| Unprivileged user, no Full Disk Access | Denied, EPERM |
| Root, no Full Disk Access | Denied, EPERM, read and write |
Root shell script from a LaunchDaemon | Denied, EPERM |
| Ad-hoc signed binary as root | Denied, EPERM |
| User with Full Disk Access | Allowed |
| Root with Full Disk Access | Allowed |
Root is not exempt. Privilege is not the axis, which is exactly what the rule set's own structure predicted: every exemption field in it names a signing identity or a team, and there is no user or privilege field anywhere in the schema.
The consequence for management tooling is the blunt one. A policy script is a shell script, and a shell script has no code signature of its own; it executes as /bin/bash or /bin/zsh, both of which are Apple platform-signed binaries. The obvious hope was that allowedPlatformSigningIds would match on the interpreter and exempt every shell script in every fleet as a class. It does not. The only platform signing ID on the allowlist is com.apple.Safari.BrowserDataImportingService. A root LaunchDaemon running /bin/bash was denied on every protected path present on the test system.
Full Disk Access is the exemption that works. The same script, unchanged, read and wrote every protected folder when its context held Full Disk Access, and was denied when it did not. That is the lever, and it is a user-or-administrator-granted TCC permission rather than anything in this policy.
Ruling out the boring explanation
A daemon without Full Disk Access also cannot read ~/Documents, because that is ordinary TCC. So a denial on its own proves nothing, and the interesting question is whether these particular denials are this policy or just TCC doing its usual job.
The exclusion list settles it. From one root LaunchDaemon, in a single run, inside the same parent directory:
| Path | Result |
|---|---|
~/Library/Application Support/Google/Chrome | Denied |
~/Library/Application Support/Google/Chrome/NativeMessagingHosts | Allowed, and it is an exclusion in the rule set |
~/Library/Application Support/Google/Chrome/Default | Denied |
~/Library/Application Support/Firefox | Denied |
~/Library/Application Support/Firefox-decoy | Allowed |
~/Library/Application Support/ZZZ-Control-Unprotected | Allowed |
~/Library/Application Support itself | Allowed |
No TCC rule produces that pattern. A directory one level below a denied path is readable because the policy excludes it by name, a near-identically named sibling of a denied path is readable because it is not in the policy, and the shared parent of all of them is readable throughout. The denials track the rule set, exclusions included.
The kernel says the same thing in its own words. During those runs it logged lines of the form:
System Policy: ls(2254) deny(1) file-read-data
/Users/admin/Library/Application Support/Google/Chrome
A real Developer ID does not get you in either
The shell-script result above is about the interpreter's platform signature. The other identity a fleet cares about is its own: the Developer ID certificate that a third-party agent, an MDM helper, or an in-house tool is signed with. I tested that directly with a valid Apple Developer ID, because the machine I was working from has one.
A three-line opendir() probe, compiled to a Mach-O and signed four ways, each run from a root LaunchDaemon with no Full Disk Access, against a protected folder and an unprotected control:
| Signature | Claimed identifier | Team | Protected folder |
|---|---|---|---|
| Ad-hoc | probe-adhoc-… | none | Denied |
| Developer ID | probe-devid (honest) | 33G6D6CH35 | Denied |
| Developer ID | com.google.Chrome | 33G6D6CH35 | Denied |
| Developer ID | org.mozilla.firefox | 33G6D6CH35 | Denied |
Every one of them read the unprotected control folder normally, so the probe works and the denial is specific to the protected path.
Two things fall out of that. A genuine Developer ID signature with a team that is not on the allowlist is denied, which is the signature almost every third-party agent and management tool actually ships with. And claiming an allowlisted identifier you do not own does not help: signing a binary with an unrelated team while labeling it com.google.Chrome was denied exactly like the honest one, so there is no cheap identifier-spoof around the boundary. That is the reassuring direction for a security control to fail in.
There is a limit worth being precise about. I cannot test the positive case, a binary carrying Chrome's real team ID, because producing one would mean holding Google's signing key. So this measures that a wrong team is denied and that a spoofed identifier cannot rescue it; it does not decompose whether the live match is on team alone or on team and identifier together. The one allowlisted identity I can see being admitted is still Apple's own importer, named in the rule set rather than run.
A platform security agent still sees it
A platform security agent still sees the attempt. Running eslogger open during a window in which the kernel logged fourteen denials against protected paths, the ESF stream captured an open event against ~/.walletwasabi from the denied process. The call fails, and the agent sees it happen.
One limit, and it matters: eslogger is Apple platform-signed. That answers whether a platform Endpoint Security client can observe this. It does not answer whether a third-party EDR is exempt from the boundary, which needs the com.apple.developer.endpoint-security.client entitlement and therefore Apple's approval. That question stays open.
The filename, the enforce key, and the version numbers
Three details resolve once you separate the delivered file from the enforced rule set, and none resolve the way the obvious reading suggests.
The filename does not match, and it is load-bearing rather than cosmetic. Reguła's punchline was a hardcoded path he read out of sandboxd on beta 3, ending in AppProtectionRules.plist. What Apple ships, in that same directory, is AppProtectionPolicy.plist. Oakley independently names the shipped file the same way, so both sides were already confirmed on paper. What was untested was whether a later beta looks for the new name. It does not. On public beta 2 the hardcoded path is still AppProtectionRules.plist, the delivered file is still AppProtectionPolicy.plist, and the operating system logs that it is falling back to its internal copy. The rename, if it is one, has not landed on the enforcement side.
The enforce key did not vanish, it is in the other artifact. Reguła's beta 3 Wasabi entry carried enforce set to true, and no entry in the delivered file has it. The question was whether it had been dropped in favor of implicit always-enforce, or whether the delivered rules were advisory. Neither: the rule set actually in force on macOS 27 carries enforce set to true on all eight protections. The key is present, explicit, and enforcing in defaultRules-2. Its absence from defaultRules-1 is a property of the delivered file, and since the delivered file is not read, that absence has no runtime meaning on macOS 27.
The two version numbers are not one file at two revisions. The delivered file is defaultRules-1; the rule set compiled into sandboxd is defaultRules-2. Different artifacts, different counters. defaultRules-2 lives in the binary and is what enforces; defaultRules-1 is delivered through XProtect and, on macOS 27, is never read.
What is still open
Two questions are worth stating plainly. One I could close on a newer beta; the other I cannot, and the reason is not scheduling.
Whether a third-party Endpoint Security client is exempt. Testing it needs the com.apple.developer.endpoint-security.client entitlement, which needs an Apple Developer account and an approval request to Apple. eslogger is the closest available stand-in and, being Apple platform-signed, it answers a different question. This one stays open with the reason attached rather than being settled by a proxy.
Whether the mismatch survives into a newer beta: checked. The detailed measurements here are public beta 2, build 26A5388g. I have since booted the current public beta, build 26A5416b: the mismatch holds, sandboxd still looks for AppProtectionRules.plist, the delivered file is still AppProtectionPolicy.plist, and the fallback still logs on boot. The internal rule set it falls back to has moved one version, to defaultRules-3, which I have not taken apart, so the exclusion counts and roster above stay scoped to beta 2. A single strings pass over sandboxd on any macOS 27 host confirms the filename it looks for, which is why the recommendation below is to check your own rather than take mine.
Three things worth doing before macOS 27
- Read the policy on your own Mac. It is in the XProtect bundle today, on whatever macOS version you are running, provided the configuration data has reached that Mac. You do not need a beta to see what Apple decided to protect.
- Audit anything that reads a browser profile as root, and stop assuming root is enough. Cookies, login data, and profile directories are all inside the boundary, none of them are excluded, and root is denied. Anything of yours that reads one needs Full Disk Access in macOS 27 or it stops.
- Check which filename your own
sandboxdis looking for. Onestringspass answers whether the delivered file is inert on the build you are running. If you are doing this on a stock machine, install the Command Line Tools first or useperl, because thestringsshim exits zero with no output and will tell you the string is absent when it is not.
What this does not claim
The absences here are the load-bearing part, so they are worth stating in their exact form.
- Apple's device management schema does not carry a key for this. Not "no key exists."
- The declarative payload's privacy permissions do not include file system services. Not "Apple removed it."
- Root is denied on macOS 27 public beta 2, measured from a
LaunchDaemonwith the exclusion list used to separate this policy from ordinary TCC. Not "root is denied on every macOS 27 build," because only one build was run. - The delivered
AppProtectionPolicy.plistis not read on public beta 2, becausesandboxdlooks forAppProtectionRules.plist. Not "Apple made a mistake," and not "the file will never be read." Intent is not measurable from a VM and a later build was not tested. sandboxdandtccdon macOS 15 and 26 contain nothing that reads this file, positive-controlled. Not "macOS 15 can never read it," because those kernels were not examined.- Unsigned binaries were not shown to be denied. Those runs were killed by the platform before the policy was ever consulted, on control paths as well as protected ones, so they measured nothing about this boundary and are excluded rather than reported.
- Apple says "may now restrict." Nothing here is more certain than Apple's own wording about Apple's intent, though the observed behavior on the build tested is not hedged.
- A platform Endpoint Security client observes the access, measured. Whether a third-party EDR is exempt from the boundary is untested and needs an entitlement Apple has to grant.
And one thing worth saying plainly against the obvious reading: this is not administrators losing control in macOS 27 across the board. The same release adds binary allowlisting through AllowedBinaries and DeniedBinaries, real administrator power on supervised Macs. Bundle-level allowlisting by bundle ID has existed on managed Macs for about fifteen years; what's new here is allowlisting a specific binary by its code-signing identity rather than the whole app bundle. The pattern is narrower and more interesting than a trend. One specific protection arrived with a user-side control surface and no administrator one.
That is the thing to watch. This particular policy is enforcing, on the build I ran, from rules built into the operating system rather than from the file the channel delivered. The question worth following is whether shipping security policy through the XProtect channel becomes the normal way it arrives. That channel updates weekly, needs no OS release, and as of 2026-08-17 had one administrator-facing control: a checkbox for uploading samples.
It is worth sitting with what the delivered file being unread actually implies. The artifact every administrator can inspect, diff and reason about is the one that is not in force, and the one in force is compiled into a binary and reachable only by reading that binary. Whatever the reason, the practical effect on macOS 27 today is that the auditable copy and the enforcing copy are not the same rules.
If you are mapping the rest of the macOS 27 privacy model, what moved off PPPC and what still needs a profile covers the declarative side in detail, the Accessibility grant removal is the one permission whose profile grant genuinely goes away, and the declarative management roundup covers what else moved this cycle. If you are auditing an endpoint agent's file access right now, the PPPC Builder will build the payload for the nine services that still answer to you.
Sources
Sources, retrieved 2026-08-16. Both schema links are pinned to the immutable seed_OS_27_0 Seed5 commit reviewed for this post:
- Howard Oakley: Apple has released a major update to XProtect for all macOS
- Howard Oakley: What just changed in XProtect
- Wojciech Reguła: Golden Gate app data protection
- Apple: macOS 27 release notes, System Integrity Protection and TCC
- Apple device-management schema: TCC / PPPC YAML, Seed5 commit 61449b0
- Apple device-management schema: App Settings YAML, Seed5 commit 61449b0
- LevelBlue SpiderLabs: Say My Name, How MioLab is building MacOS Stealer Empire
- 9to5Mac: macOS 27 public beta 2, build 26A5388g, the build measured here