affaan-m/ECC · error · Error

Refusing to ${action}: missing destination path.

Error message

Refusing to ${action}: missing destination path.

What it means

Thrown by assertWithinTrustedRoot(target, root, action) in scripts/lib/path-safety.js when `target` is falsy or not a string. This is a fail-closed path-containment guard: before any write/delete/read operation replayed from install state, the destination path must be a concrete string so it can be canonicalized and confined to the trusted root (GHSA-hfpv-w6mp-5g95).

Source

Thrown at scripts/lib/path-safety.js:87

function isWithinRoot(target, root) {
  if (!root) {
    return false;
  }

  try {
    return resolveContainment(target, root).contained;
  } catch {
    return false;
  }
}

/**
 * Fail-closed guard: throw unless `target` is contained within `root`.
 * Returns the canonicalized target path on success.
 */
function assertWithinTrustedRoot(target, root, action = 'write') {
  if (!target || typeof target !== 'string') {
    throw new Error(`Refusing to ${action}: missing destination path.`);
  }
  if (!root) {
    throw new Error(`Refusing to ${action} '${target}': no trusted install root resolved.`);
  }

  let containment;
  try {
    containment = resolveContainment(target, root);
  } catch {
    containment = null;
  }
  if (!containment || !containment.contained) {
    throw new Error(`Refusing to ${action} outside the install root: '${target}' is not within '${root}'.`);
  }
  return containment.realTarget;
}

module.exports = {

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Ensure the operation record always carries a destinationPath string before replay.
  2. Skip state entries whose path is missing rather than passing them to the guard.
  3. Validate the install-state file shape at load time and drop malformed entries.
  4. When building the target yourself, always call path.resolve on a real string.

Example fix

// before
const safe = assertWithinTrustedRoot(op.dest, trustedRoot, 'write'); // op.dest undefined

// after
if (!op || typeof op.dest !== 'string' || op.dest.length === 0) continue;
const safe = assertWithinTrustedRoot(op.dest, trustedRoot, 'write');
Defensive patterns

Strategy: validation

Validate before calling

if (!target || typeof target !== 'string') {
  throw new Error('destination path required for ' + action);
}
assertWithinTrustedRoot(target, root, action);

Type guard

function isNonEmptyPath(value) {
  return typeof value === 'string' && value.length > 0;
}

Prevention

When it happens

Trigger: Calling assertWithinTrustedRoot(undefined, root, 'write'), assertWithinTrustedRoot('', root), assertWithinTrustedRoot(null, root), or passing a non-string destination read from a malformed install-state file.

Common situations: An install-state JSON entry missing a destinationPath key; a repair/uninstall loop replaying a recorded operation whose path field was never persisted; a refactor that passes a path object instead of a string; a symlink target that resolved to empty.

Related errors


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