{"record":{"id":"dc8f6c1efceb94b8","repo":"pbakaus/impeccable","slug":"invalid-session-id-id-dc8f6c","errorCode":null,"errorMessage":"invalid session id: ${id}","messagePattern":"invalid session id: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skill/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/skill/scripts/lib/impeccable-paths.mjs#L92-L128","documentation":"Thrown by safeSessionId() in skill/scripts/lib/impeccable-paths.mjs when an id is not a string matching ^[A-Za-z0-9_-]{1,128}$. Session ids become path segments (journals, snapshots, accept receipts, preview manifests, generated component dirs) and arrive from CLI --id args and HTTP payloads, so anything containing a path separator or `..` must be rejected before path.join can escape `.impeccable/live/`. Real ids are 8 hex chars; tests use short slugs.","triggerScenarios":"Passing a session id containing `/`, `\\`, `..`, spaces, or other punctuation; an id longer than 128 chars; or a non-string (number, null) from an HTTP payload or unvalidated CLI arg. Any of these reaches safeSessionId before being used in a filesystem path.","commonSituations":"A client sending a crafted or malformed id over the live HTTP API; a script passing a filename or URL as --id; a generated id that accidentally includes a slash; id coming from an untyped JSON field.","solutions":["Generate ids from 8 hex chars (e.g. crypto.randomBytes(4).toString('hex')) to match the real-session shape.","Strip/replace any character outside [A-Za-z0-9_-] before passing the id to the API, or reject it.","If you control the caller, type-check the id is a string and within length before invoking any live-session function."],"exampleFix":"// before\nconst id = req.query.id; // \"../../etc/passwd\"\nuseSession(id);\n// after\nimport { safeSessionId } from './impeccable-paths.mjs';\nconst id = safeSessionId(req.query.id); // throws on traversal","handlingStrategy":"type-guard","validationCode":"const SESSION_RE = /^[A-Za-z0-9_-]{1,128}$/;\nfunction safeId(id) {\n  if (typeof id !== 'string' || !SESSION_RE.test(id)) {\n    throw new Error('invalid session id: ' + id);\n  }\n  return id;\n}","typeGuard":"function isSafeSessionId(id) {\n  return typeof id === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(id);\n}","tryCatchPattern":"// At the HTTP/CLI boundary, validate before any path operation.\nif (!isSafeSessionId(req.params.id)) {\n  return res.status(400).json({ error: 'invalid session id' });\n}","preventionTips":["Generate session ids from crypto.randomBytes(4).toString('hex').","Never feed user-supplied strings straight into path.join.","Treat session ids as untrusted at every trust boundary."],"tags":["security","path-traversal","validation","live-sessions"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}