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
- Open the repo's skills folder in a browser and eyeball the file list for odd ../ style paths or unexpected files
- Do not install skills from the offending repo; report it if it came from a public listing
- Update the CLI so the path-prefix logic matches the current API shape
- 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
- Validate extracted/remote paths with a segment-based '..' check before resolving them onto disk
- After resolve(), additionally assert dest.startsWith(resolve(targetDir) + sep) for defense in depth
- Only install skills from repos you trust; treat this warn as evidence of tampering, not noise
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
- Skill file path "${file.path}" resolves outside the target d
- Skill name "${skillName}" escapes the skills root
- Failed to download ${skill.name}: ${downloadData.error}
- Failed to install ${skill.name}: ${errMsg}
- This skill contains potentially malicious content and cannot
AI-assisted analysis of upstash/context7@5284672feb (2026-08-18).
Data as JSON: /api/errors/4e092c931b1a1820.
Report an issue: GitHub.