Files
qinglong/test/back/ql3PhysicalEdgeReleaseEvidence.test.cjs

635 lines
18 KiB
JavaScript

const assert = require('node:assert/strict');
const crypto = require('node:crypto');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { test } = require('node:test');
const {
buildDirectServiceStartReport,
normalizeDirectServiceStartManifest,
normalizeSession,
parseStartupReceipt,
} = require('../../scripts/ql3-physical-edge-direct-service-start.cjs');
const {
buildEvidenceReport,
canonicalDigest,
normalizeManifest,
writeNoReplace,
} = require('../../scripts/ql3-physical-edge-evidence.cjs');
const {
buildFinalReport,
buildSigningPayload,
parseArguments,
validatePhysicalEvidenceReport,
} = require('../../scripts/ql3-physical-edge-release-evidence.cjs');
const {
runBenchmark: runPluginPackageRecoveryEdgeBenchmark,
} = require('../../scripts/ql3-plugin-package-recovery-edge-benchmark.cjs');
const SCRIPT_PATH = path.resolve(
__dirname,
'../../scripts/ql3-physical-edge-release-evidence.cjs',
);
const REPOSITORY = 'https://github.com/whyour/qinglong.git';
const REVISION = 'a'.repeat(40);
const PACKAGES = [
'@qinglong/local-admin',
'@qinglong/local-application',
'@qinglong/local-command-file',
'@qinglong/local-execution',
'@qinglong/local-process',
'@qinglong/local-secret',
'@qinglong/local-sqlite',
'@qinglong/runtime-core',
'croner',
'semver',
];
function artifact() {
return {
artifactSha256: '1'.repeat(64),
artifactMetadataSha256: '2'.repeat(64),
artifactFiles: 627,
artifactBytes: 5_045_360,
entrypointSha256: '3'.repeat(64),
packages: PACKAGES,
};
}
function directManifest() {
return normalizeDirectServiceStartManifest({
schemaVersion: 1,
evidenceClass: 'physical_edge_direct_service_start_candidate',
profile: 'edge',
deviceId: 'router-a1',
serviceManager: 'systemd',
expectedArchitecture: 'arm64',
expectedFilesystem: 'ext4',
expectedArtifactSha256: '1'.repeat(64),
expectedArtifactFiles: 627,
expectedArtifactBytes: 5_045_360,
expectedNodeSha256: '4'.repeat(64),
maximumBootToActiveMs: 180_000,
maximumServiceStartBootAgeMs: 60_000,
maximumServiceStartToActiveMs: 30_000,
});
}
function physicalManifest() {
return normalizeManifest({
schemaVersion: 1,
evidenceClass: 'physical_edge_candidate',
profile: 'edge',
deviceId: 'router-a1',
deviceModel: 'Example Router A1',
soc: 'Example SoC',
storageMedium: 'emmc',
expectedArchitecture: 'arm64',
memoryBytes: {
minimum: 128 * 1024 * 1024,
maximum: 512 * 1024 * 1024,
},
expectedFilesystem: 'ext4',
});
}
function environment(bootId) {
return {
platform: 'linux',
architecture: 'arm64',
bootId,
bootAgeMs: 15_000,
dataFilesystem: 'ext4',
nodeExecutable: '/usr/bin/node',
nodeSha256: '4'.repeat(64),
nodeVersion: 'v24.18.0',
virtualizationIndicators: [],
};
}
function physicalObserved(bootId) {
return {
platform: 'linux',
architecture: 'arm64',
node: 'v24.18.0',
bootId,
kernel: '6.6.0',
distribution: { id: 'openwrt', versionId: '24.10' },
libc: 'musl-ld-musl-aarch64.so.1',
cpuModel: 'Example CPU',
cpuCount: 1,
totalMemoryBytes: 256 * 1024 * 1024,
observedModel: 'Example Router A1',
dataPath: '/mnt/ql3-evidence',
dataFilesystem: 'ext4',
dataMountOptions: ['rw', 'noatime'],
dataBytes: 4 * 1024 * 1024 * 1024,
dataAvailableBytes: 2 * 1024 * 1024 * 1024,
virtualizationIndicators: [],
};
}
function edgeWorkloadReport() {
return {
schemaVersion: 1,
profile: 'edge',
generatedAt: '2026-08-14T08:01:30.000Z',
host: {
platform: 'linux',
architecture: 'arm64',
node: 'v24.18.0',
cpuCount: 1,
totalMemoryBytes: 256 * 1024 * 1024,
},
moduleLoad: {
rssBeforeBytes: 50_000_000,
rssAfterBytes: 55_000_000,
rssDeltaBytes: 5_000_000,
},
cases: [
{
name: 'single_noop',
durationMs: 15,
baselineRssBytes: 55_000_000,
peakRssBytes: 56_000_000,
peakRssDeltaBytes: 1_000_000,
outcome: 'succeeded',
exitCode: 0,
},
{
name: 'stdout_10000_lines',
durationMs: 45,
baselineRssBytes: 56_000_000,
peakRssBytes: 58_000_000,
peakRssDeltaBytes: 2_000_000,
outcome: 'succeeded',
exitCode: 0,
output: { bytes: 270_000, lines: 10_000, writes: 20 },
},
],
cancellation: {
durationMs: 20,
outcome: 'cancelled',
termSignalSent: true,
killSignalSent: false,
},
gates: {
maxRssDeltaMb: 96,
maxCancelMs: 5000,
passed: true,
violations: [],
},
};
}
function sqliteWorkloadReport() {
return {
node: 'v24.18.0',
arch: 'arm64',
platform: 'linux',
iterations: 250,
batchSize: 10,
journalMode: 'delete',
synchronous: 'full',
transactionMs: { p50: 0.1, p95: 0.2, p99: 0.3, max: 0.4 },
maxBatchStallMs: 2,
rssDeltaMb: 1,
databaseBytes: 16_384,
integrityCheck: 'ok',
};
}
function directSession() {
const manifest = directManifest();
const sessionId = '019f0000-0000-4000-8000-000000000030';
const dataPath = '/mnt/ql3-evidence';
const deploymentRoot = `${dataPath}/.ql3-direct-service-start-deployment-${sessionId}`;
const descriptorSource = `${deploymentRoot}/service/qinglong3.service`;
const body = {
schemaVersion: 1,
evidenceClass: 'physical_edge_direct_service_start_session',
sessionId,
manifestDigest: canonicalDigest(manifest),
uid: 1000,
preparedAt: '2026-08-14T08:00:00.000Z',
artifact: artifact(),
environment: environment('019f0000-0000-4000-8000-000000000001'),
bridge: {
actionId: sessionId,
controllerRoot: '/var/lib/qinglong3-service-bridge',
intentDigest: '5'.repeat(64),
intentPath: `${deploymentRoot}/service/service-manager-intents/${sessionId}.json`,
outcomePath: `${deploymentRoot}/service/service-manager-outcomes/${sessionId}.json`,
},
paths: {
dataPath,
deploymentRoot,
artifactRoot: '/opt/qinglong3-release',
applicationEntrypoint:
'/opt/qinglong3-release/node_modules/@qinglong/local-application/dist/cli.js',
applicationConfig: `${deploymentRoot}/local-application.json`,
startupReceipt: `${deploymentRoot}/local-application.json.active.json`,
},
service: {
kind: 'systemd',
serviceName: 'qinglong3',
managerExecutable: '/usr/bin/systemctl',
managerSha256: '6'.repeat(64),
enableExecutable: '/usr/bin/systemctl',
enableSha256: '6'.repeat(64),
supervisorExecutable: null,
supervisorSha256: null,
descriptorSource,
descriptorDestination: '/etc/systemd/system/qinglong3.service',
descriptorMode: 0o644,
descriptorSha256: '7'.repeat(64),
applicationConfigSha256: '8'.repeat(64),
installArguments: [
'-o',
'root',
'-g',
'root',
'-m',
'644',
descriptorSource,
'/etc/systemd/system/qinglong3.service',
],
enableArguments: ['enable', 'qinglong3'],
},
};
return normalizeSession({ ...body, sha256: canonicalDigest(body) });
}
function startupReceipt() {
const body = {
schemaVersion: 1,
schema: 'qinglong/local-application-startup-receipt@v1',
instanceId: 'physical-direct-019f0000',
profile: 'edge',
aiStatus: 'deployment_excluded',
bootId: '019f0000-0000-4000-8000-000000000002',
activeBootAgeMs: 13_250,
processId: 101,
processStartTicks: '1200',
nodeExecutable: '/usr/bin/node',
nodeVersion: 'v24.18.0',
};
const sha256 = crypto
.createHash('sha256')
.update('qinglong.local-application-startup-receipt.v1\0', 'utf8')
.update(JSON.stringify(body), 'utf8')
.digest('hex');
return parseStartupReceipt(JSON.stringify({ ...body, sha256 }));
}
function directReport() {
const session = directSession();
const receipt = startupReceipt();
const after = environment(receipt.bootId);
return buildDirectServiceStartReport({
manifest: directManifest(),
session,
observed: {
after,
artifact: artifact(),
bridge: {
actionId: session.bridge.actionId,
intentDigest: session.bridge.intentDigest,
outcomeDigest: '9'.repeat(64),
observationDigest: 'a'.repeat(64),
state: 'active',
},
process: {
bootId: after.bootId,
nodePid: 101,
nodeParentPid: 1,
nodeStartTicks: 1200,
clockTicksPerSecond: 100,
},
receipt,
service: {
kind: 'systemd',
serviceName: 'qinglong3',
managerExecutable: '/usr/bin/systemctl',
managerSha256: '6'.repeat(64),
descriptorSha256: '7'.repeat(64),
mainPid: 101,
mainStartMonotonicUs: 12_000_000,
},
},
measurements: {
serviceStartBootAgeMs: 12_000,
activeBootAgeMs: 13_250,
bootToActiveMs: 13_250,
serviceStartToActiveMs: 1_250,
},
outcomes: {
aiStatus: 'deployment_excluded',
descriptorInstalled: true,
initSupervisionMatched: true,
managerStartMonotonicMatched: true,
nodeProcessIdentityMatched: true,
ownerBridgeOutcomeVerified: true,
serviceActive: true,
serviceEnabled: true,
startupReceiptValidated: true,
},
generatedAt: '2026-08-14T08:01:00.000Z',
});
}
async function physicalReport() {
const plugin = await runPluginPackageRecoveryEdgeBenchmark({
maxDatabaseGrowthBytes: 4 * 1024 * 1024,
maxDurationMs: 10_000,
maxRssDeltaBytes: 96 * 1024 * 1024,
});
const direct = directReport();
const observed = physicalObserved(direct.observed.after.bootId);
return buildEvidenceReport({
manifest: physicalManifest(),
observed,
workloads: [
{
name: 'edge-executor',
report: edgeWorkloadReport(),
},
{
name: 'node-sqlite-on-device-storage',
report: sqliteWorkloadReport(),
},
{
name: 'plugin-package-failed-upgrade',
report: {
...plugin,
identity: {
...plugin.identity,
platform: 'linux',
architecture: 'arm64',
},
},
},
],
supplementalEvidence: [direct],
generatedAt: '2026-08-14T08:02:00.000Z',
});
}
function writeFile(filePath, contents, mode) {
fs.writeFileSync(filePath, contents, { mode });
fs.chmodSync(filePath, mode);
}
test('requires exact phase-specific source and authority inputs', () => {
assert.equal(
parseArguments([
'prepare',
'--physical-report=/tmp/physical.json',
'--release-archive=/tmp/release.tar.gz',
`--repository=${REPOSITORY}`,
`--revision=${REVISION}`,
'--payload=/tmp/payload.json',
]).phase,
'prepare',
);
assert.equal(
parseArguments([
'finalize',
'--physical-report=/tmp/physical.json',
'--release-archive=/tmp/release.tar.gz',
'--payload=/tmp/payload.json',
'--signature=/tmp/payload.sig',
'--trusted-public-key=/tmp/release.pub',
`--expected-repository=${REPOSITORY}`,
`--expected-revision=${REVISION}`,
'--output=/tmp/release-evidence.json',
]).phase,
'finalize',
);
assert.throws(
() =>
parseArguments([
'prepare',
'--physical-report=physical.json',
'--release-archive=/tmp/release.tar.gz',
`--repository=${REPOSITORY}`,
`--revision=${REVISION}`,
'--payload=/tmp/payload.json',
]),
/physicalReportPath must be absolute/,
);
});
test('reconstructs a passed physical report and retains unsupported status', async () => {
const report = await physicalReport();
assert.equal(report.qualification.physicalCandidate, true);
assert.deepEqual(validatePhysicalEvidenceReport(report), []);
const minimalForgery = buildEvidenceReport({
manifest: report.manifest,
observed: report.observed,
workloads: [
{
name: 'edge-executor',
report: {
schemaVersion: 1,
profile: 'edge',
host: {
platform: 'linux',
architecture: 'arm64',
},
gates: { passed: true, violations: [] },
},
},
report.workloads[1],
report.workloads[2],
],
supplementalEvidence: report.supplementalEvidence,
generatedAt: report.generatedAt,
});
assert.equal(minimalForgery.qualification.physicalCandidate, true);
assert.match(
validatePhysicalEvidenceReport(minimalForgery).join('; '),
/not an exact recorder result/,
);
const withoutDirect = buildEvidenceReport({
manifest: report.manifest,
observed: report.observed,
workloads: report.workloads,
generatedAt: report.generatedAt,
});
assert.match(
validatePhysicalEvidenceReport(withoutDirect).join('; '),
/direct release service start evidence is required/,
);
const payload = buildSigningPayload({
physicalReport: report,
releaseArchive: { sha256: 'b'.repeat(64), bytes: 1024 },
repository: REPOSITORY,
revision: REVISION,
signedAt: '2026-08-14T08:03:00.000Z',
});
const final = buildFinalReport({
physicalReport: report,
payload,
signature: Buffer.alloc(64, 1),
fingerprint: 'c'.repeat(64),
});
assert.equal(final.supported, false);
assert.equal(
final.qualification.remainingRequiredEvidence.includes(
'release_archive_signature',
),
false,
);
assert.ok(
final.qualification.remainingRequiredEvidence.includes(
'power_loss_restart',
),
);
});
test('prepares and verifies one externally signed release binding', async (t) => {
const directory = fs.realpathSync(
fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-release-evidence-')),
);
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
const paths = {
physical: path.join(directory, 'physical.json'),
archive: path.join(directory, 'release.tar.gz'),
payload: path.join(directory, 'payload.json'),
signature: path.join(directory, 'payload.sig'),
publicKey: path.join(directory, 'release.pub'),
output: path.join(directory, 'release-evidence.json'),
mismatchOutput: path.join(directory, 'mismatch-evidence.json'),
tamperedOutput: path.join(directory, 'tampered-evidence.json'),
};
writeNoReplace(
paths.physical,
`${JSON.stringify(await physicalReport(), null, 2)}\n`,
);
writeFile(paths.archive, Buffer.from('bounded release archive'), 0o444);
const prepared = spawnSync(
process.execPath,
[
SCRIPT_PATH,
'prepare',
`--physical-report=${paths.physical}`,
`--release-archive=${paths.archive}`,
`--repository=${REPOSITORY}`,
`--revision=${REVISION}`,
`--payload=${paths.payload}`,
'--json',
],
{ encoding: 'utf8' },
);
assert.equal(prepared.status, 0, prepared.stderr);
assert.equal(fs.statSync(paths.payload).mode & 0o777, 0o600);
assert.equal(fs.readFileSync(paths.payload, 'utf8').endsWith('\n'), false);
const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519');
const signature = crypto.sign(
null,
fs.readFileSync(paths.payload),
privateKey,
);
writeFile(paths.signature, signature, 0o600);
writeFile(
paths.publicKey,
publicKey.export({ type: 'spki', format: 'pem' }),
0o444,
);
const finalized = spawnSync(
process.execPath,
[
SCRIPT_PATH,
'finalize',
`--physical-report=${paths.physical}`,
`--release-archive=${paths.archive}`,
`--payload=${paths.payload}`,
`--signature=${paths.signature}`,
`--trusted-public-key=${paths.publicKey}`,
`--expected-repository=${REPOSITORY}`,
`--expected-revision=${REVISION}`,
`--output=${paths.output}`,
'--json',
],
{ encoding: 'utf8' },
);
assert.equal(finalized.status, 0, finalized.stderr);
const report = JSON.parse(fs.readFileSync(paths.output, 'utf8'));
assert.equal(report.supported, false);
assert.equal(report.qualification.passed, true);
assert.equal(report.payload.release.archiveBytes, 23);
assert.ok(
report.qualification.collectedEvidence.includes(
'release_archive_signature_or_attestation',
),
);
assert.match(report.trust.publicKeyFingerprintSha256, /^[a-f0-9]{64}$/);
const sourceMismatch = spawnSync(
process.execPath,
[
SCRIPT_PATH,
'finalize',
`--physical-report=${paths.physical}`,
`--release-archive=${paths.archive}`,
`--payload=${paths.payload}`,
`--signature=${paths.signature}`,
`--trusted-public-key=${paths.publicKey}`,
`--expected-repository=${REPOSITORY}`,
`--expected-revision=${'b'.repeat(40)}`,
`--output=${paths.mismatchOutput}`,
],
{ encoding: 'utf8' },
);
assert.notEqual(sourceMismatch.status, 0);
assert.match(sourceMismatch.stderr, /payload did not match/);
assert.equal(fs.existsSync(paths.mismatchOutput), false);
const noReplace = spawnSync(
process.execPath,
[
SCRIPT_PATH,
'finalize',
`--physical-report=${paths.physical}`,
`--release-archive=${paths.archive}`,
`--payload=${paths.payload}`,
`--signature=${paths.signature}`,
`--trusted-public-key=${paths.publicKey}`,
`--expected-repository=${REPOSITORY}`,
`--expected-revision=${REVISION}`,
`--output=${paths.output}`,
],
{ encoding: 'utf8' },
);
assert.notEqual(noReplace.status, 0);
assert.match(noReplace.stderr, /EEXIST/);
fs.chmodSync(paths.archive, 0o644);
fs.appendFileSync(paths.archive, 'tampered');
fs.chmodSync(paths.archive, 0o444);
const tampered = spawnSync(
process.execPath,
[
SCRIPT_PATH,
'finalize',
`--physical-report=${paths.physical}`,
`--release-archive=${paths.archive}`,
`--payload=${paths.payload}`,
`--signature=${paths.signature}`,
`--trusted-public-key=${paths.publicKey}`,
`--expected-repository=${REPOSITORY}`,
`--expected-revision=${REVISION}`,
`--output=${paths.tamperedOutput}`,
],
{ encoding: 'utf8' },
);
assert.notEqual(tampered.status, 0);
assert.match(tampered.stderr, /payload did not match/);
assert.equal(fs.existsSync(paths.tamperedOutput), false);
});