{"record":{"id":"4081be85c9d64612","repo":"affaan-m/ECC","slug":"refusing-to-record-a-legacy-sync-path-outside-trus","errorCode":null,"errorMessage":"Refusing to record a legacy sync path outside trusted roots: ${filePath}","messagePattern":"Refusing to record a legacy sync path outside trusted roots: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/codex-legacy-sync.js","lineNumber":279,"sourceCode":"    paths: [],\n    rollbackPaths: [],\n  };\n\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;","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/affaan-m/ECC/blob/06c5e118c4d3e6c3b7f9445f973a2194c82de193/scripts/lib/codex-legacy-sync.js#L261-L297","documentation":"recordLegacySyncPath() adds a file to the sync-state's managed set. It resolves the path and asks getTrustedRoot() for a root (codexHome, recorded trustedRoots, or installedHooksPath) that contains it; with no match it refuses, so the state file can never record paths outside the directories ECC was told it owns.","triggerScenarios":"Calling recordLegacySyncPath({ statePath, filePath }) where path.resolve(filePath) lands outside codexHome and the hooks path — commonly a relative path that resolved against process.cwd(), or a codexHome option that differs from the one used at beginLegacySyncState() time.","commonSituations":"Caller computes the managed path from the repo root instead of codexHome; options objects built in two places with different codexHome values; wrong variable passed as filePath.","solutions":["Derive the path from the same codexHome passed to beginLegacySyncState(), e.g. path.join(codexHome, 'config.toml')","If the path legitimately lives elsewhere, pass it via installedHooksPath (or trustedRoots) at begin time so it becomes trusted","Fix the caller to always pass absolute paths built under the managed roots"],"exampleFix":"// before: recordLegacySyncPath({ statePath, filePath: 'config.toml' }) // resolves against cwd -> refused\nrecordLegacySyncPath({ statePath, filePath: path.join(codexHome, 'config.toml') });","handlingStrategy":"validation","validationCode":"const path = require('path');\nfunction isUnderTrustedRoot(filePath, trustedRoots) {\n  const abs = path.resolve(filePath);\n  return trustedRoots.some(r => abs === r || abs.startsWith(path.resolve(r) + path.sep));\n}\nif (!isUnderTrustedRoot(filePath, trustedRoots)) throw new Error('caller bug: path not under a trusted root: ' + filePath);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always build managed paths with path.join(codexHome, ...)","Pass one shared options object through begin/record calls","Never record relative paths"],"tags":["codex","path-validation","trusted-root"],"backgroundTag":"path-outside-trusted-root","analyzedSha":"06c5e118c4d3e6c3b7f9445f973a2194c82de193","analyzedAt":"2026-08-18T11:27:13.915Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}