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:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "project-linkedcorp-backend",
|
||||
"version": "2.7.2",
|
||||
"version": "2.8.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/server.js",
|
||||
|
||||
@@ -30,7 +30,7 @@ export const postResetPin = asyncHandler(async (req, res) => {
|
||||
});
|
||||
|
||||
export const getEntries = asyncHandler(async (req, res) => {
|
||||
ok(res, await service.listEntries(req.currentUser));
|
||||
ok(res, await service.listEntries(req.currentUser, req.query.q));
|
||||
});
|
||||
|
||||
export const getEntry = asyncHandler(async (req, res) => {
|
||||
|
||||
@@ -11,6 +11,16 @@ export async function findByUser(userId) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
// Igual que findByUser pero con notes_enc -- SOLO para que el service pueda comparar el texto de
|
||||
// las notas contra la busqueda; nunca se devuelve notes_enc en la respuesta al cliente.
|
||||
export async function findByUserForSearch(userId) {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT ${LIST_SELECT}, notes_enc FROM vault_entries WHERE user_id = ? ORDER BY title`,
|
||||
[userId]
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
|
||||
export async function findById(id) {
|
||||
const [rows] = await pool.query(`SELECT ${FULL_SELECT} FROM vault_entries WHERE id = ?`, [id]);
|
||||
return rows[0] || null;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user