diff --git a/.github/scripts/macos-signing/codex-app-server.entitlements.plist b/.github/scripts/macos-signing/codex-app-server.entitlements.plist new file mode 100644 index 000000000000..26b12f28868c --- /dev/null +++ b/.github/scripts/macos-signing/codex-app-server.entitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/.github/scripts/macos-signing/codex-code-mode-host.entitlements.plist b/.github/scripts/macos-signing/codex-code-mode-host.entitlements.plist new file mode 100644 index 000000000000..26b12f28868c --- /dev/null +++ b/.github/scripts/macos-signing/codex-code-mode-host.entitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/.github/scripts/macos-signing/codex-responses-api-proxy.entitlements.plist b/.github/scripts/macos-signing/codex-responses-api-proxy.entitlements.plist new file mode 100644 index 000000000000..d35e43ae588c --- /dev/null +++ b/.github/scripts/macos-signing/codex-responses-api-proxy.entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.allow-jit + + + diff --git a/.github/scripts/macos-signing/codex.entitlements.plist b/.github/scripts/macos-signing/codex.entitlements.plist index d35e43ae588c..26b12f28868c 100644 --- a/.github/scripts/macos-signing/codex.entitlements.plist +++ b/.github/scripts/macos-signing/codex.entitlements.plist @@ -4,5 +4,7 @@ com.apple.security.cs.allow-jit + com.apple.security.cs.allow-unsigned-executable-memory + diff --git a/.github/scripts/test_macos_signing_entitlements.py b/.github/scripts/test_macos_signing_entitlements.py new file mode 100644 index 000000000000..e56d5f3a1b6a --- /dev/null +++ b/.github/scripts/test_macos_signing_entitlements.py @@ -0,0 +1,37 @@ +import plistlib +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +SIGNING_DIR = ROOT / ".github" / "scripts" / "macos-signing" +ALLOW_JIT = "com.apple.security.cs.allow-jit" +ALLOW_UNSIGNED_EXECUTABLE_MEMORY = ( + "com.apple.security.cs.allow-unsigned-executable-memory" +) + + +class MacosSigningEntitlementsTest(unittest.TestCase): + def load(self, binary: str) -> dict[str, bool]: + path = SIGNING_DIR / f"{binary}.entitlements.plist" + with path.open("rb") as file: + return plistlib.load(file) + + def test_v8_binaries_allow_unsigned_executable_memory(self) -> None: + expected = { + ALLOW_JIT: True, + ALLOW_UNSIGNED_EXECUTABLE_MEMORY: True, + } + for binary in ["codex", "codex-app-server", "codex-code-mode-host"]: + with self.subTest(binary=binary): + self.assertEqual(self.load(binary), expected) + + def test_responses_proxy_keeps_existing_entitlements(self) -> None: + self.assertEqual( + self.load("codex-responses-api-proxy"), + {ALLOW_JIT: True}, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 93f03e63d125..9fdd66637bf8 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -573,6 +573,12 @@ jobs: zstd -d --stdout "$unsigned_path" >"$signed_path" chmod 0755 "$signed_path" + entitlements="${GITHUB_WORKSPACE}/.github/scripts/macos-signing/${binary}.entitlements.plist" + if [[ ! -f "$entitlements" ]]; then + echo "Entitlements file $entitlements not found" + exit 1 + fi + .github/scripts/macos-signing/sign_macos_code.sh \ --target "$signed_path" \ --identity unused \ @@ -580,7 +586,7 @@ jobs: --identifier "$binary" \ --options runtime \ --timestamp true \ - --entitlements .github/scripts/macos-signing/codex.entitlements.plist + --entitlements "$entitlements" mkdir -p "${report_dir}/${binary}" rcodesign print-signature-info "$signed_path" \ @@ -974,15 +980,29 @@ jobs: target="${{ matrix.target }}" packaged_dir="dist/${target}" - expected_entitlements="${GITHUB_WORKSPACE}/.github/scripts/macos-signing/codex.entitlements.plist" + case "$target" in + aarch64-apple-darwin) expected_arch="arm64" ;; + x86_64-apple-darwin) expected_arch="x86_64" ;; + *) + echo "Unexpected macOS target: $target" + exit 1 + ;; + esac verify_signed_binary() { local path="$1" - local actual_entitlements normalized_actual normalized_expected + local binary="$2" + local actual_entitlements expected_entitlements normalized_actual normalized_expected chmod 0755 "$path" + lipo "$path" -verify_arch "$expected_arch" codesign --verify --strict --verbose=2 "$path" + expected_entitlements="${GITHUB_WORKSPACE}/.github/scripts/macos-signing/${binary}.entitlements.plist" + if [[ ! -f "$expected_entitlements" ]]; then + echo "Expected entitlements file $expected_entitlements not found" + exit 1 + fi actual_entitlements="$(mktemp)" normalized_actual="$(mktemp)" normalized_expected="$(mktemp)" @@ -995,7 +1015,7 @@ jobs: for binary in ${{ matrix.binaries }}; do binary_path="${RUNNER_TEMP}/signed-binaries/${binary}" - verify_signed_binary "$binary_path" + verify_signed_binary "$binary_path" "$binary" # The app-server package contains the host, but its standalone archives # are omitted above to avoid duplicate release asset names. @@ -1007,11 +1027,11 @@ jobs: rm -rf "$direct_archive_dir" mkdir -p "$direct_archive_dir" tar -xzf "${packaged_dir}/${binary}-${target}.tar.gz" -C "$direct_archive_dir" - verify_signed_binary "${direct_archive_dir}/${binary}-${target}" + verify_signed_binary "${direct_archive_dir}/${binary}-${target}" "$binary" direct_zstd_path="${RUNNER_TEMP}/${binary}-${target}-from-zstd" zstd -d --stdout "${packaged_dir}/${binary}-${target}.zst" >"$direct_zstd_path" - verify_signed_binary "$direct_zstd_path" + verify_signed_binary "$direct_zstd_path" "$binary" done case "${{ matrix.bundle }}" in @@ -1033,8 +1053,8 @@ jobs: rm -rf "$package_dir" mkdir -p "$package_dir" tar -xzf "${packaged_dir}/${package_stem}-${target}.tar.gz" -C "$package_dir" - verify_signed_binary "${package_dir}/bin/${package_entrypoint}" - verify_signed_binary "${package_dir}/bin/codex-code-mode-host" + verify_signed_binary "${package_dir}/bin/${package_entrypoint}" "$package_entrypoint" + verify_signed_binary "${package_dir}/bin/codex-code-mode-host" "codex-code-mode-host" if [[ "${{ matrix.verify_dmg }}" != "true" ]]; then exit 0 @@ -1060,7 +1080,7 @@ jobs: trap cleanup_mount EXIT for binary in ${{ matrix.binaries }}; do - verify_signed_binary "${mount_dir}/${binary}" + verify_signed_binary "${mount_dir}/${binary}" "$binary" done cleanup_mount