feat(companion): add optional signing for release bundle artifacts
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { mkdtemp, readFile, rm, stat } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { generateKeyPairSync, verify, createPublicKey } from 'node:crypto';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { writeCompanionReleaseBundle } from './releaseBundle.js';
|
||||
|
||||
@@ -66,4 +67,53 @@ describe('writeCompanionReleaseBundle', () => {
|
||||
|
||||
await rm(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('writes signature artifact when signing key is provided', async () => {
|
||||
const tempDir = await mkdtemp(join(tmpdir(), 'flynn-companion-release-sign-'));
|
||||
const outputDir = join(tempDir, 'bundle');
|
||||
const keyPair = generateKeyPairSync('rsa', { modulusLength: 2048 });
|
||||
const privatePem = keyPair.privateKey.export({ type: 'pkcs8', format: 'pem' }).toString();
|
||||
const publicPem = keyPair.publicKey.export({ type: 'spki', format: 'pem' }).toString();
|
||||
|
||||
const result = await writeCompanionReleaseBundle({
|
||||
outputDir,
|
||||
signingKeyPem: privatePem,
|
||||
signingKeyId: 'test-key',
|
||||
manifest: {
|
||||
schemaVersion: 1,
|
||||
generatedAt: '2026-02-27T00:00:00.000Z',
|
||||
gateway: { url: 'ws://127.0.0.1:18800' },
|
||||
node: {
|
||||
nodeId: 'macos-node',
|
||||
role: 'companion',
|
||||
platform: 'macos',
|
||||
capabilities: ['ui.canvas'],
|
||||
},
|
||||
runtime: {
|
||||
heartbeatSeconds: 30,
|
||||
handoffTimeoutMs: 120000,
|
||||
autoReconnect: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.signaturePath).toBe(`${outputDir}/CHECKSUMS.sha256.sig`);
|
||||
|
||||
const checksumsRaw = await readFile(result.checksumsPath, 'utf8');
|
||||
const signatureRaw = await readFile(result.signaturePath!, 'utf8');
|
||||
const signatureLine = signatureRaw.split('\n').find((line) => line.startsWith('signature='));
|
||||
expect(signatureLine).toBeTruthy();
|
||||
expect(signatureRaw).toContain('key_id=test-key');
|
||||
|
||||
const signature = Buffer.from(String(signatureLine).replace('signature=', ''), 'base64');
|
||||
const verified = verify(
|
||||
'sha256',
|
||||
Buffer.from(checksumsRaw, 'utf8'),
|
||||
createPublicKey(publicPem),
|
||||
signature,
|
||||
);
|
||||
expect(verified).toBe(true);
|
||||
|
||||
await rm(tempDir, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user