Skip to content

[Bug]: Framework API ClientImage.config(for:) fails from sandboxed app bundle with blob-store permission error #1620

@pd95

Description

@pd95

I have done the following

  • I have searched the existing issues
  • If possible, I've reproduced the issue using the 'main' branch of this project

Steps to reproduce

Calling the Swift framework/API path ClientImage.config(for:) succeeds from
an unsandboxed SwiftPM executable, but fails from a sandboxed .app bundle
signed with the Apple Container service Mach lookup temporary exceptions.

The repro is a minimal SwiftPM executable. It:

  1. Calls ClientImage.list().
  2. Selects docker.io/library/alpine:latest from the returned local image list.
  3. Calls ClientImage.config(for: Platform.current).

Relevant code:

import ArgumentParser
import ContainerAPIClient
import ContainerizationOCI
import Foundation

@main
struct ImageConfigSandboxRepro: AsyncParsableCommand {
    @Option(help: "Local image reference to inspect.")
    var image = "docker.io/library/alpine:latest"

    func run() async throws {
        let images = try await ClientImage.list()

        guard let selectedImage = images.first(where: { $0.reference == image }) else {
            Foundation.exit(2)
        }

        let imageConfig = try await selectedImage.config(for: Platform.current).config
        print("User: \(imageConfig?.user ?? "<nil>")")
        print("Working dir: \(imageConfig?.workingDir ?? "<nil>")")
    }
}

Sandbox entitlements used for the .app bundle (ImageConfigSandboxRepro.entitlements):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>com.apple.security.app-sandbox</key>
        <true/>
        <key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
        <array>
            <string>com.apple.container.apiserver</string>
            <string>com.apple.container.core.container-core-images</string>
        </array>
    </dict>
</plist>

Info.plist used for the .app bundle:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>en</string>
        <key>CFBundleExecutable</key>
        <string>image-config-sandbox-repro</string>
        <key>CFBundleIdentifier</key>
        <string>com.example.ImageConfigSandboxRepro</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
        <string>ImageConfigSandboxRepro</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
        <string>1.0</string>
        <key>CFBundleVersion</key>
        <string>1</string>
        <key>LSBackgroundOnly</key>
        <true/>
        <key>LSMinimumSystemVersion</key>
        <string>15.0</string>
        <key>NSPrincipalClass</key>
        <string>NSApplication</string>
    </dict>
</plist>

Pull the test image. This is the only container CLI step; the repro itself
uses the Swift framework/API packages:

container image pull alpine:latest

Run the unsandboxed control:

swift run image-config-sandbox-repro

Build, wrap, sign, and run the sandboxed app bundle:

swift build

rm -rf .build/debug/ImageConfigSandboxRepro.app
mkdir -p .build/debug/ImageConfigSandboxRepro.app/Contents/MacOS
cp .build/debug/image-config-sandbox-repro \
  .build/debug/ImageConfigSandboxRepro.app/Contents/MacOS/image-config-sandbox-repro
cp Info.plist .build/debug/ImageConfigSandboxRepro.app/Contents/Info.plist

codesign --force --sign - --deep \
  --entitlements ImageConfigSandboxRepro.entitlements \
  .build/debug/ImageConfigSandboxRepro.app

.build/debug/ImageConfigSandboxRepro.app/Contents/MacOS/image-config-sandbox-repro

Current behavior

The unsandboxed run succeeds:

Listing local Apple Container images...
Found 38 image(s).
Selected image: docker.io/library/alpine:latest
About to call ClientImage.config(for: .current).
Config read succeeded.
User: <nil>
Working dir: /
Env:
  PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

The sandboxed app-bundle run fails:

Listing local Apple Container images...
Found 38 image(s).
Selected image: docker.io/library/alpine:latest
About to call ClientImage.config(for: .current).
Error: You don’t have permission to save the file “5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11” in the folder “sha256”.

The failure happens after ClientImage.list() succeeds and after the local
image has been selected through the framework/API. The failing call appears to
attempt direct access from the sandboxed client process to Apple Container's
content blob store:

~/Library/Application Support/com.apple.container/content/blobs/sha256/<digest>

Expected behavior

A sandboxed app that has the Apple Container service Mach lookup exceptions
should be able to read local image config through the Swift framework/API
surface, or that API should expose a sandbox-safe way to retrieve image config
metadata without requiring the client process to directly open Apple Container
blob-store files.

Environment

- OS: 26.5 (25F71)
- Xcode: 26.4 (17E192)
- Swift: Apple Swift version 6.3 (swiftlang-6.3.0.123.5 clang-2100.0.123.102)

Relevant log output

Error Domain=NSCocoaErrorDomain Code=513
"You don't have permission to save the file "<digest>" in the folder "sha256"."
NSFilePath=/Users/<user>/Library/Application Support/com.apple.container/content/blobs/sha256/<digest>
NSUnderlyingError=... Code=1 "Operation not permitted"

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions