{"record":{"id":"6d9f53ef2635a447","repo":"denoland/deno","slug":"refusing-to-write-tarball-with-unsafe-name-derived","errorCode":null,"errorMessage":"refusing to write tarball with unsafe name derived from package: {}","messagePattern":"refusing to write tarball with unsafe name derived from package: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/pack/npm_tarball.rs","lineNumber":37,"sourceCode":"pub fn default_tarball_filename(\n  config_file: &ConfigFile,\n  version: &str,\n) -> Result<PathBuf, AnyError> {\n  let name = config_file\n    .json\n    .name\n    .as_ref()\n    .ok_or_else(|| deno_core::anyhow::anyhow!(\"Missing name\"))?;\n  // Convert @scope/name to scope-name\n  let normalized = name.replace('@', \"\").replace('/', \"-\");\n  // The package name shape is checked against `@scope/name` higher up\n  // (see `pack` in mod.rs), but that check is loose — it does not\n  // forbid path-traversal sequences. Treat this as a hard safety\n  // boundary right before we open a file, rejecting any derived\n  // tarball name that contains `..` or path separators so we never\n  // escape the cwd regardless of upstream validation drift.\n  if normalized.contains(\"..\") || normalized.contains('/') {\n    return Err(deno_core::anyhow::anyhow!(\n      \"refusing to write tarball with unsafe name derived from package: {}\",\n      name\n    ));\n  }\n  Ok(PathBuf::from(format!(\"{}-{}.tgz\", normalized, version)))\n}\n\n/// Tar archive paths must use forward slashes, even on Windows. Output paths\n/// are computed with platform separators when they pass through `Path::display`,\n/// so normalize before writing the tar header. The replace is unconditional, so\n/// on POSIX — where a backslash is a legal filename character — it can split one\n/// legal name into several archive segments. `validate_tar_path` runs on the\n/// result to catch any `..` this introduces.\nfn to_tar_path(relative: &str) -> String {\n  relative.replace('\\\\', \"/\")\n}\n\n/// Reject archive paths that could escape the extraction root.","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/tools/pack/npm_tarball.rs#L19-L55","documentation":"A deliberate safety boundary in `deno pack`: after normalizing the package name (stripping '@' and replacing '/' with '-') the derived tarball filename is rejected if it still contains '..' or '/'. This blocks path traversal — a crafted name like \"../evil\" would otherwise make pack write outside the working directory. The upstream name-shape check is intentionally not trusted at this boundary.","triggerScenarios":"`deno pack` against a deno.json whose \"name\" contains a '..' sequence after normalization (e.g. \"../evil\" becomes \"..-evil\"), or any name containing '/'. Typically a tampered or malicious third-party config rather than a typo.","commonSituations":"Packing a cloned repo with a modified deno.json; CI building packages from untrusted sources; security reviews exercising the guard.","solutions":["Inspect \"name\" in deno.json and remove any '..' sequence; use a normal (possibly scoped) package name","Do not weaken the guard — rename the package instead","If the config came from elsewhere, treat it as malicious and audit the repo before packing again"],"exampleFix":"// deno.json before\n{ \"name\": \"../evil\", \"version\": \"1.0.0\" }\n// after\n{ \"name\": \"@scope/evil\", \"version\": \"1.0.0\" }","handlingStrategy":"validation","validationCode":"// reject unsafe names before packing\ndeno eval 'const n = (JSON.parse(Deno.readTextSync(\"deno.json\")).name ?? \"\").replaceAll(\"@\", \"\").replaceAll(\"/\", \"-\"); if (n.includes(\"..\") || n.includes(\"/\")) throw new Error(\"unsafe package name: \" + n);'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Audit deno.json name fields in CI for untrusted checkouts","Never pack repositories you have not reviewed","Keep package names to npm-legal shapes (lowercase, no dot-dot, no path separators)"],"tags":["pack","security","path-traversal","deno-json"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}