upstash/context7 · warning

Skipping file with unsafe path: ${item.path}

Error message

Skipping file with unsafe path: ${item.path}

What it means

Defensive path-traversal guard in the GitHub file downloader: after stripping the skillPath prefix from each fetched file's path, the resulting relativePath is rejected if it contains '..'. The file is skipped with a console.warn rather than written, preventing a malicious or corrupted repo tree entry from writing outside the destination skills directory (e.g. ../../.bashrc). Git itself normally forbids '..' in tree paths, so seeing this warn usually means the API response was tampered with, a proxy mangled it, or the skillPath prefix no longer matches item.path after upstream restructure.

Source

Thrown at packages/cli/src/utils/github.ts:295

    return { files: [], error: `No files found in ${skillPath}` };
  }

  const files: SkillFile[] = [];
  for (const item of skillFiles) {
    const rawUrl = `${GITHUB_RAW}/${owner}/${repo}/${branch}/${item.path}`;
    const fileResponse = await fetch(rawUrl, { headers: ghHeaders });

    if (!fileResponse.ok) {
      console.warn(`Failed to fetch ${item.path}: ${fileResponse.status}`);
      continue;
    }

    const content = await fileResponse.text();
    const relativePath = item.path.slice(skillPath.length + 1);

    // Reject paths that attempt directory traversal
    if (relativePath.includes("..")) {
      console.warn(`Skipping file with unsafe path: ${item.path}`);
      continue;
    }

    files.push({
      path: relativePath,
      content,
    });
  }

  return { files };
}

async function downloadSingleSkillFile(
  skillUrl: string,
  ghHeaders: Record<string, string>
): Promise<SkillFile[] | null> {
  let fileName: string;
  try {

View on GitHub (pinned to 5284672feb)

Solutions

  1. Open the repo's skills folder in a browser and eyeball the file list for odd ../ style paths or unexpected files
  2. Do not install skills from the offending repo; report it if it came from a public listing
  3. Update the CLI so the path-prefix logic matches the current API shape
  4. If behind an intercepting proxy, compare the raw API response with and without the proxy

Example fix

// before
const relativePath = item.path.slice(skillPath.length + 1);
if (relativePath.includes("..")) {
  console.warn(`Skipping file with unsafe path: ${item.path}`);
  continue;
}

// after — strict segment check + containment assert
const relativePath = item.path.slice(skillPath.length + 1);
const isSafe = relativePath.split("/").every((seg) => seg !== ".." && seg.length > 0);
const dest = resolve(targetDir, relativePath);
if (!isSafe || !dest.startsWith(resolve(targetDir) + sep)) {
  console.warn(`Skipping file with unsafe path: ${item.path}`);
  continue;
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Reject unsafe relative paths BEFORE any filesystem write
function isSafeRelativePath(rel: string): boolean {
  if (rel.includes("..")) return false;
  if (rel.startsWith("/") || /^[a-zA-Z]:/.test(rel)) return false; // no absolute/Windows roots
  return rel.split("/").every((seg) => seg.length > 0 && seg !== ".");
}
const safe = isSafeRelativePath(relativePath);
if (!safe) {
  console.warn(`Skipping file with unsafe path: ${item.path}`);
  continue;
}

Type guard

function isSafeSkillFilePath(item: { path: string }, skillPath: string): boolean {
  if (!item.path.startsWith(skillPath + "/")) return false; // must live under skillPath
  const rel = item.path.slice(skillPath.length + 1);
  const segments = rel.split("/");
  return segments.length > 0 && segments.every((s) => s !== ".." && s !== "." && s.length > 0);
}

Prevention

When it happens

Trigger: Installing a skill from a repo whose GitHub tree API response contains crafted paths escaping the skills folder; an intercepting corporate proxy rewriting response bodies; a CLI/skillPath version mismatch where slice() produces a mangled relative path.

Common situations: Installing skills from untrusted or hijacked repos; security tooling fuzzing the installer; virtually never seen with legitimate upstream repos — treat it as a red flag about the repo or the transport.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of upstash/context7@5284672feb (2026-08-18). Data as JSON: /api/errors/4e092c931b1a1820. Report an issue: GitHub.