fix: refuse an impersonation link when its use can't be recorded #1066

This commit is contained in:
Linus Rath
2026-10-31 22:53:34 +01:00
parent c20ecd4b1a
commit d28ee361c1
2 changed files with 16 additions and 6 deletions
@@ -1,5 +1,5 @@
// @vitest-environment node
import { mkdtempSync, readdirSync, rmSync, utimesSync } from 'node:fs';
import { mkdtempSync, readdirSync, rmSync, utimesSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
@@ -44,6 +44,14 @@ describe('consumeImpersonationJti', () => {
expect(await consumeImpersonationJti('jti-1', exp, now)).toBe(false);
});
it('refuses the token when its marker cannot be written', async () => {
// A state "dir" that is a file: the marker dir can't be created.
const blocked = path.join(stateDir, 'not-a-dir');
writeFileSync(blocked, '');
process.env.ADMIN_STATE_DIR = blocked;
expect(await consumeImpersonationJti('jti-unrecorded', exp, now)).toBe(false);
});
it('prunes markers once their token can no longer verify', async () => {
await consumeImpersonationJti('jti-old', exp, now);
const dir = path.join(stateDir, 'impersonation-jti');
+7 -5
View File
@@ -20,7 +20,8 @@ async function pruneExpired(dir: string, nowMs: number): Promise<void> {
}
/**
* Record an impersonation token's jti as used; false when it was used before.
* Record an impersonation token's jti as used; false when it was used before
* or the use can't be recorded.
*
* The in-process cache alone forgot every jti on restart and knew nothing of
* other replicas, so a captured handoff link could be redeemed again within
@@ -45,11 +46,12 @@ export async function consumeImpersonationJti(
return true;
} catch (err) {
if ((err as NodeJS.ErrnoException).code === 'EEXIST') return false;
// The state directory is documented as always writable; if it is not,
// keep impersonation working on the in-process cache.
logger.warn('Impersonation replay store unavailable, using the in-process cache only', {
// Without the marker a restart or another replica would accept the
// token again, so it is refused rather than trusted to this process's
// memory.
logger.error('Impersonation replay store unavailable, refusing the token', {
error: err instanceof Error ? err.message : 'Unknown',
});
return true;
return false;
}
}