79 lines
4.0 KiB
JavaScript
79 lines
4.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import zlib from 'node:zlib';
|
|
import diagnosticsModule from '../src/main/diagnostics-service.cjs';
|
|
|
|
const { DiagnosticsService } = diagnosticsModule;
|
|
|
|
function unzipLocalEntries(buffer) {
|
|
const entries = new Map();
|
|
let offset = 0;
|
|
while (offset + 4 <= buffer.length && buffer.readUInt32LE(offset) === 0x04034b50) {
|
|
const method = buffer.readUInt16LE(offset + 8);
|
|
const compressedSize = buffer.readUInt32LE(offset + 18);
|
|
const nameLength = buffer.readUInt16LE(offset + 26);
|
|
const extraLength = buffer.readUInt16LE(offset + 28);
|
|
const nameStart = offset + 30;
|
|
const dataStart = nameStart + nameLength + extraLength;
|
|
const name = buffer.subarray(nameStart, nameStart + nameLength).toString('utf8');
|
|
const compressed = buffer.subarray(dataStart, dataStart + compressedSize);
|
|
entries.set(name, method === 8 ? zlib.inflateRawSync(compressed) : compressed);
|
|
offset = dataStart + compressedSize;
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
test('writes structured local diagnostics and exports a secret-free support bundle', async (t) => {
|
|
const root = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-diagnostics-'));
|
|
t.after(() => rm(root, { recursive: true, force: true }));
|
|
const secret = ['gitea', 'TEST', 'ONLY', 'ULTRA', 'SECRET', '1234567890'].join('_');
|
|
const service = new DiagnosticsService({
|
|
userDataPath: root,
|
|
appInfo: { name: 'ForgeFlow', version: '0.3.0-test' },
|
|
secretProvider: () => [secret],
|
|
preferencesProvider: () => ({ diagnosticsEnabled: true, diagnosticLevel: 'debug', logRetentionDays: 14, maxLogFileMb: 8 })
|
|
});
|
|
await service.initialize();
|
|
await service.error('test.failure', {
|
|
authorization: `token ${secret}`,
|
|
password: 'unsafe-password',
|
|
message: `request failed with ${secret}`,
|
|
path: path.join(os.homedir(), 'private', 'repository'),
|
|
host: '192.168.10.20',
|
|
basePath: '/mnt/user/appdata/private-app'
|
|
});
|
|
await service.flush();
|
|
|
|
const status = await service.getStatus();
|
|
assert.equal(status.enabled, true);
|
|
assert.ok(status.fileCount >= 1);
|
|
const raw = (await Promise.all((await service.listLogFiles()).map((file) => readFile(file.path, 'utf8')))).join('\n');
|
|
assert.doesNotMatch(raw, new RegExp(secret));
|
|
assert.doesNotMatch(raw, /unsafe-password/);
|
|
assert.doesNotMatch(raw, new RegExp(os.homedir().replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
|
|
|
const destination = path.join(root, 'support.zip');
|
|
const result = await service.exportSupportBundle({
|
|
destinationPath: destination,
|
|
privacyMode: 'strict',
|
|
publicState: { gitea: { baseUrl: 'https://gitea.example.test', hasToken: true, encryptedToken: 'ciphertext' }, servers: [{ host: '192.168.10.20', username: 'deploy', basePath: '/mnt/user/appdata/private-app' }], preferences: {} },
|
|
repositories: [{ id: 1, fullName: 'jens/private-repo', localPath: path.join(os.homedir(), 'private-repo'), localStatus: { head: 'a'.repeat(40), branch: { head: 'main' }, counts: {}, clean: true } }],
|
|
operations: [{ repository: 'jens/private-repo', status: 'failed', runnerLog: `Authorization: token ${secret}` }],
|
|
preflight: { checks: [] }
|
|
});
|
|
assert.equal(service.isKnownBundlePath(result.path), true);
|
|
const entries = unzipLocalEntries(await readFile(destination));
|
|
const bundleText = [...entries.values()].map((value) => value.toString('utf8')).join('\n');
|
|
assert.doesNotMatch(bundleText, new RegExp(secret));
|
|
assert.doesNotMatch(bundleText, /ciphertext|unsafe-password|jens\/private-repo/);
|
|
assert.doesNotMatch(bundleText, /192\.168\.10\.20|\/mnt\/user\/appdata\/private-app/);
|
|
assert.match(entries.get('manifest.json').toString(), /"containsSecrets": false/);
|
|
assert.match(entries.get('repositories-sanitized.json').toString(), /fullname-[a-f0-9]{12}/);
|
|
|
|
const cleared = await service.clear();
|
|
assert.ok(cleared.fileCount >= 1, 'clear writes a new safe session marker');
|
|
});
|