|
| 1 | +**Status update on follow-ups from #62328** |
| 2 | + |
| 3 | +Here's a summary of what has been addressed so far and what remains open. |
| 4 | + |
| 5 | +### Addressed |
| 6 | + |
| 7 | +| # | Item | Status | |
| 8 | +|---|------|--------| |
| 9 | +| **1.1** | VFS bypasses permission model | Addressed — `registerVFS()` throws when `permission.isEnabled()` unless `--allow-fs-vfs` runtime flag is set (added to `src/node_options.cc` in the permission namespace) | |
| 10 | +| **1.3** | Overlapping mounts undefined behavior | Addressed — `registerVFS()` detects prefix-overlapping mount points (using `/`-terminated prefix comparison to avoid false positives like `/virtual` vs `/virtual2`) and rejects with `ERR_INVALID_STATE` | |
| 11 | +| **1.4** | RealFSProvider symlink jail escape | Addressed — `symlinkSync` validates target stays within root, `readlinkSync` translates paths to VFS-relative, `realpathSync` throws EACCES on escape | |
| 12 | +| **2.1** | VirtualFileHandle missing methods | Partially addressed — added `chmod`/`chown`/`utimes`/`datasync`/`sync` (no-op stubs), `readv`/`writev`/`appendFile`, `[Symbol.asyncDispose]()`, `readLines`/`readableWebStream`/`createReadStream`/`createWriteStream` (throw `ERR_METHOD_NOT_IMPLEMENTED`). **Still missing**: `fd` property and `EventEmitter` extension (no `'close'` event) | |
| 13 | +| **2.2** | `fsPromises.open()` not intercepted | Addressed — separate `promisesOpen` handler in `setup.js` returns VirtualFileHandle for `fsPromises.open()`, while `open` handler returns numeric FD for callback `fs.open()` | |
| 14 | +| **2.3** | Streams missing properties | Addressed — added `bytesRead`/`pending` on VirtualReadStream, `bytesWritten`/`pending` on VirtualWriteStream | |
| 15 | +| **2.4** | VirtualDir missing disposal | Addressed — added `[Symbol.asyncDispose]()`, `[Symbol.dispose]()`, `entries()` auto-close via try/finally | |
| 16 | +| **2.5** | Stats ino/dev always 0 | Addressed — unique incrementing `ino`, distinctive `dev = 4085` (0xFF9) applied to all three stats factory functions | |
| 17 | +| **2.7** | Watcher API mismatches | Partially addressed — `VFSStatWatcher.addListener`/`removeListener` fixed to `(event, listener)` signature. **Still missing**: `VFSWatcher` does not emit `'error'` events from catch blocks | |
| 18 | +| **3.1** | Streams stall on destroyed/null fd | Addressed — `_write()` calls `callback(createEBADF('write'))`, `_read()` calls `this.destroy(createEBADF('read'))` | |
| 19 | +| **3.2** | `#checkClosed()` always reports 'read' | Addressed — parameterized with `syscall` argument, all call sites pass correct syscall name, both base and subclass `#checkClosed(syscall)` pass it to `createEBADF(syscall)` | |
| 20 | +| **3.3** | `writeFileSync` misses 'ax'/'ax+' | Addressed — uses `#isAppend()` instead of manual flag check | |
| 21 | +| **3.4** | Dead ternary in `#hookProcessCwd` | Addressed — simplified to single `resolvePath(directory)` | |
| 22 | +| **3.5** | SEAProvider statSync reads full asset | Addressed — asset sizes cached in `_assetSizes` map on first access | |
| 23 | +| **3.6** | SEAProvider recursive readdir | Addressed — `#readdirRecursive()` walks subdirectories | |
| 24 | +| **3.7** | SEAProvider numeric flags | Addressed — `openSync()` handles numeric `0` (O_RDONLY) inline | |
| 25 | +| **3.8** | Shared mutable statsArray | Addressed — safety comment added explaining synchronous-copy invariant | |
| 26 | +| **5.1** | readFile handler still sync | Partially addressed — `readFile` handler in `setup.js` converted to async (`vfs.promises.readFile`), `lib/fs.js` uses `vfsResult()` to route promise to callback. Other callback handlers still use sync+nextTick | |
| 27 | +| **6.2** | Cross-VFS operations | Addressed — `checkSameVFS()` helper validates `rename`/`copyFile`/`link` src and dest resolve to same VFS; throws EXDEV otherwise | |
| 28 | +| **6.3** | Package.json cache not invalidated on unmount | Addressed — `deregisterVFS()` calls `clearPackageJSONCache()` exported from `package_json_reader.js` | |
| 29 | +| **7.1** | `restoreAll()` error handling | Addressed — each `restore()` wrapped in try/catch, AggregateError thrown after all mocks restored | |
| 30 | +| **7.2** | `MockFSContext.vfs` exposes full instance | Addressed — changed to `#vfs` private field with read-only getter | |
| 31 | +| **7.3** | Platform-specific root check | Addressed — uses `path.parse(parentDir).root !== parentDir` instead of `!== '/'` | |
| 32 | +| **8.1** | MemoryProvider stat size 0 for dynamic files | Addressed — `#createStats` calls `entry.getContentSync().length` when `entry.isDynamic()` | |
| 33 | +| **8.2** | MemoryProvider symlinkSync auto-creates dirs | Addressed — changed to `#ensureParent(normalized, false, 'symlink')` consistent with other operations | |
| 34 | +| **8.3** | MemoryProvider recursive readdir doesn't follow symlinks | Addressed — `#readdirRecursive` resolves symlinks before checking `isDirectory()` | |
| 35 | +| **8.5** | RealFSProvider no watch support | Addressed — `watch()`/`watchFile()`/`unwatchFile()` implemented with path translation, `supportsWatch = true` | |
| 36 | +| **10.1** | Watcher unbounded memory growth | Addressed — `kMaxPendingEvents = 1024` cap on `#pendingEvents`, oldest events dropped when full | |
| 37 | +| **10.2** | Redundant `#destroyed` in streams | Addressed — removed, uses inherited `this.destroyed` | |
| 38 | +| **10.3** | SEAProvider primordials | Addressed — uses `StringPrototypeReplaceAll`, `ArrayFrom`, `StringPrototypeStartsWith` from primordials | |
| 39 | + |
| 40 | +### Tests added |
| 41 | + |
| 42 | +- `test-vfs-overlapping-mounts.js` — overlapping mount rejection, including non-overlapping prefix and root cases (1.3) |
| 43 | +- `test-vfs-promises-open.js` — `fsPromises.open()` interception (2.2) |
| 44 | +- `test-vfs-stream-properties.js` — `bytesRead`/`bytesWritten`/`pending` (2.3) |
| 45 | +- `test-vfs-dir-disposal.js` — `Symbol.asyncDispose`/`Symbol.dispose` (2.4) |
| 46 | +- `test-vfs-stats-ino-dev.js` — unique ino, distinctive dev (2.5) |
| 47 | +- `test-vfs-append-write.js` — append mode correctness (3.3) |
| 48 | +- `test-vfs-cross-device.js` — EXDEV for cross-VFS rename/copyFile/link (6.2) |
| 49 | +- `test-vfs-readdir-symlink-recursive.js` — recursive readdir follows symlinks (8.3) |
| 50 | +- `test-vfs-package-json-cache.js` — cache cleared on unmount (6.3) |
| 51 | +- `test-vfs-readfile-async.js` — readFile uses async handler (5.1) |
| 52 | + |
| 53 | +### Not addressed (requires further work) |
| 54 | + |
| 55 | +| # | Item | Notes | |
| 56 | +|---|------|-------| |
| 57 | +| **1.2** | Any loaded code can call `mount()` | Design/policy discussion — needs `--experimental-vfs` flag or security event | |
| 58 | +| **2.1** (partial) | `fd` property, EventEmitter extension | VirtualFileHandle doesn't expose `fd` numeric property or emit `'close'` event | |
| 59 | +| **2.3** (partial) | VirtualWriteStream `close(callback)` | Missing explicit `close(callback)` method | |
| 60 | +| **2.7** (partial) | VFSWatcher error events | `VFSWatcher` does not emit `'error'` events from catch blocks in `#getStats()`/`#getStatsFor()` | |
| 61 | +| **4.1** | Case-insensitive path comparison (Windows) | Requires Windows-specific path normalization | |
| 62 | +| **4.2** | UNC paths unsupported | Windows-specific | |
| 63 | +| **4.3** | Virtual CWD hardcoded `/` separator | Windows-specific | |
| 64 | +| **4.4** | findVFSPackageJSON mixed separator | Note only | |
| 65 | +| **5.1** (partial) | Other callback APIs still sync | Only `readFile` converted; broader pattern remains | |
| 66 | +| **5.2** | `existsSync` in async code paths | `findVFSWith()` still uses `existsSync` in paths used by async handlers | |
| 67 | +| **5.3** | Linear scan of activeVFSList | Note only, unlikely to matter in practice | |
| 68 | +| **6.1** | Maintenance burden of 164+ interception points | Design discussion | |
| 69 | +| **6.4** | C++ format coupling in package.json serialization | Note only | |
| 70 | +| **6.5** | legacyMainResolve hardcoded extensions | Note only | |
| 71 | +| **8.4** | RealFSProvider path traversal via real-fs symlinks | `#resolvePath` validates logical path but doesn't resolve real-fs symlinks before checking | |
| 72 | +| **9.1** | Windows tests minimal | Blocked on Windows path handling (4.x items) | |
| 73 | +| **9.4** | Permission model interaction untested | Needs subprocess test with `--permission` flag | |
| 74 | +| **10.4** | VirtualFileHandle `#checkClosed` duplication | Note only — inherent to JS private fields | |
| 75 | +| **10.5** | file_system.js and setup.js are very large | Note only | |
| 76 | +| **10.6** | VirtualFD range starts at 10,000 | Note only | |
0 commit comments