@openclaw/fs-safe 0.18.0 → 0.18.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.
Files changed (145) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/advanced.d.ts +2 -1
  3. package/dist/archive-durability.d.ts +1 -1
  4. package/dist/archive-parser.wasm +0 -0
  5. package/dist/archive-plan.d.ts +64 -3
  6. package/dist/archive-plan.js +55 -1
  7. package/dist/archive-tar-wasm.d.ts +1 -1
  8. package/dist/archive-zip-directory.d.ts +1 -1
  9. package/dist/archive-zip-entry.d.ts +1 -1
  10. package/dist/archive-zip-entry.js +1 -1
  11. package/dist/archive.d.ts +8 -5
  12. package/dist/archive.js +122 -9
  13. package/dist/atomic.d.ts +3 -1
  14. package/dist/copy-publication.d.ts +1 -1
  15. package/dist/create.d.ts +16 -2
  16. package/dist/create.js +388 -2
  17. package/dist/creation-boundary.d.ts +60 -0
  18. package/dist/creation-boundary.js +371 -0
  19. package/dist/directory-mode-node.js +12 -2
  20. package/dist/durability.d.ts +2 -1
  21. package/dist/file-lock-sync-acquisition.js +2 -2
  22. package/dist/file-lock-sync-root-acquire.js +2 -6
  23. package/dist/file-lock-sync-root.d.ts +2 -1
  24. package/dist/file-lock-sync-stale-admission.js +3 -10
  25. package/dist/file-lock-sync.d.ts +4 -21
  26. package/dist/file-store-boundary.d.ts +7 -0
  27. package/dist/file-store-boundary.js +58 -1
  28. package/dist/file-store-sync-write.js +2 -3
  29. package/dist/file-store.js +1 -6
  30. package/dist/guarded-mkdir.d.ts +1 -1
  31. package/dist/index.d.ts +9 -6
  32. package/dist/json-durable-queue-read.js +7 -3
  33. package/dist/json-durable-queue.js +1 -2
  34. package/dist/json-store.js +1 -2
  35. package/dist/json.js +1 -2
  36. package/dist/move-path.js +1 -2
  37. package/dist/native-operations.js +0 -1
  38. package/dist/native-pinned-write-windows.d.ts +1 -1
  39. package/dist/native-pinned-write-windows.js +1 -1
  40. package/dist/native-pinned-write.d.ts +1 -1
  41. package/dist/native-pinned-write.js +3 -6
  42. package/dist/native-policy-parent-windows.d.ts +1 -1
  43. package/dist/native-staged-file.d.ts +2 -2
  44. package/dist/native-staged-file.js +1 -2
  45. package/dist/pinned-mutation-admission.d.ts +1 -1
  46. package/dist/pinned-open.js +6 -1
  47. package/dist/pinned-operation.d.ts +1 -1
  48. package/dist/pinned-operation.js +3 -16
  49. package/dist/pinned-write-mode.js +1 -1
  50. package/dist/pinned-write-staged.js +1 -2
  51. package/dist/pinned-write.d.ts +0 -1
  52. package/dist/pinned-write.js +3 -5
  53. package/dist/private-producer-handoff-sync.d.ts +2 -12
  54. package/dist/private-producer-handoff-sync.js +1 -1
  55. package/dist/private-producer-handoff.d.ts +3 -3
  56. package/dist/private-temp-workspace.js +1 -2
  57. package/dist/publish-copy-stage.d.ts +1 -1
  58. package/dist/publish-file.d.ts +0 -1
  59. package/dist/publish-file.js +1 -2
  60. package/dist/replace-directory.js +1 -2
  61. package/dist/replace-file-rename-policy.d.ts +1 -2
  62. package/dist/replace-file.d.ts +3 -3
  63. package/dist/replace-file.js +1 -1
  64. package/dist/root-context.js +3 -7
  65. package/dist/root-create-input.d.ts +1 -1
  66. package/dist/root-directory-creation.d.ts +1 -1
  67. package/dist/root-directory-creation.js +1 -1
  68. package/dist/root-directory-list.js +3 -7
  69. package/dist/root-entries.js +2 -2
  70. package/dist/root-errors.d.ts +4 -0
  71. package/dist/root-errors.js +5 -3
  72. package/dist/root-impl.d.ts +0 -5
  73. package/dist/root-impl.js +22 -21
  74. package/dist/root-move-noreplace.js +4 -10
  75. package/dist/root-observed-path.js +2 -4
  76. package/dist/root-options.d.ts +1 -1
  77. package/dist/root-path-existing.js +2 -3
  78. package/dist/root-path-stat.js +2 -4
  79. package/dist/root-public-types.d.ts +11 -0
  80. package/dist/root-remove-identity.js +2 -4
  81. package/dist/root-remove.js +2 -4
  82. package/dist/root-write-admission.d.ts +1 -1
  83. package/dist/root-write-admission.js +3 -7
  84. package/dist/root-write-complete-parent.js +2 -3
  85. package/dist/root-write-verification.d.ts +1 -1
  86. package/dist/root.d.ts +2 -6
  87. package/dist/secret-file.js +0 -1
  88. package/dist/secure-temp-repair.js +5 -1
  89. package/dist/sidecar-lock-acquire.js +7 -10
  90. package/dist/sidecar-lock-policy.d.ts +1 -0
  91. package/dist/sidecar-lock-policy.js +7 -0
  92. package/dist/sidecar-lock-stale-admission.js +2 -6
  93. package/dist/sidecar-lock-types.d.ts +22 -16
  94. package/dist/temp-workspace-admission.js +1 -3
  95. package/dist/temp-workspace-child-admission.d.ts +16 -2
  96. package/dist/temp-workspace-child-admission.js +71 -3
  97. package/dist/temp-workspace-descriptor.d.ts +1 -1
  98. package/dist/temp-workspace-descriptor.js +1 -2
  99. package/dist/text-atomic.js +1 -1
  100. package/dist/windows-owner.js +1 -1
  101. package/dist/windows-path-alias.d.ts +1 -0
  102. package/dist/windows-path-alias.js +5 -0
  103. package/docs/advanced.md +3 -1
  104. package/docs/secret-file.md +4 -0
  105. package/docs/sidecar-lock.md +4 -0
  106. package/docs/store.md +6 -0
  107. package/docs/temp.md +8 -1
  108. package/docs/writing.md +5 -0
  109. package/package.json +8 -8
  110. package/dist/archive-native.d.ts +0 -10
  111. package/dist/archive-native.js +0 -73
  112. package/dist/archive-options.d.ts +0 -30
  113. package/dist/archive-policy.d.ts +0 -21
  114. package/dist/archive-policy.js +0 -38
  115. package/dist/archive-tar-inspect.d.ts +0 -6
  116. package/dist/archive-tar-inspect.js +0 -62
  117. package/dist/archive-tar.d.ts +0 -18
  118. package/dist/archive-tar.js +0 -21
  119. package/dist/create-directory.d.ts +0 -18
  120. package/dist/create-directory.js +0 -111
  121. package/dist/create-file-async.d.ts +0 -6
  122. package/dist/create-file-async.js +0 -121
  123. package/dist/create-file.d.ts +0 -7
  124. package/dist/create-file.js +0 -179
  125. package/dist/creation-darwin.d.ts +0 -5
  126. package/dist/creation-darwin.js +0 -70
  127. package/dist/creation-file-state.d.ts +0 -18
  128. package/dist/creation-file-state.js +0 -118
  129. package/dist/creation-path.d.ts +0 -22
  130. package/dist/creation-path.js +0 -83
  131. package/dist/creation-permissions.d.ts +0 -18
  132. package/dist/creation-permissions.js +0 -115
  133. package/dist/file-store-copy-source.d.ts +0 -4
  134. package/dist/file-store-copy-source.js +0 -31
  135. package/dist/file-store-limit.d.ts +0 -1
  136. package/dist/file-store-limit.js +0 -7
  137. package/dist/file-store-path.d.ts +0 -2
  138. package/dist/file-store-path.js +0 -30
  139. package/dist/standalone-publication-path.d.ts +0 -1
  140. package/dist/standalone-publication-path.js +0 -6
  141. package/dist/temp-workspace-identity.d.ts +0 -14
  142. package/dist/temp-workspace-identity.js +0 -41
  143. package/dist/temp-workspace-permissions.d.ts +0 -3
  144. package/dist/temp-workspace-permissions.js +0 -32
  145. /package/dist/{archive-options.js → root-public-types.js} +0 -0
@@ -1,83 +0,0 @@
1
- import fs, {} from "node:fs";
2
- import fsAsync from "node:fs/promises";
3
- import path from "node:path";
4
- import { assertDirectoryIdentitySync, inspectDirectoryIdentitySync, } from "./directory-guard.js";
5
- import { FsSafeError } from "./errors.js";
6
- import { assertDarwinCreationDirectoryAcl, assertDarwinPrivateDirectoryMode } from "./creation-darwin.js";
7
- import { assertSynchronousCallbackResult } from "./mutation-authority.js";
8
- import { assertNoNulPathInput } from "./path.js";
9
- import { realpathSync } from "./realpath.js";
10
- import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot } from "./windows-path-alias.js";
11
- export function assertBeforeCreation(selected, permissions, assertion, kind) {
12
- if (permissions.private)
13
- assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, kind);
14
- assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
15
- selected.assertParent();
16
- if (permissions.private && kind === "directory")
17
- assertDarwinPrivateDirectoryMode(permissions.mode);
18
- if (permissions.private)
19
- assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, kind);
20
- }
21
- export function creationAdmissionFromParent(parent) {
22
- const { dev, ino } = parent.stat;
23
- if (typeof dev !== "bigint" || typeof ino !== "bigint") {
24
- throw new FsSafeError("path-mismatch", "private creation requires an exact parent identity");
25
- }
26
- const identity = inspectDirectoryIdentitySync(parent.dir, { dev, ino });
27
- return { expectedParentIdentity: { dev: identity.dev, ino: identity.ino, realPath: parent.realPath } };
28
- }
29
- export function prepareCreationPath(input, expectedParent) {
30
- expectedParent = expectedParent && { dev: expectedParent.dev, ino: expectedParent.ino, realPath: expectedParent.realPath };
31
- assertNoNulPathInput(input);
32
- assertNoWindowsPathAlias(input, "filesystem");
33
- if (process.platform === "win32" && input.replaceAll("/", "\\").split("\\")
34
- .some(component => component.endsWith(".") || component.endsWith(" "))) {
35
- throw new FsSafeError("invalid-path", "creation path contains an ambiguous Windows component");
36
- }
37
- const target = resolvePathPreservingWindowsRoot(input);
38
- const directory = path.dirname(target);
39
- if (target === directory || input.length === 0) {
40
- throw new FsSafeError("invalid-path", "creation requires a child name");
41
- }
42
- if (expectedParent)
43
- assertDirectoryIdentitySync(directory, expectedParent);
44
- const stat = inspectDirectoryIdentitySync(directory);
45
- const realPath = realpathSync.native(directory);
46
- const parent = { dir: directory, realPath, stat };
47
- const assertParent = () => {
48
- assertDirectoryIdentitySync(directory, { dev: stat.dev, ino: stat.ino, realPath });
49
- if (expectedParent)
50
- assertDirectoryIdentitySync(directory, expectedParent);
51
- };
52
- assertParent();
53
- return { target, parent, assertParent };
54
- }
55
- export function creationCollision(error) {
56
- if (error?.code === "EEXIST") {
57
- return new FsSafeError("already-exists", "creation target already exists", { cause: error });
58
- }
59
- return error;
60
- }
61
- /** Remove only the recorded empty stage; never traverse a replaced directory. */
62
- export function removeCreationDirectory(directory, identity, assertParent) {
63
- assertParent();
64
- try {
65
- assertDirectoryIdentitySync(directory, identity);
66
- fs.rmdirSync(directory);
67
- }
68
- catch (error) {
69
- if (error?.code !== "ENOENT")
70
- throw error;
71
- }
72
- }
73
- export async function removeCreationDirectoryAsync(directory, identity, assertParent) {
74
- assertParent();
75
- try {
76
- assertDirectoryIdentitySync(directory, identity);
77
- await fsAsync.rmdir(directory);
78
- }
79
- catch (error) {
80
- if (error?.code !== "ENOENT")
81
- throw error;
82
- }
83
- }
@@ -1,18 +0,0 @@
1
- export type CreationPermissions = {
2
- private?: boolean;
3
- mode?: number;
4
- };
5
- export declare function resolveCreationPermissions(options: CreationPermissions, directory: boolean): {
6
- private: boolean;
7
- mode: number | undefined;
8
- };
9
- export declare function assertPrivateFileCreationAvailable(): void;
10
- /** Full Windows identity, with security validation when requested; no repairs. */
11
- export declare function inspectCreationDirectorySync(targetPath: string, privatePath: boolean): string;
12
- export declare function inspectCreationDirectory(targetPath: string, privatePath: boolean): Promise<string>;
13
- export declare function assertPrivateDirectorySync(targetPath: string): void;
14
- export declare function assertPrivateDirectory(targetPath: string): Promise<void>;
15
- export declare function protectCreatedFileSync(fd: number, targetPath: string, parentIdentity: string): string;
16
- export declare function protectCreatedFile(fd: number, targetPath: string, parentIdentity: string): Promise<string>;
17
- export declare function verifyCreatedFileSync(fd: number, targetPath: string, identity: string, parentIdentity: string, links?: number): void;
18
- export declare function verifyCreatedFile(fd: number, targetPath: string, identity: string, parentIdentity: string, links?: number): Promise<void>;
@@ -1,115 +0,0 @@
1
- import { inspectDirectoryIdentitySync } from "./directory-guard.js";
2
- import { FsSafeError } from "./errors.js";
3
- import { getNativeBinding } from "./native.js";
4
- import { getFsSafeNativeConfig } from "./native-config.js";
5
- import { warnNativeFallback } from "./native-fallback-warning.js";
6
- import { inspectWindowsDirectoryCommand, inspectWindowsDirectoryCommandSync, protectPrivateWindowsFileCommand, protectPrivateWindowsFileCommandSync, verifyPrivateWindowsFileCommand, verifyPrivateWindowsFileCommandSync, } from "./windows-security-command.js";
7
- import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
8
- import { assertDarwinCreationDirectoryAcl, assertDarwinPrivateCreationAvailable, assertDarwinPrivateDirectoryMode } from "./creation-darwin.js";
9
- export function resolveCreationPermissions(options, directory) {
10
- const privatePath = options.private;
11
- const mode = options.mode;
12
- if (privatePath !== undefined && typeof privatePath !== "boolean") {
13
- throw new TypeError("private must be a boolean");
14
- }
15
- if (mode !== undefined && (!Number.isInteger(mode) || mode < 0 || mode > 0o7777)) {
16
- throw new RangeError("mode must contain only permission bits");
17
- }
18
- if (privatePath && mode !== undefined && (mode & 0o7077) !== 0) {
19
- throw new FsSafeError("insecure-permissions", "private creation requires owner-only permission bits");
20
- }
21
- if (privatePath)
22
- assertDarwinPrivateCreationAvailable();
23
- if (privatePath && directory)
24
- assertDarwinPrivateDirectoryMode(mode ?? 0o700);
25
- return { private: privatePath === true, mode: mode ?? (privatePath ? directory ? 0o700 : 0o600 : undefined) };
26
- }
27
- function assertPrivatePosixDirectory(stat) {
28
- if (typeof process.getuid !== "function" || stat.uid !== BigInt(process.getuid())) {
29
- throw new FsSafeError("not-owned", "private directory must belong to the current user");
30
- }
31
- if ((stat.mode & 3647n) !== 0n) {
32
- throw new FsSafeError("insecure-permissions", "existing directory is not private");
33
- }
34
- }
35
- function unavailable() {
36
- throw new FsSafeError("helper-unavailable", "private Windows creation requires an up-to-date native helper");
37
- }
38
- export function assertPrivateFileCreationAvailable() {
39
- const native = getNativeBinding();
40
- if (process.platform !== "win32" || getFsSafeNativeConfig().mode !== "require")
41
- return;
42
- if (typeof native?.createPrivateDirectoryWithParentIdentity !== "function" ||
43
- typeof native.inspectWindowsDirectory !== "function" ||
44
- typeof native.protectPrivateWindowsFile !== "function" ||
45
- typeof native.verifyPrivateWindowsFile !== "function")
46
- unavailable();
47
- }
48
- function commandFallback() {
49
- if (getFsSafeNativeConfig().mode === "require")
50
- unavailable();
51
- warnNativeFallback("windows-private-creation", "Private Windows creation uses a slower built-in system command.");
52
- }
53
- function creationOperation(name) {
54
- const native = getNativeBinding();
55
- const operation = native?.[name];
56
- if (typeof operation === "function")
57
- return operation.bind(native);
58
- commandFallback();
59
- return undefined;
60
- }
61
- /** Full Windows identity, with security validation when requested; no repairs. */
62
- export function inspectCreationDirectorySync(targetPath, privatePath) {
63
- assertNoWindowsPathAlias(targetPath, "filesystem");
64
- const inspect = creationOperation("inspectWindowsDirectory");
65
- return (inspect ? inspect(targetPath, privatePath) : inspectWindowsDirectoryCommandSync(targetPath, privatePath)).identity;
66
- }
67
- export async function inspectCreationDirectory(targetPath, privatePath) {
68
- assertNoWindowsPathAlias(targetPath, "filesystem");
69
- const inspect = creationOperation("inspectWindowsDirectory");
70
- return (inspect ? inspect(targetPath, privatePath) : await inspectWindowsDirectoryCommand(targetPath, privatePath)).identity;
71
- }
72
- export function assertPrivateDirectorySync(targetPath) {
73
- const stat = inspectDirectoryIdentitySync(targetPath);
74
- if (process.platform === "win32")
75
- inspectCreationDirectorySync(targetPath, true);
76
- else {
77
- assertPrivatePosixDirectory(stat);
78
- assertDarwinCreationDirectoryAcl(targetPath, stat);
79
- }
80
- inspectDirectoryIdentitySync(targetPath, stat);
81
- }
82
- export async function assertPrivateDirectory(targetPath) {
83
- const stat = inspectDirectoryIdentitySync(targetPath);
84
- if (process.platform === "win32")
85
- await inspectCreationDirectory(targetPath, true);
86
- else {
87
- assertPrivatePosixDirectory(stat);
88
- assertDarwinCreationDirectoryAcl(targetPath, stat);
89
- }
90
- inspectDirectoryIdentitySync(targetPath, stat);
91
- }
92
- export function protectCreatedFileSync(fd, targetPath, parentIdentity) {
93
- const protect = creationOperation("protectPrivateWindowsFile");
94
- return (protect ? protect(fd, targetPath, parentIdentity)
95
- : protectPrivateWindowsFileCommandSync(fd, targetPath, parentIdentity)).identity;
96
- }
97
- export async function protectCreatedFile(fd, targetPath, parentIdentity) {
98
- const protect = creationOperation("protectPrivateWindowsFile");
99
- return (protect ? protect(fd, targetPath, parentIdentity)
100
- : await protectPrivateWindowsFileCommand(fd, targetPath, parentIdentity)).identity;
101
- }
102
- export function verifyCreatedFileSync(fd, targetPath, identity, parentIdentity, links = 1) {
103
- const verify = creationOperation("verifyPrivateWindowsFile");
104
- if (verify)
105
- verify(fd, targetPath, identity, parentIdentity, links);
106
- else
107
- verifyPrivateWindowsFileCommandSync(fd, targetPath, identity, parentIdentity, links);
108
- }
109
- export async function verifyCreatedFile(fd, targetPath, identity, parentIdentity, links = 1) {
110
- const verify = creationOperation("verifyPrivateWindowsFile");
111
- if (verify)
112
- verify(fd, targetPath, identity, parentIdentity, links);
113
- else
114
- await verifyPrivateWindowsFileCommand(fd, targetPath, identity, parentIdentity, links);
115
- }
@@ -1,4 +0,0 @@
1
- export declare function readFileStoreCopySource(params: {
2
- sourcePath: string;
3
- maxBytes: number;
4
- }): Promise<Buffer>;
@@ -1,31 +0,0 @@
1
- import fs from "node:fs";
2
- import { FsSafeError } from "./errors.js";
3
- import { assertFileStoreMaxBytes } from "./file-store-limit.js";
4
- import { readRegularFile } from "./regular-file.js";
5
- import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
6
- export async function readFileStoreCopySource(params) {
7
- assertNoWindowsPathAlias(params.sourcePath, "filesystem", "source path uses a Windows filesystem namespace alias");
8
- const sourceStat = fs.lstatSync(params.sourcePath);
9
- if (sourceStat.isSymbolicLink() || !sourceStat.isFile()) {
10
- throw new FsSafeError("not-file", "source path is not a file");
11
- }
12
- assertFileStoreMaxBytes(sourceStat.size, params.maxBytes);
13
- try {
14
- return (await readRegularFile({ filePath: params.sourcePath, maxBytes: params.maxBytes }))
15
- .buffer;
16
- }
17
- catch (error) {
18
- const message = error instanceof Error ? error.message : String(error);
19
- if (message.includes("regular file") || message.includes("not a regular file")) {
20
- throw new FsSafeError("not-file", "source path is not a file", {
21
- cause: error instanceof Error ? error : undefined,
22
- });
23
- }
24
- if (message.includes(`exceeds ${params.maxBytes} bytes`)) {
25
- throw new FsSafeError("too-large", `file exceeds maximum size of ${params.maxBytes} bytes`, {
26
- cause: error instanceof Error ? error : undefined,
27
- });
28
- }
29
- throw error;
30
- }
31
- }
@@ -1 +0,0 @@
1
- export declare function assertFileStoreMaxBytes(size: number, limit: number | undefined): void;
@@ -1,7 +0,0 @@
1
- import { FsSafeError } from "./errors.js";
2
- // Store operation boundaries admit the primitive limit before reaching this helper.
3
- export function assertFileStoreMaxBytes(size, limit) {
4
- if (limit !== undefined && size > limit) {
5
- throw new FsSafeError("too-large", `file exceeds maximum size of ${limit} bytes`);
6
- }
7
- }
@@ -1,2 +0,0 @@
1
- export declare function assertRelativePath(relativePath: string): string;
2
- export declare function resolveStorePath(rootDir: string, relativePath: string): string;
@@ -1,30 +0,0 @@
1
- import path from "node:path";
2
- import { FsSafeError } from "./errors.js";
3
- import { isPathInside, splitSafeRelativePath } from "./path.js";
4
- export function assertRelativePath(relativePath) {
5
- const raw = relativePath.trim();
6
- if (!raw || raw !== relativePath) {
7
- throw new FsSafeError("invalid-path", "store key must be non-empty and unpadded");
8
- }
9
- const segments = splitSafeRelativePath(raw);
10
- if (segments.length === 0 ||
11
- segments.join("/") !== raw ||
12
- raw.normalize("NFC") !== raw ||
13
- segments.some((segment) => /[ .]$/u.test(segment))) {
14
- throw new FsSafeError("invalid-path", "store key must use one canonical relative spelling");
15
- }
16
- return raw;
17
- }
18
- export function resolveStorePath(rootDir, relativePath) {
19
- const key = assertRelativePath(relativePath);
20
- // FileStore constructors snapshot an absolute root before this helper runs.
21
- // Do not re-resolve it: Node drops the trailing separator from an exact
22
- // Windows namespace drive root such as `\\?\C:\`.
23
- const root = path.isAbsolute(rootDir) ? rootDir : path.resolve(rootDir);
24
- // The immutable key already passed segment validation; keep the containment check.
25
- const target = path.resolve(root, key);
26
- if (!isPathInside(root, target)) {
27
- throw new FsSafeError("outside-workspace", "relative path escapes root");
28
- }
29
- return target;
30
- }
@@ -1 +0,0 @@
1
- export declare function admitStandalonePublicationPath(value: string, message?: string): string;
@@ -1,6 +0,0 @@
1
- import { anchorWindowsDriveRelativePath, assertNoWindowsPathAlias, } from "./windows-path-alias.js";
2
- export function admitStandalonePublicationPath(value, message) {
3
- const admittedPath = anchorWindowsDriveRelativePath(value);
4
- assertNoWindowsPathAlias(admittedPath, "filesystem", message);
5
- return admittedPath;
6
- }
@@ -1,14 +0,0 @@
1
- import { type BigIntStats, type Stats } from "node:fs";
2
- export type TempWorkspaceIdentity = Readonly<{
3
- dev: bigint;
4
- ino: bigint;
5
- }>;
6
- export type TempWorkspaceNumericIdentity = Readonly<{
7
- dev: number;
8
- ino: number;
9
- }>;
10
- export type TempWorkspaceIdentityStat = BigIntStats | Stats;
11
- export declare const TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY: boolean;
12
- export declare function projectTempWorkspaceNumericIdentity(identity: TempWorkspaceIdentity): TempWorkspaceNumericIdentity | undefined;
13
- export declare function inspectTempWorkspaceDescriptorIdentitySync(fd: number, expected: TempWorkspaceIdentity, numeric: TempWorkspaceNumericIdentity | undefined): TempWorkspaceIdentityStat;
14
- export declare function inspectTempWorkspaceDirectoryIdentitySync(dir: string, expected: TempWorkspaceIdentity, numeric: TempWorkspaceNumericIdentity | undefined): TempWorkspaceIdentityStat;
@@ -1,41 +0,0 @@
1
- import fsSync, {} from "node:fs";
2
- import { observeDirectoryIdentitySync } from "./directory-guard.js";
3
- import { FsSafeError } from "./errors.js";
4
- import { recordFileObservationFailure } from "./file-observation.js";
5
- import { inspectFileIdentitySync } from "./strict-file-identity.js";
6
- export const TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY = process.platform === "linux" || process.platform === "darwin";
7
- export function projectTempWorkspaceNumericIdentity(identity) {
8
- const dev = Number(identity.dev);
9
- const ino = Number(identity.ino);
10
- if (!Number.isSafeInteger(dev) || dev < 0 || BigInt(dev) !== identity.dev ||
11
- !Number.isSafeInteger(ino) || ino < 0 || BigInt(ino) !== identity.ino) {
12
- return undefined;
13
- }
14
- return Object.freeze({ dev, ino });
15
- }
16
- function identityMismatch() {
17
- const error = new FsSafeError("path-mismatch", "file identity changed or could not be verified");
18
- recordFileObservationFailure(error, "identity");
19
- throw error;
20
- }
21
- function inspectNumericIdentity(current, expected) {
22
- if (!Number.isSafeInteger(current.dev) || current.dev < 0 || current.dev !== expected.dev ||
23
- !Number.isSafeInteger(current.ino) || current.ino < 0 || current.ino !== expected.ino) {
24
- identityMismatch();
25
- }
26
- return current;
27
- }
28
- export function inspectTempWorkspaceDescriptorIdentitySync(fd, expected, numeric) {
29
- // A malformed or mismatched numeric observation is definite and never
30
- // retried. Unsafe receipts and other platforms retain exact replay.
31
- if (TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY && numeric) {
32
- return inspectNumericIdentity(fsSync.fstatSync(fd), numeric);
33
- }
34
- return inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), expected);
35
- }
36
- export function inspectTempWorkspaceDirectoryIdentitySync(dir, expected, numeric) {
37
- if (TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY && numeric) {
38
- return inspectNumericIdentity(observeDirectoryIdentitySync(dir), numeric);
39
- }
40
- return inspectFileIdentitySync(() => observeDirectoryIdentitySync(dir, { bigint: true }), expected);
41
- }
@@ -1,3 +0,0 @@
1
- import type { BigIntStats, Stats } from "node:fs";
2
- export declare function validateTempWorkspaceDirMode(mode: number): void;
3
- export declare function assertTrustedTempWorkspaceDirectory(stat: Pick<BigIntStats, "uid" | "mode"> | Pick<Stats, "uid" | "mode">, uid: number | undefined, child?: boolean): void;
@@ -1,32 +0,0 @@
1
- import { FsSafeError } from "./errors.js";
2
- export function validateTempWorkspaceDirMode(mode) {
3
- if (!Number.isInteger(mode) || mode < 0 || mode > 0o7777) {
4
- throw new FsSafeError("insecure-permissions", "temp workspace dirMode must be permission bits");
5
- }
6
- if (process.platform !== "win32" && (mode & 0o022) !== 0) {
7
- throw new FsSafeError("insecure-permissions", "temp workspace must not be group/world writable");
8
- }
9
- }
10
- export function assertTrustedTempWorkspaceDirectory(stat, uid, child = false) {
11
- if (uid === undefined)
12
- return;
13
- const ownedByUser = typeof stat.uid === "bigint" ? stat.uid === BigInt(uid) : stat.uid === uid;
14
- const ownedByRoot = typeof stat.uid === "bigint" ? stat.uid === 0n : stat.uid === 0;
15
- if (!ownedByUser && (child || !ownedByRoot)) {
16
- throw new FsSafeError("not-owned", "temp workspace directory has an untrusted owner");
17
- }
18
- // A root/current-user-owned sticky directory protects children owned by us,
19
- // including the usual shared system temp directory. Never chmod that parent.
20
- const writable = typeof stat.mode === "bigint"
21
- ? (stat.mode & 18n) !== 0n
22
- : Number.isSafeInteger(stat.mode) && stat.mode >= 0 && (stat.mode & 0o022) !== 0;
23
- const sticky = typeof stat.mode === "bigint"
24
- ? (stat.mode & 512n) !== 0n
25
- : Number.isSafeInteger(stat.mode) && stat.mode >= 0 && (stat.mode & 0o1000) !== 0;
26
- if (typeof stat.mode !== "bigint" && (!Number.isSafeInteger(stat.mode) || stat.mode < 0)) {
27
- throw new FsSafeError("insecure-permissions", "temp workspace directory permissions are invalid");
28
- }
29
- if (writable && (child || !sticky)) {
30
- throw new FsSafeError("insecure-permissions", "temp workspace directory is group/world writable without sticky protection");
31
- }
32
- }