Sparkle: Binary delta apply intermediate-symlink traversal in malicious .delta
Date: 2026-05
Affected: swift/github.com/sparkle-project/Sparkle
Attack vector: path-traversal
Category: vulnerability-disclosure
Severity: Medium
CVE: CVE-2026-47121
CWE: CWE-22
CWE-59
CVSS: 6.1 (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:N/I:H/A:N)

## Summary Binary delta apply intermediate-symlink traversal in malicious .delta `Autoupdate/SUBinaryDeltaApply.m` enforces `relativePath.pathComponents containsObject:@".."` and rejects writes whose immediate parent directory IS itself a symbolic link, but does not detect symlinks deeper in the relative path. `Autoupdate/SPUSparkleDeltaArchive.m`'s `extractItem:` will create symlinks in the destination tree from archive content (no `..` check on the symlink target), and a subsequent `Extract` item targeting `<symlink>/foo/bar` then escapes the destination tree via `fopen(path, "wb")` because the kernel resolves the intermediate symlink during the open call. This is a defense-in-depth issue: exploitation requires a maliciously-crafted `.delta` that passes EdDSA signature verification, i.e. EdDSA private-key compromise. With the AppInstaller running as root for system-domain installs, it gives the holder of a stolen signing key arbitrary file write at root level via the delta-apply path, which is a strictly broader primitive than the "drop-in replacement bundle" install they would otherwise have. Affected versions: 1.x (master branch), 2.x branch including 2.9.1. ## Details ### Symlink writeable from archive `Autoupdate/SPUSparkleDeltaArchive.m:557-678`'s `extractItem:` handles symlinks if the archive item carries `S_ISLNK(mode)`: ```objc } else { // Link files if (PARTIAL_IO_CHUNK_SIZE < decodedLength) { ...too long... } if (decodedLength > PATH_MAX) { ..


Mitigations:

Architectural layers (MAESTRO):