(dirPath: string)
| 156 | * @returns An error message if invalid, or null if valid |
| 157 | */ |
| 158 | export function validateProjectPath(dirPath: string): string | null { |
| 159 | const resolved = path.resolve(dirPath); |
| 160 | |
| 161 | // Block sensitive system directories |
| 162 | if (SENSITIVE_PATHS.has(resolved) || SENSITIVE_PATHS.has(resolved.toLowerCase())) { |
| 163 | return `Refusing to operate on sensitive system directory: ${resolved}`; |
| 164 | } |
| 165 | |
| 166 | // Also block common sensitive home subdirectories |
| 167 | const homeDir = require('os').homedir(); |
| 168 | const sensitiveHomeDirs = ['.ssh', '.gnupg', '.aws', '.config']; |
| 169 | for (const dir of sensitiveHomeDirs) { |
| 170 | const sensitivePath = path.join(homeDir, dir); |
| 171 | if (resolved === sensitivePath || resolved.startsWith(sensitivePath + path.sep)) { |
| 172 | return `Refusing to operate on sensitive directory: ${resolved}`; |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | // Verify it's a real directory |
| 177 | try { |
| 178 | const stats = fs.statSync(resolved); |
| 179 | if (!stats.isDirectory()) { |
| 180 | return `Path is not a directory: ${resolved}`; |
| 181 | } |
| 182 | } catch { |
| 183 | return `Path does not exist or is not accessible: ${resolved}`; |
| 184 | } |
| 185 | |
| 186 | return null; |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | * Safely parse JSON with a fallback value. |
no test coverage detected