@vibe-agent-toolkit/utils 0.1.42 → 0.2.0-rc.10
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 +81 -19
- package/dist/asset-reference.js +3 -11
- package/dist/asset-reference.js.map +1 -1
- package/dist/compare-code-units.d.ts +28 -0
- package/dist/compare-code-units.d.ts.map +1 -0
- package/dist/compare-code-units.js +33 -0
- package/dist/compare-code-units.js.map +1 -0
- package/dist/crawl-timing.d.ts +657 -0
- package/dist/crawl-timing.d.ts.map +1 -0
- package/dist/crawl-timing.js +765 -0
- package/dist/crawl-timing.js.map +1 -0
- package/dist/crawl.d.ts +2 -4
- package/dist/crawl.d.ts.map +1 -1
- package/dist/crawl.js +2 -4
- package/dist/crawl.js.map +1 -1
- package/dist/dirent-kind.d.ts +83 -0
- package/dist/dirent-kind.d.ts.map +1 -0
- package/dist/dirent-kind.js +129 -0
- package/dist/dirent-kind.js.map +1 -0
- package/dist/entrypoint.d.ts +54 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +71 -0
- package/dist/entrypoint.js.map +1 -0
- package/dist/env-flag.d.ts +51 -0
- package/dist/env-flag.d.ts.map +1 -0
- package/dist/env-flag.js +62 -0
- package/dist/env-flag.js.map +1 -0
- package/dist/errors/errno.d.ts +51 -0
- package/dist/errors/errno.d.ts.map +1 -0
- package/dist/errors/errno.js +112 -0
- package/dist/errors/errno.js.map +1 -0
- package/dist/errors/vat-error.d.ts +60 -0
- package/dist/errors/vat-error.d.ts.map +1 -0
- package/dist/errors/vat-error.js +86 -0
- package/dist/errors/vat-error.js.map +1 -0
- package/dist/file-crawler.d.ts +60 -0
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +214 -24
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs/file-hash.d.ts.map +1 -1
- package/dist/fs/file-hash.js +0 -1
- package/dist/fs/file-hash.js.map +1 -1
- package/dist/fs-utils.d.ts +628 -38
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +770 -71
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +5 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +9 -1
- package/dist/fs.js.map +1 -1
- package/dist/git-root-cache.d.ts +44 -0
- package/dist/git-root-cache.d.ts.map +1 -0
- package/dist/git-root-cache.js +68 -0
- package/dist/git-root-cache.js.map +1 -0
- package/dist/git-run.d.ts +167 -0
- package/dist/git-run.d.ts.map +1 -0
- package/dist/git-run.js +154 -0
- package/dist/git-run.js.map +1 -0
- package/dist/git-snapshot.d.ts +199 -0
- package/dist/git-snapshot.d.ts.map +1 -0
- package/dist/git-snapshot.js +250 -0
- package/dist/git-snapshot.js.map +1 -0
- package/dist/git-tracker.d.ts +166 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +250 -22
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts +95 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +227 -81
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +13 -4
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +12 -4
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +15 -6
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/glob/glob-pattern.js +5 -5
- package/dist/glob/glob-pattern.js.map +1 -1
- package/dist/index.d.ts +33 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +113 -33
- package/dist/index.js.map +1 -1
- package/dist/listing-refusal.d.ts +112 -0
- package/dist/listing-refusal.d.ts.map +1 -0
- package/dist/listing-refusal.js +105 -0
- package/dist/listing-refusal.js.map +1 -0
- package/dist/numeric-args.d.ts +24 -0
- package/dist/numeric-args.d.ts.map +1 -0
- package/dist/numeric-args.js +37 -0
- package/dist/numeric-args.js.map +1 -0
- package/dist/path-containment.d.ts +64 -0
- package/dist/path-containment.d.ts.map +1 -0
- package/dist/path-containment.js +111 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/path-core.d.ts +143 -18
- package/dist/path-core.d.ts.map +1 -1
- package/dist/path-core.js +171 -23
- package/dist/path-core.js.map +1 -1
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +42 -47
- package/dist/path-utils.js.map +1 -1
- package/dist/path.d.ts +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +1 -1
- package/dist/path.js.map +1 -1
- package/dist/process.d.ts +7 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +7 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts +7 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +24 -10
- package/dist/project-utils.js.map +1 -1
- package/dist/project.d.ts +7 -5
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +7 -5
- package/dist/project.js.map +1 -1
- package/dist/safe-exec.d.ts +40 -11
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +111 -25
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts +2 -2
- package/dist/skill-targets.js +2 -2
- package/dist/skill-test/auth-resolver.d.ts +10 -2
- package/dist/skill-test/auth-resolver.d.ts.map +1 -1
- package/dist/skill-test/auth-resolver.js +26 -11
- package/dist/skill-test/auth-resolver.js.map +1 -1
- package/dist/skill-test/index.d.ts +1 -1
- package/dist/skill-test/index.d.ts.map +1 -1
- package/dist/skill-test/index.js +1 -1
- package/dist/skill-test/index.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +30 -1
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +71 -5
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/skill-test/transcript.d.ts +41 -2
- package/dist/skill-test/transcript.d.ts.map +1 -1
- package/dist/skill-test/transcript.js +55 -10
- package/dist/skill-test/transcript.js.map +1 -1
- package/dist/spawn-hardened.js +0 -1
- package/dist/spawn-hardened.js.map +1 -1
- package/dist/stdio-blocking.js +13 -14
- package/dist/stdio-blocking.js.map +1 -1
- package/dist/test-helpers.d.ts +130 -79
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +229 -124
- package/dist/test-helpers.js.map +1 -1
- package/dist/testing/executables.d.ts +32 -0
- package/dist/testing/executables.d.ts.map +1 -0
- package/dist/testing/executables.js +70 -0
- package/dist/testing/executables.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts +79 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -0
- package/dist/testing/hostile-tree.js +164 -0
- package/dist/testing/hostile-tree.js.map +1 -0
- package/dist/testing/platform-gates.d.ts +31 -0
- package/dist/testing/platform-gates.d.ts.map +1 -0
- package/dist/testing/platform-gates.js +31 -0
- package/dist/testing/platform-gates.js.map +1 -0
- package/dist/testing/temp-dir.d.ts +255 -0
- package/dist/testing/temp-dir.d.ts.map +1 -0
- package/dist/testing/temp-dir.js +363 -0
- package/dist/testing/temp-dir.js.map +1 -0
- package/dist/testing.d.ts +94 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +119 -3
- package/dist/testing.js.map +1 -1
- package/dist/text-content.d.ts +197 -0
- package/dist/text-content.d.ts.map +1 -0
- package/dist/text-content.js +354 -0
- package/dist/text-content.js.map +1 -0
- package/dist/text-file.d.ts +45 -0
- package/dist/text-file.d.ts.map +1 -0
- package/dist/text-file.js +51 -0
- package/dist/text-file.js.map +1 -0
- package/dist/text.d.ts +16 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +16 -0
- package/dist/text.js.map +1 -0
- package/dist/timing-dump.d.ts +136 -0
- package/dist/timing-dump.d.ts.map +1 -0
- package/dist/timing-dump.js +229 -0
- package/dist/timing-dump.js.map +1 -0
- package/dist/windows-shell.d.ts +0 -1
- package/dist/windows-shell.d.ts.map +1 -1
- package/dist/windows-shell.js +0 -1
- package/dist/windows-shell.js.map +1 -1
- package/dist/zod-introspection.js +2 -2
- package/dist/zod-introspection.js.map +1 -1
- package/eslint/README.md +224 -50
- package/eslint/index.cjs +111 -97
- package/eslint/index.d.cts +16 -6
- package/eslint/rules/callee-name.cjs +23 -0
- package/eslint/rules/commands-import-boundary.cjs +148 -0
- package/eslint/rules/dead-import.cjs +62 -12
- package/eslint/rules/dirent-type-needs-symlink-check.cjs +282 -0
- package/eslint/rules/eslint-rule-factory.cjs +21 -3
- package/eslint/rules/exempt-path-matcher.cjs +2 -1
- package/eslint/rules/explicit-zod-strictness.cjs +120 -0
- package/eslint/rules/no-bare-dynamic-import-path.cjs +5 -1
- package/eslint/rules/no-bare-executable-spawn.cjs +70 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +213 -0
- package/eslint/rules/no-blind-catch.cjs +171 -0
- package/eslint/rules/no-child-process-execSync.cjs +8 -0
- package/eslint/rules/no-decaying-referent.cjs +217 -0
- package/eslint/rules/no-dotdot-containment.cjs +160 -0
- package/eslint/rules/no-file-url-string-concat.cjs +4 -1
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +228 -0
- package/eslint/rules/no-fs-mkdirSync.cjs +8 -0
- package/eslint/rules/no-fs-promises-cp.cjs +7 -0
- package/eslint/rules/no-fs-realpathSync.cjs +8 -0
- package/eslint/rules/no-hardcoded-path-split.cjs +29 -18
- package/eslint/rules/no-io-in-unit-tier.cjs +129 -0
- package/eslint/rules/no-literal-process-exit.cjs +129 -0
- package/eslint/rules/no-manual-path-normalize.cjs +146 -96
- package/eslint/rules/no-os-tmpdir.cjs +8 -0
- package/eslint/rules/no-path-operations-in-comparisons.cjs +7 -2
- package/eslint/rules/no-path-sep-in-strings.cjs +7 -2
- package/eslint/rules/no-path-startswith.cjs +7 -2
- package/eslint/rules/no-process-exit-in-phase.cjs +127 -0
- package/eslint/rules/no-raw-node-path.cjs +563 -0
- package/eslint/rules/no-raw-text-decode.cjs +305 -0
- package/eslint/rules/no-registry-count-pin.cjs +255 -0
- package/eslint/rules/no-self-package-import.cjs +168 -0
- package/eslint/rules/no-test-scoped-functions.cjs +10 -3
- package/eslint/rules/no-unix-shell-commands.cjs +6 -2
- package/eslint/rules/no-unsafe-root-join.cjs +23 -2
- package/eslint/rules/no-url-pathname-for-fs.cjs +5 -1
- package/eslint/rules/no-version-literal.cjs +151 -0
- package/eslint/rules/prefer-startswith-over-regex.cjs +33 -1
- package/eslint/rules/require-justified-skip.cjs +8 -2
- package/eslint/rules/safe-import.cjs +1 -1
- package/package.json +16 -16
- package/dist/link-auth/build-headers.d.ts +0 -34
- package/dist/link-auth/build-headers.d.ts.map +0 -1
- package/dist/link-auth/build-headers.js +0 -58
- package/dist/link-auth/build-headers.js.map +0 -1
- package/dist/link-auth/expand-macro.d.ts +0 -38
- package/dist/link-auth/expand-macro.d.ts.map +0 -1
- package/dist/link-auth/expand-macro.js +0 -133
- package/dist/link-auth/expand-macro.js.map +0 -1
- package/dist/link-auth/macros.yaml +0 -50
- package/dist/link-auth/resolve-token.d.ts +0 -83
- package/dist/link-auth/resolve-token.d.ts.map +0 -1
- package/dist/link-auth/resolve-token.js +0 -101
- package/dist/link-auth/resolve-token.js.map +0 -1
- package/dist/link-auth/resolve.d.ts +0 -102
- package/dist/link-auth/resolve.d.ts.map +0 -1
- package/dist/link-auth/resolve.js +0 -66
- package/dist/link-auth/resolve.js.map +0 -1
- package/dist/link-auth/rewrite.d.ts +0 -52
- package/dist/link-auth/rewrite.d.ts.map +0 -1
- package/dist/link-auth/rewrite.js +0 -102
- package/dist/link-auth/rewrite.js.map +0 -1
- package/dist/link-auth/select-provider.d.ts +0 -30
- package/dist/link-auth/select-provider.d.ts.map +0 -1
- package/dist/link-auth/select-provider.js +0 -55
- package/dist/link-auth/select-provider.js.map +0 -1
- package/dist/link-auth/template.d.ts +0 -40
- package/dist/link-auth/template.d.ts.map +0 -1
- package/dist/link-auth/template.js +0 -89
- package/dist/link-auth/template.js.map +0 -1
- package/dist/link-auth/transforms.d.ts +0 -46
- package/dist/link-auth/transforms.d.ts.map +0 -1
- package/dist/link-auth/transforms.js +0 -52
- package/dist/link-auth/transforms.js.map +0 -1
- package/dist/template-entry.d.ts +0 -10
- package/dist/template-entry.d.ts.map +0 -1
- package/dist/template-entry.js +0 -10
- package/dist/template-entry.js.map +0 -1
- package/dist/template.d.ts +0 -7
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -18
- package/dist/template.js.map +0 -1
- package/eslint/rules/no-path-join.cjs +0 -14
- package/eslint/rules/no-path-relative.cjs +0 -14
- package/eslint/rules/no-path-resolve.cjs +0 -14
- package/eslint/rules/path-function-rule-factory.cjs +0 -417
package/dist/test-helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Absolute paths for the executables test fixtures spawn.
|
|
3
|
+
*
|
|
4
|
+
* A test that spawns `'git'` or `'node'` by bare name asks the OS to search
|
|
5
|
+
* `PATH`, and a writable directory on `PATH` turns that into a place to plant
|
|
6
|
+
* a binary (the class SonarCloud's S4036 flags). Fixtures resolve the binary
|
|
7
|
+
* ONCE, here, to an absolute path and spawn that: `node` is the process that
|
|
8
|
+
* is running the test (`process.execPath`, the only node whose version the
|
|
9
|
+
* test can vouch for), and `git` is found by walking `PATH`.
|
|
10
|
+
*
|
|
11
|
+
* Why a second `PATH` walk beside `safe-exec.ts`'s `which`: `which` resolves
|
|
12
|
+
* in-process too, but it is a third-party package, and the `./testing` subpath
|
|
13
|
+
* is pinned dependency-free (`subpath-purity.test.ts`) — an adopter's test
|
|
14
|
+
* suite pulls nothing in. This walk answers the same question under that
|
|
15
|
+
* constraint: the first REGULAR FILE on `PATH` named `name` (with a `PATHEXT`
|
|
16
|
+
* extension on Windows) that the process may execute.
|
|
17
|
+
*/
|
|
18
|
+
/** The `node` running this test, absolute. */
|
|
19
|
+
export declare const NODE_EXECUTABLE: string;
|
|
20
|
+
/** The names to try in one `PATH` directory: `PATHEXT` variants on Windows, the bare name elsewhere. */
|
|
21
|
+
export declare function executableCandidates(name: string, platform: NodeJS.Platform, pathext: string | undefined): string[];
|
|
22
|
+
/**
|
|
23
|
+
* The first executable named `name` on `PATH`, absolute.
|
|
24
|
+
*
|
|
25
|
+
* @throws {Error} when nothing on `PATH` is executable under that name — a
|
|
26
|
+
* fixture that needs `git` and has none should fail at the first spawn with
|
|
27
|
+
* the reason, not with the OS's `ENOENT` for a bare word.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveExecutable(name: string): string;
|
|
30
|
+
/** `git`, absolute, resolved on first use and cached for the process. */
|
|
31
|
+
export declare function gitExecutable(): string;
|
|
32
|
+
//# sourceMappingURL=executables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executables.d.ts","sourceRoot":"","sources":["../../src/testing/executables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,8CAA8C;AAC9C,eAAO,MAAM,eAAe,EAAE,MAAyB,CAAC;AAExD,wGAAwG;AACxG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAInH;AAcD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUtD;AAID,yEAAyE;AACzE,wBAAgB,aAAa,IAAI,MAAM,CAGtC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Absolute paths for the executables test fixtures spawn.
|
|
3
|
+
*
|
|
4
|
+
* A test that spawns `'git'` or `'node'` by bare name asks the OS to search
|
|
5
|
+
* `PATH`, and a writable directory on `PATH` turns that into a place to plant
|
|
6
|
+
* a binary (the class SonarCloud's S4036 flags). Fixtures resolve the binary
|
|
7
|
+
* ONCE, here, to an absolute path and spawn that: `node` is the process that
|
|
8
|
+
* is running the test (`process.execPath`, the only node whose version the
|
|
9
|
+
* test can vouch for), and `git` is found by walking `PATH`.
|
|
10
|
+
*
|
|
11
|
+
* Why a second `PATH` walk beside `safe-exec.ts`'s `which`: `which` resolves
|
|
12
|
+
* in-process too, but it is a third-party package, and the `./testing` subpath
|
|
13
|
+
* is pinned dependency-free (`subpath-purity.test.ts`) — an adopter's test
|
|
14
|
+
* suite pulls nothing in. This walk answers the same question under that
|
|
15
|
+
* constraint: the first REGULAR FILE on `PATH` named `name` (with a `PATHEXT`
|
|
16
|
+
* extension on Windows) that the process may execute.
|
|
17
|
+
*/
|
|
18
|
+
import { accessSync, constants, statSync } from 'node:fs';
|
|
19
|
+
import { delimiter } from 'node:path';
|
|
20
|
+
import { isFilesystemAccessError } from '../errors/errno.js';
|
|
21
|
+
import { safePath } from '../path-core.js';
|
|
22
|
+
/** The `node` running this test, absolute. */
|
|
23
|
+
export const NODE_EXECUTABLE = process.execPath;
|
|
24
|
+
/** The names to try in one `PATH` directory: `PATHEXT` variants on Windows, the bare name elsewhere. */
|
|
25
|
+
export function executableCandidates(name, platform, pathext) {
|
|
26
|
+
if (platform !== 'win32')
|
|
27
|
+
return [name];
|
|
28
|
+
const extensions = (pathext ?? '.EXE;.CMD;.BAT;.COM').split(';').filter((ext) => ext !== '');
|
|
29
|
+
return [...extensions.map((ext) => `${name}${ext}`), name];
|
|
30
|
+
}
|
|
31
|
+
/** Is `full` a regular file the process may execute? A directory named `git` passes `X_OK` (search permission) and is not one. */
|
|
32
|
+
function isExecutableFile(full) {
|
|
33
|
+
try {
|
|
34
|
+
accessSync(full, constants.X_OK);
|
|
35
|
+
return statSync(full).isFile();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
// Absent or not executable here: the next directory on PATH may have it.
|
|
39
|
+
if (isFilesystemAccessError(error))
|
|
40
|
+
return false;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The first executable named `name` on `PATH`, absolute.
|
|
46
|
+
*
|
|
47
|
+
* @throws {Error} when nothing on `PATH` is executable under that name — a
|
|
48
|
+
* fixture that needs `git` and has none should fail at the first spawn with
|
|
49
|
+
* the reason, not with the OS's `ENOENT` for a bare word.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveExecutable(name) {
|
|
52
|
+
const candidates = executableCandidates(name, process.platform, process.env['PATHEXT']);
|
|
53
|
+
for (const dir of (process.env['PATH'] ?? '').split(delimiter)) {
|
|
54
|
+
if (dir === '')
|
|
55
|
+
continue;
|
|
56
|
+
for (const candidate of candidates) {
|
|
57
|
+
const full = safePath.join(dir, candidate);
|
|
58
|
+
if (isExecutableFile(full))
|
|
59
|
+
return full;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`No executable named "${name}" on PATH (${process.env['PATH'] ?? '<unset>'})`);
|
|
63
|
+
}
|
|
64
|
+
let resolvedGit;
|
|
65
|
+
/** `git`, absolute, resolved on first use and cached for the process. */
|
|
66
|
+
export function gitExecutable() {
|
|
67
|
+
resolvedGit ??= resolveExecutable('git');
|
|
68
|
+
return resolvedGit;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=executables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executables.js","sourceRoot":"","sources":["../../src/testing/executables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,8CAA8C;AAC9C,MAAM,CAAC,MAAM,eAAe,GAAW,OAAO,CAAC,QAAQ,CAAC;AAExD,wGAAwG;AACxG,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,QAAyB,EAAE,OAA2B;IACvG,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,CAAC,OAAO,IAAI,qBAAqB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;IAC7F,OAAO,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC;AAED,kIAAkI;AAClI,SAAS,gBAAgB,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yEAAyE;QACzE,IAAI,uBAAuB,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACjD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,UAAU,GAAG,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACxF,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/D,IAAI,GAAG,KAAK,EAAE;YAAE,SAAS;QACzB,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC3C,IAAI,gBAAgB,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,cAAc,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,GAAG,CAAC,CAAC;AACjG,CAAC;AAED,IAAI,WAA+B,CAAC;AAEpC,yEAAyE;AACzE,MAAM,UAAU,aAAa;IAC3B,WAAW,KAAK,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One hostile fixture tree, built the same way for every sink.
|
|
3
|
+
*
|
|
4
|
+
* A correctness sweep found a delete, a copy and an uninstall that
|
|
5
|
+
* each walked out of their root, and found them by hand-building the same
|
|
6
|
+
* shapes — a symlink pointing out, a symlink pointing back at the root, a
|
|
7
|
+
* `..`-named entry, an unreadable directory, a traversal name — one throwaway
|
|
8
|
+
* fixture per probe. This module
|
|
9
|
+
* is that fixture, once, so a sink's test says `buildHostileTree(base)` and
|
|
10
|
+
* `it.each(HOSTILE_NAMES)` and is refusing the shapes the next sink's test is
|
|
11
|
+
* refusing too. A shape added here reaches every sink that uses it.
|
|
12
|
+
*
|
|
13
|
+
* ⛔ Framework-free, like everything under `testing/`: no `vitest` import, so
|
|
14
|
+
* the `./testing` subpath keeps the empty third-party set its purity pin
|
|
15
|
+
* asserts. Fields that the host cannot build are `null` — route those through
|
|
16
|
+
* the suite's own `skip()` so the skip is visible in the report.
|
|
17
|
+
*/
|
|
18
|
+
/** The names a sink must refuse when they arrive as "the entry to act on". */
|
|
19
|
+
export declare const HOSTILE_NAMES: readonly string[];
|
|
20
|
+
/** The tree {@link buildHostileTree} plants, every path absolute and forward-slashed. */
|
|
21
|
+
export interface HostileTree {
|
|
22
|
+
/** The trusted root — the directory a sink must stay inside. */
|
|
23
|
+
readonly root: string;
|
|
24
|
+
/** A regular directory under the root; `root/member`. */
|
|
25
|
+
readonly member: string;
|
|
26
|
+
/** A member whose NAME begins with two dots; `root/..cache`. Legitimate. */
|
|
27
|
+
readonly dotdotNamed: string;
|
|
28
|
+
/** A sibling of the root, outside it. */
|
|
29
|
+
readonly outside: string;
|
|
30
|
+
/** `outside/victim` — holds `secret.txt`; a traversal that succeeds deletes or copies this. */
|
|
31
|
+
readonly victim: string;
|
|
32
|
+
/** `root/link-out` → `outside/victim`, or `null` where the host cannot symlink. */
|
|
33
|
+
readonly linkOut: string | null;
|
|
34
|
+
/** `root/link-in` → `root/member`, or `null` where the host cannot symlink. */
|
|
35
|
+
readonly linkIn: string | null;
|
|
36
|
+
/** `root/dangling` → a path that does not exist, or `null` where the host cannot symlink. */
|
|
37
|
+
readonly dangling: string | null;
|
|
38
|
+
/** `root/loop` → `root` itself — a walk that follows it never ends — or `null` where the host cannot symlink. */
|
|
39
|
+
readonly linkLoop: string | null;
|
|
40
|
+
/** A link OUTSIDE the tree that points AT the root, or `null` where the host cannot symlink. */
|
|
41
|
+
readonly rootAlias: string | null;
|
|
42
|
+
/** `root/unreadable`, mode 000, or `null` where the host cannot deny reads. */
|
|
43
|
+
readonly unreadable: string | null;
|
|
44
|
+
/** A directory whose own name is 200 characters, under the root; `null` where the OS refused it. */
|
|
45
|
+
readonly longPath: string | null;
|
|
46
|
+
/** Restore modes and remove everything this call created. Idempotent. */
|
|
47
|
+
cleanup(): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Plant the hostile tree under `base`, which must exist and be empty enough
|
|
51
|
+
* to take `root`, `outside` and `root-alias` as direct children.
|
|
52
|
+
*
|
|
53
|
+
* @param base - A scratch directory the caller owns (a per-test temp dir)
|
|
54
|
+
* @returns The tree, with `null` for every shape this host cannot build
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildHostileTree(base: string): HostileTree;
|
|
57
|
+
/** A hostile tree replanted per test, driven from the suite's own hooks. */
|
|
58
|
+
export interface HostileTreePerTest {
|
|
59
|
+
/** Mint a scratch root and plant a fresh tree in it. Drive from `beforeEach`. */
|
|
60
|
+
plant: () => void;
|
|
61
|
+
/** Tear the tree down and remove the scratch root. Drive from `afterEach`. */
|
|
62
|
+
clear: () => void;
|
|
63
|
+
/** The tree planted by the most recent {@link HostileTreePerTest.plant}. */
|
|
64
|
+
tree: () => HostileTree;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Hold a per-test hostile tree, so a suite's wiring is three lines:
|
|
68
|
+
* `const hostile = hostileTreePerTest('x'); beforeEach(hostile.plant); afterEach(hostile.clear);`.
|
|
69
|
+
*
|
|
70
|
+
* Same shape and same reasons as `replantableCorpus`: the tree comes back
|
|
71
|
+
* through a GETTER because it is reminted per test, `plant` clears any tree
|
|
72
|
+
* still standing so nested `describe`s do not leak one, and `tree()` before
|
|
73
|
+
* `plant()` throws by name rather than surfacing as an undefined read.
|
|
74
|
+
*
|
|
75
|
+
* @param prefix - `mkdtemp` prefix for the scratch root, so a leak names its suite
|
|
76
|
+
* @returns Plant/clear/tree, to be driven from the caller's own hooks
|
|
77
|
+
*/
|
|
78
|
+
export declare function hostileTreePerTest(prefix: string): HostileTreePerTest;
|
|
79
|
+
//# sourceMappingURL=hostile-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostile-tree.d.ts","sourceRoot":"","sources":["../../src/testing/hostile-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAYH,8EAA8E;AAC9E,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAW1C,CAAC;AAEF,yFAAyF;AACzF,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iHAAiH;IACjH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gGAAgG;IAChG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,oGAAoG;IACpG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,yEAAyE;IACzE,OAAO,IAAI,IAAI,CAAC;CACjB;AAuBD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA8D1D;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,4EAA4E;IAC5E,IAAI,EAAE,MAAM,WAAW,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAuBrE"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One hostile fixture tree, built the same way for every sink.
|
|
3
|
+
*
|
|
4
|
+
* A correctness sweep found a delete, a copy and an uninstall that
|
|
5
|
+
* each walked out of their root, and found them by hand-building the same
|
|
6
|
+
* shapes — a symlink pointing out, a symlink pointing back at the root, a
|
|
7
|
+
* `..`-named entry, an unreadable directory, a traversal name — one throwaway
|
|
8
|
+
* fixture per probe. This module
|
|
9
|
+
* is that fixture, once, so a sink's test says `buildHostileTree(base)` and
|
|
10
|
+
* `it.each(HOSTILE_NAMES)` and is refusing the shapes the next sink's test is
|
|
11
|
+
* refusing too. A shape added here reaches every sink that uses it.
|
|
12
|
+
*
|
|
13
|
+
* ⛔ Framework-free, like everything under `testing/`: no `vitest` import, so
|
|
14
|
+
* the `./testing` subpath keeps the empty third-party set its purity pin
|
|
15
|
+
* asserts. Fields that the host cannot build are `null` — route those through
|
|
16
|
+
* the suite's own `skip()` so the skip is visible in the report.
|
|
17
|
+
*/
|
|
18
|
+
import { chmodSync, rmSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { isFilesystemAccessError } from '../errors/errno.js';
|
|
20
|
+
import { mkdirSyncReal, safePath } from '../path-utils.js';
|
|
21
|
+
import { createSymlink, symlinkCapability } from '../test-helpers.js';
|
|
22
|
+
import { CANNOT_DENY_READS } from './platform-gates.js';
|
|
23
|
+
import { createTempDir, removeTempDir } from './temp-dir.js';
|
|
24
|
+
/** The names a sink must refuse when they arrive as "the entry to act on". */
|
|
25
|
+
export const HOSTILE_NAMES = [
|
|
26
|
+
'..',
|
|
27
|
+
'../victim',
|
|
28
|
+
'../../victim',
|
|
29
|
+
'a/../../victim',
|
|
30
|
+
String.raw `..\victim`,
|
|
31
|
+
'.',
|
|
32
|
+
'',
|
|
33
|
+
'/victim',
|
|
34
|
+
String.raw `C:\victim`,
|
|
35
|
+
`nul${String.fromCodePoint(0)}byte`,
|
|
36
|
+
];
|
|
37
|
+
/** A link, or `null` when this host cannot make one (the capability probe said so). */
|
|
38
|
+
function tryLink(cap, target, link, type) {
|
|
39
|
+
if (cap === null)
|
|
40
|
+
return null;
|
|
41
|
+
createSymlink(cap, target, link, type);
|
|
42
|
+
return link;
|
|
43
|
+
}
|
|
44
|
+
function tryMkdir(dir) {
|
|
45
|
+
try {
|
|
46
|
+
mkdirSyncReal(dir);
|
|
47
|
+
return dir;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
// Only a name the OS will not take is "cannot build" — `ENAMETOOLONG` on
|
|
51
|
+
// a host with a shorter limit. Anything else is a broken fixture.
|
|
52
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENAMETOOLONG') {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Plant the hostile tree under `base`, which must exist and be empty enough
|
|
60
|
+
* to take `root`, `outside` and `root-alias` as direct children.
|
|
61
|
+
*
|
|
62
|
+
* @param base - A scratch directory the caller owns (a per-test temp dir)
|
|
63
|
+
* @returns The tree, with `null` for every shape this host cannot build
|
|
64
|
+
*/
|
|
65
|
+
export function buildHostileTree(base) {
|
|
66
|
+
const root = safePath.join(base, 'root');
|
|
67
|
+
const member = safePath.join(root, 'member');
|
|
68
|
+
const dotdotNamed = safePath.join(root, '..cache');
|
|
69
|
+
const outside = safePath.join(base, 'outside');
|
|
70
|
+
const victim = safePath.join(outside, 'victim');
|
|
71
|
+
mkdirSyncReal(member, { recursive: true });
|
|
72
|
+
mkdirSyncReal(safePath.join(root, 'nested', 'deep'), { recursive: true });
|
|
73
|
+
mkdirSyncReal(dotdotNamed);
|
|
74
|
+
mkdirSyncReal(victim, { recursive: true });
|
|
75
|
+
writeFileSync(safePath.join(victim, 'secret.txt'), 'TOKEN=abc\n');
|
|
76
|
+
writeFileSync(safePath.join(member, 'file.txt'), 'member\n');
|
|
77
|
+
const cap = symlinkCapability();
|
|
78
|
+
const linkOut = tryLink(cap, victim, safePath.join(root, 'link-out'), 'dir');
|
|
79
|
+
const linkIn = tryLink(cap, member, safePath.join(root, 'link-in'), 'dir');
|
|
80
|
+
const dangling = tryLink(cap, safePath.join(root, 'does-not-exist'), safePath.join(root, 'dangling'), 'file');
|
|
81
|
+
const linkLoop = tryLink(cap, root, safePath.join(root, 'loop'), 'dir');
|
|
82
|
+
const rootAlias = tryLink(cap, root, safePath.join(base, 'root-alias'), 'dir');
|
|
83
|
+
let unreadable = null;
|
|
84
|
+
if (!CANNOT_DENY_READS) {
|
|
85
|
+
unreadable = safePath.join(root, 'unreadable');
|
|
86
|
+
mkdirSyncReal(unreadable);
|
|
87
|
+
writeFileSync(safePath.join(unreadable, 'hidden.txt'), 'x');
|
|
88
|
+
chmodSync(unreadable, 0o000);
|
|
89
|
+
}
|
|
90
|
+
const longPath = tryMkdir(safePath.join(root, 'L'.repeat(200)));
|
|
91
|
+
let cleaned = false;
|
|
92
|
+
return {
|
|
93
|
+
root,
|
|
94
|
+
member,
|
|
95
|
+
dotdotNamed,
|
|
96
|
+
outside,
|
|
97
|
+
victim,
|
|
98
|
+
linkOut,
|
|
99
|
+
linkIn,
|
|
100
|
+
dangling,
|
|
101
|
+
linkLoop,
|
|
102
|
+
rootAlias,
|
|
103
|
+
unreadable,
|
|
104
|
+
longPath,
|
|
105
|
+
cleanup: () => {
|
|
106
|
+
if (cleaned)
|
|
107
|
+
return;
|
|
108
|
+
cleaned = true;
|
|
109
|
+
if (unreadable !== null) {
|
|
110
|
+
try {
|
|
111
|
+
chmodSync(unreadable, 0o700);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
// Already removed by the subject under test, which a delete sink is
|
|
115
|
+
// entitled to do; a refusal to restore anything else stays loud.
|
|
116
|
+
if (!isFilesystemAccessError(error))
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const dir of [root, outside, rootAlias]) {
|
|
121
|
+
if (dir !== null)
|
|
122
|
+
rmSync(dir, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Hold a per-test hostile tree, so a suite's wiring is three lines:
|
|
129
|
+
* `const hostile = hostileTreePerTest('x'); beforeEach(hostile.plant); afterEach(hostile.clear);`.
|
|
130
|
+
*
|
|
131
|
+
* Same shape and same reasons as `replantableCorpus`: the tree comes back
|
|
132
|
+
* through a GETTER because it is reminted per test, `plant` clears any tree
|
|
133
|
+
* still standing so nested `describe`s do not leak one, and `tree()` before
|
|
134
|
+
* `plant()` throws by name rather than surfacing as an undefined read.
|
|
135
|
+
*
|
|
136
|
+
* @param prefix - `mkdtemp` prefix for the scratch root, so a leak names its suite
|
|
137
|
+
* @returns Plant/clear/tree, to be driven from the caller's own hooks
|
|
138
|
+
*/
|
|
139
|
+
export function hostileTreePerTest(prefix) {
|
|
140
|
+
let scratch;
|
|
141
|
+
let planted;
|
|
142
|
+
const clear = () => {
|
|
143
|
+
planted?.cleanup();
|
|
144
|
+
planted = undefined;
|
|
145
|
+
if (scratch !== undefined)
|
|
146
|
+
removeTempDir(scratch);
|
|
147
|
+
scratch = undefined;
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
plant: () => {
|
|
151
|
+
clear();
|
|
152
|
+
scratch = createTempDir(prefix);
|
|
153
|
+
planted = buildHostileTree(scratch);
|
|
154
|
+
},
|
|
155
|
+
clear,
|
|
156
|
+
tree: () => {
|
|
157
|
+
if (planted === undefined) {
|
|
158
|
+
throw new Error(`hostileTreePerTest('${prefix}'): tree() before plant() — the suite is missing its beforeEach`);
|
|
159
|
+
}
|
|
160
|
+
return planted;
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=hostile-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostile-tree.js","sourceRoot":"","sources":["../../src/testing/hostile-tree.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host facts a fixture has to ask before it can build a refusal.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ Framework-free, like everything under `testing/`: no `vitest` import, so
|
|
5
|
+
* the `./testing` subpath keeps the empty third-party set its purity pin
|
|
6
|
+
* asserts. Route a `true` here through the suite's own `skip()` so the skip is
|
|
7
|
+
* visible in the report — a fixture that silently no-ops is a passing test for
|
|
8
|
+
* a property nobody exercised.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Whether a `chmod 000` on this host denies anything.
|
|
12
|
+
*
|
|
13
|
+
* Two hosts read every mode as readable: Windows, where POSIX mode bits do not
|
|
14
|
+
* bind at all, and a POSIX process running as root, which bypasses them. On
|
|
15
|
+
* either, a fixture directory made unreadable is still readable, so a test of
|
|
16
|
+
* "the walker refuses what it cannot list" would pass by listing it.
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ Evaluated ONCE at module load, as a constant rather than a function, on
|
|
19
|
+
* purpose: the answer is a property of the process's identity and platform,
|
|
20
|
+
* neither of which changes mid-run, and a constant is what `it.skipIf(...)`
|
|
21
|
+
* takes. This one line used to be spelled in 28 test files under three names
|
|
22
|
+
* (`CANNOT_DENY_READS`, `PERMISSIONS_ENFORCED`, inline), which is 28 places
|
|
23
|
+
* for the `getuid` guard to drift.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CANNOT_DENY_READS: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The positive spelling, for the suites that phrase the gate as "only run
|
|
28
|
+
* where modes bind". Same fact, no second evaluation.
|
|
29
|
+
*/
|
|
30
|
+
export declare const PERMISSIONS_ENFORCED: boolean;
|
|
31
|
+
//# sourceMappingURL=platform-gates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-gates.d.ts","sourceRoot":"","sources":["../../src/testing/platform-gates.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,EAAE,OACkE,CAAC;AAEnG;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,OAA4B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host facts a fixture has to ask before it can build a refusal.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ Framework-free, like everything under `testing/`: no `vitest` import, so
|
|
5
|
+
* the `./testing` subpath keeps the empty third-party set its purity pin
|
|
6
|
+
* asserts. Route a `true` here through the suite's own `skip()` so the skip is
|
|
7
|
+
* visible in the report — a fixture that silently no-ops is a passing test for
|
|
8
|
+
* a property nobody exercised.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Whether a `chmod 000` on this host denies anything.
|
|
12
|
+
*
|
|
13
|
+
* Two hosts read every mode as readable: Windows, where POSIX mode bits do not
|
|
14
|
+
* bind at all, and a POSIX process running as root, which bypasses them. On
|
|
15
|
+
* either, a fixture directory made unreadable is still readable, so a test of
|
|
16
|
+
* "the walker refuses what it cannot list" would pass by listing it.
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ Evaluated ONCE at module load, as a constant rather than a function, on
|
|
19
|
+
* purpose: the answer is a property of the process's identity and platform,
|
|
20
|
+
* neither of which changes mid-run, and a constant is what `it.skipIf(...)`
|
|
21
|
+
* takes. This one line used to be spelled in 28 test files under three names
|
|
22
|
+
* (`CANNOT_DENY_READS`, `PERMISSIONS_ENFORCED`, inline), which is 28 places
|
|
23
|
+
* for the `getuid` guard to drift.
|
|
24
|
+
*/
|
|
25
|
+
export const CANNOT_DENY_READS = process.platform === 'win32' || (typeof process.getuid === 'function' && process.getuid() === 0);
|
|
26
|
+
/**
|
|
27
|
+
* The positive spelling, for the suites that phrase the gate as "only run
|
|
28
|
+
* where modes bind". Same fact, no second evaluation.
|
|
29
|
+
*/
|
|
30
|
+
export const PERMISSIONS_ENFORCED = !CANNOT_DENY_READS;
|
|
31
|
+
//# sourceMappingURL=platform-gates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-gates.js","sourceRoot":"","sources":["../../src/testing/platform-gates.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAC5B,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAEnG;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAY,CAAC,iBAAiB,CAAC"}
|