143 lines
6.9 KiB
JavaScript
143 lines
6.9 KiB
JavaScript
import { Worker } from 'node:worker_threads';
|
|
|
|
const encoder = new TextEncoder();
|
|
const crcTable = new Uint32Array(256);
|
|
for (let index = 0; index < 256; index += 1) {
|
|
let value = index;
|
|
for (let bit = 0; bit < 8; bit += 1) value = (value >>> 1) ^ (0xedb88320 & -(value & 1));
|
|
crcTable[index] = value >>> 0;
|
|
}
|
|
|
|
function crc32(bytes) {
|
|
let value = 0xffffffff;
|
|
for (const byte of bytes) value = (value >>> 8) ^ crcTable[(value ^ byte) & 0xff];
|
|
return (value ^ 0xffffffff) >>> 0;
|
|
}
|
|
|
|
function guid(index, prefix) {
|
|
return `${prefix}${index.toString(16).padStart(6, '0')}-1111-4111-8111-${index.toString(16).padStart(12, '0')}`;
|
|
}
|
|
|
|
function storedZip(entries) {
|
|
const localParts = [];
|
|
const centralParts = [];
|
|
let localOffset = 0;
|
|
for (const entry of entries) {
|
|
const name = encoder.encode(entry.path);
|
|
const checksum = crc32(entry.bytes);
|
|
const local = Buffer.alloc(30 + name.byteLength);
|
|
local.writeUInt32LE(0x04034b50, 0);
|
|
local.writeUInt16LE(20, 4);
|
|
local.writeUInt16LE(0x0800, 6);
|
|
local.writeUInt16LE(0, 8);
|
|
local.writeUInt32LE(checksum, 14);
|
|
local.writeUInt32LE(entry.bytes.byteLength, 18);
|
|
local.writeUInt32LE(entry.bytes.byteLength, 22);
|
|
local.writeUInt16LE(name.byteLength, 26);
|
|
Buffer.from(name).copy(local, 30);
|
|
localParts.push(local, entry.bytes);
|
|
|
|
const central = Buffer.alloc(46 + name.byteLength);
|
|
central.writeUInt32LE(0x02014b50, 0);
|
|
central.writeUInt16LE(20, 4);
|
|
central.writeUInt16LE(20, 6);
|
|
central.writeUInt16LE(0x0800, 8);
|
|
central.writeUInt16LE(0, 10);
|
|
central.writeUInt32LE(checksum, 16);
|
|
central.writeUInt32LE(entry.bytes.byteLength, 20);
|
|
central.writeUInt32LE(entry.bytes.byteLength, 24);
|
|
central.writeUInt16LE(name.byteLength, 28);
|
|
central.writeUInt32LE(localOffset, 42);
|
|
Buffer.from(name).copy(central, 46);
|
|
centralParts.push(central);
|
|
localOffset += local.byteLength + entry.bytes.byteLength;
|
|
}
|
|
const directory = Buffer.concat(centralParts);
|
|
const end = Buffer.alloc(22);
|
|
end.writeUInt32LE(0x06054b50, 0);
|
|
end.writeUInt16LE(entries.length, 8);
|
|
end.writeUInt16LE(entries.length, 10);
|
|
end.writeUInt32LE(directory.byteLength, 12);
|
|
end.writeUInt32LE(localOffset, 16);
|
|
return Buffer.concat([...localParts, directory, end]);
|
|
}
|
|
|
|
const entries = [];
|
|
const registryRootClsid = '{A3CCFC41-DFDB-43a5-8D26-0FE8B954DA51}';
|
|
const registryItemClsid = '{9CD4B2F4-923D-47f5-A062-E897DD1DAD50}';
|
|
const preferenceItemsPerBackup = 150;
|
|
for (let index = 1; index <= 300; index += 1) {
|
|
const root = `synthetic-${index.toString().padStart(4, '0')}`;
|
|
const metadata = `<?xml version="1.0"?><BackupInst><ID>{${guid(index, '22')}}</ID><GPOGuid>{${guid(index, '11')}}</GPOGuid><GPODomain>synthetic.example</GPODomain><BackupTime>2026-07-21T12:00:00Z</BackupTime></BackupInst>`;
|
|
const registryItems = Array.from({ length: preferenceItemsPerBackup }, (_, itemIndex) => {
|
|
const unique = ((index - 1) * preferenceItemsPerBackup + itemIndex + 1).toString(16).padStart(12, '0');
|
|
return `<Registry clsid="${registryItemClsid}" uid="{33333333-3333-4333-8333-${unique}}"><Properties action="U" default="0" hive="HKEY_LOCAL_MACHINE" key="Software\\Synthetic\\${itemIndex}" name="Mode" type="REG_SZ" value="Strict"/></Registry>`;
|
|
}).join('\n');
|
|
const registry = `<RegistrySettings clsid="${registryRootClsid}">\n${registryItems}\n</RegistrySettings>`;
|
|
entries.push({ path: `${root}/Backup.xml`, bytes: Buffer.from(metadata) });
|
|
entries.push({ path: `${root}/DomainSysvol/GPO/gpt.ini`, bytes: Buffer.from('[General]') });
|
|
entries.push({ path: `${root}/DomainSysvol/GPO/Machine/Preferences/Registry/Registry.xml`, bytes: Buffer.from(registry) });
|
|
}
|
|
const filler = Buffer.alloc(70_000, 0x41);
|
|
for (let index = entries.length; index < 2_040; index += 1) {
|
|
entries.push({ path: `synthetic-0001/DomainSysvol/GPO/Machine/Scripts/filler-${index}.txt`, bytes: filler });
|
|
}
|
|
|
|
const envelope = storedZip(entries);
|
|
if (envelope.byteLength < 79_000_000 || envelope.byteLength > 100_000_000) throw new Error('Synthetic stress envelope has an unexpected size.');
|
|
const recognitionTransfer = new Uint8Array(envelope.byteLength);
|
|
recognitionTransfer.set(envelope);
|
|
const recognitionWorker = new Worker(new URL('../../dist-server/gpo-inventory-worker.mjs', import.meta.url), {
|
|
workerData: { operation: 'recognition', bytes: recognitionTransfer },
|
|
transferList: [recognitionTransfer.buffer],
|
|
resourceLimits: { maxOldGenerationSizeMb: 512, maxYoungGenerationSizeMb: 128, stackSizeMb: 8 }
|
|
});
|
|
const recognition = await new Promise((resolve, reject) => {
|
|
const timer = setTimeout(() => reject(new Error('Synthetic GPO recognition worker timed out.')), 120_000);
|
|
recognitionWorker.once('message', message => {
|
|
clearTimeout(timer);
|
|
if (!message?.ok) reject(new Error(message?.error?.diagnostics?.[0]?.code ?? 'Synthetic recognition worker failed.'));
|
|
else resolve(message.recognition);
|
|
});
|
|
recognitionWorker.once('error', reject);
|
|
});
|
|
await recognitionWorker.terminate();
|
|
if (recognition.recognition.status !== 'gpo-backup-candidate') {
|
|
throw new Error('Synthetic folder transport was not recognized as a GPO backup candidate.');
|
|
}
|
|
if (Object.keys(recognition).sort().join(',') !== 'recognition,version') {
|
|
throw new Error('Recognition preflight returned non-compact inventory data.');
|
|
}
|
|
|
|
|
|
const transferred = new Uint8Array(envelope.byteLength);
|
|
transferred.set(envelope);
|
|
const worker = new Worker(new URL('../../dist-server/gpo-inventory-worker.mjs', import.meta.url), {
|
|
workerData: {
|
|
bytes: transferred,
|
|
options: {
|
|
projectId: 'synthetic-load-project',
|
|
sourceArtifactId: 'synthetic-load-artifact',
|
|
discoveredAt: '2026-07-21T12:00:00.000Z'
|
|
}
|
|
},
|
|
transferList: [transferred.buffer],
|
|
resourceLimits: { maxOldGenerationSizeMb: 512, maxYoungGenerationSizeMb: 128, stackSizeMb: 8 }
|
|
});
|
|
|
|
const result = await new Promise((resolve, reject) => {
|
|
const timer = setTimeout(() => reject(new Error('Synthetic large GPO worker timed out.')), 120_000);
|
|
worker.once('message', message => {
|
|
clearTimeout(timer);
|
|
if (!message?.ok) reject(new Error(message?.error?.diagnostics?.[0]?.code ?? 'Synthetic worker failed.'));
|
|
else resolve(message.backup);
|
|
});
|
|
worker.once('error', reject);
|
|
});
|
|
await worker.terminate();
|
|
if (result.backupInstances.length !== 300) throw new Error('Synthetic large GPO worker returned an unexpected instance count.');
|
|
const observationCount = result.backupInstances.reduce((sum, instance) => sum + instance.policyObservations.length, 0);
|
|
if (observationCount !== 300 * preferenceItemsPerBackup) throw new Error('Synthetic large GPO worker lost Preference observations.');
|
|
if ('container' in result || 'metadataDocuments' in result || 'preferenceDocuments' in result) throw new Error('Worker returned non-persistent intermediate data.');
|
|
console.log(`Synthetic GPO load passed: ${entries.length} entries, ${envelope.byteLength} bytes, ${result.backupInstances.length} instances, ${observationCount} observations.`);
|