pbakaus/impeccable · error
invalid session id: ${id}
Error message
invalid session id: ${id} What it means
safeSessionId() validates an id before it is joined into filesystem paths under `.impeccable/live/` (journals, snapshots, accept receipts, preview manifests, generated component dirs). The regex `^[A-Za-z0-9_-]{1,128}$` rejects path separators (/ \), `..`, whitespace, and any id over 128 chars. path.join would otherwise happily traverse out of the live directory, so this is a path-traversal guard at a trust boundary (CLI --id args and HTTP payloads).
Source
Thrown at plugin/skills/impeccable/scripts/lib/impeccable-paths.mjs:110
return filePath;
}
export function removeLiveServerInfo(cwd = process.cwd(), options = {}) {
for (const filePath of [getLiveServerPath(cwd, options), getLegacyLiveServerPath(cwd, options)]) {
try { fs.unlinkSync(filePath); } catch {}
}
}
/**
* Session IDs become path segments (journals, snapshots, accept receipts,
* preview manifests, generated component dirs). They arrive from CLI `--id`
* arguments and HTTP payloads, so anything containing a separator or `..` must
* be rejected before it reaches path.join, which would happily escape
* `.impeccable/live/`. Real IDs are 8 hex chars; the tests use short slugs.
*/
export function safeSessionId(id) {
if (typeof id !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(id)) {
throw new Error('invalid session id: ' + id);
}
return id;
}
export function getLiveSessionsDir(cwd = process.cwd(), options = {}) {
return path.join(getLiveDir(cwd, options), 'sessions');
}
export function getLegacyLiveSessionsDir(cwd = process.cwd(), options = {}) {
return path.join(resolveProjectRoot(cwd, options), '.impeccable-live', 'sessions');
}
export function getLiveAnnotationsDir(cwd = process.cwd(), options = {}) {
return path.join(getLiveDir(cwd, options), 'annotations');
}
export function getCritiqueDir(cwd = process.cwd(), options = {}) {
return path.join(getImpeccableDir(cwd, options), CRITIQUE_DIR);View on GitHub (pinned to d14711ae3d)
Solutions
- Pass only ids matching [A-Za-z0-9_-]{1,128} (real ids are 8 hex chars).
- If the id originates from untrusted input, sanitize or reject it before it reaches the live APIs (run the same regex).
- Generate ids with a hex/uuid source rather than deriving them from paths.
Example fix
// before
safeSessionId('../etc/passwd'); // throws
safeSessionId('a/b'); // throws
// after
safeSessionId('a1b2c3d4'); // ok Defensive patterns
Strategy: validation
Validate before calling
function isValidSessionId(id) {
return typeof id === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(id);
}
if (!isValidSessionId(id)) {
// reject untrusted input at the trust boundary; never forward to safeSessionId
return res.status(400).json({ error: 'invalid session id' });
} Type guard
function isSafeSessionId(id) {
return typeof id === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(id);
} Try / catch
try {
safeSessionId(id);
} catch (e) {
if (String(e.message).startsWith('invalid session id')) {
return res.status(400).json({ error: 'invalid session id' });
}
throw e;
} Prevention
- Validate session ids at every trust boundary (CLI args, HTTP payloads) with the same regex.
- Generate ids from hex/uuid sources; never derive them from raw filenames or URLs.
- Treat this guard as a path-traversal defense — do not weaken the regex.
When it happens
Trigger: Passing a session id containing '/', '\\', '..', spaces, special characters, or exceeding 128 characters to any API that routes through safeSessionId; e.g. an HTTP payload { id: '../etc/passwd' } or a CLI `--id a/b`.
Common situations: User-supplied or request-supplied ids reaching path construction unfiltered; migrating from short test slugs to ids that include separators; an id generated from a filename or URL that retains a slash.
Related errors
- invalid session id: ${id}
- Invalid svelte-component source file
- Svelte-component source file escapes project root
- invalid catalog: ${validation.errors.join('; ')}
- concept-seed: --scope must be direction or surface
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/6ba1ab3989588f65.
Report an issue: GitHub.