fix(contracts): advertise a v-mode portable Windows absolute-path pattern (#7152)

Use portable hex escapes for Windows absolute-path separators in the shared local path schema. Preserve host path semantics and caller spelling; validate Annex B/u/v compilation and Node win32 agreement. Closes #7151.
This commit is contained in:
pablodiazjorge authored and GitHub committed 2026-10-26 20:56:47 +08:00
1 parent 63417bf3f0
commit 4557791a2e
2 files changed
+42 -15

No files matched your search

+33 -14
View File
@@ -1,22 +1,27 @@
import { isAbsolute } from "node:path";
import { isAbsolute, win32 } from "node:path";
import { expect, it } from "vitest";
import { absoluteLocalPathStringSchema } from "./localPath.js";
import {
absoluteLocalPathStringSchema,
windowsAbsolutePathPattern,
} from "./localPath.js";
const PATHS = [
"/absolute/path",
"/",
"relative/path",
"C:\\absolute\\path",
"c:/absolute/path",
"C:relative/path",
"\\\\server\\share\\path",
"\\rooted\\path",
"//server/share/path",
"file:///absolute/path",
];
it("matches the host path module for absolute local paths", () => {
for (const path of [
"/absolute/path",
"/",
"relative/path",
"C:\\absolute\\path",
"c:/absolute/path",
"C:relative/path",
"\\\\server\\share\\path",
"\\rooted\\path",
"//server/share/path",
"file:///absolute/path",
]) {
for (const path of PATHS) {
expect(absoluteLocalPathStringSchema.safeParse(path).success).toBe(
isAbsolute(path),
);
@@ -26,3 +31,17 @@ it("matches the host path module for absolute local paths", () => {
absoluteLocalPathStringSchema.safeParse("/absolute/with\0nul").success,
).toBe(false);
});
it("keeps the Windows absolute-path pattern valid in every client mode", () => {
// Annex B and `u` accept an unescaped `/` inside a class; `v` does not, and a
// client that compiles advertised patterns rejects the complete tools request.
for (const flags of ["", "u", "v"])
expect(
() => new RegExp(windowsAbsolutePathPattern.source, flags),
flags,
).not.toThrow();
for (const path of PATHS)
expect(windowsAbsolutePathPattern.test(path), path).toBe(
win32.isAbsolute(path),
);
});
+9 -1
View File
@@ -19,9 +19,17 @@ export const nulFreeStringSchema = z
/** A selected local filesystem path: nonempty and free of NUL bytes. */
export const localPathStringSchema = nulFreeStringSchema.min(1);
/**
* A Windows absolute path: rooted, UNC, or drive-qualified. The classes spell
* backslash and slash as `\x5C` and `\x2F` because `v` mode rejects an
* unescaped `/` inside a class, and Windows hosts advertise this pattern.
*/
export const windowsAbsolutePathPattern =
/^(?:[\x5C\x2F]|[A-Za-z]:[\x5C\x2F])/u;
/** A caller-selected path that is absolute on the host running REA. */
export const absoluteLocalPathStringSchema = localPathStringSchema.regex(
process.platform === "win32" ? /^(?:[\\/]|[A-Za-z]:[\\/])/u : /^\//u,
process.platform === "win32" ? windowsAbsolutePathPattern : /^\//u,
"must be an absolute local filesystem path",
);