{"record":{"id":"73c7eb6dc896869f","repo":"affaan-m/ECC","slug":"path-traversal-rejected-relpath-73c7eb","errorCode":null,"errorMessage":"Path traversal rejected: ${relPath}","messagePattern":"Path traversal rejected: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/hooks/plugin-hook-bootstrap.js","lineNumber":58,"sourceCode":"  }\n\n  const match = rootDir.match(/^\\/([a-zA-Z])(?:\\/(.*))?$/);\n  if (!match) {\n    return rootDir;\n  }\n\n  const [, driveLetter, rest = ''] = match;\n  return `${driveLetter.toUpperCase()}:/${rest}`;\n}\n\nfunction resolveTarget(rootDir, relPath) {\n  const resolvedRoot = path.resolve(rootDir);\n  const resolvedTarget = path.resolve(rootDir, relPath);\n  if (\n    resolvedTarget !== resolvedRoot &&\n    !resolvedTarget.startsWith(resolvedRoot + path.sep)\n  ) {\n    throw new Error(`Path traversal rejected: ${relPath}`);\n  }\n  return resolvedTarget;\n}\n\nlet _cachedShell = undefined;\nlet _cachedBash = undefined;\n\nfunction isPowerShellBin(bin) {\n  const base = path.basename(bin).toLowerCase();\n  return base === 'pwsh.exe' || base === 'pwsh' || base === 'powershell.exe' || base === 'powershell';\n}\n\nfunction findShellBinary() {\n  if (_cachedShell !== undefined) return _cachedShell;\n\n  const candidates = [];\n\n  // Explicit override always wins — check before any platform probing.","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/hooks/plugin-hook-bootstrap.js#L40-L76","documentation":"plugin-hook-bootstrap.js uses the same resolveTarget containment guard as observe-runner.js: it resolves relPath against the root and requires the result to be the root itself or live under it. A relPath that resolves outside the root is rejected before any script is dispatched to the shell.","triggerScenarios":"A hook bootstrap relative path containing '..' that climbs above the plugin root, or an absolute path passed where a relative subpath is expected.","commonSituations":"Plugin root env vars pointing at the wrong directory, a relocated plugin install, or adversarial hook configuration attempting to execute files outside the plugin.","solutions":["Point the plugin root env var at the actual installed ECC plugin directory","Pass only relative paths that stay inside the plugin tree","Reinstall ECC to restore a clean plugin root layout"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const path = require('path');\nfunction assertBootstrapWithinRoot(rootDir, relPath) {\n  const root = path.resolve(rootDir);\n  const target = path.resolve(rootDir, relPath);\n  if (target !== root && !target.startsWith(root + path.sep)) {\n    throw new Error(`Bootstrap path traversal rejected (pre-check): ${relPath}`);\n  }\n}","typeGuard":"function isBootstrapPathInsideRoot(rootDir, relPath) {\n  const root = path.resolve(rootDir);\n  const target = path.resolve(rootDir, relPath);\n  return target === root || target.startsWith(root + path.sep);\n}","tryCatchPattern":"try { resolveTarget(root, rel); }\ncatch (err) {\n  if (/Path traversal rejected/.test(err.message)) {\n    console.error('Bootstrap refused a path outside the plugin root; aborting.');\n    process.exit(126);\n  }\n  throw err;\n}","preventionTips":["Keep the plugin root env var pointing at the genuine install directory","Only pass relative subpaths to the bootstrap, never absolute or '..'-laden paths","Validate hook configuration before deploying it to shared environments"],"tags":["security","path-traversal","hooks","plugin-bootstrap"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}