different-ai/openwork · error
Archive contains an unsafe path
Error message
Archive contains an unsafe path
What it means
importWorkspaceConfig imports a workspace config ZIP and extracts only safe entries. Before writing any file it runs isSafeArchivePath, which rejects absolute paths, Windows drive letters, and any segment that normalizes to '..' or an empty part. This is a zip-slip guard: it prevents a crafted archive from writing files outside targetDir via traversal paths.
Source
Thrown at apps/desktop/electron/workspace-archive.mjs:299
excluded,
}, null, 2)}\n`,
});
await writeZip(outputPath, files);
return { outputPath, included: included.length, excluded };
}
export async function importWorkspaceConfig({ archivePath, targetDir, name }) {
if (await pathExists(targetDir)) {
if (!(await isDirectoryEmpty(targetDir))) throw new Error("Target folder must be empty");
}
await mkdir(targetDir, { recursive: true });
const archiveStats = await stat(archivePath);
if (archiveStats.size > MAX_ARCHIVE_BYTES) throw new Error("Workspace archive is too large.");
const buffer = await readFile(archivePath);
for (const entry of listZipEntries(buffer)) {
if (entry.name === "manifest.json" || entry.name.endsWith("/")) continue;
if (!isSafeArchivePath(entry.name)) throw new Error("Archive contains an unsafe path");
if (!(entry.name === "opencode.json" || entry.name.startsWith(".opencode/"))) continue;
if (isSecretName(path.basename(entry.name))) continue;
const outPath = path.join(targetDir, ...entry.name.split("/"));
await mkdir(path.dirname(outPath), { recursive: true });
await writeFile(outPath, readZipEntryData(buffer, entry));
}
const opencodeDir = path.join(targetDir, ".opencode");
if (!(await pathExists(opencodeDir))) throw new Error("Archive is missing .opencode config");
const openworkPath = path.join(opencodeDir, "openwork.json");
let preset = "starter";
let workspaceName = typeof name === "string" && name.trim() ? name.trim() : null;
if (await pathExists(openworkPath)) {
const raw = await readFile(openworkPath, "utf8");
try {
const config = JSON.parse(raw);View on GitHub (pinned to 2b7df46e8a)
Solutions
- Open the archive and inspect entry names for '..', leading '/', or 'X:' prefixes; rebuild the archive with plain relative paths (e.g. '.opencode/agents/foo.md').
- Re-export the archive with exportWorkspaceConfig from a working workspace, which always emits safe relative names.
- If a tool keeps producing backslash names, normalize them to '/' before zipping — ZIP entry names must use forward slashes.
Example fix
// before: entry name '../../evil.sh' aborts the whole import // after: repack the archive with safe relative entry names zip -r workspace.zip manifest.json opencode.json .opencode/
Defensive patterns
Strategy: validation
Validate before calling
import { listZipEntries } from "./workspace-archive.mjs";
function assertSafeArchive(buffer) {
for (const e of listZipEntries(buffer)) {
if (e.name === "manifest.json" || e.name.endsWith("/")) continue;
if (e.name.startsWith("/") || /^[A-Za-z]:/.test(e.name) ||
e.name.split("/").some((p) => p === ".." || p === "")) {
throw new Error(`Unsafe archive entry: ${e.name}`);
}
}
} Type guard
function isSafeArchivePath(name) {
if (!name || name.startsWith("/") || /^[A-Za-z]:/.test(name)) return false;
const parts = name.split("/");
return !parts.some((p) => p === ".." || p === "");
} Try / catch
try {
await importWorkspaceConfig({ archivePath, targetDir });
} catch (err) {
if (err.message === "Archive contains an unsafe path") {
// reject the archive; do not retry, it is malicious or corrupt
} else throw err;
} Prevention
- Only import archives produced by exportWorkspaceConfig or a trusted source.
- Pre-scan ZIP entry names before import and reject any with '..' or absolute segments.
- Keep targetDir empty and dedicated so a bad import cannot clobber other files.
When it happens
Trigger: Calling importWorkspaceConfig({ archivePath, targetDir }) with a ZIP whose entry names contain '..' segments (e.g. '../../.bashrc'), start with '/', or look like 'C:\evil'.
Common situations: Importing an archive edited or rebuilt by a third-party tool that stores backslash or drive-letter paths; a shared/hand-modified archive; a malicious archive from an untrusted source.
Related errors
- path_escape
- path_escape
- External URL protocol "${parsed.protocol}" is not allowed.
- invalid_payload
- Attachment hostname ${hostname} resolved to a private or res
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/19e3c14f242384bf.
Report an issue: GitHub.