42 lines
1.7 KiB
TypeScript
42 lines
1.7 KiB
TypeScript
/**
|
|
* Regression test for audit LOW #1/#40: a secret that literally starts with the
|
|
* reserved "enc:v1:" prefix was passed through encrypt() unchanged and stored as
|
|
* PLAINTEXT. encrypt() now only skips re-encrypting values that are genuinely our
|
|
* ciphertext (valid prefix AND a decodable, long-enough payload).
|
|
*/
|
|
import assert from 'node:assert/strict';
|
|
import { describe, it, before } from 'node:test';
|
|
|
|
import { encrypt, decrypt, looksLikeCiphertext } from '../src/lib/server/encryption';
|
|
|
|
before(() => {
|
|
// Deterministic key so encrypt/decrypt round-trip in the test env.
|
|
process.env.ENCRYPTION_KEY ||= Buffer.alloc(32, 7).toString('base64');
|
|
});
|
|
|
|
describe('encryption reserved-prefix bypass (audit #1/#40)', () => {
|
|
it('encrypts a password that starts with the reserved prefix (no plaintext leak)', () => {
|
|
const pw = 'enc:v1:hunter2';
|
|
const enc = encrypt(pw)!;
|
|
assert.notEqual(enc, pw, 'must not store the prefixed password verbatim');
|
|
assert.equal(decrypt(enc), pw, 'must round-trip back to the original');
|
|
});
|
|
|
|
it('looksLikeCiphertext is false for a plaintext that merely starts with the prefix', () => {
|
|
assert.equal(looksLikeCiphertext('enc:v1:hunter2'), false);
|
|
assert.equal(looksLikeCiphertext('enc:v1:'), false);
|
|
assert.equal(looksLikeCiphertext('enc:v1:!!!not-base64!!!'), false);
|
|
});
|
|
|
|
it('looksLikeCiphertext is true for a real encrypted blob, and it is not double-encrypted', () => {
|
|
const enc = encrypt('some-secret')!;
|
|
assert.equal(looksLikeCiphertext(enc), true);
|
|
assert.equal(encrypt(enc), enc, 'genuine ciphertext must not be re-encrypted');
|
|
});
|
|
|
|
it('normal passwords still round-trip', () => {
|
|
const n = 'S3cr3t-P@ss';
|
|
assert.equal(decrypt(encrypt(n)!), n);
|
|
});
|
|
});
|