mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
fix: preserve cipher edit time during auto repair
This commit is contained in:
@@ -823,6 +823,9 @@ export async function handleUpdateCipher(request: Request, env: Env, userId: str
|
|||||||
const incomingPasswordHistory = readCipherProp<PasswordHistory[] | null>(cipherData, ['passwordHistory', 'PasswordHistory']);
|
const incomingPasswordHistory = readCipherProp<PasswordHistory[] | null>(cipherData, ['passwordHistory', 'PasswordHistory']);
|
||||||
const incomingRevisionDate = readCipherRevisionDate(cipherData);
|
const incomingRevisionDate = readCipherRevisionDate(cipherData);
|
||||||
const hasAttachmentMigrationMetadata = hasIncomingAttachmentMetadata(cipherData);
|
const hasAttachmentMigrationMetadata = hasIncomingAttachmentMetadata(cipherData);
|
||||||
|
const preserveRevisionDate =
|
||||||
|
shouldPreserveRepairableCipherUris(request)
|
||||||
|
&& (body.preserveRevisionDate === true || cipherData.preserveRevisionDate === true);
|
||||||
|
|
||||||
if (incomingKey.present && !shouldAcceptCipherKey(incomingKey.value)) {
|
if (incomingKey.present && !shouldAcceptCipherKey(incomingKey.value)) {
|
||||||
return errorResponse('Cipher key encryption is not supported by this server. Resync the client and try again.', 400);
|
return errorResponse('Cipher key encryption is not supported by this server. Resync the client and try again.', 400);
|
||||||
@@ -840,9 +843,10 @@ export async function handleUpdateCipher(request: Request, env: Env, userId: str
|
|||||||
|
|
||||||
// Opaque passthrough: merge existing stored data with ALL incoming client fields.
|
// Opaque passthrough: merge existing stored data with ALL incoming client fields.
|
||||||
// Unknown/future fields from the client are preserved; server-controlled fields are protected.
|
// Unknown/future fields from the client are preserved; server-controlled fields are protected.
|
||||||
|
const { preserveRevisionDate: _preserveRevisionDate, PreserveRevisionDate: _pascalPreserveRevisionDate, ...cipherDataWithoutFlags } = cipherData;
|
||||||
const cipher: Cipher = {
|
const cipher: Cipher = {
|
||||||
...existingCipher, // start with all existing stored data (including unknowns)
|
...existingCipher, // start with all existing stored data (including unknowns)
|
||||||
...cipherData, // overlay all client data (including new/unknown fields)
|
...cipherDataWithoutFlags, // overlay all client data (including new/unknown fields)
|
||||||
// Server-controlled fields (never from client)
|
// Server-controlled fields (never from client)
|
||||||
id: existingCipher.id,
|
id: existingCipher.id,
|
||||||
userId: existingCipher.userId,
|
userId: existingCipher.userId,
|
||||||
@@ -850,7 +854,7 @@ export async function handleUpdateCipher(request: Request, env: Env, userId: str
|
|||||||
favorite: cipherData.favorite ?? existingCipher.favorite,
|
favorite: cipherData.favorite ?? existingCipher.favorite,
|
||||||
reprompt: cipherData.reprompt ?? existingCipher.reprompt,
|
reprompt: cipherData.reprompt ?? existingCipher.reprompt,
|
||||||
createdAt: existingCipher.createdAt,
|
createdAt: existingCipher.createdAt,
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: preserveRevisionDate ? existingCipher.updatedAt : new Date().toISOString(),
|
||||||
archivedAt: readCipherArchivedAt(cipherData, existingCipher.archivedAt ?? null),
|
archivedAt: readCipherArchivedAt(cipherData, existingCipher.archivedAt ?? null),
|
||||||
deletedAt: existingCipher.deletedAt,
|
deletedAt: existingCipher.deletedAt,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -927,6 +927,7 @@ export async function repairCipherUriChecksums(
|
|||||||
? cipher.fields.map(({ decName: _decName, decValue: _decValue, ...field }) => field)
|
? cipher.fields.map(({ decName: _decName, decValue: _decValue, ...field }) => field)
|
||||||
: null,
|
: null,
|
||||||
lastKnownRevisionDate: cipher.revisionDate ?? null,
|
lastKnownRevisionDate: cipher.revisionDate ?? null,
|
||||||
|
preserveRevisionDate: true,
|
||||||
};
|
};
|
||||||
if (keys.key) payload.key = keys.key;
|
if (keys.key) payload.key = keys.key;
|
||||||
|
|
||||||
@@ -1091,7 +1092,9 @@ export async function repairCipherKeyMismatches(
|
|||||||
if (!cipher?.id || !looksLikeCipherString(cipher.key)) continue;
|
if (!cipher?.id || !looksLikeCipherString(cipher.key)) continue;
|
||||||
if (!(await hasItemKeyFieldMismatch(cipher, userEnc, userMac))) continue;
|
if (!(await hasItemKeyFieldMismatch(cipher, userEnc, userMac))) continue;
|
||||||
if (hasUnresolvedEncryptedFields(cipher)) continue;
|
if (hasUnresolvedEncryptedFields(cipher)) continue;
|
||||||
await updateCipher(authedFetch, session, cipher, draftFromDecryptedCipher(cipher));
|
await updateCipher(authedFetch, session, cipher, draftFromDecryptedCipher(cipher), {
|
||||||
|
preserveRevisionDate: true,
|
||||||
|
});
|
||||||
repaired += 1;
|
repaired += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1225,9 +1228,13 @@ export async function updateCipher(
|
|||||||
authedFetch: AuthedFetch,
|
authedFetch: AuthedFetch,
|
||||||
session: SessionState,
|
session: SessionState,
|
||||||
cipher: Cipher,
|
cipher: Cipher,
|
||||||
draft: VaultDraft
|
draft: VaultDraft,
|
||||||
|
extraPayload?: Record<string, unknown>
|
||||||
): Promise<Cipher> {
|
): Promise<Cipher> {
|
||||||
const payload = await buildCipherPayload(session, draft, cipher);
|
const payload = await buildCipherPayload(session, draft, cipher);
|
||||||
|
if (extraPayload) {
|
||||||
|
Object.assign(payload, extraPayload);
|
||||||
|
}
|
||||||
|
|
||||||
const resp = await authedFetch(`/api/ciphers/${encodeURIComponent(cipher.id)}`, {
|
const resp = await authedFetch(`/api/ciphers/${encodeURIComponent(cipher.id)}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
|
|||||||
Reference in New Issue
Block a user