mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-10-23 03:47:00 +09:00
fix(cli): report actual flag state when decrypting an encrypted store without a key (#4378)
Co-authored-by: Aniruddha Adak <aniruddhaadak80@users.noreply.github.com>
This commit is contained in:
1 parent
0103362f08
commit
c949f62703
3 files changed
+50
-2
No files matched your search
@@ -113,6 +113,34 @@ describe('vault (ADR-096 Phase 1)', () => {
|
||||
process.env.CLAUDE_FLOW_ENCRYPTION_KEY = 'c'.repeat(64);
|
||||
expect(getKey().length).toBe(32);
|
||||
});
|
||||
|
||||
it('keeps the original message on the default (encrypt) path', () => {
|
||||
process.env.CLAUDE_FLOW_ENCRYPT_AT_REST = '1';
|
||||
delete process.env.CLAUDE_FLOW_ENCRYPTION_KEY;
|
||||
expect(() => getKey()).toThrow(/is set but/);
|
||||
});
|
||||
|
||||
it('does not claim the flag is set on the decrypt path (#3212)', () => {
|
||||
delete process.env.CLAUDE_FLOW_ENCRYPT_AT_REST;
|
||||
delete process.env.CLAUDE_FLOW_ENCRYPTION_KEY;
|
||||
expect(() => getKey('decrypt')).not.toThrow(/is set but/);
|
||||
expect(() => getKey('decrypt')).toThrow(/is required but not set/);
|
||||
});
|
||||
|
||||
it('echoes the flag actual value and on-disk state on the decrypt path', () => {
|
||||
delete process.env.CLAUDE_FLOW_ENCRYPT_AT_REST;
|
||||
delete process.env.CLAUDE_FLOW_ENCRYPTION_KEY;
|
||||
expect(() => getKey('decrypt')).toThrow(
|
||||
/CLAUDE_FLOW_ENCRYPT_AT_REST=unset.*RFE1-encrypted/,
|
||||
);
|
||||
process.env.CLAUDE_FLOW_ENCRYPT_AT_REST = '0';
|
||||
expect(() => getKey('decrypt')).toThrow(/CLAUDE_FLOW_ENCRYPT_AT_REST=0/);
|
||||
});
|
||||
|
||||
it('warns a new key will not decrypt the existing store', () => {
|
||||
delete process.env.CLAUDE_FLOW_ENCRYPTION_KEY;
|
||||
expect(() => getKey('decrypt')).toThrow(/will not decrypt it/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encrypt / decrypt round-trip', () => {
|
||||
|
||||
@@ -69,6 +69,15 @@ export function isEncryptionEnabled(): boolean {
|
||||
* encryption is enabled but no key resolves, this throws with a clear
|
||||
* message rather than silently falling back to plaintext (fail-closed).
|
||||
*
|
||||
* The optional `context` selects which missing-key message to throw:
|
||||
* - `'encrypt'` (default): the caller is about to encrypt because
|
||||
* `CLAUDE_FLOW_ENCRYPT_AT_REST` is set, so the original message is
|
||||
* accurate.
|
||||
* - `'decrypt'`: the caller hit an RFE1-encrypted blob on read (e.g. a
|
||||
* store restored from another machine) while the enable flag may be
|
||||
* unset (issue #3212). The message echoes the flag's actual value and
|
||||
* the on-disk state instead of asserting the flag is set.
|
||||
*
|
||||
* Accepted encodings (auto-detected by length):
|
||||
* - 64-char hex (32 bytes)
|
||||
* - 44-char base64 (32 bytes + padding)
|
||||
@@ -77,9 +86,18 @@ export function isEncryptionEnabled(): boolean {
|
||||
* Anything else is rejected — we'd rather fail loudly than encrypt with a
|
||||
* truncated key.
|
||||
*/
|
||||
export function getKey(): Buffer {
|
||||
export function getKey(context: 'encrypt' | 'decrypt' = 'encrypt'): Buffer {
|
||||
const raw = process.env[ENV_KEY_VAR];
|
||||
if (!raw) {
|
||||
if (context === 'decrypt') {
|
||||
const flag = process.env[ENV_ENABLE_FLAG] ?? 'unset';
|
||||
throw new Error(
|
||||
`${ENV_KEY_VAR} is required but not set ` +
|
||||
`(${ENV_ENABLE_FLAG}=${flag}; the store on disk is RFE1-encrypted). ` +
|
||||
`Supply the key this store was encrypted with — a newly generated key will not decrypt it. ` +
|
||||
`Provide a 32-byte key as 64-char hex or 44-char base64.`,
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`${ENV_ENABLE_FLAG} is set but ${ENV_KEY_VAR} is not. ` +
|
||||
`Provide a 32-byte key as 64-char hex or 44-char base64. ` +
|
||||
|
||||
@@ -244,7 +244,9 @@ export function readFileMaybeEncrypted(
|
||||
const raw = readFileSync(path);
|
||||
let plain: Buffer;
|
||||
if (isEncryptedBlob(raw)) {
|
||||
plain = decryptBuffer(raw, getKey());
|
||||
// 'decrypt': the blob on disk is RFE1-encrypted but the enable flag may
|
||||
// be unset (e.g. store restored from another machine) — see #3212.
|
||||
plain = decryptBuffer(raw, getKey('decrypt'));
|
||||
} else {
|
||||
plain = raw;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user