schollz/croc · error · Error

Remote path escapes the destination: ${value}

Error message

Remote path escapes the destination: ${value}

What it means

cleanSegments() splits a remote path on '/' (after converting backslashes) and throws if any segment is '..'. This blocks path-traversal: a peer-supplied path like '../../etc/passwd' must never escape the destination directory chosen by the receiver.

Source

Thrown at web/src/protocol/metadata.ts:15

import { base64ToBytes } from "./bytes";
import type {
  OfferedFile,
  SenderInfoWire,
  TransferOffer,
  WireFileInfo,
} from "./types";

function cleanSegments(value: string) {
  const replaced = value.replaceAll("\\", "/");
  if (replaced.includes("\0")) throw new Error("A remote path contains a null byte");
  const segments: string[] = [];
  for (const segment of replaced.split("/")) {
    if (segment === "" || segment === ".") continue;
    if (segment === "..") throw new Error(`Remote path escapes the destination: ${value}`);
    if ([...segment].some((character) => !/\P{C}/u.test(character))) {
      throw new Error(`Remote path contains a non-printable character: ${value}`);
    }
    segments.push(segment);
  }
  return segments;
}

export function normalizeFolder(value = ".") {
  if (/^(?:[a-zA-Z]:|\/)/.test(value)) {
    throw new Error(`Remote path must be relative: ${value}`);
  }
  const segments = cleanSegments(value);
  const normalized = segments.join("/") || ".";
  if (normalized.includes(".ssh")) {
    throw new Error(`Remote path is not allowed: ${value}`);
  }
  return normalized;

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. Refuse the transfer from the peer that sent the traversal payload; this is a security guard, not a recoverable condition.
  2. If you are sending, normalize folders to clean relative paths ('a/b', not 'a/../b') before constructing SenderInfoWire.
  3. In security tests, assert this exact error is thrown for traversal payloads — its absence is a vulnerability.
Defensive patterns

Strategy: validation

Validate before calling

function isSafeRelativePath(value: string): boolean {
  const segs = value.replaceAll("\\", "/").split("/");
  return !segs.includes("..");
}
// check before accepting an offer
if ([...(info.FilesToTransfer ?? []), ...(info.EmptyFoldersToTransfer ?? [])]
    .some((f) => !isSafeRelativePath(f.fr ?? "."))) {
  rejectOffer("traversal attempt");
}

Try / catch

try {
  const offer = validateSenderInfo(info);
} catch (error) {
  if (error instanceof Error && error.message.startsWith("Remote path escapes the destination")) {
    abortTransferAndWarnUser("peer attempted path traversal");
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: validateSenderInfo() processes an offer whose file folder/name or empty-folder entry contains a '..' segment after backslash normalization — e.g. fr='docs/../..' or n='a/../../x'.

Common situations: Hostile sender attempting directory traversal against the receiver's download directory; fuzz corpus containing traversal payloads; legitimate sender whose folder strings were built with '..' segments that were never normalized on the Go side (rare — croc normalizes before sending).

Related errors


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/00c5ac3f3a4cad1c. Report an issue: GitHub.