feat: version 2.8.0 -- buscador en Contraseñas (nombre/usuario/enlace/notas)

Filtra las tarjetas mientras se escribe. Nombre/usuario/enlace se
comparan en texto plano; las notas (cifradas) se descifran solo en
el servidor para la comparacion -- el contenido nunca viaja de vuelta
al cliente salvo al revelar esa entrada puntual.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-10-01 12:57:10 -05:00
parent 127ca2ba7e
commit 551ed9b790
9 changed files with 61 additions and 11 deletions
+20 -3
View File
@@ -44,9 +44,26 @@ function decryptEntry(entry) {
};
}
export async function listEntries(currentUser) {
const rows = await repo.findByUser(currentUser.id);
return rows; // sin password/notes -- la lista nunca trae datos sensibles
// Buscar por nombre/usuario/enlace es trivial (van en texto plano). Las notas SI estan cifradas
// -- se descifran aca mismo, en el servidor, solo para comparar contra la busqueda; el contenido
// nunca sale hacia el cliente (se saca con stripSensitive antes de devolver, igual que siempre).
// No se audita como "view": nunca se le muestra el texto de la nota al usuario, solo si matchea.
export async function listEntries(currentUser, query) {
const q = query?.trim().toLowerCase();
if (!q) return repo.findByUser(currentUser.id);
const rows = await repo.findByUserForSearch(currentUser.id);
return rows
.filter((row) => {
if ([row.title, row.url, row.username].some((v) => v && v.toLowerCase().includes(q))) return true;
if (!row.notes_enc) return false;
try {
return decrypt(row.notes_enc).toLowerCase().includes(q);
} catch {
return false;
}
})
.map(stripSensitive);
}
// "Revelar" una entrada: unica operacion que descifra la contraseña -- se audita.