63 lines
6.0 KiB
JavaScript
63 lines
6.0 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const crypto = require('crypto');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const test = require('node:test');
|
|
const { PINNED_ETAHEN, createPs4PkgInstallerAdapter, validateOrder } = require('../../src/services/ps4-pkg-installer-adapter');
|
|
|
|
const evidence = () => ({ ...PINNED_ETAHEN });
|
|
const component = (type = 'base', data = 'pkg') => ({ componentId: type, type, size: Buffer.byteLength(data), sha256: crypto.createHash('sha256').update(data).digest('hex') });
|
|
function adapter(overrides = {}) {
|
|
let postScanVerified = overrides.postScanVerified ?? true;
|
|
const calls = [];
|
|
const value = createPs4PkgInstallerAdapter({ mode: overrides.mode || 'dpi', versionEvidenceProvider: overrides.versionEvidenceProvider || evidence, transport: overrides.transport || { probe: async () => ({ reachable: true }), handoff: async input => { calls.push(input); return { res: '0' }; } }, urlPolicy: overrides.urlPolicy || (async input => ({ allowed: true, url: input.url })), artifactServer: overrides.artifactServer, allowedRoots: overrides.allowedRoots || [], postScan: { verify: async () => ({ verified: postScanVerified, scanId: 'scan-1' }) }, timeoutMs: overrides.timeoutMs || 50 });
|
|
return { value, calls, setPostScan(value) { postScanVerified = value; } };
|
|
}
|
|
|
|
test('version-pinned fake DPI accepts handoff but only post-scan returns installed evidence', async () => {
|
|
const fixture = adapter();
|
|
const probe = await fixture.value.probe();
|
|
assert.equal(probe.available, true); assert.equal(probe.version, '2.5B'); assert.equal(probe.capabilities.installedEvidenceFromResponse, false);
|
|
const item = component();
|
|
const output = await fixture.value.execute({ component: item, handoff: { url: 'https://packages.example/base.pkg' } });
|
|
assert.deepEqual({ handoffAccepted: output.handoffAccepted, installed: output.installed, requiresPostScan: output.requiresPostScan }, { handoffAccepted: true, installed: false, requiresPostScan: true });
|
|
fixture.setPostScan(false); assert.equal((await fixture.value.verify({ component: item, target: {}, outputEvidence: output })).verified, false);
|
|
fixture.setPostScan(true); const verified = await fixture.value.verify({ component: item, target: {}, outputEvidence: output }); assert.equal(verified.verified, true); assert.equal(verified.source, 'post-install-scan');
|
|
});
|
|
|
|
test('base, update, DLC and unlocker order is deterministic and invalid order fails', () => {
|
|
assert.deepEqual(validateOrder(['base', 'update', 'dlc', 'unlocker'].map(type => ({ componentId: type, type }))), ['base', 'update', 'dlc', 'unlocker']);
|
|
assert.throws(() => validateOrder([{ componentId: 'update', type: 'update' }, { componentId: 'base', type: 'base' }]), error => error.code === 'PKG_INSTALL_ORDER');
|
|
assert.throws(() => validateOrder([{ componentId: 'base1', type: 'base' }, { componentId: 'base2', type: 'base' }]), error => error.code === 'PKG_INSTALL_ORDER');
|
|
});
|
|
|
|
test('file handoff enforces containment, exact size/hash and hash-bound URL policy', async () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-pkg-adapter-'));
|
|
const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-pkg-outside-'));
|
|
try {
|
|
const filePath = path.join(root, 'base.pkg'); fs.writeFileSync(filePath, 'pkg');
|
|
const fixture = adapter({ allowedRoots: [root], artifactServer: { expose: async input => ({ url: `https://packages.example/${input.sha256}.pkg` }) }, urlPolicy: async input => ({ allowed: input.url.includes(input.sha256), url: input.url }) });
|
|
const output = await fixture.value.execute({ component: component(), handoff: { filePath } }); assert.equal(output.handoffAccepted, true);
|
|
const outsidePath = path.join(outside, 'base.pkg'); fs.writeFileSync(outsidePath, 'pkg');
|
|
await assert.rejects(() => fixture.value.execute({ component: component(), handoff: { filePath: outsidePath } }), error => error.code === 'PKG_HANDOFF_CONTAINMENT');
|
|
await assert.rejects(() => fixture.value.execute({ component: { ...component(), sha256: '0'.repeat(64) }, handoff: { filePath } }), error => error.code === 'PKG_HANDOFF_HASH');
|
|
const linkedDirectory = path.join(root, 'linked-outside');
|
|
fs.symlinkSync(outside, linkedDirectory, process.platform === 'win32' ? 'junction' : 'dir');
|
|
let exposed = 0;
|
|
const hostile = adapter({ allowedRoots: [root], artifactServer: { expose: async () => { exposed += 1; return { url: 'https://packages.example/unsafe.pkg' }; } } });
|
|
await assert.rejects(() => hostile.value.execute({ component: component(), handoff: { filePath: path.join(linkedDirectory, 'base.pkg') } }), error => error.code === 'PKG_HANDOFF_CONTAINMENT');
|
|
assert.equal(exposed, 0);
|
|
} finally { fs.rmSync(root, { recursive: true, force: true }); fs.rmSync(outside, { recursive: true, force: true }); }
|
|
});
|
|
|
|
test('unsupported version, DPIv2, malformed service, failure and timeout fail safely', async () => {
|
|
assert.equal((await adapter({ versionEvidenceProvider: async () => ({ ...PINNED_ETAHEN, version: 'future' }) }).value.probe()).available, false);
|
|
assert.equal((await adapter({ mode: 'dpi-v2' }).value.probe()).reason, 'protocol-unverified');
|
|
await assert.rejects(() => adapter({ transport: { probe: async () => ({ reachable: true }), handoff: async () => ({ ok: true }) } }).value.execute({ component: component(), handoff: { url: 'https://packages.example/base.pkg' } }), error => error.code === 'PKG_INSTALL_RESPONSE');
|
|
await assert.rejects(() => adapter({ transport: { probe: async () => ({ reachable: false, error: 'offline' }), handoff: async () => ({ res: 0 }) } }).value.execute({ component: component(), handoff: { url: 'https://packages.example/base.pkg' } }), error => error.code === 'PKG_INSTALL_UNAVAILABLE');
|
|
await assert.rejects(() => adapter({ timeoutMs: 5, transport: { probe: async () => ({ reachable: true }), handoff: () => new Promise(() => {}) } }).value.execute({ component: component(), handoff: { url: 'https://packages.example/base.pkg' } }), error => error.code === 'PKG_INSTALL_TIMEOUT');
|
|
});
|