mirror of
https://github.com/openai/codex.git
synced 2026-11-12 21:20:34 +00:00
Add sign-only mode and tighten macOS signing verification (#60671)
## What changed - Add a `sign-only` operation to sign macOS CLI packages without notarization. - Validate the configured signing certificate before signing both standard and provisioned packages. - Require the configured team ID for every executable during verification and extend leaf certificate fingerprint checks to standard packages. ## Testing Add coverage for signing a standard package without notarization and rejecting unexpected signing certificates in both standard and provisioned packages. GitOrigin-RevId: 0794c7666abfe981b59b47311e3a9108223b8f57
This commit is contained in:
@@ -188,15 +188,20 @@ def verify(package, reports, expected_target, configuration: ProfileConfiguratio
|
||||
binary = package / relative
|
||||
target = package / APP if relative == EXECUTABLE else binary
|
||||
prefix = reports / f"{binary.name}-cert-"
|
||||
subprocess.run(
|
||||
["codesign", "-d", f"--extract-certificates={prefix}", str(target)],
|
||||
check=True,
|
||||
)
|
||||
if (
|
||||
hashlib.sha256(Path(f"{prefix}0").read_bytes()).hexdigest()
|
||||
!= configuration.certificate_sha256
|
||||
):
|
||||
raise ValueError(f"Unexpected signing certificate: {relative}")
|
||||
verify_certificate(target, prefix, configuration)
|
||||
|
||||
|
||||
def verify_certificate(target, prefix, configuration: ProfileConfiguration):
|
||||
"""Require the independently approved leaf certificate for each signed executable."""
|
||||
subprocess.run(
|
||||
["codesign", "-d", f"--extract-certificates={prefix}", str(target)],
|
||||
check=True,
|
||||
)
|
||||
if (
|
||||
hashlib.sha256(Path(f"{prefix}0").read_bytes()).hexdigest()
|
||||
!= configuration.certificate_sha256
|
||||
):
|
||||
raise ValueError(f"Unexpected signing certificate: {target}")
|
||||
|
||||
|
||||
def parse_profile_args(parser, configuration: ProfileConfiguration | None):
|
||||
|
||||
@@ -12,7 +12,7 @@ import provisioned_macos_cli_package as bundle
|
||||
|
||||
def main(configuration: bundle.ProfileConfiguration | None = None):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("operation", choices=("sign", "verify"))
|
||||
parser.add_argument("operation", choices=("sign", "sign-only", "verify"))
|
||||
parser.add_argument("package", type=Path)
|
||||
args, configuration = bundle.parse_profile_args(parser, configuration)
|
||||
package = args.package.resolve(strict=True)
|
||||
@@ -24,11 +24,13 @@ def main(configuration: bundle.ProfileConfiguration | None = None):
|
||||
reports = Path(repo_root) / "signing-verification"
|
||||
reports.mkdir(parents=True, exist_ok=True)
|
||||
provisioned = os.environ.get("PROVISIONED_MACOS", "false") == "true"
|
||||
signing = args.operation in ("sign", "sign-only")
|
||||
if signing:
|
||||
configuration.validate_signing_certificate(
|
||||
Path(os.environ["OAI_AKV_SIGNING_CERTIFICATE_PEM"])
|
||||
)
|
||||
if provisioned:
|
||||
if args.operation == "sign":
|
||||
configuration.validate_signing_certificate(
|
||||
Path(os.environ["OAI_AKV_SIGNING_CERTIFICATE_PEM"])
|
||||
)
|
||||
if signing:
|
||||
bundle.prepare(package, reports, configuration)
|
||||
else:
|
||||
bundle.verify(package, reports, os.environ["TARGET"], configuration)
|
||||
@@ -44,7 +46,7 @@ def main(configuration: bundle.ProfileConfiguration | None = None):
|
||||
target = package / bundle.APP
|
||||
identifier = bundle.CODE_SIGNING_ID
|
||||
entitlements = reports / "codex-provisioned-entitlements.plist"
|
||||
if args.operation == "sign":
|
||||
if signing:
|
||||
command = [
|
||||
"bash",
|
||||
str(bundle.SIGNING / "sign_macos_code.sh"),
|
||||
@@ -72,7 +74,7 @@ def main(configuration: bundle.ProfileConfiguration | None = None):
|
||||
stdout=output,
|
||||
check=True,
|
||||
)
|
||||
if not provisioned:
|
||||
if args.operation == "sign" and not provisioned:
|
||||
subprocess.run(
|
||||
[
|
||||
"bash",
|
||||
@@ -103,11 +105,11 @@ def main(configuration: bundle.ProfileConfiguration | None = None):
|
||||
" and certificate 1[field.1.2.840.113635.100.6.2.6] exists"
|
||||
" and certificate leaf[field.1.2.840.113635.100.6.1.13] exists"
|
||||
)
|
||||
requirement += (
|
||||
f' and certificate leaf[subject.OU] = "{configuration.team_id}"'
|
||||
)
|
||||
if provisioned and relative == "bin/codex":
|
||||
requirement += (
|
||||
f' and identifier "{bundle.CODE_SIGNING_ID}"'
|
||||
f' and certificate leaf[subject.OU] = "{configuration.team_id}"'
|
||||
)
|
||||
requirement += f' and identifier "{bundle.CODE_SIGNING_ID}"'
|
||||
subprocess.run(
|
||||
[
|
||||
"codesign",
|
||||
@@ -120,6 +122,10 @@ def main(configuration: bundle.ProfileConfiguration | None = None):
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
if not provisioned:
|
||||
bundle.verify_certificate(
|
||||
target, reports / f"{name}-cert-", configuration
|
||||
)
|
||||
signature = reports / f"{name}-signature.txt"
|
||||
with signature.open("wb") as output:
|
||||
subprocess.run(
|
||||
|
||||
@@ -26,7 +26,7 @@ with open(os.environ["CALL_LOG"], "a") as log:
|
||||
if name == "codesign":
|
||||
for arg in args:
|
||||
if arg.startswith("--extract-certificates="):
|
||||
Path(arg.split("=", 1)[1] + "0").write_bytes(Path(os.environ["MOCK_CERTIFICATE"]).read_bytes())
|
||||
Path(arg.split("=", 1)[1] + "0").write_bytes(b"wrong certificate" if os.environ.get("MOCK_WRONG_CERTIFICATE") else Path(os.environ["MOCK_CERTIFICATE"]).read_bytes())
|
||||
if "--test-requirement" in args and os.environ.get("MOCK_WRONG_SIGNING_IDENTITY"):
|
||||
print("code failed to satisfy specified code requirement(s)", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -149,6 +149,26 @@ class MacosSigningTests(ProvisioningTestCase):
|
||||
check=False,
|
||||
)
|
||||
|
||||
def test_sign_only_signs_standard_package_without_notarization(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory).resolve()
|
||||
package, driver, env = self.fixture(root, False, "aarch64-apple-darwin")
|
||||
result = self.run_driver(driver, "sign-only", package, env)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
calls = [
|
||||
json.loads(line)
|
||||
for line in (root / "calls.jsonl").read_text().splitlines()
|
||||
]
|
||||
self.assertEqual(
|
||||
[
|
||||
call[call.index("--target") + 1]
|
||||
for call in calls
|
||||
if call[0] == "sign_macos_code.sh"
|
||||
],
|
||||
[str(package / path) for path in ("bin/codex", *bundle.HELPERS)],
|
||||
)
|
||||
self.assertFalse(any(call[0].startswith("notarize") for call in calls))
|
||||
|
||||
def test_standard_and_provisioned_signing_and_verification(self):
|
||||
for provisioned, target, omit_empty in itertools.product(
|
||||
(False, True),
|
||||
@@ -333,6 +353,7 @@ class MacosSigningTests(ProvisioningTestCase):
|
||||
(
|
||||
"MOCK_WRONG_SIGNING_IDENTITY",
|
||||
"MOCK_WRONG_ENTITLEMENTS",
|
||||
"MOCK_WRONG_CERTIFICATE",
|
||||
"MOCK_EXECUTOR_ENTITLEMENTS",
|
||||
),
|
||||
):
|
||||
@@ -353,6 +374,8 @@ class MacosSigningTests(ProvisioningTestCase):
|
||||
"code failed to satisfy specified code requirement(s)",
|
||||
result.stderr,
|
||||
)
|
||||
elif failure == "MOCK_WRONG_CERTIFICATE":
|
||||
self.assertIn("Unexpected signing certificate", result.stderr)
|
||||
else:
|
||||
self.assertIn("unexpected", result.stdout)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user