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