@openclaw/fs-safe 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/device-path.d.ts.map +1 -1
- package/dist/device-path.js +24 -2
- package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
- package/dist/native/darwin-x64/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
- package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
- package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +3 -2
- package/dist/safe-path-segment.d.ts.map +1 -1
- package/dist/safe-path-segment.js +16 -4
- package/dist/temp-target.d.ts.map +1 -1
- package/dist/temp-target.js +51 -3
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.1 - 2026-08-01
|
|
4
|
+
|
|
5
|
+
### Security and Correctness
|
|
6
|
+
|
|
7
|
+
- Stop trimming surrounding whitespace when normalizing Windows paths for comparison, so a space-padded root no longer compares equal to its unpadded sibling and `isPathInside` keeps reporting outside paths as outside.
|
|
8
|
+
- Apply `denyMutations` prefixes to the directory they name on Windows. A prefix with trailing whitespace previously compared equal to its unpadded sibling, so the named directory stayed writable while the sibling was blocked; the two `preserves trailing whitespace` deny-mutation cases now run on Windows instead of being skipped.
|
|
9
|
+
- Replace backtracking-prone path-segment, temp-name, and Windows device-path sanitizers with linear scans to keep attacker-controlled inputs from causing excessive CPU use.
|
|
10
|
+
|
|
11
|
+
### Docs and Tooling
|
|
12
|
+
|
|
13
|
+
- Refresh the npm and Rust build toolchains, including the `zip` 8.6 native archive reader and the latest pinned CodeQL v4 action.
|
|
14
|
+
- Publish only the validated release tarball after asserting its byte identity against the release manifest, and tolerate npm registry propagation with bounded exponential backoff.
|
|
15
|
+
|
|
3
16
|
## 0.5.0 - 2026-07-27
|
|
4
17
|
|
|
5
18
|
### Highlights
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-path.d.ts","sourceRoot":"","sources":["../src/device-path.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,0BAA0B,GAClC,cAAc,GACd,UAAU,GACV,gBAAgB,CAAC;AAErB,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B,CAAC;
|
|
1
|
+
{"version":3,"file":"device-path.d.ts","sourceRoot":"","sources":["../src/device-path.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,0BAA0B,GAClC,cAAc,GACd,UAAU,GACV,gBAAgB,CAAC;AAErB,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC5B,CAAC;AAgIF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,2BAAgC,GACxC,yBAAyB,GAAG,SAAS,CAWvC;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAET;AAED,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,2BAA2B,GACpC,IAAI,CAON"}
|
package/dist/device-path.js
CHANGED
|
@@ -45,6 +45,28 @@ const WINDOWS_RESERVED_DEVICE_NAMES = new Set([
|
|
|
45
45
|
"LPT²",
|
|
46
46
|
"LPT³",
|
|
47
47
|
]);
|
|
48
|
+
const WINDOWS_SEPARATOR_CHAR_CODE = 0x5c;
|
|
49
|
+
const WINDOWS_IGNORED_SPACE_CHAR_CODE = 0x20;
|
|
50
|
+
const WINDOWS_IGNORED_DOT_CHAR_CODE = 0x2e;
|
|
51
|
+
function trimTrailingWindowsSeparators(value) {
|
|
52
|
+
let end = value.length;
|
|
53
|
+
while (end > 0 && value.charCodeAt(end - 1) === WINDOWS_SEPARATOR_CHAR_CODE) {
|
|
54
|
+
end -= 1;
|
|
55
|
+
}
|
|
56
|
+
return end === value.length ? value : value.slice(0, end);
|
|
57
|
+
}
|
|
58
|
+
function trimTrailingWindowsIgnoredChars(value) {
|
|
59
|
+
let end = value.length;
|
|
60
|
+
while (end > 0) {
|
|
61
|
+
const charCode = value.charCodeAt(end - 1);
|
|
62
|
+
if (charCode !== WINDOWS_IGNORED_SPACE_CHAR_CODE &&
|
|
63
|
+
charCode !== WINDOWS_IGNORED_DOT_CHAR_CODE) {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
end -= 1;
|
|
67
|
+
}
|
|
68
|
+
return end === value.length ? value : value.slice(0, end);
|
|
69
|
+
}
|
|
48
70
|
function candidateReadPaths(filePath) {
|
|
49
71
|
if (!filePath.startsWith("file://")) {
|
|
50
72
|
return [filePath];
|
|
@@ -73,10 +95,10 @@ function matchPosixDeviceReadPath(filePath, cwd) {
|
|
|
73
95
|
return undefined;
|
|
74
96
|
}
|
|
75
97
|
function normalizeWindowsDeviceBaseName(filePath) {
|
|
76
|
-
const normalized = filePath.replace(/\//g, "\\")
|
|
98
|
+
const normalized = trimTrailingWindowsSeparators(filePath.replace(/\//g, "\\"));
|
|
77
99
|
const lastSegment = normalized.split("\\").filter(Boolean).at(-1) ?? normalized;
|
|
78
100
|
const withoutStream = lastSegment.split(":")[0] ?? lastSegment;
|
|
79
|
-
const withoutTrailingIgnoredChars = withoutStream
|
|
101
|
+
const withoutTrailingIgnoredChars = trimTrailingWindowsIgnoredChars(withoutStream);
|
|
80
102
|
return (withoutTrailingIgnoredChars.split(".")[0] ?? withoutTrailingIgnoredChars).toUpperCase();
|
|
81
103
|
}
|
|
82
104
|
function matchWindowsDeviceReadPath(filePath) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAKzB,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,GAChC,MAAM,kBAAkB,CAAC;AAM1B,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWvE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAI1E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,SAA6B,GAAG,IAAI,CAIjG;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CA4BlE;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAExE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAa/F;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAChE,OAAO,CAUT;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,CAMhE;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAuBpE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAOrF"}
|
package/dist/path.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
|
-
import { normalizeLowercaseStringOrEmpty } from "./string-coerce.js";
|
|
5
4
|
export { assertNoUnsafeDeviceReadPath, isUnsafeDeviceReadPath, matchUnsafeDeviceReadPath, } from "./device-path.js";
|
|
6
5
|
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
|
|
7
6
|
const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
|
|
@@ -14,7 +13,9 @@ export function normalizeWindowsPathForComparison(input) {
|
|
|
14
13
|
normalized = `\\\\${normalized.slice(4)}`;
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
|
-
|
|
16
|
+
// Lowercase only: this value feeds Windows containment math, so surrounding
|
|
17
|
+
// whitespace is part of the path and must not be trimmed away.
|
|
18
|
+
return normalized.replaceAll("/", "\\").toLowerCase();
|
|
18
19
|
}
|
|
19
20
|
export function isNodeError(value) {
|
|
20
21
|
return Boolean(value && typeof value === "object" && "code" in value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-path-segment.d.ts","sourceRoot":"","sources":["../src/safe-path-segment.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"safe-path-segment.d.ts","sourceRoot":"","sources":["../src/safe-path-segment.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAcF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAaT;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAUR;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAWR;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAeR"}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { FsSafeError } from "./errors.js";
|
|
2
2
|
const SAFE_PATH_SEGMENT_PATTERN = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
|
|
3
3
|
const SAFE_DOT_PREFIX_PATH_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
4
|
+
const HYPHEN_CHAR_CODE = 0x2d;
|
|
5
|
+
function trimHyphenEdges(value) {
|
|
6
|
+
let start = 0;
|
|
7
|
+
let end = value.length;
|
|
8
|
+
while (start < end && value.charCodeAt(start) === HYPHEN_CHAR_CODE) {
|
|
9
|
+
start += 1;
|
|
10
|
+
}
|
|
11
|
+
while (end > start && value.charCodeAt(end - 1) === HYPHEN_CHAR_CODE) {
|
|
12
|
+
end -= 1;
|
|
13
|
+
}
|
|
14
|
+
return start === 0 && end === value.length ? value : value.slice(start, end);
|
|
15
|
+
}
|
|
4
16
|
export function isSafePathSegment(segment, options = {}) {
|
|
5
17
|
return (segment !== "" &&
|
|
6
18
|
segment !== "." &&
|
|
@@ -26,10 +38,10 @@ export function sanitizeSafePathSegment(value, fallback, options = {}) {
|
|
|
26
38
|
.trim()
|
|
27
39
|
.replace(/[\\/]+/g, "-")
|
|
28
40
|
.replace(/\0/g, "")
|
|
29
|
-
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
30
|
-
|
|
31
|
-
if (isSafePathSegment(
|
|
32
|
-
return
|
|
41
|
+
.replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
42
|
+
const trimmed = trimHyphenEdges(sanitized);
|
|
43
|
+
if (isSafePathSegment(trimmed, options)) {
|
|
44
|
+
return trimmed;
|
|
33
45
|
}
|
|
34
46
|
return assertSafePathSegment(fallback, { ...options, label: "fallback path segment" });
|
|
35
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-target.d.ts","sourceRoot":"","sources":["../src/temp-target.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;
|
|
1
|
+
{"version":3,"file":"temp-target.d.ts","sourceRoot":"","sources":["../src/temp-target.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AA0EF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAaT;AAyBD,wBAAsB,QAAQ,CAAC,MAAM,EAAE;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqBpB;AAED,wBAAsB,YAAY,CAAC,CAAC,EAClC,MAAM,EAAE;IACN,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3C,EACD,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|
package/dist/temp-target.js
CHANGED
|
@@ -4,8 +4,56 @@ import path from "node:path";
|
|
|
4
4
|
import { assertSafePathSegment, sanitizeSafePathSegment } from "./safe-path-segment.js";
|
|
5
5
|
import { resolveSecureTempRoot } from "./secure-temp-dir.js";
|
|
6
6
|
import { registerTempPathForExit } from "./temp-cleanup.js";
|
|
7
|
+
const HYPHEN_CHAR_CODE = 0x2d;
|
|
8
|
+
const DOT_CHAR_CODE = 0x2e;
|
|
9
|
+
const NUMBER_ZERO_CHAR_CODE = 0x30;
|
|
10
|
+
const NUMBER_NINE_CHAR_CODE = 0x39;
|
|
11
|
+
const UPPERCASE_A_CHAR_CODE = 0x41;
|
|
12
|
+
const UPPERCASE_Z_CHAR_CODE = 0x5a;
|
|
13
|
+
const UNDERSCORE_CHAR_CODE = 0x5f;
|
|
14
|
+
const LOWERCASE_A_CHAR_CODE = 0x61;
|
|
15
|
+
const LOWERCASE_Z_CHAR_CODE = 0x7a;
|
|
16
|
+
function trimHyphenEdges(value) {
|
|
17
|
+
let start = 0;
|
|
18
|
+
let end = value.length;
|
|
19
|
+
while (start < end && value.charCodeAt(start) === HYPHEN_CHAR_CODE) {
|
|
20
|
+
start += 1;
|
|
21
|
+
}
|
|
22
|
+
while (end > start && value.charCodeAt(end - 1) === HYPHEN_CHAR_CODE) {
|
|
23
|
+
end -= 1;
|
|
24
|
+
}
|
|
25
|
+
return start === 0 && end === value.length ? value : value.slice(start, end);
|
|
26
|
+
}
|
|
27
|
+
function isExtensionCharCode(charCode) {
|
|
28
|
+
return ((charCode >= NUMBER_ZERO_CHAR_CODE && charCode <= NUMBER_NINE_CHAR_CODE) ||
|
|
29
|
+
(charCode >= UPPERCASE_A_CHAR_CODE && charCode <= UPPERCASE_Z_CHAR_CODE) ||
|
|
30
|
+
(charCode >= LOWERCASE_A_CHAR_CODE && charCode <= LOWERCASE_Z_CHAR_CODE) ||
|
|
31
|
+
charCode === DOT_CHAR_CODE ||
|
|
32
|
+
charCode === UNDERSCORE_CHAR_CODE ||
|
|
33
|
+
charCode === HYPHEN_CHAR_CODE);
|
|
34
|
+
}
|
|
35
|
+
function trailingExtensionChars(value) {
|
|
36
|
+
let start = value.length;
|
|
37
|
+
while (start > 0 && isExtensionCharCode(value.charCodeAt(start - 1))) {
|
|
38
|
+
start -= 1;
|
|
39
|
+
}
|
|
40
|
+
return start === value.length ? "" : value.slice(start);
|
|
41
|
+
}
|
|
42
|
+
function trimLeadingExtensionPunctuation(value) {
|
|
43
|
+
let start = 0;
|
|
44
|
+
while (start < value.length) {
|
|
45
|
+
const charCode = value.charCodeAt(start);
|
|
46
|
+
if (charCode !== DOT_CHAR_CODE &&
|
|
47
|
+
charCode !== UNDERSCORE_CHAR_CODE &&
|
|
48
|
+
charCode !== HYPHEN_CHAR_CODE) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
start += 1;
|
|
52
|
+
}
|
|
53
|
+
return start === 0 ? value : value.slice(start);
|
|
54
|
+
}
|
|
7
55
|
function sanitizePrefix(prefix) {
|
|
8
|
-
const normalized = prefix.replace(/[^a-zA-Z0-9_-]+/g, "-")
|
|
56
|
+
const normalized = trimHyphenEdges(prefix.replace(/[^a-zA-Z0-9_-]+/g, "-"));
|
|
9
57
|
return normalized || "tmp";
|
|
10
58
|
}
|
|
11
59
|
function sanitizeExtension(extension) {
|
|
@@ -13,8 +61,8 @@ function sanitizeExtension(extension) {
|
|
|
13
61
|
return "";
|
|
14
62
|
}
|
|
15
63
|
const normalized = extension.startsWith(".") ? extension : `.${extension}`;
|
|
16
|
-
const suffix = normalized
|
|
17
|
-
const token = suffix
|
|
64
|
+
const suffix = trailingExtensionChars(normalized);
|
|
65
|
+
const token = trimLeadingExtensionPunctuation(suffix);
|
|
18
66
|
return token ? `.${token}` : "";
|
|
19
67
|
}
|
|
20
68
|
export function sanitizeTempFileName(fileName) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -145,14 +145,14 @@
|
|
|
145
145
|
},
|
|
146
146
|
"optionalDependencies": {
|
|
147
147
|
"jszip": "^3.10.1",
|
|
148
|
-
"tar": "7.5.
|
|
148
|
+
"tar": "7.5.22"
|
|
149
149
|
},
|
|
150
150
|
"devDependencies": {
|
|
151
|
-
"@napi-rs/cli": "3.
|
|
152
|
-
"@types/node": "^26.1.
|
|
151
|
+
"@napi-rs/cli": "3.8.1",
|
|
152
|
+
"@types/node": "^26.1.2",
|
|
153
153
|
"@vitest/coverage-v8": "4.1.10",
|
|
154
154
|
"typescript": "^7.0.2",
|
|
155
|
-
"vite": "8.
|
|
155
|
+
"vite": "8.2.0",
|
|
156
156
|
"vitest": "^4.1.10"
|
|
157
157
|
},
|
|
158
158
|
"engines": {
|