{"record":{"id":"79e23e4fb073ff04","repo":"affaan-m/ECC","slug":"refusing-to-manage-legacy-sync-path-through-symlin","errorCode":null,"errorMessage":"Refusing to manage legacy sync path through symlinked ancestor: ${filePath}","messagePattern":"Refusing to manage legacy sync path through symlinked ancestor: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/codex-legacy-sync.js","lineNumber":282,"sourceCode":"\n  for (const [key, filePath] of [['config', configPath], ['agents', agentsPath]]) {\n    if (priorState) break;\n    const snapshot = readRegularFileNoFollow(filePath, 'utf8');\n    state.before[key] = snapshot ? snapshot.content : null;\n  }\n  atomicWriteJson(statePath, state);\n  return statePath;\n}\n\nfunction recordLegacySyncPath(options) {\n  const state = readState(options.statePath);\n  const filePath = path.resolve(options.filePath);\n  const trustedRoot = getTrustedRoot(state, filePath);\n  if (!trustedRoot) {\n    throw new Error(`Refusing to record a legacy sync path outside trusted roots: ${filePath}`);\n  }\n  if (hasUnsafeManagedAncestor(filePath, trustedRoot)) {\n    throw new Error(`Refusing to manage legacy sync path through symlinked ancestor: ${filePath}`);\n  }\n  if (!state.paths.some(entry => entry.path === filePath)) {\n    const snapshot = snapshotLegacyPath(filePath);\n    state.paths.push(snapshot);\n    state.rollbackPaths = [...(state.rollbackPaths || []), { ...snapshot }];\n    atomicWriteJson(options.statePath, state);\n  }\n}\n\nfunction rollbackLegacyCodexSync(options) {\n  const state = readState(options.statePath);\n  const restoredPaths = [];\n  const retainedPaths = [];\n\n  const rollbackPaths = Array.isArray(state.rollbackPaths) ? state.rollbackPaths : state.paths;\n  for (const entry of [...rollbackPaths].reverse()) {\n    const filePath = path.resolve(entry.path);\n    const trustedRoot = getTrustedRoot(state, filePath);","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/codex-legacy-sync.js#L264-L300","documentation":"recordLegacySyncPath rejects file paths that have a symlinked ancestor between the file and its trusted root (hasUnsafeManagedAncestor). Managing files through symlinked directories could cause the tool to write to an unexpected location, so it fails closed.","triggerScenarios":"Calling recordLegacySyncPath with a filePath where some ancestor directory (between filePath and the trusted root) is a symlink — detected by hasUnsafeManagedAncestor during path recording.","commonSituations":"Codex home or a managed subdirectory is a symlink (e.g. dotfiles managed via symlink farms like GNU Stow); passing /tmp-based symlinked paths; a macOS environment where ~/Library or similar is symlinked.","solutions":["Remove the symlink in the ancestor chain and use a real directory (or symlink the file itself, not an ancestor)","Pass the fully resolved real path whose ancestors are actual directories","Use a trusted root that is itself the real (non-symlinked) location of the managed files","Re-run after fixing the directory layout"],"exampleFix":"// before\nrecordLegacySyncPath({ statePath, filePath: '/home/me/.codex/config.toml' }) // ~/.codex -> /dotfiles/codex\n// after\n// manage the real location, whose ancestors are not symlinks:\nrecordLegacySyncPath({ statePath, filePath: '/dotfiles/codex/config.toml' });","handlingStrategy":"validation","validationCode":"function hasSymlinkAncestor(p, stop) {\n  let cur = path.dirname(path.resolve(p));\n  const end = path.resolve(stop);\n  while (cur !== end && cur !== path.parse(cur).root) {\n    if (fs.lstatSync(cur).isSymbolicLink()) return true;\n    cur = path.dirname(cur);\n  }\n  return false;\n}","typeGuard":"const safeToManage = (p, root) => !hasSymlinkAncestor(p, root);","tryCatchPattern":"try { recordLegacySyncPath(opts); } catch (e) {\n  if (String(e.message).includes('symlinked ancestor')) console.error('Resolve symlinked directories before syncing', opts.filePath);\n  else throw e;\n}","preventionTips":["Avoid symlink-farm dotfile managers for managed Codex dirs, or sync the real directory","Run fs.realpathSync on candidate paths before passing them","Audit ~/.codex and ancestors with `ls -la` for symlinks","Prefer hard copies over directory symlinks for tool-managed config"],"tags":["security","symlink","path-validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}