Apple: path traversal
HighPublished Aug 30, 2026
## Impact A malicious or compromised OCI registry may be able to serve a manifest or index whose descriptor `digest` field contains path-traversal (`..`) components, causing `container pull`, `run`, `create` (or any other consumer of `ContainerizationOCI`) to open a host file outside the local content store's blob directory during import. The confirmed impact is a file existence and readability oracle: an attacker may be able to observe whether a path exists and is readable on the macOS host, based on success or error behavior. Before the fix described below, the digest file read was unbounded, such that a `digest` with path traversal could refer to a very large or unreadable-to-EOF file, exhausting memory and/or denying service to the pulling process. ## Details `Descriptor.digest` (`Sources/ContainerizationOCI/Descriptor.swift`) was a plain `String` with no format validation, decoded straight from registry responses and on-disk OCI layouts via Swift's synthesized `Codable` conformance. `String.trimmingDigestPrefix` (`Sources/ContainerizationOCI/Content/String+Extension.swift`), used throughout the codebase to turn a digest into a filesystem path component, only split the stri...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| github.com/apple/container Product | < 1.3.0 | 1.3.0 |
| github.com/apple/containerization Product | < 0.41.0 | 0.41.0 |
Details and references
## Impact A malicious or compromised OCI registry may be able to serve a manifest or index whose descriptor `digest` field contains path-traversal (`..`) components, causing `container pull`, `run`, `create` (or any other consumer of `ContainerizationOCI`) to open a host file outside the local content store's blob directory during import. The confirmed impact is a file existence and readability oracle: an attacker may be able to observe whether a path exists and is readable on the macOS host, based on success or error behavior. Before the fix described below, the digest file read was unbounded, such that a `digest` with path traversal could refer to a very large or unreadable-to-EOF file, exhausting memory and/or denying service to the pulling process. ## Details `Descriptor.digest` (`Sources/ContainerizationOCI/Descriptor.swift`) was a plain `String` with no format validation, decoded straight from registry responses and on-disk OCI layouts via Swift's synthesized `Codable` conformance. `String.trimmingDigestPrefix` (`Sources/ContainerizationOCI/Content/String+Extension.swift`), used throughout the codebase to turn a digest into a filesystem path component, only split the string on `:` and returned the remainder verbatim — a digest such as `sha256:../../etc/hosts` came back as `../../etc/hosts`, untouched. `LocalContentStore.get(digest:)` (`Sources/ContainerizationOCI/Content/LocalContentStore.swift`) appended that unvalidated string directly onto the store's `blobs/sha256` directory with `URL.appendingPathComponent`, which does not collapse `..`, and opened the result. Every `container pull`/`run`/`create` reaches this sink through `ImageStore+Import.swift`'s `fetch()` and `getManifestContent()`, so a single malicious manifest is enough — no separate ingestion step is required. `getManifestContent()` doesn't just check whether the traversed path exists. It tries to `.decode()` the file's contents as an OCI manifest or index. Most traversed files aren't valid JSON in that shape, so `JSONDecoder` throws. That error can include a short fragment of the file's actual bytes near the parse failure. This only reaches the local operator's own error output or logs — the registry never sees it. The fix described below closes this too: it rejects the digest before a traversed path is ever built, so `.decode()` never runs on a traversed file at all. The same unvalidated digest does not also give a write primitive. `ImageStore+Import.swift`'s `fetch()` has a cache-hit branch that copies an already-resolved blob into the ingest directory with `FileManager.copyItem`, using the same digest string for both source and destination. `_blobPath` (`blobs/sha256`) and the ingest session directory sit at the same depth under the content store root. A malicious digest's `../` prefix resolves both paths to the same file. The copy fails with `EEXIST`. It never writes attacker-controlled content anywhere new. ## Affected code - `Sources/ContainerizationOCI/Descriptor.swift` (`digest: String`) — decoded with no format validation. - `Sources/ContainerizationOCI/Content/String+Extension.swift` (`trimmingDigestPrefix`) — strips the algorithm prefix without validating what remains. - `Sources/ContainerizationOCI/Content/LocalContentStore.swift` (`get`, `delete`, `delete(digests:)`) — unvalidated digest used to build blob paths. - `Sources/ContainerizationOCI/Content/LocalContent.swift` (`data()`, `decode()`) — read the entire target file into memory with no size bound, independent of the traversal (this is what makes the memory-exhaustion variant possible). - `Sources/Containerization/Image/ImageStore/ImageStore+Import.swift` (`fetch`, `getManifestContent`) — reachable from `container pull`/`run`/`create` on every image reference, not just a special import mode. **Not affected:** the `copyItem` cache-hit branch in `fetch()` cannot be used as a write primitive, for the reason described in Details above. ## Mitigations 1. U
- Severity from
- GitHub (reviewed advisory)
More Apple advisories
All Apple| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Aug 30 | Unpacking a crafted image layer with an invalid length extended-attribute name crashes the unpacking process | Medium | 0.41.0+1 more |
| Aug 30 | Unpacking a crafted image layer with a long(invalid) file name crashes the unpacking process | Medium | 0.41.0+1 more |
| Aug 30 | Loading an OCI image layout can read host files through a symlink | Medium | 0.41.0+1 more |
| Aug 30 | `RegistryClient` follows the `WWW-Authenticate` realm without validating its host or scheme | Medium | 0.41.0 |
| Aug 30 | ## Impact An attacker who can choose a container or exec id may be able to... | High | 0.41.0 |
| Aug 25 | Apple iOS and iPadOS: buffer overflow | Medium5.5 | 26.6+2 more |