{"record":{"id":"068a3cece297dcb2","repo":"santifer/career-ops","slug":"label-escapes-the-tracker-workspace-abspath","errorCode":null,"errorMessage":"${label} escapes the tracker workspace: ${absPath}","messagePattern":"(.+?) escapes the tracker workspace: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"generate-pdf.mjs","lineNumber":88,"sourceCode":" * @throws {Error} when the canonical path escapes the tracker workspace.\n */\nfunction assertInsideWorkspace(absPath, label) {\n  let probe = absPath;\n  const tail = [];\n  while (!existsSync(probe)) {\n    tail.unshift(basename(probe));\n    const parent = dirname(probe);\n    if (parent === probe) break; // reached the filesystem root\n    probe = parent;\n  }\n  let canonical;\n  try {\n    canonical = existsSync(probe) ? resolve(realpathSync(probe), ...tail) : absPath;\n  } catch {\n    // Canonicalization failed (realpath raced away, permission error): containment\n    // is unprovable, so fail closed rather than fall back to a lexical form that a\n    // symlinked ancestor could slip past.\n    throw new Error(`${label} escapes the tracker workspace: ${absPath}`);\n  }\n  const rel = relative(__workspaceRoot, canonical);\n  if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) {\n    throw new Error(`${label} escapes the tracker workspace: ${absPath}`);\n  }\n  return absPath;\n}\n\n// Ensure output directory exists (fresh setup)\nmkdirSync(resolve(workspaceRoot, 'output'), { recursive: true });\n\n/**\n * Normalize text for ATS compatibility by converting problematic Unicode.\n *\n * ATS parsers and legacy systems often fail on em-dashes, smart quotes,\n * zero-width characters, and non-breaking spaces. These cause mojibake,\n * parsing errors, or display issues. See issue #1.\n *","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/generate-pdf.mjs#L70-L106","documentation":"assertInsideWorkspace() in generate-pdf.mjs canonicalizes the deepest existing ancestor of the path with realpathSync(). If that call throws — the ancestor vanished between existsSync() and realpathSync() (a TOCTOU race), a parent directory denies read/traverse (EACCES), or an NFS/symlink quirk — containment is unprovable, and the guard fails closed rather than fall back to a lexical path a symlinked ancestor could slip past.","triggerScenarios":"A concurrent process deletes or renames a parent directory mid-render; the workspace sits under a parent dir with restrictive permissions; a symlinked ancestor whose target is on a flaky mount.","commonSituations":"Parallel batch renders racing a cleanup job over output/; running as a different user than the workspace owner; containerized runs where only part of the path is visible.","solutions":["Re-run once — a transient realpath race self-heals when the filesystem settles.","If persistent, check traverse permission on every parent: `namei -l <path>` shows where access stops, and fix ownership/permissions there.","Avoid symlinked ancestors for the workspace; use the real directory path."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import { existsSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n// cheap pre-flight: the deepest existing ancestor must be stable and readable\nfunction ancestorsReadable(p) {\n  let d = p;\n  while (!existsSync(d)) d = dirname(d);\n  try { accessSync(d, constants.R_OK); return true; } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  assertInsideWorkspace(path, 'input');\n} catch (err) {\n  if (err.message.includes('escapes the tracker workspace')) {\n    if (isTransientFsError) { await once(setTimeout, 250); assertInsideWorkspace(path, 'input'); } // one retry for realpath races\n    else { console.error(`Path containment failed for ${path} — check permissions/symlinks on its parents.`); process.exit(1); }\n  } else throw err;\n}","preventionTips":["Don't run filesystem cleanup jobs that delete workspace directories while renders are in flight.","Ensure every parent of the workspace is traversable (namei -l) by the running user.","Avoid placing the workspace behind symlinks or on flaky network mounts."],"tags":["security","symlink","race-condition","realpath","permissions"],"backgroundTag":"path-traversal-guard","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}