fix(encryption): return null on decryption failure to prevent sending encrypted tokens to providers

- When decrypt() encounters a malformed encrypted value (invalid format), return null instead of the ciphertext
- When decrypt() fails due to decryption error (wrong key, auth tag mismatch), return null instead of the ciphertext
- This prevents encrypted tokens from being sent to upstream APIs
- Fixes [400]: Improperly formed request errors when routing to Kiro and other providers with unavailable credentials
- Updated test expectations to match the new correct behavior: decrypt returns null on any failure
This commit is contained in:
oyi77 2026-04-21 03:16:52 +07:00
parent 08d0e9f8b4
commit 763d353f5c
2 changed files with 11 additions and 6 deletions

View file

@ -110,14 +110,16 @@ export function decrypt(ciphertext: string | null | undefined): string | null |
console.warn(
"[Encryption] Found encrypted data but STORAGE_ENCRYPTION_KEY is not set. Cannot decrypt."
);
return ciphertext;
// Return null instead of encrypted ciphertext to prevent sending encrypted tokens to providers
return null;
}
const body = ciphertext.slice(PREFIX.length);
const parts = body.split(":");
if (parts.length !== 3) {
console.error("[Encryption] Malformed encrypted value");
return ciphertext;
// Return null instead of encrypted ciphertext to prevent sending malformed encrypted tokens to providers
return null;
}
const [ivHex, encryptedHex, authTagHex] = parts;
@ -134,7 +136,8 @@ export function decrypt(ciphertext: string | null | undefined): string | null |
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
console.error("[Encryption] Decryption failed:", message);
return ciphertext;
// Return null instead of encrypted ciphertext to prevent sending encrypted tokens to providers
return null;
}
}

View file

@ -66,7 +66,7 @@ test("connection field helpers encrypt and decrypt all supported credential fiel
assert.deepEqual(decrypted, connection);
});
test("decrypt returns the original ciphertext when the value is malformed or the key is wrong", async () => {
test("decrypt returns null when the value is malformed or the key is wrong", async () => {
process.env.STORAGE_ENCRYPTION_KEY = "task-304-secret-c";
const firstModule = await importFresh("src/lib/db/encryption.ts");
const encrypted = firstModule.encrypt("top-secret");
@ -74,6 +74,8 @@ test("decrypt returns the original ciphertext when the value is malformed or the
process.env.STORAGE_ENCRYPTION_KEY = "task-304-secret-d";
const secondModule = await importFresh("src/lib/db/encryption.ts");
assert.equal(secondModule.decrypt(encrypted), encrypted);
assert.equal(secondModule.decrypt("enc:v1:not-valid"), "enc:v1:not-valid");
// When decryption fails with wrong key, return null (not encrypted ciphertext)
// This prevents sending encrypted tokens to APIs
assert.equal(secondModule.decrypt(encrypted), null);
assert.equal(secondModule.decrypt("enc:v1:not-valid"), null);
});