fix(security): scope checkpoint routes to session — close 2 IDORs (v2.7.2)
Flagged by the automated push security review on v2.7.1. - GET /checkpoints?chat_id= : the chat_id branch filtered by chat_id alone (any session's chat_id read its checkpoints). Now joins chats and gates on chats.session_id. - restoreCheckpoint scope guard was fail-open: `cp.session_id && cp.session_id !== sessionId` fell through on a null denormalized session_id, allowing a cross-session restore (worktree reset + transcript trim). Now resolves the owning session via the checkpoint's chat and denies on missing/mismatch. - Adds a DB-integration regression for the null-session_id cross-session case. Both scope authoritatively through chats.session_id (checkpoints.session_id is a nullable hint). Coder suite 234 passing; 7/7 checkpoint tests (incl. the regression) against live postgres+git; typecheck clean. Hotfix on v2.7.1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -159,8 +159,17 @@ export async function restoreCheckpoint(
|
||||
if (!cp) {
|
||||
throw new CheckpointNotFoundError('checkpoint not found');
|
||||
}
|
||||
if (opts?.sessionId && cp.session_id && cp.session_id !== opts.sessionId) {
|
||||
throw new CheckpointNotFoundError('checkpoint not in session');
|
||||
// Authorization scope (fail-safe): the checkpoint's chat must belong to the
|
||||
// requested session. cp.session_id is a denormalized hint that may be null, so
|
||||
// gating on it directly fails open — resolve the owning session via chats
|
||||
// (authoritative; chat_id is NOT NULL) and deny on any mismatch or missing row.
|
||||
if (opts?.sessionId) {
|
||||
const [owner] = await sql<{ session_id: string | null }[]>`
|
||||
SELECT session_id FROM chats WHERE id = ${cp.chat_id}
|
||||
`;
|
||||
if (!owner || owner.session_id !== opts.sessionId) {
|
||||
throw new CheckpointNotFoundError('checkpoint not in session');
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Resolve the worktree path (by worktree_id, else the session's active one).
|
||||
|
||||
Reference in New Issue
Block a user