{"record":{"id":"bf57e6fd4efd7bd0","repo":"chatboxai/chatbox","slug":"invalid-script-name-path-traversal-not-allowed","errorCode":null,"errorMessage":"Invalid script name: path traversal not allowed","messagePattern":"Invalid script name: path traversal not allowed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/skills/ipc-handlers.ts","lineNumber":182,"sourceCode":"  ipcMain.handle(\n    'skills:execute-script',\n    async (\n      _event,\n      params: { skillName: string; scriptName: string; args?: string[] }\n    ): Promise<{ success: boolean; stdout: string; stderr: string; exitCode: number | null }> => {\n      const { skillName, scriptName, args = [] } = params\n\n      try {\n        if (!skillName || !scriptName) {\n          throw new Error('Skill name and script name are required')\n        }\n\n        if (skillName.includes('..') || skillName.includes('/') || skillName.includes('\\\\')) {\n          throw new Error('Invalid skill name: path traversal not allowed')\n        }\n\n        if (scriptName.includes('..') || scriptName.includes('/') || scriptName.includes('\\\\')) {\n          throw new Error('Invalid script name: path traversal not allowed')\n        }\n\n        const skillsDir = getSkillsDir()\n        const scriptPath = path.join(skillsDir, skillName, 'scripts', scriptName)\n        if (!fs.existsSync(scriptPath)) {\n          throw new Error(`Script not found: ${scriptName}`)\n        }\n        const resolvedSkillsDir = fs.realpathSync(skillsDir)\n        const resolvedScriptPath = fs.realpathSync(scriptPath)\n        if (!resolvedScriptPath.startsWith(`${resolvedSkillsDir}${path.sep}`)) {\n          throw new Error('Script path escapes skills directory')\n        }\n\n        const scriptDir = path.dirname(resolvedScriptPath)\n\n        return await new Promise((resolve) => {\n          const TIMEOUT_MS = 30_000\n          let stdout = ''","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/skills/ipc-handlers.ts#L164-L200","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Keep all executable scripts directly under skills/{name}/scripts/ with flat filenames.","Validate scriptName in the renderer with the same strict allowlist as skillName.","If subdirectories are required, enumerate scripts at install time and pass an index, not a path."],"exampleFix":"// before\nif (scriptName.includes('..') || scriptName.includes('/') || scriptName.includes('\\\\')) {\n  throw new Error('Invalid script name: path traversal not allowed')\n}\n\n// after — reuse a shared strict identifier validator\nconst SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/\nif (!SAFE_NAME.test(scriptName)) {\n  throw new Error(`Invalid script name: ${scriptName}`)\n}","handlingStrategy":"validation","validationCode":"const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/\nfunction isSafeScriptName(name: string): boolean {\n  return SAFE_NAME.test(name)\n}\nif (!isSafeScriptName(scriptName)) {\n  throw new Error(`Invalid script name: ${scriptName}`)\n}","typeGuard":"function isSafeName(name: unknown): name is string {\n  return typeof name === 'string' && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name)\n}","tryCatchPattern":"const result = await ipcRenderer.invoke('skills:execute-script', params)\nif (!result.success && /path traversal/i.test(result.stderr)) {\n  showToast('Script name contains invalid characters')\n}","preventionTips":["Keep scripts flat under scripts/ — no subdirectories in scriptName.","Validate scriptName with the same strict allowlist as skillName.","When listing scripts for the UI, filter out anything failing the allowlist so it can never be selected."],"tags":["security","path-traversal","ipc","skills","validation"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}