perf(infra): fast path posix containment checks

This commit is contained in:
Peter Steinberger
2026-05-02 10:50:23 +01:00
parent 6cc7b759e9
commit f7b24a697d
3 changed files with 23 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
### Changes
- Infra/path-guards: add a fast path for canonical absolute POSIX containment checks, avoiding repeated `path.resolve` and `path.relative` work in hot filesystem walkers. Refs #75895, #75575, and #68782. Thanks @Enderfga.
- Tools: add a platform-level tool descriptor planner for descriptor-first visibility, generic availability checks, and executor references. Thanks @shakkernerd.
- Docs/Codex: clarify that ChatGPT/Codex subscription setups should use `openai/gpt-*` with `agentRuntime.id: "codex"` for native Codex runtime, while `openai-codex/*` remains the PI OAuth route. Thanks @pashpashpash.
- Plugins/source checkout: load bundled plugins from the `extensions/*` pnpm workspace tree in source checkouts, so plugin-local dependencies and edits are used directly while packaged installs keep using the built runtime tree. Thanks @vincentkoc.

View File

@@ -74,6 +74,15 @@ describe("isPathInside", () => {
["/workspace/root", "/workspace/root/nested/file.txt", true],
["/workspace/root", "/workspace/root/..file.txt", true],
["/workspace/root", "/workspace/root/../escape.txt", false],
["/workspace/root", "/workspace/rootless/file.txt", false],
["/workspace/root", "/workspace/root/a/b/c/d/e/file.txt", true],
["/workspace/root", "/workspace/root/a/..", true],
["/workspace/root", "/workspace/root/a/../..", false],
["/workspace/root", "/workspace/root/a/b/../../../escape", false],
["/", "/anything/at/all", true],
["/", "/", true],
["foo", "foo/bar", true],
["foo", "../escape", false],
])("checks posix containment %s -> %s", (basePath, targetPath, expected) => {
expect(isPathInside(basePath, targetPath)).toBe(expected);
});

View File

@@ -4,6 +4,7 @@ import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
const PARENT_SEGMENT_PREFIX = /^\.\.(?:[\\/]|$)/u;
const POSIX_SEPARATOR_CHAR_CODE = 0x2f;
export function normalizeWindowsPathForComparison(input: string): string {
let normalized = path.win32.normalize(input);
@@ -44,6 +45,18 @@ export function isPathInside(root: string, target: string): boolean {
);
}
if (
root.length > 0 &&
root.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
target.length >= root.length &&
target.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
!target.includes("/..") &&
(target === root ||
(target.startsWith(root) && target.charCodeAt(root.length) === POSIX_SEPARATOR_CHAR_CODE))
) {
return true;
}
const resolvedRoot = path.resolve(root);
const resolvedTarget = path.resolve(target);
const relative = path.relative(resolvedRoot, resolvedTarget);