← All issues

[23] Pass an extra ApplicationBundleIdentifier through PCM

Severity: Low | Component: WebKit Networking / PCM | f8277ab

Rated Low because the diff is a feature addition rather than a vulnerability fix, but it relocates a privacy-proxy fail-closed flag from an unconditional set in the loader to a conditional set in a centralised helper and broadens the adattributiond daemon's sandbox/entitlements to permit direct network egress — two structural changes that narrow a privacy invariant and widen the daemon's attack surface.

Adds a "secondary" identifier (com.apple.webkit.adattributiond) plumbed through PrivateClickMeasurementManager and NetworkLoader::start, sets it on the NSURLSessionConfiguration for adattributiond's URLSession, and grants the daemon network-client/socket-delegate/networkserviceproxy entitlements plus mach-lookup for com.apple.networkserviceproxy. The _privacyProxyFailClosed flag moves from PrivateClickMeasurementNetworkLoaderCocoa.mm (unconditional) to NetworkDataTaskCocoa.mm::setPCMDataCarriedOnRequest next to _needsNetworkTrackingPrevention; per the commit message it now only applies outside debug mode.

Source/WebKit/NetworkProcess/PrivateClickMeasurement/cocoa/PrivateClickMeasurementNetworkLoaderCocoa.mm

WTF::switchOn(applicationBundleIdentifier,
- [&] (const String& bundleIdentifier) {
- configuration.get()._sourceApplicationBundleIdentifier = bundleIdentifier.createNSString().get();
+ [&] (const std::pair<String, String>& bundleIdentifiers) {
+ configuration.get()._sourceApplicationBundleIdentifier = bundleIdentifiers.first.createNSString().get();
+ configuration.get()._sourceApplicationSecondaryIdentifier = bundleIdentifiers.second.createNSString().get();
}, ...);
- [request _setPrivacyProxyFailClosed:YES];
+ [request setAttribution:NSURLRequestAttributionUser];

Source/WebKit/NetworkProcess/cocoa/NetworkDataTaskCocoa.mm

request._needsNetworkTrackingPrevention = YES;
+ request._privacyProxyFailClosed = YES;

Source/WebKit/Scripts/process-entitlements.sh

+ plistbuddy Add :com.apple.private.network.socket-delegate bool YES
+ plistbuddy Add :com.apple.security.network.client bool YES
+ plistbuddy Add :com.apple.private.networkserviceproxy bool YES
+ plistbuddy Add :com.apple.security.exception.mach-lookup.global-name:0 string com.apple.networkserviceproxy

Privacy-proxy fail-closed guard relocated and made conditional on debug mode, paired with a sandbox/entitlement relaxation that hands the attribution daemon a network egress path.

ApplicationBundleIdentifierOrAuditToken becomes ApplicationBundleIdentifiersOrAuditToken (Variant<std::pair<String, String>, Vector<uint8_t>>) and is threaded through PrivateClickMeasurementManager::create/ctor, NetworkLoader::start, managerOrProxy, and initializePCMStorageInDirectory. The Cocoa loader sets both _sourceApplicationBundleIdentifier and _sourceApplicationSecondaryIdentifier. PCMDaemonEntryPoint.mm hard-codes the secondary identifier to com.apple.webkit.adattributiond. setAttribution:NSURLRequestAttributionUser is now set on PCM report requests. The adattributiond sandbox profile is relaxed to read com.apple.networkextension.uuidcache.plist.

PCM is WebKit's privacy-preserving ad-click attribution mechanism — a click on a source site stores metadata, and on later conversion an attribution report is sent to the source's endpoint. On iOS, PCM runs in an XPC daemon (adattributiond) using NSURLSession to post reports. _sourceApplicationBundleIdentifier/_sourceApplicationSecondaryIdentifier are NSURLSessionConfiguration SPIs that tag outgoing traffic with the originating application's identity. _privacyProxyFailClosed is an NSMutableURLRequest SPI that tells the networking stack to fail the request rather than fall back to a direct connection when the privacy relay is unavailable; without it, a relay outage degrades to a direct connection that exposes the client IP. networkserviceproxy is the system service that brokers network connections for sandboxed daemons.

The refactor itself is mechanical and ownership-preserving — the Variant still holds either a value-pair of Strings or a Vector<uint8_t>, all by-value, so no lifetime change. The two behavioural deltas matter. The relocation of _privacyProxyFailClosed narrows the invariant from "set on this request object before it leaves the loader" to "set somewhere downstream conditional on PCM data being carried"; reviewers should be wary of any future code path that constructs a PCM-bound request but bypasses setPCMDataCarriedOnRequest, since the fail-closed guarantee would silently degrade.

🔒

The privacy-proxy and sandbox/entitlement implications of routing PCM traffic through a network-capable daemon are examined in depth.

Subscribe to read more

🔒

Multiple reusable audit patterns identified around flag-relocation, daemon entitlement expansion, and Variant-shape migrations, each with concrete starting points.

Subscribe to read more