chatboxai/chatbox · error · Error

Invalid script name: path traversal not allowed

Error message

Invalid script name: path traversal not allowed

What it means

Thrown by the skills:execute-script IPC handler when scriptName contains '..', '/', or '\\'. Mirror of the skillName guard (104), this prevents the script filename segment from escaping the scripts/ subdirectory. Together with 104 and the realpath containment check (107), it forms the layered defense against arbitrary file execution.

Source

Thrown at src/main/skills/ipc-handlers.ts:182

  ipcMain.handle(
    'skills:execute-script',
    async (
      _event,
      params: { skillName: string; scriptName: string; args?: string[] }
    ): Promise<{ success: boolean; stdout: string; stderr: string; exitCode: number | null }> => {
      const { skillName, scriptName, args = [] } = params

      try {
        if (!skillName || !scriptName) {
          throw new Error('Skill name and script name are required')
        }

        if (skillName.includes('..') || skillName.includes('/') || skillName.includes('\\')) {
          throw new Error('Invalid skill name: path traversal not allowed')
        }

        if (scriptName.includes('..') || scriptName.includes('/') || scriptName.includes('\\')) {
          throw new Error('Invalid script name: path traversal not allowed')
        }

        const skillsDir = getSkillsDir()
        const scriptPath = path.join(skillsDir, skillName, 'scripts', scriptName)
        if (!fs.existsSync(scriptPath)) {
          throw new Error(`Script not found: ${scriptName}`)
        }
        const resolvedSkillsDir = fs.realpathSync(skillsDir)
        const resolvedScriptPath = fs.realpathSync(scriptPath)
        if (!resolvedScriptPath.startsWith(`${resolvedSkillsDir}${path.sep}`)) {
          throw new Error('Script path escapes skills directory')
        }

        const scriptDir = path.dirname(resolvedScriptPath)

        return await new Promise((resolve) => {
          const TIMEOUT_MS = 30_000
          let stdout = ''

View on GitHub (pinned to 81571269ad)

Solutions

  1. Keep all executable scripts directly under skills/{name}/scripts/ with flat filenames.
  2. Validate scriptName in the renderer with the same strict allowlist as skillName.
  3. If subdirectories are required, enumerate scripts at install time and pass an index, not a path.

Example fix

// before
if (scriptName.includes('..') || scriptName.includes('/') || scriptName.includes('\\')) {
  throw new Error('Invalid script name: path traversal not allowed')
}

// after — reuse a shared strict identifier validator
const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/
if (!SAFE_NAME.test(scriptName)) {
  throw new Error(`Invalid script name: ${scriptName}`)
}
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/
function isSafeScriptName(name: string): boolean {
  return SAFE_NAME.test(name)
}
if (!isSafeScriptName(scriptName)) {
  throw new Error(`Invalid script name: ${scriptName}`)
}

Type guard

function isSafeName(name: unknown): name is string {
  return typeof name === 'string' && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name)
}

Try / catch

const result = await ipcRenderer.invoke('skills:execute-script', params)
if (!result.success && /path traversal/i.test(result.stderr)) {
  showToast('Script name contains invalid characters')
}

Prevention

When it happens

Trigger: An IPC payload sets scriptName to '../foo.sh', 'subdir/run.sh', or '..\\evil.bat'. path.join(skillsDir, skillName, 'scripts', scriptName) would resolve these to a path outside the intended scripts directory.

Common situations: A skill organizes scripts in subdirectories and the caller passes a relative path; a malicious payload attempts to execute a sibling file; buggy construction of scriptName from a UI list that includes directory prefixes.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/bf57e6fd4efd7bd0. Report an issue: GitHub.