@vibe-agent-toolkit/utils 0.2.0-rc.11 → 0.2.0-rc.12
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/README.md +111 -9
- package/dist/archive/archive.d.ts +129 -0
- package/dist/archive/archive.d.ts.map +1 -0
- package/dist/archive/archive.js +223 -0
- package/dist/archive/archive.js.map +1 -0
- package/dist/archive/entries.d.ts +132 -0
- package/dist/archive/entries.d.ts.map +1 -0
- package/dist/archive/entries.js +223 -0
- package/dist/archive/entries.js.map +1 -0
- package/dist/archive/limits.d.ts +39 -0
- package/dist/archive/limits.d.ts.map +1 -0
- package/dist/archive/limits.js +59 -0
- package/dist/archive/limits.js.map +1 -0
- package/dist/archive/records.d.ts +14 -0
- package/dist/archive/records.d.ts.map +1 -0
- package/dist/archive/records.js +3 -0
- package/dist/archive/records.js.map +1 -0
- package/dist/archive/tar-reader.d.ts +18 -0
- package/dist/archive/tar-reader.d.ts.map +1 -0
- package/dist/archive/tar-reader.js +173 -0
- package/dist/archive/tar-reader.js.map +1 -0
- package/dist/archive/zip-reader.d.ts +76 -0
- package/dist/archive/zip-reader.d.ts.map +1 -0
- package/dist/archive/zip-reader.js +376 -0
- package/dist/archive/zip-reader.js.map +1 -0
- package/dist/archive.d.ts +14 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +12 -0
- package/dist/archive.js.map +1 -0
- package/dist/asset-reference.d.ts +17 -1
- package/dist/asset-reference.d.ts.map +1 -1
- package/dist/asset-reference.js +31 -7
- package/dist/asset-reference.js.map +1 -1
- package/dist/dirent-kind.d.ts +13 -40
- package/dist/dirent-kind.d.ts.map +1 -1
- package/dist/dirent-kind.js +21 -69
- package/dist/dirent-kind.js.map +1 -1
- package/dist/entrypoint.d.ts +2 -2
- package/dist/entrypoint.d.ts.map +1 -1
- package/dist/entrypoint.js +4 -3
- package/dist/entrypoint.js.map +1 -1
- package/dist/env-flag.d.ts +2 -0
- package/dist/env-flag.d.ts.map +1 -1
- package/dist/env-flag.js +2 -0
- package/dist/env-flag.js.map +1 -1
- package/dist/errors/confirmed-absent.d.ts +41 -0
- package/dist/errors/confirmed-absent.d.ts.map +1 -0
- package/dist/errors/confirmed-absent.js +43 -0
- package/dist/errors/confirmed-absent.js.map +1 -0
- package/dist/errors/errno-table.d.ts +177 -0
- package/dist/errors/errno-table.d.ts.map +1 -0
- package/dist/errors/errno-table.js +343 -0
- package/dist/errors/errno-table.js.map +1 -0
- package/dist/errors/fs-boundary.d.ts +36 -0
- package/dist/errors/fs-boundary.d.ts.map +1 -0
- package/dist/errors/fs-boundary.js +121 -0
- package/dist/errors/fs-boundary.js.map +1 -0
- package/dist/errors/fs-fault.d.ts +176 -0
- package/dist/errors/fs-fault.d.ts.map +1 -0
- package/dist/errors/fs-fault.js +215 -0
- package/dist/errors/fs-fault.js.map +1 -0
- package/dist/errors/git-errno.d.ts +13 -0
- package/dist/errors/git-errno.d.ts.map +1 -0
- package/dist/errors/git-errno.js +45 -0
- package/dist/errors/git-errno.js.map +1 -0
- package/dist/errors/path-present.d.ts +72 -0
- package/dist/errors/path-present.d.ts.map +1 -0
- package/dist/errors/path-present.js +110 -0
- package/dist/errors/path-present.js.map +1 -0
- package/dist/errors/rag-index-empty.d.ts +16 -0
- package/dist/errors/rag-index-empty.d.ts.map +1 -0
- package/dist/errors/rag-index-empty.js +16 -0
- package/dist/errors/rag-index-empty.js.map +1 -0
- package/dist/errors/suppressed-faults.d.ts +34 -0
- package/dist/errors/suppressed-faults.d.ts.map +1 -0
- package/dist/errors/suppressed-faults.js +66 -0
- package/dist/errors/suppressed-faults.js.map +1 -0
- package/dist/file-crawler.d.ts +13 -18
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +140 -24
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs-utils.d.ts +75 -50
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +148 -158
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +4 -4
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +4 -4
- package/dist/fs.js.map +1 -1
- package/dist/git-snapshot.d.ts +25 -17
- package/dist/git-snapshot.d.ts.map +1 -1
- package/dist/git-snapshot.js +65 -11
- package/dist/git-snapshot.js.map +1 -1
- package/dist/git-tracker.d.ts +11 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +29 -6
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +7 -35
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +1 -1
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +5 -4
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/in-order.d.ts +93 -0
- package/dist/in-order.d.ts.map +1 -0
- package/dist/in-order.js +165 -0
- package/dist/in-order.js.map +1 -0
- package/dist/index.d.ts +20 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -1
- package/dist/kill-process-tree.d.ts +43 -0
- package/dist/kill-process-tree.d.ts.map +1 -0
- package/dist/kill-process-tree.js +48 -0
- package/dist/kill-process-tree.js.map +1 -0
- package/dist/link-following.d.ts +115 -0
- package/dist/link-following.d.ts.map +1 -0
- package/dist/link-following.js +283 -0
- package/dist/link-following.js.map +1 -0
- package/dist/listing-refusal.d.ts +44 -0
- package/dist/listing-refusal.d.ts.map +1 -1
- package/dist/listing-refusal.js +79 -2
- package/dist/listing-refusal.js.map +1 -1
- package/dist/name-fold.d.ts +25 -0
- package/dist/name-fold.d.ts.map +1 -0
- package/dist/name-fold.js +40 -0
- package/dist/name-fold.js.map +1 -0
- package/dist/path-containment.d.ts +3 -1
- package/dist/path-containment.d.ts.map +1 -1
- package/dist/path-containment.js +13 -7
- package/dist/path-containment.js.map +1 -1
- package/dist/path-utils.d.ts +14 -17
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +37 -55
- package/dist/path-utils.js.map +1 -1
- package/dist/process.d.ts +2 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +2 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +8 -9
- package/dist/project-utils.js.map +1 -1
- package/dist/safe-exec.d.ts +6 -2
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +1 -1
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts.map +1 -1
- package/dist/skill-targets.js +3 -1
- package/dist/skill-targets.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +0 -15
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +11 -50
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/special-file.d.ts +18 -0
- package/dist/special-file.d.ts.map +1 -0
- package/dist/special-file.js +21 -0
- package/dist/special-file.js.map +1 -0
- package/dist/terminating-signals.d.ts +129 -0
- package/dist/terminating-signals.d.ts.map +1 -0
- package/dist/terminating-signals.js +210 -0
- package/dist/terminating-signals.js.map +1 -0
- package/dist/test-helpers.d.ts +9 -5
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +18 -21
- package/dist/test-helpers.js.map +1 -1
- package/dist/test-scratch-guard.d.ts +34 -0
- package/dist/test-scratch-guard.d.ts.map +1 -0
- package/dist/test-scratch-guard.js +45 -0
- package/dist/test-scratch-guard.js.map +1 -0
- package/dist/testing/archive-fixtures.d.ts +147 -0
- package/dist/testing/archive-fixtures.d.ts.map +1 -0
- package/dist/testing/archive-fixtures.js +449 -0
- package/dist/testing/archive-fixtures.js.map +1 -0
- package/dist/testing/executables.d.ts +6 -0
- package/dist/testing/executables.d.ts.map +1 -1
- package/dist/testing/executables.js +20 -9
- package/dist/testing/executables.js.map +1 -1
- package/dist/testing/fault-fs-preload.d.ts +16 -0
- package/dist/testing/fault-fs-preload.d.ts.map +1 -0
- package/dist/testing/fault-fs-preload.js +22 -0
- package/dist/testing/fault-fs-preload.js.map +1 -0
- package/dist/testing/fault-fs.d.ts +115 -0
- package/dist/testing/fault-fs.d.ts.map +1 -0
- package/dist/testing/fault-fs.js +411 -0
- package/dist/testing/fault-fs.js.map +1 -0
- package/dist/testing/fault-spec.d.ts +83 -0
- package/dist/testing/fault-spec.d.ts.map +1 -0
- package/dist/testing/fault-spec.js +132 -0
- package/dist/testing/fault-spec.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -1
- package/dist/testing/hostile-tree.js +3 -4
- package/dist/testing/hostile-tree.js.map +1 -1
- package/dist/testing/platform-gates.d.ts +8 -0
- package/dist/testing/platform-gates.d.ts.map +1 -1
- package/dist/testing/platform-gates.js +24 -0
- package/dist/testing/platform-gates.js.map +1 -1
- package/dist/testing/temp-dir.d.ts +31 -0
- package/dist/testing/temp-dir.d.ts.map +1 -1
- package/dist/testing/temp-dir.js +63 -0
- package/dist/testing/temp-dir.js.map +1 -1
- package/dist/testing/tier-timeout.d.ts +34 -0
- package/dist/testing/tier-timeout.d.ts.map +1 -0
- package/dist/testing/tier-timeout.js +45 -0
- package/dist/testing/tier-timeout.js.map +1 -0
- package/dist/testing/tree-snapshot.d.ts +71 -0
- package/dist/testing/tree-snapshot.d.ts.map +1 -0
- package/dist/testing/tree-snapshot.js +163 -0
- package/dist/testing/tree-snapshot.js.map +1 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +8 -1
- package/dist/testing.js.map +1 -1
- package/dist/text-file.d.ts +14 -1
- package/dist/text-file.d.ts.map +1 -1
- package/dist/text-file.js +41 -8
- package/dist/text-file.js.map +1 -1
- package/dist/timing-dump.d.ts.map +1 -1
- package/dist/timing-dump.js +2 -10
- package/dist/timing-dump.js.map +1 -1
- package/dist/tree-change/apply-decisions.d.ts +101 -0
- package/dist/tree-change/apply-decisions.d.ts.map +1 -0
- package/dist/tree-change/apply-decisions.js +139 -0
- package/dist/tree-change/apply-decisions.js.map +1 -0
- package/dist/tree-change/apply.d.ts +98 -0
- package/dist/tree-change/apply.d.ts.map +1 -0
- package/dist/tree-change/apply.js +364 -0
- package/dist/tree-change/apply.js.map +1 -0
- package/dist/tree-change/copy-decisions.d.ts +58 -0
- package/dist/tree-change/copy-decisions.d.ts.map +1 -0
- package/dist/tree-change/copy-decisions.js +66 -0
- package/dist/tree-change/copy-decisions.js.map +1 -0
- package/dist/tree-change/copy-tree.d.ts +125 -0
- package/dist/tree-change/copy-tree.d.ts.map +1 -0
- package/dist/tree-change/copy-tree.js +279 -0
- package/dist/tree-change/copy-tree.js.map +1 -0
- package/dist/tree-change/files.d.ts +167 -0
- package/dist/tree-change/files.d.ts.map +1 -0
- package/dist/tree-change/files.js +423 -0
- package/dist/tree-change/files.js.map +1 -0
- package/dist/tree-change/followed-walk.d.ts +54 -0
- package/dist/tree-change/followed-walk.d.ts.map +1 -0
- package/dist/tree-change/followed-walk.js +81 -0
- package/dist/tree-change/followed-walk.js.map +1 -0
- package/dist/tree-change/host-chain.d.ts +48 -0
- package/dist/tree-change/host-chain.d.ts.map +1 -0
- package/dist/tree-change/host-chain.js +85 -0
- package/dist/tree-change/host-chain.js.map +1 -0
- package/dist/tree-change/identity-compare.d.ts +61 -0
- package/dist/tree-change/identity-compare.d.ts.map +1 -0
- package/dist/tree-change/identity-compare.js +115 -0
- package/dist/tree-change/identity-compare.js.map +1 -0
- package/dist/tree-change/identity.d.ts +62 -0
- package/dist/tree-change/identity.d.ts.map +1 -0
- package/dist/tree-change/identity.js +152 -0
- package/dist/tree-change/identity.js.map +1 -0
- package/dist/tree-change/index.d.ts +24 -0
- package/dist/tree-change/index.d.ts.map +1 -0
- package/dist/tree-change/index.js +14 -0
- package/dist/tree-change/index.js.map +1 -0
- package/dist/tree-change/link-chain.d.ts +180 -0
- package/dist/tree-change/link-chain.d.ts.map +1 -0
- package/dist/tree-change/link-chain.js +377 -0
- package/dist/tree-change/link-chain.js.map +1 -0
- package/dist/tree-change/link-containment.d.ts +105 -0
- package/dist/tree-change/link-containment.d.ts.map +1 -0
- package/dist/tree-change/link-containment.js +196 -0
- package/dist/tree-change/link-containment.js.map +1 -0
- package/dist/tree-change/link-walk.d.ts +49 -0
- package/dist/tree-change/link-walk.d.ts.map +1 -0
- package/dist/tree-change/link-walk.js +69 -0
- package/dist/tree-change/link-walk.js.map +1 -0
- package/dist/tree-change/plan.d.ts +206 -0
- package/dist/tree-change/plan.d.ts.map +1 -0
- package/dist/tree-change/plan.js +320 -0
- package/dist/tree-change/plan.js.map +1 -0
- package/dist/tree-change/readable-tree.d.ts +44 -0
- package/dist/tree-change/readable-tree.d.ts.map +1 -0
- package/dist/tree-change/readable-tree.js +52 -0
- package/dist/tree-change/readable-tree.js.map +1 -0
- package/dist/tree-change/rollback-error.d.ts +29 -0
- package/dist/tree-change/rollback-error.d.ts.map +1 -0
- package/dist/tree-change/rollback-error.js +31 -0
- package/dist/tree-change/rollback-error.js.map +1 -0
- package/dist/tree-change/staging-names.d.ts +27 -0
- package/dist/tree-change/staging-names.d.ts.map +1 -0
- package/dist/tree-change/staging-names.js +40 -0
- package/dist/tree-change/staging-names.js.map +1 -0
- package/dist/tree-change/tree-walk.d.ts +81 -0
- package/dist/tree-change/tree-walk.d.ts.map +1 -0
- package/dist/tree-change/tree-walk.js +212 -0
- package/dist/tree-change/tree-walk.js.map +1 -0
- package/dist/win32-namespace.d.ts +24 -0
- package/dist/win32-namespace.d.ts.map +1 -0
- package/dist/win32-namespace.js +31 -0
- package/dist/win32-namespace.js.map +1 -0
- package/dist/yaml/surgical-yaml.d.ts +12 -3
- package/dist/yaml/surgical-yaml.d.ts.map +1 -1
- package/dist/yaml/surgical-yaml.js +16 -6
- package/dist/yaml/surgical-yaml.js.map +1 -1
- package/eslint/README.md +12 -4
- package/eslint/rules/fs-binding-scan.cjs +278 -0
- package/eslint/rules/no-adhoc-errno.cjs +364 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +2 -3
- package/eslint/rules/no-blind-catch.cjs +1 -1
- package/eslint/rules/no-destructive-fs.cjs +359 -0
- package/eslint/rules/no-existssync.cjs +70 -0
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +3 -2
- package/eslint/rules/no-link-following-fs.cjs +68 -0
- package/eslint/rules/no-literal-process-exit.cjs +3 -16
- package/eslint/rules/no-literal-test-timeout.cjs +214 -0
- package/eslint/rules/no-raw-signal-listener.cjs +224 -0
- package/eslint/rules/no-stdout-outside-writer.cjs +135 -0
- package/package.json +10 -2
- package/dist/errors/errno.d.ts +0 -51
- package/dist/errors/errno.d.ts.map +0 -1
- package/dist/errors/errno.js +0 -112
- package/dist/errors/errno.js.map +0 -1
- package/eslint/rules/no-fs-promises-cp.cjs +0 -43
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE resolver of a symbolic-link chain: where a path leads, walked hop by hop from what
|
|
3
|
+
* `lstat` and `readlink` say alone, so nothing is handed to the kernel until the whole chain has
|
|
4
|
+
* been read as text and shown to be local.
|
|
5
|
+
*
|
|
6
|
+
* That order is the point. A Windows kernel asked to resolve a path through a link to
|
|
7
|
+
* `\\host\share` opens an SMB session and offers the operator's NTLM credentials to that host
|
|
8
|
+
* before any answer comes back — so a `realpath` or a `stat` that follows links is a SECOND lock,
|
|
9
|
+
* asked only once this walk has proved the chain local, never the first.
|
|
10
|
+
*
|
|
11
|
+
* Pure: the caller supplies the path {@link PathFlavor} and the probe, so a test runs Windows
|
|
12
|
+
* semantics on any host. The flavor decides two things the kernel decides:
|
|
13
|
+
*
|
|
14
|
+
* - what separates a link text's components, and what makes it absolute or a network path
|
|
15
|
+
* (`\\host\share`, `\\?\UNC\…`, `\\.\…`, `//host/share` under win32; on POSIX a backslash is a
|
|
16
|
+
* character of a name and `//x` is the local root);
|
|
17
|
+
* - what `..` means. POSIX resolves a link before the `..` after it, physically (the parent of
|
|
18
|
+
* where the link LED). Windows joins a relative link text onto the link's directory and collapses
|
|
19
|
+
* every `..` lexically before reparsing — `x\..\y` is `y` whether `x` exists, is a file or a link —
|
|
20
|
+
* so under win32 each splice is collapsed by string first, and every component it leaves is examined.
|
|
21
|
+
*
|
|
22
|
+
* Running out of hops ({@link MAX_LINK_HOPS}) is a refusal, never an answer: a chain the walk did
|
|
23
|
+
* not finish reading is a chain the kernel would finish for it. A CYCLE is different, and is told
|
|
24
|
+
* apart exactly: the walk meets the same link with the same text left to read, so every text in
|
|
25
|
+
* the cycle has been read and is local, and the kernel can only answer `ELOOP` — it resolves to
|
|
26
|
+
* nothing, as a dangling link does, in every lane.
|
|
27
|
+
*
|
|
28
|
+
* An untrusted tree ({@link ChainRule.outside} `refuse`) may be read on either host — a clone made
|
|
29
|
+
* here is installed there — so its every link text is also held to the OTHER flavor's rules
|
|
30
|
+
* ({@link untrustedTextRefusal}): a text that is absolute, drive-relative or a network path on
|
|
31
|
+
* either kernel is refused on both.
|
|
32
|
+
*/
|
|
33
|
+
import { posix } from 'node:path';
|
|
34
|
+
import { toForwardSlashAnyPlatform } from '../path-core.js';
|
|
35
|
+
/** The flavor of the host VAT runs on — the edge where a pure caller's flavor is bound. */
|
|
36
|
+
export const HOST_PATH_FLAVOR = process.platform === 'win32' ? 'win32' : 'posix';
|
|
37
|
+
/**
|
|
38
|
+
* How many links one resolution follows before it refuses. Windows' limit — 63 reparse points in
|
|
39
|
+
* one path — is the larger of the two kernels' (Linux's `MAXSYMLINKS` is 40), so no chain a kernel
|
|
40
|
+
* would still follow is ever cut short and answered.
|
|
41
|
+
*/
|
|
42
|
+
export const MAX_LINK_HOPS = 63;
|
|
43
|
+
/** `X:\…` / `X:/…`, slashes already forward: the one absolute form of a link text a Windows kernel resolves locally. */
|
|
44
|
+
const WIN32_DRIVE_ABSOLUTE = /^([A-Za-z]:)\//;
|
|
45
|
+
/** `\\?\C:\…` / `\\.\C:\…`, slashes already forward: a local drive through the Win32 namespace — a NAMED path only. */
|
|
46
|
+
const WIN32_NAMESPACED_DRIVE = /^\/\/[?.]\/([A-Za-z]:)(?:\/|$)/;
|
|
47
|
+
/** `\\?\Volume{GUID}\…`, slashes already forward: a local volume by its GUID — a NAMED path only. */
|
|
48
|
+
const WIN32_VOLUME = /^\/\/\?\/(Volume\{[0-9A-Fa-f-]+\})(?:\/|$)/;
|
|
49
|
+
/** `\\host\share\…` / `\\?\UNC\host\share\…`, slashes already forward: the share a NAMED path (never a link's text) hangs from. */
|
|
50
|
+
const WIN32_SHARE_ROOT = /^\/\/(?:\?\/UNC\/)?([^/?.][^/]*\/[^/]+)(?:\/|$)/i;
|
|
51
|
+
const WIN32_DRIVE_ROOT = /^([A-Za-z]:)(?:\/|$)/;
|
|
52
|
+
const WIN32_DRIVE_RELATIVE = /^[A-Za-z]:/;
|
|
53
|
+
/** The non-empty, non-`.` components of `text`, spelled with POSIX separators (a win32 text is converted first). */
|
|
54
|
+
function partsOf(text) {
|
|
55
|
+
return text.split(posix.sep).filter((part) => part !== '' && part !== '.');
|
|
56
|
+
}
|
|
57
|
+
const DRIVE_RELATIVE = 'drive-relative';
|
|
58
|
+
const CLIMBS_OUT = 'climbs-out';
|
|
59
|
+
/**
|
|
60
|
+
* How a link's TEXT reads under `flavor` — an ALLOWLIST under win32, never a list of network
|
|
61
|
+
* spellings: a text is local only when it is relative (no leading separator, no drive) or
|
|
62
|
+
* drive-absolute (`X:\…`). Every other form — UNC, `\\?\…`, `\\.\…`, the NT object paths libuv's
|
|
63
|
+
* `readlink` hands back verbatim (`\??\GLOBALROOT\Device\Mup\…`, `\??\Volume{…}\…`, `\Device\…`,
|
|
64
|
+
* `\DosDevices\…`, `\GLOBAL??\…`), a root-relative `\x`, and anything not recognised — is a
|
|
65
|
+
* network or device path, which the walk never resolves and the kernel is never asked about.
|
|
66
|
+
*
|
|
67
|
+
* @param text - A link's text, verbatim
|
|
68
|
+
* @param flavor - Whose rules
|
|
69
|
+
*/
|
|
70
|
+
function textForm(text, flavor) {
|
|
71
|
+
if (flavor === 'posix') {
|
|
72
|
+
return text.startsWith('/') ? { form: 'rooted', rooted: { prefix: '/', parts: partsOf(text) } } : { form: 'relative', parts: partsOf(text) };
|
|
73
|
+
}
|
|
74
|
+
const slashed = toForwardSlashAnyPlatform(text);
|
|
75
|
+
const drive = WIN32_DRIVE_ABSOLUTE.exec(slashed);
|
|
76
|
+
if (drive?.[1] !== undefined)
|
|
77
|
+
return { form: 'rooted', rooted: { prefix: `${drive[1].toUpperCase()}/`, parts: partsOf(slashed.slice(drive[0].length)) } };
|
|
78
|
+
if (WIN32_DRIVE_RELATIVE.test(slashed))
|
|
79
|
+
return { form: DRIVE_RELATIVE };
|
|
80
|
+
if (slashed.startsWith('/'))
|
|
81
|
+
return { form: 'network' };
|
|
82
|
+
return { form: 'relative', parts: partsOf(slashed) };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* How a NAMED path (the start or the root — what the operator or VAT spelled, never a link's text)
|
|
86
|
+
* reads. Under win32 it may hang from a drive, a namespaced drive, a volume or a share: a tree on
|
|
87
|
+
* `\\host\share` is the operator's choice, while a link's text naming any of them is never
|
|
88
|
+
* followed ({@link textForm}). A rooted-but-driveless `\x` hangs from `drive`.
|
|
89
|
+
*/
|
|
90
|
+
function anchoredForm(path, flavor, drive) {
|
|
91
|
+
if (flavor === 'posix')
|
|
92
|
+
return textForm(path, flavor);
|
|
93
|
+
const slashed = toForwardSlashAnyPlatform(path);
|
|
94
|
+
const rooted = (prefix, rest) => ({ form: 'rooted', rooted: { prefix, parts: partsOf(rest) } });
|
|
95
|
+
const anchors = [
|
|
96
|
+
[WIN32_DRIVE_ROOT, (name) => `${name.toUpperCase()}/`],
|
|
97
|
+
[WIN32_NAMESPACED_DRIVE, (name) => `${name.toUpperCase()}/`],
|
|
98
|
+
[WIN32_VOLUME, (name) => `//?/${name}/`],
|
|
99
|
+
[WIN32_SHARE_ROOT, (name) => `//${name}/`],
|
|
100
|
+
];
|
|
101
|
+
for (const [pattern, prefixOf] of anchors) {
|
|
102
|
+
const match = pattern.exec(slashed);
|
|
103
|
+
if (match?.[1] !== undefined)
|
|
104
|
+
return rooted(prefixOf(match[1]), slashed.slice(match[0].length));
|
|
105
|
+
}
|
|
106
|
+
if (slashed.startsWith('//'))
|
|
107
|
+
return { form: 'network' };
|
|
108
|
+
return slashed.startsWith('/') ? rooted(drive, slashed) : textForm(path, flavor);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The root a NAMED path hangs from — `/`, a drive's `C:/`, a share's `//host/share/` — for a walk
|
|
112
|
+
* of the user's own path, which may lead anywhere local.
|
|
113
|
+
*
|
|
114
|
+
* @param path - Absolute, forward- or back-slashed
|
|
115
|
+
* @param flavor - Whose rules
|
|
116
|
+
* @throws TypeError for a path that is not absolute under `flavor`
|
|
117
|
+
*/
|
|
118
|
+
export function namedPathRoot(path, flavor) {
|
|
119
|
+
const form = anchoredForm(path, flavor, '/');
|
|
120
|
+
if (form.form !== 'rooted')
|
|
121
|
+
throw new TypeError(`namedPathRoot: not an absolute local path: ${path}`);
|
|
122
|
+
return form.rooted.prefix;
|
|
123
|
+
}
|
|
124
|
+
const FLAVORS = ['posix', 'win32'];
|
|
125
|
+
/**
|
|
126
|
+
* Why a link text in an UNTRUSTED tree is refused by its text alone, under EITHER flavor — the rule
|
|
127
|
+
* a chain in a `refuse` tree is held to at every hop, and the one a caller that has only the text
|
|
128
|
+
* (a git clone reading link blobs before checkout) asks directly.
|
|
129
|
+
*
|
|
130
|
+
* A text is also `unportable` when it holds a NUL (the OS cuts the text short there, so the walk
|
|
131
|
+
* would judge bytes no kernel sees) or a component ending in a dot or a space (Win32 strips those,
|
|
132
|
+
* so `.. ` would climb where the walk read a name) — the win32 rule held on every host.
|
|
133
|
+
*
|
|
134
|
+
* @param text - A link's text, verbatim
|
|
135
|
+
* @returns why either kernel would not read it as a relative text inside the tree; `undefined` for one it would
|
|
136
|
+
*/
|
|
137
|
+
export function untrustedTextRefusal(text) {
|
|
138
|
+
if (isUnportableText(text))
|
|
139
|
+
return 'unportable';
|
|
140
|
+
const [posixForm, win32Form] = FLAVORS.map((flavor) => textForm(text, flavor).form);
|
|
141
|
+
// `/etc/x` is a POSIX absolute path (and a device path to Windows); `\\host\share`, `\??\…` and
|
|
142
|
+
// `//host/share` name a network or device path to Windows whatever POSIX reads them as.
|
|
143
|
+
if (win32Form === 'network')
|
|
144
|
+
return posixForm === 'rooted' && !toForwardSlashAnyPlatform(text).startsWith('//') ? 'absolute' : 'network';
|
|
145
|
+
if (win32Form === DRIVE_RELATIVE)
|
|
146
|
+
return DRIVE_RELATIVE;
|
|
147
|
+
if (posixForm === 'rooted' || win32Form === 'rooted')
|
|
148
|
+
return 'absolute';
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
/** A component that is a name ending in `.` or a space (never `.` or `..` themselves): Win32 strips the ending. */
|
|
152
|
+
const STRIPPED_ENDING = /(?:^|[/\\])(?!\.\.?(?:[/\\]|$))[^/\\]*[. ](?:[/\\]|$)/;
|
|
153
|
+
/** Whether `text` holds a NUL, or a component Windows would rename by stripping its ending. */
|
|
154
|
+
function isUnportableText(text) {
|
|
155
|
+
return text.includes(String.fromCodePoint(0)) || STRIPPED_ENDING.test(text);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Collapse `..` by string — what Windows does to a name before it reparses it — saying whether a
|
|
159
|
+
* `..` met nothing left to pop: the name climbed above the place it started from.
|
|
160
|
+
*/
|
|
161
|
+
function collapse(parts) {
|
|
162
|
+
const out = [];
|
|
163
|
+
let climbed = false;
|
|
164
|
+
for (const part of parts) {
|
|
165
|
+
if (part !== '..')
|
|
166
|
+
out.push(part);
|
|
167
|
+
else if (out.pop() === undefined)
|
|
168
|
+
climbed = true;
|
|
169
|
+
}
|
|
170
|
+
return { parts: out, climbed };
|
|
171
|
+
}
|
|
172
|
+
/** {@link collapse}, never above the root. */
|
|
173
|
+
function collapsed(parts) {
|
|
174
|
+
return collapse(parts).parts;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Why a committed link — `linkPath` (its `/`-separated place in the tree) whose text is `text` — is
|
|
178
|
+
* refused by its text alone, before it exists on disk: {@link untrustedTextRefusal}, or `climbs-out`
|
|
179
|
+
* when, joined onto the link's directory and split by EITHER flavor's separators, its `..`s rise above
|
|
180
|
+
* the tree's root — the same collapse the walk applies under win32.
|
|
181
|
+
*
|
|
182
|
+
* @returns The refusal, or `undefined` for a text that stays inside by its text alone
|
|
183
|
+
*/
|
|
184
|
+
export function untrustedLinkRefusal(linkPath, text) {
|
|
185
|
+
const why = untrustedTextRefusal(text);
|
|
186
|
+
if (why !== undefined)
|
|
187
|
+
return why;
|
|
188
|
+
const splitters = [(value) => value, toForwardSlashAnyPlatform];
|
|
189
|
+
const climbs = splitters.some((spell) => collapse([...partsOf(posix.dirname(spell(linkPath))), ...partsOf(spell(text))]).climbed);
|
|
190
|
+
return climbs ? CLIMBS_OUT : undefined;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* A name folded as Windows compares names: each character upcased on its own (an ordinal, simple
|
|
194
|
+
* case fold — never `toLowerCase`, never a mapping that changes a character's length, like `ß` → `SS`).
|
|
195
|
+
*/
|
|
196
|
+
export function win32FoldedName(name) {
|
|
197
|
+
let folded = '';
|
|
198
|
+
for (const character of name) {
|
|
199
|
+
const upper = character.toUpperCase();
|
|
200
|
+
folded += upper.length === character.length ? upper : character;
|
|
201
|
+
}
|
|
202
|
+
return folded;
|
|
203
|
+
}
|
|
204
|
+
/** `prefix` + `parts`, forward-slashed. */
|
|
205
|
+
function joined(prefix, parts) {
|
|
206
|
+
return prefix + parts.join('/');
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Whether `path` is `root` or below it, both forward-slashed — case-blind under win32 by the
|
|
210
|
+
* ordinal fold Windows compares names with ({@link win32FoldedName}).
|
|
211
|
+
*/
|
|
212
|
+
export function isWithinUnder(path, root, flavor) {
|
|
213
|
+
const [p, r] = flavor === 'win32' ? [win32FoldedName(path), win32FoldedName(root)] : [path, root];
|
|
214
|
+
const base = r.endsWith('/') ? r : `${r}/`;
|
|
215
|
+
return p === r || p.startsWith(base);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Splice a link's text into the walk in place of the link (already popped from `done`), as
|
|
219
|
+
* `flavor`'s kernel would; or the refusal of a text it never resolves.
|
|
220
|
+
*/
|
|
221
|
+
function splice(walk, link, text, rule) {
|
|
222
|
+
walk.link = link;
|
|
223
|
+
walk.text = text;
|
|
224
|
+
const form = textForm(text, rule.flavor);
|
|
225
|
+
if (form.form === 'network')
|
|
226
|
+
return { why: 'network', link, text };
|
|
227
|
+
if (form.form === 'drive-relative')
|
|
228
|
+
return { why: DRIVE_RELATIVE, link, text };
|
|
229
|
+
if (rule.outside === 'refuse') {
|
|
230
|
+
const why = untrustedTextRefusal(text);
|
|
231
|
+
if (why !== undefined)
|
|
232
|
+
return { why, link, text };
|
|
233
|
+
}
|
|
234
|
+
if (form.form === 'rooted') {
|
|
235
|
+
if (rule.flavor === 'win32')
|
|
236
|
+
return rebase(walk, form.rooted.prefix, collapsed([...form.rooted.parts, ...walk.pending]), rule);
|
|
237
|
+
walk.prefix = form.rooted.prefix;
|
|
238
|
+
walk.pending = [...form.rooted.parts, ...walk.pending];
|
|
239
|
+
walk.done = [];
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
if (rule.flavor === 'win32') {
|
|
243
|
+
// Joined onto the link's directory and collapsed by string BEFORE anything is looked at, then
|
|
244
|
+
// re-walked: a `..` never asks what the component before it is.
|
|
245
|
+
const whole = [...walk.done, ...form.parts, ...walk.pending];
|
|
246
|
+
// The collapse forgets a climb that comes back in by the root's own name; a tree that may not be left sees it on the way.
|
|
247
|
+
if (rule.outside === 'refuse' && risesAbove(whole, walk.base.parts.length))
|
|
248
|
+
return { why: CLIMBS_OUT, link, text };
|
|
249
|
+
return rebase(walk, walk.prefix, collapsed(whole), rule);
|
|
250
|
+
}
|
|
251
|
+
walk.pending = [...form.parts, ...walk.pending];
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
/** Whether collapsing `parts` by string ever leaves fewer than `floor` components: the name passes above the root. */
|
|
255
|
+
function risesAbove(parts, floor) {
|
|
256
|
+
let depth = 0;
|
|
257
|
+
for (const part of parts) {
|
|
258
|
+
if (part === '..')
|
|
259
|
+
depth -= 1;
|
|
260
|
+
else if (part !== '.' && part !== '')
|
|
261
|
+
depth += 1;
|
|
262
|
+
if (depth < floor && part === '..')
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Re-walk the whole name `prefix` + `parts` (a win32 splice, already collapsed): from the tree's
|
|
269
|
+
* root when the name is below it — the root's own components are real directories — else from the
|
|
270
|
+
* name's own root, which only a tree whose links may leave it allows.
|
|
271
|
+
*/
|
|
272
|
+
function rebase(walk, prefix, parts, rule) {
|
|
273
|
+
const { base } = walk;
|
|
274
|
+
if (isWithinUnder(joined(prefix, parts), joined(base.prefix, base.parts), rule.flavor)) {
|
|
275
|
+
walk.prefix = base.prefix;
|
|
276
|
+
walk.done = [...base.parts];
|
|
277
|
+
walk.pending = parts.slice(base.parts.length);
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
if (rule.outside === 'refuse')
|
|
281
|
+
return { why: CLIMBS_OUT, link: walk.link, text: walk.text };
|
|
282
|
+
walk.prefix = prefix;
|
|
283
|
+
walk.done = [];
|
|
284
|
+
walk.pending = parts;
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
/** The walk's starting position for `start`: from the root when `start` is below it, else from its own root. */
|
|
288
|
+
function startOf(start, rule, rootParts) {
|
|
289
|
+
const form = anchoredForm(start, rule.flavor, rootParts.prefix);
|
|
290
|
+
if (form.form === 'network')
|
|
291
|
+
return { why: 'network', link: start, text: start };
|
|
292
|
+
if (form.form !== 'rooted')
|
|
293
|
+
return { why: DRIVE_RELATIVE, link: start, text: start };
|
|
294
|
+
const parts = rule.flavor === 'win32' ? collapsed(form.rooted.parts) : [...form.rooted.parts];
|
|
295
|
+
const path = joined(form.rooted.prefix, parts);
|
|
296
|
+
const root = joined(rootParts.prefix, rootParts.parts);
|
|
297
|
+
if (isWithinUnder(path, root, rule.flavor) && (rule.flavor === 'win32' || !parts.includes('..'))) {
|
|
298
|
+
return { base: rootParts, prefix: rootParts.prefix, done: [...rootParts.parts], pending: parts.slice(rootParts.parts.length), link: start, text: start };
|
|
299
|
+
}
|
|
300
|
+
if (rule.outside === 'refuse')
|
|
301
|
+
return { why: CLIMBS_OUT, link: start, text: start };
|
|
302
|
+
return { base: rootParts, prefix: form.rooted.prefix, done: [], pending: parts, link: start, text: start };
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Where `start` leads, every link on the way read by its text before anything follows it.
|
|
306
|
+
*
|
|
307
|
+
* @param start - An absolute path, in `rule.flavor`'s spelling (forward or back slashes)
|
|
308
|
+
* @param rule - The flavor, the tree, and what the chain may do outside it
|
|
309
|
+
* @param probe - `lstat` and `readlink`, and nothing that follows a link
|
|
310
|
+
* @returns The local path the kernel may now be asked about, nothing, or the refusal
|
|
311
|
+
* @throws whatever `probe` throws: a probe the OS refused is the caller's to classify
|
|
312
|
+
*/
|
|
313
|
+
export function walkLinkChain(start, rule, probe) {
|
|
314
|
+
const rootForm = anchoredForm(rule.root, rule.flavor, '/');
|
|
315
|
+
if (rootForm.form !== 'rooted')
|
|
316
|
+
throw new TypeError(`walkLinkChain: the root must be a local absolute path, not ${rule.root}`);
|
|
317
|
+
const root = joined(rootForm.rooted.prefix, rootForm.rooted.parts);
|
|
318
|
+
const begun = startOf(start, rule, rootForm.rooted);
|
|
319
|
+
if ('why' in begun)
|
|
320
|
+
return { kind: 'refused', refusal: begun };
|
|
321
|
+
const walk = begun;
|
|
322
|
+
let final = 'directory';
|
|
323
|
+
// Each link met, with the text still to read after it: meeting one again is a cycle, exactly.
|
|
324
|
+
const followed = { met: new Set(), hops: 0 };
|
|
325
|
+
for (let part = walk.pending.shift(); part !== undefined; part = walk.pending.shift()) {
|
|
326
|
+
const step = stepTo(walk, part, rule, root, probe);
|
|
327
|
+
if (step.kind !== 'step')
|
|
328
|
+
return step;
|
|
329
|
+
if (step.stands === 'link') {
|
|
330
|
+
const ended = followLink(walk, rule, probe, followed);
|
|
331
|
+
if (ended !== undefined)
|
|
332
|
+
return ended;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
final = step.stands;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return { kind: 'resolved', path: joined(walk.prefix, walk.done), final };
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Take one component: the kind that now stands at the walk's place (a `..` is a directory — the
|
|
342
|
+
* parent of a real directory), or the answer that ends the walk.
|
|
343
|
+
*/
|
|
344
|
+
function stepTo(walk, part, rule, root, probe) {
|
|
345
|
+
if (part === '..')
|
|
346
|
+
walk.done.pop();
|
|
347
|
+
else
|
|
348
|
+
walk.done.push(part);
|
|
349
|
+
const here = joined(walk.prefix, walk.done);
|
|
350
|
+
if (rule.outside === 'refuse' && !isWithinUnder(here, root, rule.flavor))
|
|
351
|
+
return { kind: 'refused', refusal: { why: CLIMBS_OUT, link: walk.link, text: walk.text } };
|
|
352
|
+
if (part === '..')
|
|
353
|
+
return { kind: 'step', stands: 'directory' };
|
|
354
|
+
const kind = probe.kindAt(here);
|
|
355
|
+
if (kind === 'absent' || (kind === 'file' && walk.pending.length > 0))
|
|
356
|
+
return { kind: 'nothing', at: here, cause: 'absent' };
|
|
357
|
+
return { kind: 'step', stands: kind };
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Follow the link at the walk's place by its text: a cycle when this link was already met with the
|
|
361
|
+
* same text left to read, the hop limit's refusal past {@link MAX_LINK_HOPS} links, the text's own
|
|
362
|
+
* refusal, or `undefined` to walk on. The hop count bounds the walk on its own, cycle or not.
|
|
363
|
+
*/
|
|
364
|
+
function followLink(walk, rule, probe, followed) {
|
|
365
|
+
const here = joined(walk.prefix, walk.done);
|
|
366
|
+
const state = `${here}\0${walk.pending.join('/')}`;
|
|
367
|
+
followed.hops += 1;
|
|
368
|
+
if (followed.met.has(state))
|
|
369
|
+
return { kind: 'nothing', at: here, cause: 'cycle' };
|
|
370
|
+
followed.met.add(state);
|
|
371
|
+
if (followed.hops > MAX_LINK_HOPS)
|
|
372
|
+
return { kind: 'refused', refusal: { why: 'hop-limit', link: here } };
|
|
373
|
+
walk.done.pop();
|
|
374
|
+
const refusal = splice(walk, here, probe.textAt(here), rule);
|
|
375
|
+
return refusal === undefined ? undefined : { kind: 'refused', refusal };
|
|
376
|
+
}
|
|
377
|
+
//# sourceMappingURL=link-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-chain.js","sourceRoot":"","sources":["../../src/tree-change/link-chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule for a tree whose links are KEPT: none of them may lead outside it.
|
|
3
|
+
*
|
|
4
|
+
* A copy that preserves links carries each link's text verbatim, so a link out of the tree
|
|
5
|
+
* arrives pointing at whatever stands at that path beside the COPY — the installing user's own
|
|
6
|
+
* files — and every reader of the copy that follows links reads them as the tree's content. So a
|
|
7
|
+
* tree that came from somewhere VAT does not own (a cloned repository, a directory an operator
|
|
8
|
+
* installs from) is checked here before anything reads it.
|
|
9
|
+
*/
|
|
10
|
+
import { type FsSide } from '../errors/fs-fault.js';
|
|
11
|
+
import { type LinkChain, type PathFlavor } from './link-chain.js';
|
|
12
|
+
/** What {@link refuseEscapingLinks} checks, and how its refusal names it. */
|
|
13
|
+
export interface LinkContainmentCheck {
|
|
14
|
+
/** The tree no link may lead out of. */
|
|
15
|
+
readonly root: string;
|
|
16
|
+
/** Where in it to look; the whole of `root` when omitted. */
|
|
17
|
+
readonly subtree?: string;
|
|
18
|
+
/** A directory under `root` that is never entered (a clone's `.git`). */
|
|
19
|
+
readonly skip?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the tree is read where it stands, or will be RELOCATED (an install copies it, each
|
|
22
|
+
* link's text verbatim).
|
|
23
|
+
*
|
|
24
|
+
* - `in-place`: a link is judged by where it leads NOW. Its chain is read first, link by link,
|
|
25
|
+
* by the one resolver ({@link treeChain}), so nothing is resolved until every text on the way
|
|
26
|
+
* is: a network, absolute or drive-relative text anywhere on the way, a climb above the root,
|
|
27
|
+
* or a chain no kernel finishes, is refused. Then its real path must be inside. One that leads
|
|
28
|
+
* to nothing (a dangle, a cycle) reads nothing, so it is not refused.
|
|
29
|
+
* - `relocated`: where a link leads now proves nothing about where its text leads once the tree
|
|
30
|
+
* is somewhere else, so the text itself is walked ({@link linkTextRefusal}): relative, never
|
|
31
|
+
* above the root, through no other link, and ending at something that exists. Then the chain
|
|
32
|
+
* from it is read as in place, and only then is the kernel asked ({@link kernelRefusal}).
|
|
33
|
+
*/
|
|
34
|
+
readonly placement: 'in-place' | 'relocated';
|
|
35
|
+
/** Which side the tree is on: every probe of it the OS refuses — a listing, an `lstat`, a `readlink`, a link's target — is that side's fault. */
|
|
36
|
+
readonly side: FsSide;
|
|
37
|
+
/** Whose kernel reads the tree: bound from `process.platform` at the edge (`HOST_PATH_FLAVOR`). */
|
|
38
|
+
readonly flavor: PathFlavor;
|
|
39
|
+
/** What the tree is, for the refusal: `the cloned repository`. */
|
|
40
|
+
readonly tree: string;
|
|
41
|
+
/** What a link must stay inside, for the refusal: `the clone`. */
|
|
42
|
+
readonly within: string;
|
|
43
|
+
/** What keeping the link would do, for the refusal — a sentence without its full stop. */
|
|
44
|
+
readonly consequence: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Where `candidate` stands relative to `root`, on real paths. The root itself counts as inside
|
|
48
|
+
* (`isUnderRoot` calls a root `outside` itself, which is right for a sink and wrong here).
|
|
49
|
+
*
|
|
50
|
+
* @param root - The tree
|
|
51
|
+
* @param candidate - A path in any spelling, existing or not
|
|
52
|
+
*/
|
|
53
|
+
export declare function treeContainment(root: string, candidate: string): 'inside' | 'outside' | 'absent';
|
|
54
|
+
/**
|
|
55
|
+
* Where `path` leads in an UNTRUSTED tree, read link by link by the one resolver — `lstat` and
|
|
56
|
+
* `readlink` alone — before anything resolves it: an absolute, drive-relative or network text on
|
|
57
|
+
* the way (under either flavor: the tree may be read on the other host), a climb above `root`,
|
|
58
|
+
* or a chain longer than the hop limit that is not a cycle, is refused; a cycle leads to
|
|
59
|
+
* nothing, as a dangling link does; nothing outside `root` is probed.
|
|
60
|
+
*
|
|
61
|
+
* @param root - The tree, spelled as `path` spells it (its own components are never probed)
|
|
62
|
+
* @param path - A path below it (a link, or a subpath the operator named)
|
|
63
|
+
* @param flavor - Whose kernel will resolve it: bound from the host at the edge
|
|
64
|
+
* @throws The raw errno of an `lstat` or `readlink` the OS refused, for the caller to classify
|
|
65
|
+
*/
|
|
66
|
+
export declare function treeChain(root: string, path: string, flavor: PathFlavor): LinkChain;
|
|
67
|
+
/**
|
|
68
|
+
* The last lock on a link of a tree that will be relocated: the KERNEL's resolution of it, at
|
|
69
|
+
* the source, must exist and be inside the tree. Asked only once the chain from the link has been
|
|
70
|
+
* read and shown local ({@link treeChain}). The walk of the text ({@link linkTextRefusal}) shows
|
|
71
|
+
* the text stays inside wherever the tree goes; this shows the walk's reading of the text is the
|
|
72
|
+
* kernel's. A probe that failed because the link resolves to nothing
|
|
73
|
+
* (`isUnresolvableLinkError`: it dangles, it is part of a cycle, Windows cannot follow its reparse
|
|
74
|
+
* data) is a refusal of the link on every host: nothing can be shown about where it leads. Every
|
|
75
|
+
* other failure — the OS declining (`EACCES`, `EPERM`), running short (`EMFILE`), held (`EBUSY`) or
|
|
76
|
+
* unable to look (`EIO`, `ETIMEDOUT`, Windows' ambiguous `UNKNOWN`) — says nothing about the link:
|
|
77
|
+
* a fault on the tree, thrown classified by the one write-lane catch body
|
|
78
|
+
* (`assertUnresolvableLink`, on the side the caller names in `check.side`, found as content).
|
|
79
|
+
*
|
|
80
|
+
* Exported apart from the walk so the lock can be shown to hold ALONE: with a correct walk, a
|
|
81
|
+
* link the kernel resolves outside never reaches it from a real tree.
|
|
82
|
+
*
|
|
83
|
+
* @param root - The tree, as its canonical path
|
|
84
|
+
* @param link - The link, spelled by its real directory
|
|
85
|
+
* @param text - The link's own text, for the refusal
|
|
86
|
+
* @returns Why the link is refused, after "is a symbolic link ", or `undefined` when the kernel agrees
|
|
87
|
+
* @throws FsFaultError on `check.side` for a probe that failed for any other reason; an error that carries no errno as it was
|
|
88
|
+
*/
|
|
89
|
+
export declare function kernelRefusal(check: LinkContainmentCheck, root: string, link: string, text: string): string | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* Refuse a tree holding a symbolic link that may not be kept: one leading outside it, and — for a
|
|
92
|
+
* tree that will be relocated — one whose text cannot be shown to stay inside wherever the tree
|
|
93
|
+
* goes ({@link LinkContainmentCheck.placement}). Every directory is walked at its REAL path, once: a
|
|
94
|
+
* link that stays inside is followed to the directory it names, so a link into a sibling cannot
|
|
95
|
+
* smuggle out a second hop, a link cycle ends, and a directory first reached through an alias still
|
|
96
|
+
* has its own links judged where they live.
|
|
97
|
+
*
|
|
98
|
+
* @param check - The tree, where in it to look, and how the refusal names it
|
|
99
|
+
* @throws VatError `COPY_LINK_ESCAPES_SOURCE` — the tree's own link, so the input's refusal —
|
|
100
|
+
* naming the first link found leading out; FsFaultError on `check.side`, found as content, for
|
|
101
|
+
* every probe of the tree the OS refused (a listing, an `lstat`, a `readlink`, a relocated link's
|
|
102
|
+
* target)
|
|
103
|
+
*/
|
|
104
|
+
export declare function refuseEscapingLinks(check: LinkContainmentCheck): void;
|
|
105
|
+
//# sourceMappingURL=link-containment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-containment.d.ts","sourceRoot":"","sources":["../../src/tree-change/link-containment.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EAA0B,KAAK,MAAM,EAAmB,MAAM,uBAAuB,CAAC;AAQ7F,OAAO,EAAqB,KAAK,SAAS,EAAiB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGpG,6EAA6E;AAC7E,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,SAAS,EAAE,UAAU,GAAG,WAAW,CAAC;IAC7C,iJAAiJ;IACjJ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mGAAmG;IACnG,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0FAA0F;IAC1F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAGhG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,CAEnF;AAyCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAavH;AAqCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAgBrE"}
|