Files
RemoteDesk/packaging/windows/test-update-manifest.mjs
T
曾志威 5db6b9ef68
ci / rust (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / package-preview (push) Canceled after 0s
ci / package-installer (push) Canceled after 0s
ci / linux-agent (push) Canceled after 0s
ci / edge-service (push) Canceled after 0s
ci / coturn-pop (push) Canceled after 0s
ci / package-windows-host (push) Canceled after 0s
Initial commit
2026-08-14 00:35:42 +08:00

78 lines
3.3 KiB
JavaScript

import { createHash, generateKeyPairSync, verify } from 'node:crypto'
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
import process from 'node:process'
import { spawnSync } from 'node:child_process'
function runGenerator(script, args, expectedSuccess) {
const result = spawnSync(process.execPath, [script, ...args], {
encoding: 'utf8',
windowsHide: true,
})
if ((result.status === 0) !== expectedSuccess) {
throw new Error(`generator exit ${result.status}: ${result.stderr || result.stdout}`)
}
}
const root = await mkdtemp(path.join(tmpdir(), 'remotedesk-update-manifest-'))
try {
const script = path.resolve('packaging/windows/create-update-manifest.mjs')
const installer = path.join(root, 'RemoteDesk-M0-9.8.7-windows-x64.msi')
const privateKeyPath = path.join(root, 'private.pem')
const output = path.join(root, 'stable.json')
const publicOutput = path.join(root, 'public.txt')
const installerBytes = Buffer.from('deterministic MSI fixture bytes', 'utf8')
const { privateKey, publicKey } = generateKeyPairSync('ed25519')
await writeFile(installer, installerBytes)
await writeFile(privateKeyPath, privateKey.export({ type: 'pkcs8', format: 'pem' }))
const validArgs = [
'--installer', installer,
'--installer-url', 'https://updates.example.test/releases/RemoteDesk-M0-9.8.7-windows-x64.msi',
'--private-key', privateKeyPath,
'--version', '9.8.7',
'--channel', 'stable',
'--target', 'windows-x64',
'--output', output,
'--public-key-output', publicOutput,
]
runGenerator(script, validArgs, true)
const envelope = JSON.parse(await readFile(output, 'utf8'))
if (envelope.schema !== 1 || typeof envelope.payload !== 'string' || typeof envelope.signature !== 'string') {
throw new Error('generated envelope schema is invalid')
}
const payloadBytes = Buffer.from(envelope.payload, 'base64')
const signature = Buffer.from(envelope.signature, 'base64')
if (signature.length !== 64 || !verify(null, payloadBytes, publicKey, signature)) {
throw new Error('generated Ed25519 signature is invalid')
}
const payload = JSON.parse(payloadBytes.toString('utf8'))
const expectedHash = createHash('sha256').update(installerBytes).digest('hex')
if (payload.product !== 'remotedesk'
|| payload.channel !== 'stable'
|| payload.version !== '9.8.7'
|| payload.target !== 'windows-x64'
|| payload.installer.sha256 !== expectedHash
|| payload.installer.size_bytes !== installerBytes.length) {
throw new Error('signed payload does not bind the installer and release metadata')
}
const expectedPublicKey = Buffer.from(publicKey.export({ format: 'jwk' }).x, 'base64url').toString('base64')
if ((await readFile(publicOutput, 'ascii')).trim() !== expectedPublicKey) {
throw new Error('published raw update public key does not match the signing key')
}
runGenerator(script, validArgs, false)
runGenerator(script, [
'--installer', installer,
'--installer-url', 'http://updates.example.test/RemoteDesk.msi',
'--private-key', privateKeyPath,
'--version', '9.8.7',
'--output', path.join(root, 'invalid.json'),
], false)
process.stdout.write('update manifest generator contract: OK\n')
} finally {
await rm(root, { recursive: true, force: true })
}