(userProvidedPath: string | undefined)
| 318 | * @throws {Error} If the path is missing, contains traversal patterns, or is outside allowed directories |
| 319 | */ |
| 320 | export const validateSQLitePath = (userProvidedPath: string | undefined): string => { |
| 321 | const allowedDirs = getAllowedSQLiteBaseDirs() |
| 322 | const defaultDir = allowedDirs[0] |
| 323 | |
| 324 | if (process.env.PATH_TRAVERSAL_SAFETY === 'false') { |
| 325 | if (!userProvidedPath || userProvidedPath.trim() === '') { |
| 326 | return path.join(defaultDir, 'database.sqlite') |
| 327 | } |
| 328 | const bypassPath = userProvidedPath.trim() |
| 329 | return path.isAbsolute(bypassPath) ? bypassPath : path.resolve(path.join(defaultDir, bypassPath)) |
| 330 | } |
| 331 | |
| 332 | if (!userProvidedPath || userProvidedPath.trim() === '') { |
| 333 | throw new Error('Invalid SQLite path: database path is required') |
| 334 | } |
| 335 | |
| 336 | const basePath = userProvidedPath.trim() |
| 337 | |
| 338 | if (basePath.includes('..')) throw new Error('Invalid SQLite path: path traversal attempt detected') |
| 339 | if (basePath.toLowerCase().includes('%2e') || basePath.toLowerCase().includes('%2f') || basePath.toLowerCase().includes('%5c')) |
| 340 | throw new Error('Invalid SQLite path: encoded path traversal attempt detected') |
| 341 | // eslint-disable-next-line no-control-regex |
| 342 | if (/\0/.test(basePath) || /[\x00-\x1f]/.test(basePath)) |
| 343 | throw new Error('Invalid SQLite path: null bytes or control characters detected') |
| 344 | if (/^[a-zA-Z]:\\/.test(basePath)) throw new Error('Invalid SQLite path: Windows absolute paths are not allowed') |
| 345 | if (/^\\\\[^\\]/.test(basePath)) throw new Error('Invalid SQLite path: UNC paths are not allowed') |
| 346 | if (/^\\\\\?\\/.test(basePath)) throw new Error('Invalid SQLite path: extended-length paths are not allowed') |
| 347 | |
| 348 | const resolvedPath = path.isAbsolute(basePath) ? path.resolve(basePath) : path.resolve(path.join(defaultDir, basePath)) |
| 349 | |
| 350 | if (resolvedPath.includes('..')) throw new Error('Invalid SQLite path: path traversal detected in resolved path') |
| 351 | |
| 352 | if (!isPathWithinAllowedSQLiteDirs(resolvedPath, allowedDirs)) { |
| 353 | throw new Error( |
| 354 | `Invalid SQLite path: path must be within allowed directories (${allowedDirs.join(', ')}). Attempted path: ${resolvedPath}` |
| 355 | ) |
| 356 | } |
| 357 | |
| 358 | return resolvedPath |
| 359 | } |
| 360 | |
| 361 | /** |
| 362 | * Sanitize a file name to prevent path traversal attacks. |
no test coverage detected