{"record":{"id":"d16ef28be568ca92","repo":"abhigyanpatwari/GitNexus","slug":"refusing-symlink-in-auto-sync-clone-root-path-c","errorCode":null,"errorMessage":"Refusing symlink in auto-sync clone root path: ${current}","messagePattern":"Refusing symlink in auto-sync clone root path: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/auto-sync/path-security.ts","lineNumber":254,"sourceCode":"    }\n  }\n}\n\nasync function assertNoSymlinkPath(root: string): Promise<void> {\n  const parsed = path.parse(root);\n  let current = parsed.root;\n  const parts = root.slice(parsed.root.length).split(path.sep).filter(Boolean);\n  for (const part of parts) {\n    current = path.join(current, part);\n    let stat;\n    try {\n      stat = await fs.lstat(current);\n    } catch (err: unknown) {\n      if ((err as NodeJS.ErrnoException).code === 'ENOENT') break;\n      throw err;\n    }\n    if (stat.isSymbolicLink())\n      throw new Error(`Refusing symlink in auto-sync clone root path: ${current}`);\n  }\n}\n\nexport async function assertDirectoryOwnerAndPermissions(root: string): Promise<void> {\n  const stat = await fs.stat(root);\n  if (!stat.isDirectory()) throw new Error(`auto-sync clone root is not a directory: ${root}`);\n  // POSIX uid/mode have no meaning on Windows, and this runs on every tick for\n  // every project, so throwing here failed 100% of repos forever while `watch\n  // status` still read `running`. Skip the ownership assertions rather than the\n  // whole feature: the caller's other guards — dangerous-root rejection\n  // (including the Windows system roots), symlink refusal, realpath containment\n  // and the GitNexus-internal-root check — all still apply, and managed git runs\n  // with `core.hooksPath` pinned to the null device.\n  if (process.platform === 'win32') return;\n  if (typeof process.getuid === 'function' && stat.uid !== process.getuid()) {\n    throw new Error(`auto-sync clone root is owned by uid ${stat.uid}, not current process uid`);\n  }\n  const mode = stat.mode & 0o777;","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/auto-sync/path-security.ts#L236-L272","documentation":"Thrown by assertNoSymlinkPath (via resolveConfiguredCloneRoot) after lstat reveals that some component of the clone-root path is a symbolic link. The guard walks the path from the filesystem root down, lstat-ing each segment; a symlink anywhere in the chain (even in a parent directory) is rejected because the real containment checks and dangerous-root checks were performed on the lexical path and a symlink could redirect writes to an unvetted location.","triggerScenarios":"Thrown at gitnexus/src/core/auto-sync/path-security.ts:254 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Replace the symlink with a real directory (mv the target into place or bind-mount it)","Point the auto-sync configuration directly at the symlink's resolved real path","Use a physical directory on the same filesystem to avoid needing the symlink at all"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-09-08T00:40:44.970Z","contentChangedAt":"2026-09-08T00:40:44.970Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}