Fix Code Signing
Release rolling / release (push) Successful in 9m7s

This commit is contained in:
Raven Scott
2026-07-31 22:28:54 -04:00
parent 32496ebab2
commit 4c5c06898a
3 changed files with 126 additions and 2 deletions
+12
View File
@@ -0,0 +1,12 @@
<?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.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
+86 -2
View File
@@ -13,6 +13,22 @@ const fs = require('fs')
const path = require('path')
const { spawnSync, execFileSync } = require('child_process')
/**
* macOS entitlements embedded in every signed Bare standalone binary.
*
* Bare (V8) needs a JIT CodeRange; under Hardened Runtime (`runtime` flag) that
* requires `allow-jit` + `allow-unsigned-executable-memory`. Native addons in
* the bundle (e.g. bare-type's linker-signed prebuild) dlopen from a temp dir
* and are signed by a different/absent team — `disable-library-validation` is
* required so their Team ID mismatch does not abort the process.
*/
const ENTITLEMENTS = path.join(__dirname, '..', 'build', 'bare-os.entitlements')
const REQUIRED_ENTITLEMENTS = [
'com.apple.security.cs.allow-jit',
'com.apple.security.cs.allow-unsigned-executable-memory',
'com.apple.security.cs.disable-library-validation',
]
function log(...a) {
console.log('[sign-macos]', ...a)
}
@@ -102,9 +118,18 @@ function codesignDarwin(binPath, id) {
} catch {
// ignore
}
const args = ['--force', '--sign', id, '--timestamp=none', binPath]
const args = [
'--force',
'--sign',
id,
'--entitlements',
ENTITLEMENTS,
binPath,
]
if (id !== '-') {
args.splice(3, 1, '--options', 'runtime', '--timestamp')
args.push('--options', 'runtime', '--timestamp')
} else {
args.push('--timestamp=none')
}
const r = spawnSync('codesign', args, { encoding: 'utf8' })
if (r.status !== 0) {
@@ -116,9 +141,27 @@ function codesignDarwin(binPath, id) {
if (v.status !== 0) {
throw new Error(`codesign verify failed:\n${v.stderr || v.stdout}`)
}
verifyEntitlements(binPath)
log('verify ok')
}
function verifyEntitlements(binPath) {
const r = spawnSync('codesign', ['-d', '--entitlements', '-', binPath], {
encoding: 'utf8',
})
if (r.status !== 0) {
throw new Error(`entitlement inspection failed:\n${r.stderr || r.stdout}`)
}
const out = r.stdout || ''
const missing = REQUIRED_ENTITLEMENTS.filter((key) => !out.includes(key))
if (missing.length) {
throw new Error(
`entitlements missing from signed binary: ${missing.join(', ')}\n${out}`
)
}
log('entitlements ok')
}
function codesignRcodesign(binPath) {
const bin = which('rcodesign')
if (!bin) {
@@ -137,6 +180,8 @@ function codesignRcodesign(binPath) {
password,
'--code-signature-flags',
'runtime',
'--entitlements-xml-file',
ENTITLEMENTS,
binPath,
],
{ encoding: 'utf8' }
@@ -144,9 +189,45 @@ function codesignRcodesign(binPath) {
if (r.status !== 0) {
throw new Error(`rcodesign sign failed:\n${r.stderr || r.stdout}`)
}
verifyEntitlementsRcodesign(bin, binPath)
log('rcodesign ok')
}
/** Best-effort check that the entitlements plist made it into the Mach-O. */
function verifyEntitlementsRcodesign(bin, binPath) {
const r = spawnSync(
bin,
['print-signature-info', '--json', binPath],
{ encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 }
)
if (r.status !== 0) {
log(
`WARN: could not inspect entitlements via rcodesign (continuing): ${
r.stderr || r.stdout
}`
)
return
}
try {
const info = JSON.parse(r.stdout)
const ent = info.entitlements || info.entitlements_plist || {}
const text = JSON.stringify(ent)
const missing = REQUIRED_ENTITLEMENTS.filter((key) => !text.includes(key))
if (missing.length) {
throw new Error(
`entitlements missing from rcodesign-signed binary: ${missing.join(', ')}`
)
}
log('entitlements ok')
} catch (err) {
if (err instanceof SyntaxError) {
log('WARN: could not parse rcodesign signature info (continuing)')
return
}
throw err
}
}
/**
* @param {string} binPath
* @returns {Promise<void>}
@@ -157,6 +238,9 @@ async function signBinary(binPath) {
return
}
if (!fs.existsSync(binPath)) throw new Error(`missing binary: ${binPath}`)
if (!fs.existsSync(ENTITLEMENTS)) {
throw new Error(`missing entitlements file: ${ENTITLEMENTS}`)
}
const id = identity()
if (process.platform === 'darwin') {
codesignDarwin(binPath, id)
+28
View File
@@ -112,6 +112,33 @@ function assertSize(product, host, file) {
return st.size
}
/** Darwin JIT/library-validation entitlements required under Hardened Runtime. */
const REQUIRED_ENTITLEMENTS = [
'com.apple.security.cs.allow-jit',
'com.apple.security.cs.allow-unsigned-executable-memory',
'com.apple.security.cs.disable-library-validation',
]
function assertDarwinEntitlements(product, host, file) {
if (!host.startsWith('darwin') || process.platform !== 'darwin') return
const r = spawnSync('codesign', ['-d', '--entitlements', '-', file], {
encoding: 'utf8',
})
if (r.status !== 0) {
throw new Error(
`[verify] ${product}/${host}: codesign -d --entitlements failed: ${r.stderr || r.stdout}`
)
}
const out = r.stdout || ''
const missing = REQUIRED_ENTITLEMENTS.filter((key) => !out.includes(key))
if (missing.length) {
throw new Error(
`[verify] ${product}/${host}: missing macOS entitlements: ${missing.join(', ')}`
)
}
console.log(`[verify] ${product}/${host}: darwin entitlements ok`)
}
function assertMarkers(file) {
// Sample start of file as latin1 for cheap string checks on embedded bundle
const fd = fs.openSync(file, 'r')
@@ -190,6 +217,7 @@ function main() {
}
assertSize(product, host, file)
assertMarkers(file)
assertDarwinEntitlements(product, host, file)
checked++
if (product === 'booter' && host === nativeHost) {
smokeNativeBooter(file)