{"record":{"id":"d3d6803fccb2ee26","repo":"google-gemini/gemini-cli","slug":"invalid-path-directory-traversal-not-allowed","errorCode":null,"errorMessage":"Invalid path: Directory traversal not allowed.","messagePattern":"Invalid path: Directory traversal not allowed\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/skillUtils.ts","lineNumber":151,"sourceCode":"      sourcePath = tempDirToClean;\n\n      onLog(`Extracting skill from ${source}...`);\n      await extract(path.resolve(source), { dir: tempDirToClean });\n    }\n\n    // If a subpath is provided, resolve it against the cloned/local root.\n    if (subpath) {\n      sourcePath = path.join(sourcePath, subpath);\n    }\n\n    sourcePath = path.resolve(sourcePath);\n\n    // Quick security check to prevent directory traversal out of temp dir when cloning\n    if (tempDirToClean) {\n      const resolvedTemp = path.resolve(tempDirToClean);\n      const relative = path.relative(resolvedTemp, sourcePath);\n      if (isPathTraversal(relative)) {\n        throw new Error('Invalid path: Directory traversal not allowed.');\n      }\n    }\n\n    onLog(`Searching for skills in ${sourcePath}...`);\n    const skills = await loadSkillsFromDir(sourcePath);\n\n    if (skills.length === 0) {\n      throw new Error(\n        `No valid skills found in ${source}${subpath ? ` at path \"${subpath}\"` : ''}. Ensure a SKILL.md file exists with valid frontmatter.`,\n      );\n    }\n\n    const workspaceDir = process.cwd();\n    const storage = new Storage(workspaceDir);\n    const targetDir =\n      scope === 'workspace'\n        ? storage.getProjectSkillsDir()\n        : Storage.getUserSkillsDir();","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/utils/skillUtils.ts#L133-L169","documentation":"Security guard thrown during skill installation from a remote/local source when resolving the source path against the temp clone directory would escape it (directory traversal). isPathTraversal returns true if the relative path is '..', starts with '../', or is absolute — preventing a malicious subpath argument from reading outside the temp dir.","triggerScenarios":"A `subpath` argument like '../../../etc' or an absolute path is supplied when installing a skill from a source that was cloned into tempDirToClean, making path.relative(tempDir, sourcePath) escape upward.","commonSituations":"User passes a subpath with leading '../'. A skill registry/URL serves a SKILL.md whose declared paths attempt traversal. Absolute subpath supplied. Misconfigured source URL whose repo layout differs from expectations.","solutions":["Use a subpath that stays within the skill source root (no '../', no leading '/').","If installing from a local dir, point `source` directly at the skill folder instead of using a subpath escape.","Audit any third-party skill source for malicious path declarations before installing."],"exampleFix":"// before\n// installSkill({ source: 'https://github.com/x/y', subpath: '../../../etc' })\n\n// after\n// installSkill({ source: 'https://github.com/x/y', subpath: 'skills/my-skill' })","handlingStrategy":"validation","validationCode":"const path = require('path');\nfunction assertNoTraversal(baseDir, target) {\n  const rel = path.relative(path.resolve(baseDir), path.resolve(baseDir, target));\n  if (rel === '..' || rel.startsWith('..' + path.sep) || path.isAbsolute(rel)) {\n    throw new Error('Subpath escapes the skill source root');\n  }\n}","typeGuard":"const isSafeSubpath = (baseDir, sub) => {\n  const rel = path.relative(path.resolve(baseDir), path.resolve(baseDir, sub));\n  return rel !== '..' && !rel.startsWith('..' + path.sep) && !path.isAbsolute(rel);\n};","tryCatchPattern":null,"preventionTips":["Reject subpaths containing '..' or leading '/' at the input boundary.","Treat third-party skill sources as untrusted until audited."],"tags":["skill","security","path-traversal","validation","installation"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}