This commit is contained in:
@@ -6,6 +6,14 @@ const espree = require('espree');
|
||||
|
||||
const { normalizeLanguage, translateText } = require('../renderer/i18n');
|
||||
|
||||
test('online backup metadata displays source IP without exposing full keys', () => {
|
||||
assert.equal(translateText('IP unbekannt', 'en'), 'IP unknown');
|
||||
assert.equal(translateText('IP unknown', 'de'), 'IP unbekannt');
|
||||
const source = fs.readFileSync(path.join(__dirname, '../renderer/app.js'), 'utf8');
|
||||
assert.ok(source.includes('`IP: ${entry.sourceIp}`'));
|
||||
assert.ok(source.includes('key.textContent = entry.displayKey;'));
|
||||
});
|
||||
|
||||
test('online backup selection defaults to unlimited in both languages', () => {
|
||||
assert.equal(translateText('Unbegrenzt (Standard)', 'en'), 'Unlimited (default)');
|
||||
assert.equal(translateText('Unlimited (default)', 'de'), 'Unbegrenzt (Standard)');
|
||||
|
||||
@@ -70,6 +70,20 @@ function writeKeyring(filePath, keys, generation = null) {
|
||||
}
|
||||
|
||||
describe('encrypted online backup keyring', () => {
|
||||
it('persists valid source IPs across reloads alongside legacy entries', async () => {
|
||||
const { keyring, filePath } = fixture();
|
||||
const first = { ...keyring.prepare(validKey(), timestamp), sourceIp: '2001:db8::42' };
|
||||
await keyring.commit(first);
|
||||
await keyring.commit(keyring.prepare(validKey(), timestamp));
|
||||
const listed = await keyring.list();
|
||||
assert.equal(listed.issues.length, 0);
|
||||
assert.equal(listed.entries.find(entry => entry.id === first.id).sourceIp, '2001:db8::42');
|
||||
assert.equal(JSON.parse(fs.readFileSync(filePath, 'utf8')).keys.find(entry => entry.id === first.id).sourceIp, '2001:db8::42');
|
||||
await assert.rejects(keyring.commit({ ...keyring.prepare(validKey(), timestamp), sourceIp: 'invalid' }));
|
||||
const removal = await keyring.prepareRemove(first.id);
|
||||
await keyring.commitRemove(removal);
|
||||
assert.equal((await keyring.list()).entries.length, 1);
|
||||
});
|
||||
it('persists the spec keys schema without plaintext and returns frozen sanitized entries', async () => {
|
||||
const { filePath, keyring } = fixture();
|
||||
const key = validKey();
|
||||
|
||||
@@ -60,7 +60,8 @@ function createFixture(overrides = {}) {
|
||||
id: entry.id,
|
||||
displayKey: `${key.slice(0, 9)}…${key.slice(-4)}`,
|
||||
createdAt: entry.createdAt,
|
||||
expiresAt: entry.expiresAt ?? null
|
||||
expiresAt: entry.expiresAt ?? null,
|
||||
...(entry.sourceIp ? { sourceIp: entry.sourceIp } : {})
|
||||
});
|
||||
},
|
||||
getKey: async (entryId) => {
|
||||
@@ -101,6 +102,7 @@ function createFixture(overrides = {}) {
|
||||
events.push('upload');
|
||||
assert.equal(uploadedRecord, record);
|
||||
if (overrides.uploadError) throw overrides.uploadError;
|
||||
return overrides.uploadMetadata;
|
||||
},
|
||||
deleteBackup: async (value) => {
|
||||
events.push(`delete:${value}`);
|
||||
@@ -176,6 +178,15 @@ function deleteVerifier(value) {
|
||||
}
|
||||
|
||||
describe('transactional online backup manager', () => {
|
||||
it('keeps server-provided source IP in the committed and listed metadata', async () => {
|
||||
const fixture = createFixture({ initialKey: null, uploadMetadata: { sourceIp: '2001:db8::1' } });
|
||||
const created = await fixture.manager.createManaged();
|
||||
assert.equal(created.ok, true);
|
||||
assert.equal(created.entry.sourceIp, '2001:db8::1');
|
||||
const listed = await fixture.manager.listManaged();
|
||||
assert.equal(listed.entries[0].sourceIp, '2001:db8::1');
|
||||
assert.ok(!JSON.stringify(listed).includes(key));
|
||||
});
|
||||
it('creates in prepare, upload, commit order and returns only the sanitized entry', async () => {
|
||||
const fixture = createFixture({ initialKey: null });
|
||||
|
||||
|
||||
@@ -36,13 +36,14 @@ test('server persists recovery without plaintext secrets, supports normal import
|
||||
const publicKey = await downloadRecoveryPublicKey(url);
|
||||
assert.equal(publicKey, pair.publicKey);
|
||||
const backup = createOnlineBackup({ language: 'de', password: 'test-secret' }, 'test', undefined, '1d', publicKey);
|
||||
await uploadOnlineBackup(backup.record, url);
|
||||
assert.deepEqual(await uploadOnlineBackup(backup.record, url), { sourceIp: '127.0.0.1' });
|
||||
const recordPath = join(rootDir, `${backup.record.id}.json`);
|
||||
const raw = await readFile(recordPath, 'utf8');
|
||||
assert.ok(!raw.includes(backup.key));
|
||||
assert.ok(!raw.includes('test-secret'));
|
||||
const record = JSON.parse(raw);
|
||||
assert.equal(record.version, 3);
|
||||
assert.equal(record.version, 4);
|
||||
assert.equal(record.sourceIp, '127.0.0.1');
|
||||
assert.equal(recoverBackupKey(backup.record.id, record, pair.privateKey), backup.key);
|
||||
assert.equal((await downloadOnlineBackup(backup.key, url)).settings.language, 'de');
|
||||
const privateFile = join(rootDir, 'private.pem');
|
||||
@@ -53,6 +54,18 @@ test('server persists recovery without plaintext secrets, supports normal import
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.equal((await readFile(output, 'utf8')).trim(), backup.key);
|
||||
assert.ok(!result.stdout.includes(backup.key));
|
||||
const detailedOutput = join(rootDir, 'details.txt');
|
||||
assert.equal(spawnSync(process.execPath, [cli, 'recover', recordPath, privateFile, detailedOutput, '--details']).status, 0);
|
||||
const details = await readFile(detailedOutput, 'utf8');
|
||||
assert.match(details, /^\d{2}\.\d{2}\.\d{4} - \d{2}:\d{2} \| IP: 127\.0\.0\.1 \| MHU2-/);
|
||||
assert.ok(details.trim().endsWith(backup.key));
|
||||
const legacyRecord = { ...record, version: 3 };
|
||||
delete legacyRecord.sourceIp;
|
||||
await writeFile(recordPath, JSON.stringify(legacyRecord));
|
||||
assert.equal((await downloadOnlineBackup(backup.key, url)).settings.language, 'de');
|
||||
const legacyDetails = join(rootDir, 'legacy-details.txt');
|
||||
assert.equal(spawnSync(process.execPath, [cli, 'recover', recordPath, privateFile, legacyDetails, '--details']).status, 0);
|
||||
assert.ok((await readFile(legacyDetails, 'utf8')).includes(' | IP: unbekannt | '));
|
||||
assert.equal(spawnSync(process.execPath, [cli, 'recover', recordPath, privateFile, output]).status, 1);
|
||||
const other = createOnlineBackup({}, 'test', undefined, 'forever', publicKey);
|
||||
await uploadOnlineBackup(other.record, url);
|
||||
|
||||
Reference in New Issue
Block a user