feat(entries): encrypt template at rest, guided tour, import fixes, cleanup
Batched session work sharing app.js / index.html / Entries.pas, so it can't
split cleanly without interactive hunk staging.
- feat: encrypt `template` metadata at rest (template_enc/iv, added to
ENCRYPTED_META_FIELDS). withEncryptedMeta skips an absent template key so
partial re-ships (add-tag, move-to-folder) don't wipe it via LHasTemplate.
Cleartext column kept as migration fallback. +3 unit tests.
- feat: first-run guided tour ("How it works") — spotlight + bubble, no GIFs,
re-launchable from Settings, seen-flag in DPAPI prefs.
- fix(import): preserve original created_at on restore (was stamped to import
time); restore entry icons on overwrite (PUT ignores icon_b64).
- fix(settings): correct clipboard-privacy copy (already excluded from Win+V);
PIN text 4-6 -> 4-12; reorder Set-PIN above unlock-method; move tray/startup
toggles to General; dedicated backup-password button + warning status; tab icons.
- chore: remove dead legacy monolith (app-legacy.js, index-legacy.html,
style-legacy.css) + unused passkeyBtn stub.
- docs: full-source review (CODE_AUDIT 6b), template + favorite/pinned notes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -224,3 +224,42 @@ test('decryptPwd: wrong key returns "[ERROR]" (not garbage plaintext)', async ()
|
||||
T.state.cryptoKey = k2.cryptoKey;
|
||||
assert.equal(await T.decryptPwd(encrypted, iv), '[ERROR]');
|
||||
});
|
||||
|
||||
// --- Metadata-at-rest (§1.3): template encrypted like username/site/... ---
|
||||
|
||||
test('withEncryptedMeta: template is encrypted at rest + round-trips', async () => {
|
||||
assert.ok(T.ENCRYPTED_META_FIELDS.includes('template'),
|
||||
'template must be an encrypted meta field');
|
||||
const { cryptoKey } = await T.deriveKeyAndVerifier('m', 's', 100000, T.HASH_ALGO_V2);
|
||||
T.state.cryptoKey = cryptoKey;
|
||||
|
||||
const body = await T.withEncryptedMeta({
|
||||
username: 'u', site: 's', title: 't', tags: '', template: 'credit-card',
|
||||
});
|
||||
assert.equal(body.template, '', 'cleartext template blanked on write');
|
||||
assert.ok(body.template_enc && body.template_iv, 'ciphertext written');
|
||||
assert.notEqual(body.template_enc, 'credit-card', 'not stored in cleartext');
|
||||
|
||||
// GET returns the ciphertext (cleartext column blanked) → decrypt restores it.
|
||||
const row = { template: '', template_enc: body.template_enc, template_iv: body.template_iv };
|
||||
await T.decryptEntryMeta([row]);
|
||||
assert.equal(row.template, 'credit-card');
|
||||
});
|
||||
|
||||
test('withEncryptedMeta: omitting template preserves it (partial re-ship guard)', async () => {
|
||||
const { cryptoKey } = await T.deriveKeyAndVerifier('m', 's', 100000, T.HASH_ALGO_V2);
|
||||
T.state.cryptoKey = cryptoKey;
|
||||
// add-tag / move-to-folder bodies carry no `template` key. The choke point
|
||||
// must NOT synthesise template_enc='' — that would make the server wipe the
|
||||
// stored template (LHasTemplate fires on the present-but-empty key).
|
||||
const body = await T.withEncryptedMeta({ site: 's', title: 't', username: 'u', tags: 'x' });
|
||||
assert.ok(!('template' in body), 'no cleartext template key added');
|
||||
assert.ok(!('template_enc' in body), 'no ciphertext template key added');
|
||||
});
|
||||
|
||||
test('decryptEntryMeta: un-migrated row keeps its cleartext template', async () => {
|
||||
// Row predates encryption: cleartext `template` present, no template_enc.
|
||||
const row = { template: 'ssh-key' };
|
||||
await T.decryptEntryMeta([row]);
|
||||
assert.equal(row.template, 'ssh-key', 'cleartext preserved until migration');
|
||||
});
|
||||
|
||||
@@ -152,6 +152,8 @@ function loadApp(overrides = {}) {
|
||||
computeStrength: (typeof computeStrength !== 'undefined' ? computeStrength : undefined),
|
||||
// merge (async, coupled — tests stub the io seams below)
|
||||
applyRemoteSnapshot, buildSyncSnapshot,
|
||||
// metadata-at-rest
|
||||
withEncryptedMeta, decryptEntryMeta, ENCRYPTED_META_FIELDS,
|
||||
};
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user