(file)
| 348 | // Returns the id on a clean read, null on any error or missing file. |
| 349 | // Symlink rejection matches the pre-keychain hardening from PR #109. |
| 350 | function _readWorkspaceIdFromFs(file) { |
| 351 | const dir = path.dirname(file); |
| 352 | try { |
| 353 | const dirStat = fs.lstatSync(dir, { throwIfNoEntry: false }); |
| 354 | if (dirStat && dirStat.isSymbolicLink()) return null; |
| 355 | const fileStat = fs.lstatSync(file, { throwIfNoEntry: false }); |
| 356 | if (!fileStat) return null; |
| 357 | if (fileStat.isSymbolicLink() || !fileStat.isFile()) return null; |
| 358 | const raw = fs.readFileSync(file, 'utf8').trim(); |
| 359 | if (raw && /^[a-f0-9]{32,}$/i.test(raw)) return raw; |
| 360 | return null; |
| 361 | } catch { |
| 362 | return null; |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | // Atomically create <workspace>/.evolver/workspace-id with the given id |
| 367 | // (or generate one if `id` is null). Returns the id that ended up on |
no outgoing calls
no test coverage detected