mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: enhance deployment process and update dependencies
- Updated the deployment script to build the web application before deploying. - Upgraded Wrangler dependency from 4.61.1 to 4.69.0. feat: add import item limit and request body size limit - Introduced a new limit for the maximum total items allowed in a single import (5000). - Set a hard body size limit for JSON API endpoints (25 MB). feat: validate KDF parameters during registration and password change - Added validation for KDF parameters to ensure compliance with Bitwarden's minimum requirements. - Enhanced error handling for invalid KDF parameters during user registration and password change. feat: clean up R2 files on user deletion - Implemented cleanup of R2 files associated with user attachments and sends before deleting user metadata. feat: verify folder ownership when creating or updating ciphers - Added checks to ensure that users cannot reference folders owned by other users when creating or updating ciphers. fix: handle corrupted cipher data gracefully - Improved error handling when retrieving ciphers from the database to avoid crashes due to corrupted data. feat: increment send access count atomically - Added a method to atomically increment the access count for sends and return whether the update was successful. fix: enforce request body size limits - Implemented checks to reject oversized request bodies for non-file upload paths. fix: update error handling for database initialization - Enhanced error logging for database initialization failures while providing a generic message to clients. feat: enhance security with Content Security Policy - Added a Content Security Policy to the web application to improve security against XSS attacks. fix: remove plaintext TOTP secret from localStorage - Updated the TOTP enabling process to remove the plaintext secret from localStorage after it is stored on the server. fix: ensure only PBKDF2 hash is sent for public send access - Modified the public send access payload to ensure only the PBKDF2 hash is sent, never the plaintext password.
This commit is contained in:
+39
-5
@@ -425,7 +425,13 @@ export class StorageService {
|
||||
|
||||
async getCipher(id: string): Promise<Cipher | null> {
|
||||
const row = await this.db.prepare('SELECT data FROM ciphers WHERE id = ?').bind(id).first<{ data: string }>();
|
||||
return row?.data ? (JSON.parse(row.data) as Cipher) : null;
|
||||
if (!row?.data) return null;
|
||||
try {
|
||||
return JSON.parse(row.data) as Cipher;
|
||||
} catch {
|
||||
console.error('Corrupted cipher data, id:', id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async saveCipher(cipher: Cipher): Promise<void> {
|
||||
@@ -460,7 +466,9 @@ export class StorageService {
|
||||
|
||||
async getAllCiphers(userId: string): Promise<Cipher[]> {
|
||||
const res = await this.db.prepare('SELECT data FROM ciphers WHERE user_id = ? ORDER BY updated_at DESC').bind(userId).all<{ data: string }>();
|
||||
return (res.results || []).map(r => JSON.parse(r.data) as Cipher);
|
||||
return (res.results || []).flatMap(r => {
|
||||
try { return [JSON.parse(r.data) as Cipher]; } catch { return []; }
|
||||
});
|
||||
}
|
||||
|
||||
async getCiphersPage(userId: string, includeDeleted: boolean, limit: number, offset: number): Promise<Cipher[]> {
|
||||
@@ -475,7 +483,9 @@ export class StorageService {
|
||||
)
|
||||
.bind(userId, limit, offset)
|
||||
.all<{ data: string }>();
|
||||
return (res.results || []).map(r => JSON.parse(r.data) as Cipher);
|
||||
return (res.results || []).flatMap(r => {
|
||||
try { return [JSON.parse(r.data) as Cipher]; } catch { return []; }
|
||||
});
|
||||
}
|
||||
|
||||
async getCiphersByIds(ids: string[], userId: string): Promise<Cipher[]> {
|
||||
@@ -484,7 +494,9 @@ export class StorageService {
|
||||
const placeholders = ids.map(() => '?').join(',');
|
||||
const stmt = this.db.prepare(`SELECT data FROM ciphers WHERE user_id = ? AND id IN (${placeholders})`);
|
||||
const res = await stmt.bind(userId, ...ids).all<{ data: string }>();
|
||||
return (res.results || []).map(r => JSON.parse(r.data) as Cipher);
|
||||
return (res.results || []).flatMap(r => {
|
||||
try { return [JSON.parse(r.data) as Cipher]; } catch { return []; }
|
||||
});
|
||||
}
|
||||
|
||||
async bulkMoveCiphers(ids: string[], folderId: string | null, userId: string): Promise<void> {
|
||||
@@ -555,7 +567,12 @@ export class StorageService {
|
||||
.all<{ data: string }>();
|
||||
|
||||
for (const row of (res.results || [])) {
|
||||
const cipher = JSON.parse(row.data) as Cipher;
|
||||
let cipher: Cipher;
|
||||
try {
|
||||
cipher = JSON.parse(row.data) as Cipher;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
cipher.folderId = null;
|
||||
cipher.updatedAt = now;
|
||||
await this.saveCipher(cipher);
|
||||
@@ -857,6 +874,23 @@ export class StorageService {
|
||||
).run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increment access_count and update updated_at.
|
||||
* Returns true if the row was updated (send still available),
|
||||
* false if max_access_count has already been reached.
|
||||
*/
|
||||
async incrementSendAccessCount(sendId: string): Promise<boolean> {
|
||||
const now = new Date().toISOString();
|
||||
const result = await this.db
|
||||
.prepare(
|
||||
'UPDATE sends SET access_count = access_count + 1, updated_at = ? ' +
|
||||
'WHERE id = ? AND (max_access_count IS NULL OR access_count < max_access_count)'
|
||||
)
|
||||
.bind(now, sendId)
|
||||
.run();
|
||||
return (result.meta.changes ?? 0) > 0;
|
||||
}
|
||||
|
||||
async deleteSend(id: string, userId: string): Promise<void> {
|
||||
await this.db.prepare('DELETE FROM sends WHERE id = ? AND user_id = ?').bind(id, userId).run();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user