(file-panel): refuse 8.3 short names and \?\ paths into credential directories (#390) - #399
Conversation
…rectories The sensitive-path denylist tested the literal path and the JS realpath, which keeps 8.3 short names and fails with EISDIR on a \?\ path through a junction. Both guards now share one candidate builder that also tries the native realpath, strips the extended prefix, resolves the deepest existing ancestor of a missing path, and treats an unexplained resolution failure as sensitive. Closes #390
|
Reviewing |
|
Adversarial review at |
…dots, walk ancestors once Review follow-up for #390. A failure of either realpath (other than ENOENT/ENOTDIR) now marks the path sensitive even when the other one resolved. On win32 a candidate with trailing dots and spaces removed from each segment is matched too, since the shell opens the real file. The missing-path ancestor walk uses one lstat per level and resolves only the first existing ancestor instead of two realpaths per level. Refs #390
|
Reviewing |
|
Re-review at |
Win32 normalises the \.\ device prefix; only \?\ skips normalisation. The trailing-dot candidate was skipped for any stripped prefix, so \.\C:\...\.ssh.\id_rsa passed both guards. Skip it only for \?\. Refs #390
|
Re-review at |
# Conflicts: # CHANGELOG.md
Closes #390
What changed
The sensitive-path denylist (
isSensitivePath, and its async twinisSensitivePathAsyncfrom #389) tested the literal path and the JSfs.realpathresult. On Windows that misses two spellings of a credential location:SSH~1\id_rsa): the JS realpath walker keeps the short name;\\?\path through a junction into a denylisted directory: the JS realpath throwsEISDIR,resolveOnDiskreturned null and only the literal was tested.Both guards now share one candidate builder (
sensitiveCandidates/sensitiveCandidatesAsyncinresolve-path-on-disk.js) and match the denylist against:\\?\/\\.\prefix removed (\\?\UNC\h\sbecomes\\h\s);fs.realpath.native, which expands short names). Both are tried and every result is matched, because they diverge in each direction;ENOENT/ENOTDIRthat yields no path at all makes the path sensitive.resolveOnDisk/resolveOnDiskAsyncare unchanged (the allowlist and containment checks keep comparing against the JS realpath). Rationale is in.ai/contexts/ipc-bridge.md, "Sensitive-path candidates".Tests
New
test/sensitive-path-windows.test.js:stripExtendedPrefix(run on every platform);.sshfor an existing and a not-yet-created file, a\\?\path through a junction, a\\?\path with a short name, a\\?\path when only the JS realpath can resolve it, fail-closed onEACCES, and two negatives (ordinary file, missing file under an ordinary directory). Each is asserted on both guards.Scripting.FileSystemObject; they skip with a stated reason only if the volume generates no short names (it does here).Before the fix 8 of the 11 original tests failed (
stripExtendedPrefix is not a function, and the short-name /\\?\cases returned false). After: 12/12, plus the related files (resolve-path-on-disk-async,ipc-path-validator,terminal-path-target,terminal-path-links,read-file-for-panel-bounds,viewer-save-guard).Mutations run (each reverted):
EACCESclassed as "missing": the fail-closed test red;EISDIRand fail-closed catches it, so stripping is defence in depth here; it is covered by the pure tests only.Not verified