fix(windows): tolerate LPAC EPERM from realpathSync.native on directories

f0080cd2e stopped JS realpathSync from walking to C:\ under LPAC by using
realpathSync.native and comparing canonical-to-canonical. Hosted Windows on
#6846 still failed the six native_mcp activations with
`EPERM: operation not permitted, realpath '...\source'`: admitReviewedClosure
canonicalizes the reviewed source directory, and native realpath opens it with
FILE_FLAG_BACKUP_SEMANTICS, which AppContainers refuse.

On win32 EPERM/EACCES, keep stripVerbatim(normalize(path)) instead of
throwing. Core already refused links while granting that tree. File checks
still prefer native when it succeeds. Bundles rebuilt.

Evidence (Linux box, node 20): node --test test/canonical-path.test.mjs
passes. Windows LPAC proof is hosted Test (windows-latest).

Signed-off-by: CodeWhale Bot <[email protected]>
This commit is contained in:
CodeWhale Bot committed 2026-10-05 08:02:09 -07:00
1 parent 6958ca6dd7
commit 5d110e82fd
5 files changed
+60 -5

No files matched your search

+10 -1
View File
@@ -6106,7 +6106,16 @@ var init_skill_filesystem = __esm({
import { realpathSync } from "node:fs";
import { posix, win32 } from "node:path";
function canonicalPath(path, platform = process.platform) {
return stripVerbatim(platform === "win32" ? realpathSync.native(path) : realpathSync(path), platform);
if (platform !== "win32") return realpathSync(path);
try {
return stripVerbatim(realpathSync.native(path), platform);
} catch (error) {
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
if (code === "EPERM" || code === "EACCES") {
return stripVerbatim(win32.normalize(path), platform);
}
throw error;
}
}
function stripVerbatim(path, platform = process.platform) {
if (platform !== "win32") return path;
+10 -1
View File
@@ -6482,7 +6482,16 @@ var init_skill_filesystem = __esm({
import { realpathSync } from "node:fs";
import { posix, win32 } from "node:path";
function canonicalPath(path, platform = process.platform) {
return stripVerbatim(platform === "win32" ? realpathSync.native(path) : realpathSync(path), platform);
if (platform !== "win32") return realpathSync(path);
try {
return stripVerbatim(realpathSync.native(path), platform);
} catch (error) {
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
if (code === "EPERM" || code === "EACCES") {
return stripVerbatim(win32.normalize(path), platform);
}
throw error;
}
}
function stripVerbatim(path, platform = process.platform) {
if (platform !== "win32") return path;
+10 -1
View File
@@ -6057,7 +6057,16 @@ var init_skill_filesystem = __esm({
import { realpathSync } from "node:fs";
import { posix, win32 } from "node:path";
function canonicalPath(path, platform = process.platform) {
return stripVerbatim(platform === "win32" ? realpathSync.native(path) : realpathSync(path), platform);
if (platform !== "win32") return realpathSync(path);
try {
return stripVerbatim(realpathSync.native(path), platform);
} catch (error) {
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
if (code === "EPERM" || code === "EACCES") {
return stripVerbatim(win32.normalize(path), platform);
}
throw error;
}
}
function stripVerbatim(path, platform = process.platform) {
if (platform !== "win32") return path;
+10 -1
View File
@@ -7,7 +7,16 @@ import { resolve, relative, isAbsolute, sep } from "node:path";
import { realpathSync } from "node:fs";
import { posix, win32 } from "node:path";
function canonicalPath(path, platform = process.platform) {
return stripVerbatim(platform === "win32" ? realpathSync.native(path) : realpathSync(path), platform);
if (platform !== "win32") return realpathSync(path);
try {
return stripVerbatim(realpathSync.native(path), platform);
} catch (error) {
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
if (code === "EPERM" || code === "EACCES") {
return stripVerbatim(win32.normalize(path), platform);
}
throw error;
}
}
function stripVerbatim(path, platform = process.platform) {
if (platform !== "win32") return path;
@@ -10,6 +10,13 @@
* never compared with a raw one. Both sides go through `canonicalPath`, then
* `pathKey`, and containment is `relative(canonical root, canonical target)`.
*
* Under LPAC, `realpathSync.native` still fails with EPERM on some paths —
* especially directories such as the reviewed `source/` root — because the
* open uses FILE_FLAG_BACKUP_SEMANTICS. Core already refused links while
* granting that tree, so on EPERM/EACCES we keep a stable stripped spelling
* instead of throwing and aborting admitReviewedClosure. Link refusal for
* files still goes through the same helper when native succeeds.
*
* Elsewhere `realpathSync` is unchanged.
*/
import { realpathSync } from 'node:fs'
@@ -18,7 +25,19 @@ import { posix, win32 } from 'node:path'
type Platform = NodeJS.Platform
export function canonicalPath(path: string, platform: Platform = process.platform): string {
return stripVerbatim(platform === 'win32' ? realpathSync.native(path) : realpathSync(path), platform)
if (platform !== 'win32') return realpathSync(path)
try {
return stripVerbatim(realpathSync.native(path), platform)
} catch (error) {
const code = error && typeof error === 'object' && 'code' in error ? (error as { code?: string }).code : undefined
// LPAC cannot open some granted paths the way native realpath requires
// (notably directories). Fall back to a stable spelling; Core's grant
// already refused links/reparse points in the admitted tree.
if (code === 'EPERM' || code === 'EACCES') {
return stripVerbatim(win32.normalize(path), platform)
}
throw error
}
}
/** Drop the Win32 verbatim prefix: `\\?\C:\x` → `C:\x`, `\\?\UNC\h\s` → `\\h\s`. Case is kept. */