{"record":{"id":"f595f1fec7d60138","repo":"usebruno/bruno","slug":"invalid-file-name","errorCode":null,"errorMessage":"Invalid file name","messagePattern":"Invalid file name","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/bruno-electron/src/ipc/collection.js","lineNumber":2637,"sourceCode":"      });\n\n      return { success: true, filePath };\n    } catch (error) {\n      throw error;\n    }\n  });\n\n  ipcMain.handle('renderer:export-collection-postman', async (event, dirPath, fileName, content, overwrite = false) => {\n    try {\n      if (!dirPath || !fs.existsSync(dirPath)) {\n        throw new Error('Export location does not exist');\n      }\n\n      // ensure the resolved path is inside the export directory\n      const resolvedDir = path.resolve(dirPath);\n      const filePath = path.resolve(resolvedDir, fileName);\n      if (!filePath.startsWith(resolvedDir + path.sep) && filePath !== resolvedDir) {\n        throw new Error('Invalid file name');\n      }\n\n      if (!overwrite && fs.existsSync(filePath)) {\n        throw new Error(`path: ${filePath} already exists`);\n      }\n\n      await writeFile(filePath, content);\n\n      return { success: true, filePath };\n    } catch (error) {\n      return Promise.reject(error);\n    }\n  });\n\n  ipcMain.handle('renderer:is-bruno-collection-zip', async (event, zipFilePath) => {\n    try {\n      const zip = new AdmZip(zipFilePath);\n      const entries = zip.getEntries().map((e) => e.entryName);","sourceCodeStart":2619,"sourceCodeEnd":2655,"githubUrl":"https://github.com/usebruno/bruno/blob/9bdd81c7bdc57006e5f5ebffb79321a8d979f712/packages/bruno-electron/src/ipc/collection.js#L2619-L2655","documentation":"Path-traversal guard in renderer:export-collection-postman. After resolving dirPath + fileName, it verifies the resulting filePath starts with resolvedDir + path.sep (or equals it). Any fileName containing `../`, drive letters, or absolute paths that escapes resolvedDir triggers this throw.","triggerScenarios":"Passing a fileName containing path separators or `..` segments so that path.resolve(resolvedDir, fileName) escapes resolvedDir.","commonSituations":"Malicious or malformed filename input (`../../../../etc/passwd`, absolute `/tmp/x`); user-typed names with slashes; conversion output with embedded path separators.","solutions":["Sanitize fileName with path.basename(fileName) before passing it.","Reject any fileName containing path separators or null bytes on the renderer side.","If subdirectories are legitimately needed, explicit opt-in with allowlist."],"exampleFix":"// before\nipcRenderer.invoke('renderer:export-collection-postman', dir, fileName, content);\n\n// after\nconst safeName = path.basename(fileName).replace(/[\\x00]/g, '');\nif (!safeName) throw new Error('Invalid file name');\nipcRenderer.invoke('renderer:export-collection-postman', dir, safeName, content);","handlingStrategy":"validation","validationCode":"const path = require('path');\nconst safe = path.basename(fileName).replace(/[\\x00/\\\\]/g, '');\nif (!safe) throw new Error('Invalid file name');\n// pass `safe` instead of fileName","typeGuard":null,"tryCatchPattern":"try {\n  await ipcRenderer.invoke('renderer:export-collection-postman', dirPath, safeName, content, overwrite);\n} catch (err) {\n  if (/Invalid file name/.test(err.message)) {\n    // sanitize and retry with path.basename\n  } else {\n    throw err;\n  }\n}","preventionTips":["Always pass path.basename(fileName) — never a raw user-typed string with separators.","Reject filenames containing null bytes or path separators on the renderer side.","Treat this error as a possible attack indicator; log and audit when it fires."],"tags":["security","path-traversal","ipc","export","postman","validation"],"backgroundTag":null,"analyzedSha":"9bdd81c7bdc57006e5f5ebffb79321a8d979f712","analyzedAt":"2026-08-13T04:09:25.751Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}