affaan-m/ECC · error
Path traversal rejected: ${relPath}
Error message
Path traversal rejected: ${relPath} What it means
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.
Source
Thrown at scripts/hooks/plugin-hook-bootstrap.js:58
}
const match = rootDir.match(/^\/([a-zA-Z])(?:\/(.*))?$/);
if (!match) {
return rootDir;
}
const [, driveLetter, rest = ''] = match;
return `${driveLetter.toUpperCase()}:/${rest}`;
}
function resolveTarget(rootDir, relPath) {
const resolvedRoot = path.resolve(rootDir);
const resolvedTarget = path.resolve(rootDir, relPath);
if (
resolvedTarget !== resolvedRoot &&
!resolvedTarget.startsWith(resolvedRoot + path.sep)
) {
throw new Error(`Path traversal rejected: ${relPath}`);
}
return resolvedTarget;
}
let _cachedShell = undefined;
let _cachedBash = undefined;
function isPowerShellBin(bin) {
const base = path.basename(bin).toLowerCase();
return base === 'pwsh.exe' || base === 'pwsh' || base === 'powershell.exe' || base === 'powershell';
}
function findShellBinary() {
if (_cachedShell !== undefined) return _cachedShell;
const candidates = [];
// Explicit override always wins — check before any platform probing.View on GitHub (pinned to 01e15490f0)
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
Defensive patterns
Strategy: validation
Validate before calling
const path = require('path');
function assertBootstrapWithinRoot(rootDir, relPath) {
const root = path.resolve(rootDir);
const target = path.resolve(rootDir, relPath);
if (target !== root && !target.startsWith(root + path.sep)) {
throw new Error(`Bootstrap path traversal rejected (pre-check): ${relPath}`);
}
} Type guard
function isBootstrapPathInsideRoot(rootDir, relPath) {
const root = path.resolve(rootDir);
const target = path.resolve(rootDir, relPath);
return target === root || target.startsWith(root + path.sep);
} Try / catch
try { resolveTarget(root, rel); }
catch (err) {
if (/Path traversal rejected/.test(err.message)) {
console.error('Bootstrap refused a path outside the plugin root; aborting.');
process.exit(126);
}
throw err;
} Prevention
- 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
When it happens
Trigger: A hook bootstrap relative path containing '..' that climbs above the plugin root, or an absolute path passed where a relative subpath is expected.
Common situations: Plugin root env vars pointing at the wrong directory, a relocated plugin install, or adversarial hook configuration attempting to execute files outside the plugin.
Related errors
- Path traversal rejected: ${relPath}
- TypeScript compiler not found. Install root dev dependencies
- ${source} is missing the expected catalog marker
- Invalid ${flag}: expected a single cache path segment
- File path contains unsafe shell characters
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/73c7eb6dc896869f.
Report an issue: GitHub.