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

  1. Point the plugin root env var at the actual installed ECC plugin directory
  2. Pass only relative paths that stay inside the plugin tree
  3. 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

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


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/73c7eb6dc896869f. Report an issue: GitHub.