'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const crypto = require('crypto'); const fs = require('fs'); const os = require('os'); const path = require('path'); const { Readable } = require('stream'); const { Response } = require('node-fetch'); const { createGameUpdateService, normalizeManifest, officialSonyUrl, ps4VersionUrl, parseVersionXml, findVersionFileUri, } = require('../../src/services/game-update-service'); test('Sony update manifests require contiguous hash-verified official pieces', () => { const source = 'https://gs2.ww.prod.dl.playstation.net/path/CUSA00001-update.json'; const manifest = normalizeManifest({ originalFileSize: 12, packageDigest: 'a'.repeat(64), pieces: [ { url: 'https://gs2.ww.prod.dl.playstation.net/path/CUSA00001-part1.pkg', fileOffset: 0, fileSize: 5, hashValue: 'b'.repeat(40) }, { url: 'https://gs2.ww.prod.dl.playstation.net/path/CUSA00001-part2.pkg', fileOffset: 5, fileSize: 7, hashValue: 'c'.repeat(40) }, ], }, source, 'CUSA00001'); assert.equal(manifest.gameId, 'CUSA00001'); assert.equal(manifest.originalFileSize, 12); assert.equal(manifest.pieces.length, 2); assert.equal(manifest.packageDigest, 'a'.repeat(64)); assert.throws(() => normalizeManifest({ originalFileSize: 12, pieces: [{ url: 'https://evil.example/CUSA00001.pkg', fileOffset: 0, fileSize: 12, hashValue: 'b'.repeat(40) }], }, source), /playstation\.net/); assert.throws(() => normalizeManifest({ originalFileSize: 12, pieces: [{ url: 'https://gs2.ww.prod.dl.playstation.net/CUSA00001.pkg', fileOffset: 1, fileSize: 11, hashValue: 'b'.repeat(40) }], }, source), /aaneensluitend/); }); test('Sony URL validation blocks credentials, ports and lookalike domains', () => { assert.equal(officialSonyUrl('http://gs2.ww.prod.dl.playstation.net/file.json').hostname, 'gs2.ww.prod.dl.playstation.net'); assert.throws(() => officialSonyUrl('https://playstation.net.evil.example/file.json'), /playstation\.net/); assert.throws(() => officialSonyUrl('https://user:pass@playstation.net/file.json'), /credentials/); assert.throws(() => officialSonyUrl('https://playstation.net:444/file.json'), /poort/); }); test('PS4 version URLs and Sony XML manifests are discovered without pasted JSON URLs', () => { assert.equal( ps4VersionUrl('CUSA03041'), 'http://gs-sec.ww.np.dl.playstation.net/plo/np/CUSA03041/f3bc1dbcdfc190b2efdda011bdd49cbe344c0ea418faf1b511616b8460f8a835/CUSA03041-ver.xml', ); const parsed = parseVersionXml(` `, 'CUSA03041'); assert.equal(parsed.version, '01.32'); assert.equal(parsed.manifestUrl, 'http://gs2.ww.prod.dl.playstation.net/path/CUSA03041-update.json'); assert.throws(() => parseVersionXml('', 'CUSA03041'), /declaraties/); assert.throws(() => parseVersionXml('', 'CUSA03041'), /hoort niet/); }); test('PS5 versionFileUri is found in bounded nested param JSON structures', () => { const uri = 'https://sgst.prod.dl.playstation.net/sgst/prod/00/np/PPSA00001_00/example-version.xml'; assert.equal(findVersionFileUri({ application: { metadata: { versionFileUri: uri } } }), uri); assert.equal(findVersionFileUri({ application: {} }), ''); }); test('automatic discovery resolves a PS4 version XML into the existing safe manifest plan', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-update-discovery-')); const gameId = 'CUSA03041'; const versionUrl = ps4VersionUrl(gameId); const manifestUrl = `https://gs2.ww.prod.dl.playstation.net/path/${gameId}-update.json`; const packageUrl = `https://gs2.ww.prod.dl.playstation.net/path/${gameId}.pkg`; const fetchImpl = async url => { if (url === versionUrl) return new Response(``); if (url === manifestUrl) return new Response(JSON.stringify({ originalFileSize: 4, pieces: [{ url: packageUrl, fileOffset: 0, fileSize: 4, hashValue: 'b'.repeat(40) }], })); throw new Error(`Unexpected URL ${url}`); }; const service = createGameUpdateService({ db: { listGames: () => [{ game_id: gameId }], listBackups: () => [] }, ftp: {}, paths: { backups: path.join(root, 'backups'), cache: path.join(root, 'cache') }, fetchImpl, }); try { const plan = await service.inspectAutomatic({ gameId }); assert.equal(plan.manifest.gameId, gameId); assert.equal(service.catalog().games[0].discovery.status, 'available'); } finally { fs.rmSync(root, { recursive: true, force: true }); } }); test('package downloads disable the JSON size cap but enforce exact bytes and hashes', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-update-')); const payload = Buffer.from('safe-package'); const gameId = 'CUSA00001'; const manifestUrl = `https://gs2.ww.prod.dl.playstation.net/path/${gameId}-update.json`; const packageUrl = `https://gs2.ww.prod.dl.playstation.net/path/${gameId}.pkg`; const manifest = { originalFileSize: payload.length, packageDigest: crypto.createHash('sha256').update(payload).digest('hex'), pieces: [{ url: packageUrl, fileOffset: 0, fileSize: payload.length, hashValue: crypto.createHash('sha1').update(payload).digest('hex'), }], }; const fetchImpl = async (url, options) => { if (url === manifestUrl) return new Response(JSON.stringify(manifest), { headers: { 'content-type': 'application/json' } }); assert.equal(url, packageUrl); assert.equal(options.size, 0); return new Response(Readable.from([payload]), { headers: { 'content-length': String(payload.length) } }); }; const service = createGameUpdateService({ db: { listGames: () => [] }, ftp: {}, paths: { backups: path.join(root, 'backups'), cache: path.join(root, 'cache') }, settings: { get: (key, fallback) => key === 'publicOrigin' ? 'https://chimera.example.test' : fallback }, fetchImpl, }); try { const plan = await service.inspectManifest({ url: manifestUrl, gameId }); const queued = service.queueDownload({ token: plan.token }); let job; for (let attempt = 0; attempt < 100; attempt += 1) { job = service.listJobs().find(item => item.id === queued.id); if (['completed', 'failed'].includes(job.status)) break; await new Promise(resolve => setTimeout(resolve, 10)); } assert.equal(job.status, 'completed', job.error); assert.deepEqual(fs.readFileSync(job.localPath), payload); const handoff = await service.preparePackageHandoff({ id: job.id }); assert.match(handoff.url, /^https:\/\/chimera\.example\.test\/game-update-package\/[A-Za-z0-9_-]{43}\/CUSA00001\.pkg$/); assert.equal(service.resolvePackageHandoff(new URL(handoff.url).pathname).sha256, manifest.packageDigest); assert.equal(handoff.installExecuted, false); } finally { fs.rmSync(root, { recursive: true, force: true }); } });