{"record":{"id":"752da7fb4a0b8650","repo":"google-gemini/gemini-cli","slug":"invalid-skill-name-path-traversal-detected","errorCode":null,"errorMessage":"Invalid skill name: Path traversal detected.","messagePattern":"Invalid skill name: Path traversal detected\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/skillUtils.ts","lineNumber":187,"sourceCode":"        : Storage.getUserSkillsDir();\n\n    if (!(await requestConsent(skills, targetDir))) {\n      throw new Error('Skill installation cancelled by user.');\n    }\n\n    const resolvedTarget = path.resolve(targetDir);\n    await fs.mkdir(resolvedTarget, { recursive: true });\n\n    const installedSkills: Array<{ name: string; location: string }> = [];\n\n    for (const skill of skills) {\n      const skillName = skill.name;\n      const skillDir = path.dirname(skill.location);\n      const destPath = path.resolve(resolvedTarget, skillName);\n\n      const relative = path.relative(resolvedTarget, destPath);\n      if (isInvalidSubpath(relative)) {\n        throw new Error('Invalid skill name: Path traversal detected.');\n      }\n\n      const exists = await fs.lstat(destPath).catch(() => null);\n      if (exists) {\n        onLog(`Skill \"${skillName}\" already exists. Overwriting...`);\n        await fs.rm(destPath, { recursive: true, force: true });\n      }\n\n      await fs.cp(skillDir, destPath, { recursive: true });\n      installedSkills.push({ name: skillName, location: destPath });\n    }\n\n    return installedSkills;\n  } finally {\n    if (tempDirToClean) {\n      await fs.rm(tempDirToClean, { recursive: true, force: true });\n    }\n  }","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/utils/skillUtils.ts#L169-L205","documentation":"Security guard thrown during skill installation when a skill's name resolves to a destination path outside the target skills directory (path traversal via the skill name). isInvalidSubpath returns true if the relative path is empty, equals '..', starts with '../', or is absolute — blocking names like '../esc', '/abs', or names that resolve to the target dir itself.","triggerScenarios":"skill.name contains traversal characters (e.g. '../foo') or is absolute, so path.relative(resolvedTarget, destPath) is flagged by isInvalidSubpath before the copy. Also triggers if skill.name resolves exactly to resolvedTarget (relative === '').","commonSituations":"A malicious SKILL.md declares a name with '../' to write outside the skills dir. A skill name that is an absolute path. A skill whose name, after sanitization, collapses to the target directory. Third-party skill source with crafted frontmatter.","solutions":["Audit the SKILL.md frontmatter `name` field for traversal characters or absolute paths and correct it.","Only install skills from trusted sources; treat a traversal name as a red flag.","Sanitize/normalize skill names before publishing so they are simple slugs."],"exampleFix":"// before: SKILL.md frontmatter\n// ---\n// name: ../escape\n// ---\n\n// after\n// ---\n// name: my-skill\n// ---","handlingStrategy":"validation","validationCode":"const path = require('path');\nfunction assertValidSkillName(name, targetDir) {\n  const rel = path.relative(path.resolve(targetDir), path.resolve(targetDir, name));\n  if (rel === '' || rel === '..' || rel.startsWith('..' + path.sep) || path.isAbsolute(rel)) {\n    throw new Error(`Skill name '${name}' is invalid (traversal or empty)`);\n  }\n}","typeGuard":"const isSafeSkillName = (name, targetDir) => {\n  const rel = path.relative(path.resolve(targetDir), path.resolve(targetDir, name));\n  return rel !== '' && rel !== '..' && !rel.startsWith('..' + path.sep) && !path.isAbsolute(rel);\n};","tryCatchPattern":null,"preventionTips":["Constrain skill names to a slug pattern (^[a-z0-9][a-z0-9-]*$) at authoring time.","Audit third-party SKILL.md frontmatter before installing."],"tags":["skill","security","path-traversal","validation","installation","frontmatter"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}