fix: harden residual route and restore checks
This commit is contained in:
@@ -4,6 +4,7 @@ import { hashSecret } from '../src/lib/server/security';
|
||||
import {
|
||||
parseRestoreMarkerPhase,
|
||||
validateRestoreMarkerTarget,
|
||||
SYNTHETIC_CHARACTER_ID_MIN,
|
||||
validateRestoreMarkerWrite,
|
||||
} from '../src/lib/server/restore-marker';
|
||||
|
||||
@@ -15,9 +16,13 @@ validateRestoreMarkerWrite({
|
||||
allowWrite: process.env.SMOKE_ALLOW_WRITE,
|
||||
confirmation: process.env.RESTORE_MARKER_CONFIRMATION,
|
||||
});
|
||||
const characterId = Number(process.env.RESTORE_MARKER_CHARACTER_ID ?? '998000001');
|
||||
const characterId = Number(process.env.RESTORE_MARKER_CHARACTER_ID ?? String(SYNTHETIC_CHARACTER_ID_MIN + 1));
|
||||
const expiryRaw = process.env.RESTORE_MARKER_SESSION_EXPIRES_AT;
|
||||
const sessionExpiresAt = expiryRaw === undefined ? Date.now() + 7 * 24 * 60 * 60_000 : Number(expiryRaw);
|
||||
if (!marker || !/^[A-Za-z0-9._-]{8,100}$/.test(marker)) throw new Error('RESTORE_MARKER must be a synthetic 8–100 character identifier');
|
||||
if (!Number.isSafeInteger(characterId) || characterId < 90_000_000) throw new Error('RESTORE_MARKER_CHARACTER_ID must be a synthetic safe integer');
|
||||
if (!Number.isSafeInteger(characterId) || characterId < SYNTHETIC_CHARACTER_ID_MIN) throw new Error(`RESTORE_MARKER_CHARACTER_ID must be a reserved synthetic ID >= ${SYNTHETIC_CHARACTER_ID_MIN}`);
|
||||
if (!Number.isSafeInteger(sessionExpiresAt) || sessionExpiresAt <= Date.now()) throw new Error('RESTORE_MARKER_SESSION_EXPIRES_AT must be a future millisecond timestamp');
|
||||
if (phase !== 'write' && expiryRaw === undefined) throw new Error('RESTORE_MARKER_SESSION_EXPIRES_AT is required when verifying or invalidating a marker');
|
||||
const store = new ConvexStore(target.toString(), process.env.CONVEX_BRIDGE_SECRET!);
|
||||
const sessionHash = hashSecret(`restore-marker-session:${marker}`);
|
||||
const ownerHash = hashSecret(`restore-marker-owner:${marker}`);
|
||||
@@ -30,20 +35,20 @@ if (phase === 'write') {
|
||||
ownerHash,
|
||||
scopes: ['esi-assets.read_assets.v1'],
|
||||
encryptedTokens: `restore-marker-placeholder:${marker}`,
|
||||
tokenExpiresAt: now + 60_000,
|
||||
tokenExpiresAt: sessionExpiresAt,
|
||||
sessionHash,
|
||||
sessionExpiresAt: now + 60 * 60_000,
|
||||
sessionExpiresAt,
|
||||
});
|
||||
console.log(`Restore marker written: character=${characterId}, user=${user.userId}`);
|
||||
console.log(`Restore marker written: character=${characterId}, user=${user.userId}, expiresAt=${sessionExpiresAt}`);
|
||||
} else if (phase === 'verify') {
|
||||
const session = await store.session(sessionHash);
|
||||
assert(session, 'restore marker session is missing');
|
||||
const characters = await store.characters(session.userId);
|
||||
assert(characters.some((character) => character.id === characterId && character.name === `Restore marker ${marker}`), 'restore marker character is missing or changed');
|
||||
console.log(`Restore marker verified: character=${characterId}, user=${session.userId}`);
|
||||
console.log(`Restore marker verified: character=${characterId}, user=${session.userId}, expiresAt=${sessionExpiresAt}`);
|
||||
} else if (phase === 'expect-invalidated') {
|
||||
assert.equal(await store.session(sessionHash), null, 'restore marker session remains usable; invalidation is incomplete');
|
||||
console.log(`Restore marker invalidation verified: character=${characterId}`);
|
||||
console.log(`Restore marker invalidation verified: character=${characterId}, beforeExpiresAt=${sessionExpiresAt}`);
|
||||
} else {
|
||||
throw new Error('RESTORE_MARKER_PHASE must be write, verify, or expect-invalidated');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user