{"record":{"id":"6ba1ab3989588f65","repo":"pbakaus/impeccable","slug":"invalid-session-id-id","errorCode":null,"errorMessage":"invalid session id: ${id}","messagePattern":"invalid session id: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/skills/impeccable/scripts/lib/impeccable-paths.mjs","lineNumber":110,"sourceCode":"  return filePath;\n}\n\nexport function removeLiveServerInfo(cwd = process.cwd(), options = {}) {\n  for (const filePath of [getLiveServerPath(cwd, options), getLegacyLiveServerPath(cwd, options)]) {\n    try { fs.unlinkSync(filePath); } catch {}\n  }\n}\n\n/**\n * Session IDs become path segments (journals, snapshots, accept receipts,\n * preview manifests, generated component dirs). They arrive from CLI `--id`\n * arguments and HTTP payloads, so anything containing a separator or `..` must\n * be rejected before it reaches path.join, which would happily escape\n * `.impeccable/live/`. Real IDs are 8 hex chars; the tests use short slugs.\n */\nexport function safeSessionId(id) {\n  if (typeof id !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(id)) {\n    throw new Error('invalid session id: ' + id);\n  }\n  return id;\n}\n\nexport function getLiveSessionsDir(cwd = process.cwd(), options = {}) {\n  return path.join(getLiveDir(cwd, options), 'sessions');\n}\n\nexport function getLegacyLiveSessionsDir(cwd = process.cwd(), options = {}) {\n  return path.join(resolveProjectRoot(cwd, options), '.impeccable-live', 'sessions');\n}\n\nexport function getLiveAnnotationsDir(cwd = process.cwd(), options = {}) {\n  return path.join(getLiveDir(cwd, options), 'annotations');\n}\n\nexport function getCritiqueDir(cwd = process.cwd(), options = {}) {\n  return path.join(getImpeccableDir(cwd, options), CRITIQUE_DIR);","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/pbakaus/impeccable/blob/d14711ae3d1a1dd62dee61a358d27f107c51ccd0/plugin/skills/impeccable/scripts/lib/impeccable-paths.mjs#L92-L128","documentation":"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).","triggerScenarios":"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`.","commonSituations":"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.","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."],"exampleFix":"// before\nsafeSessionId('../etc/passwd'); // throws\nsafeSessionId('a/b');            // throws\n\n// after\nsafeSessionId('a1b2c3d4');       // ok","handlingStrategy":"validation","validationCode":"function isValidSessionId(id) {\n  return typeof id === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(id);\n}\nif (!isValidSessionId(id)) {\n  // reject untrusted input at the trust boundary; never forward to safeSessionId\n  return res.status(400).json({ error: 'invalid session id' });\n}","typeGuard":"function isSafeSessionId(id) {\n  return typeof id === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(id);\n}","tryCatchPattern":"try {\n  safeSessionId(id);\n} catch (e) {\n  if (String(e.message).startsWith('invalid session id')) {\n    return res.status(400).json({ error: 'invalid session id' });\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["validation","security","path-traversal"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}