fix(hololake): harden immutable broadcast activation

This commit is contained in:
冰朔 2026-08-13 18:37:30 +08:00
commit aa3df05775
2 changed files with 47 additions and 6 deletions

View file

@ -43,7 +43,7 @@ function readTrustedJson(file, options) {
return { bytes: fs.readFileSync(file), value: JSON.parse(fs.readFileSync(file, 'utf8')) }
}
function validatePlatform(platform, releaseDirectory, enforceRootOwner) {
function validatePlatform(platform, releaseDirectory, enforceRootOwner, sourceCommit) {
const url = new URL(requireText(platform?.url, 'HOLOLAKE_RELEASE_PACKAGE_URL_REQUIRED'))
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash) {
fail('HOLOLAKE_RELEASE_PACKAGE_URL_INVALID')
@ -71,11 +71,13 @@ function validatePlatform(platform, releaseDirectory, enforceRootOwner) {
).value
if (
codeReceipt?.schema !== 'hololake.platform-code-signature-receipt/v1' ||
codeReceipt?.state !== 'DEVELOPER_ID_SIGNATURE_STRICT_AND_GATEKEEPER_ACCEPTED'
codeReceipt?.state !== 'DEVELOPER_ID_SIGNATURE_STRICT_AND_GATEKEEPER_ACCEPTED' ||
codeReceipt?.sourceCommit !== sourceCommit
) fail('HOLOLAKE_RELEASE_CODESIGN_RECEIPT_INVALID')
if (
notarizationReceipt?.schema !== 'hololake.apple-notarization-receipt/v1' ||
notarizationReceipt?.state !== 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED'
notarizationReceipt?.state !== 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED' ||
notarizationReceipt?.sourceCommit !== sourceCommit
) fail('HOLOLAKE_RELEASE_NOTARIZATION_RECEIPT_INVALID')
return { pathname: url.pathname, file: packagePath, size: packageInfo.size }
}
@ -97,16 +99,27 @@ function loadActiveRelease(stateRoot, enforceRootOwner) {
requireText(activation.humanApprovalReceipt, 'HOLOLAKE_RELEASE_HUMAN_APPROVAL_REQUIRED')
const broadcastPath = safeRelative(stateRoot, activation.broadcastRelativePath, 'HOLOLAKE_RELEASE_BROADCAST_PATH_INVALID')
const pipelinePath = safeRelative(stateRoot, activation.pipelineReceiptRelativePath, 'HOLOLAKE_RELEASE_PIPELINE_PATH_INVALID')
const approvalPath = safeRelative(stateRoot, activation.humanApprovalReceiptRelativePath, 'HOLOLAKE_RELEASE_HUMAN_APPROVAL_PATH_INVALID')
const broadcastDocument = readTrustedJson(broadcastPath, { enforceRootOwner })
const pipelineReceipt = readTrustedJson(pipelinePath, { enforceRootOwner }).value
const approvalReceipt = readTrustedJson(approvalPath, { enforceRootOwner }).value
if (sha256(broadcastDocument.bytes) !== activation.broadcastSha256) fail('HOLOLAKE_RELEASE_BROADCAST_SHA256_MISMATCH')
if (
pipelineReceipt?.schema !== 'hololake.signed-release-pipeline-receipt/v1' ||
pipelineReceipt?.state !== 'SIGNED_NOTARIZED_RELEASE_BROADCAST_READY_FOR_JD_CONTROLLER_UPLOAD' ||
pipelineReceipt?.broadcastSha256 !== activation.broadcastSha256 ||
!/^[a-f0-9]{40}$/.test(pipelineReceipt?.sourceCommit || '') ||
pipelineReceipt?.automaticUpload !== false ||
pipelineReceipt?.automaticActivation !== false
) fail('HOLOLAKE_RELEASE_PIPELINE_RECEIPT_INVALID')
if (
approvalReceipt?.schema !== 'hololake.release-broadcast-human-approval/v1' ||
approvalReceipt?.state !== 'HUMAN_APPROVED_EXACT_SIGNED_NOTARIZED_RELEASE' ||
approvalReceipt?.approvalId !== activation.humanApprovalReceipt ||
approvalReceipt?.releaseId !== releaseId ||
approvalReceipt?.version !== version ||
approvalReceipt?.broadcastSha256 !== activation.broadcastSha256
) fail('HOLOLAKE_RELEASE_HUMAN_APPROVAL_INVALID')
const broadcast = broadcastDocument.value
if (
broadcast?.schema !== 'hololake.release-broadcast/v1' ||
@ -122,7 +135,7 @@ function loadActiveRelease(stateRoot, enforceRootOwner) {
const releaseDirectory = path.dirname(broadcastPath)
const packages = new Map()
for (const platform of Object.values(broadcast.platforms)) {
const accepted = validatePlatform(platform, releaseDirectory, enforceRootOwner)
const accepted = validatePlatform(platform, releaseDirectory, enforceRootOwner, pipelineReceipt.sourceCommit)
if (packages.has(accepted.pathname)) fail('HOLOLAKE_RELEASE_PACKAGE_ROUTE_COLLISION')
packages.set(accepted.pathname, accepted)
}
@ -226,7 +239,11 @@ export function main(env = process.env) {
return server
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
export function isMainModule(argvPath, moduleUrl) {
return Boolean(argvPath) && fs.realpathSync(path.resolve(argvPath)) === fs.realpathSync(fileURLToPath(moduleUrl))
}
if (isMainModule(process.argv[1], import.meta.url)) {
try {
main()
} catch (error) {