{"record":{"id":"41dfcf7a15bfb04e","repo":"affaan-m/ECC","slug":"seedpaths-entries-must-stay-inside-reporoot-ent","errorCode":null,"errorMessage":"seedPaths entries must stay inside repoRoot: ${entry}","messagePattern":"seedPaths entries must stay inside repoRoot: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/tmux-worktree-orchestrator.js","lineNumber":71,"sourceCode":"function normalizeSeedPaths(seedPaths, repoRoot) {\n  const resolvedRepoRoot = path.resolve(repoRoot);\n  const entries = Array.isArray(seedPaths) ? seedPaths : [];\n  const seen = new Set();\n  const normalized = [];\n\n  for (const entry of entries) {\n    if (typeof entry !== 'string' || entry.trim().length === 0) {\n      continue;\n    }\n\n    const absolutePath = path.resolve(resolvedRepoRoot, entry);\n    const relativePath = path.relative(resolvedRepoRoot, absolutePath);\n\n    if (\n      relativePath.startsWith('..') ||\n      path.isAbsolute(relativePath)\n    ) {\n      throw new Error(`seedPaths entries must stay inside repoRoot: ${entry}`);\n    }\n\n    const normalizedPath = relativePath.split(path.sep).join('/');\n    if (seen.has(normalizedPath)) {\n      continue;\n    }\n\n    seen.add(normalizedPath);\n    normalized.push(normalizedPath);\n  }\n\n  return normalized;\n}\n\nfunction overlaySeedPaths({ repoRoot, seedPaths, worktreePath }) {\n  const normalizedSeedPaths = normalizeSeedPaths(seedPaths, repoRoot);\n\n  for (const seedPath of normalizedSeedPaths) {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/tmux-worktree-orchestrator.js#L53-L89","documentation":"normalizeSeedPaths resolves each seed path against the repo root and rejects entries that escape it (relative path starting with '..' or resolving to an absolute path outside the root). This is a path-traversal guard ensuring seed data can only be copied from within the repository.","triggerScenarios":"Passing seedPaths entries like '../shared/config' or absolute paths outside the repo (e.g. '/etc/passwd'); symlinks whose resolution lands outside repoRoot; a config with OS-specific paths built on a different machine.","commonSituations":"Reusing a seed-paths config across repos where the referenced sibling directory lives outside the current root; attempts to seed from a global folder; symlinked entries in config pointing to a home directory.","solutions":["Change the entry to a path relative to and inside repoRoot (e.g. 'skills/foo', 'scripts/lib').","Copy the needed files into the repo first, then reference them via seedPaths.","If the source must live outside, move it into the repo or create a symlink inside the repo that resolves within the root.","Check for symlinks resolving outside the root and re-point them within the repo."],"exampleFix":"// before\nseedPaths: ['../shared/skills']\n// after\nseedPaths: ['skills'] // after copying shared skills into the repo","handlingStrategy":"validation","validationCode":"const path = require('path');\nfunction isInsideRepo(repoRoot, entry) {\n  const rel = path.relative(path.resolve(repoRoot), path.resolve(repoRoot, entry));\n  return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);\n}","typeGuard":"const safeSeedEntry = (e, root) => typeof e === 'string' && e.length > 0 && !path.relative(path.resolve(root), path.resolve(root, e)).startsWith('..') && !path.isAbsolute(path.relative(path.resolve(root), path.resolve(root, e)));","tryCatchPattern":"try {\n  const seeds = normalizeSeedPaths(repoRoot, entries);\n} catch (e) {\n  if (String(e.message).startsWith('seedPaths entries must stay inside repoRoot')) {\n    console.error('Offending entry:', e.message.split(': ').pop());\n  }\n  throw e;\n}","preventionTips":["Store seed paths as repo-relative paths only.","Avoid symlinks that point outside the repository.","Validate seed paths in CI against path.relative checks.","Never accept absolute paths or '..' segments in seed config."],"tags":["path-traversal","validation","tmux"],"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"}