@descryy/runtime-controller 0.0.0

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 (61) hide show
  1. package/dist/capability-registry.d.ts +24 -0
  2. package/dist/capability-registry.d.ts.map +1 -0
  3. package/dist/capability-registry.js +42 -0
  4. package/dist/capability-registry.js.map +1 -0
  5. package/dist/collector-version.d.ts +10 -0
  6. package/dist/collector-version.d.ts.map +1 -0
  7. package/dist/collector-version.js +12 -0
  8. package/dist/collector-version.js.map +1 -0
  9. package/dist/container-sandbox.d.ts +188 -0
  10. package/dist/container-sandbox.d.ts.map +1 -0
  11. package/dist/container-sandbox.js +233 -0
  12. package/dist/container-sandbox.js.map +1 -0
  13. package/dist/controller.d.ts +162 -0
  14. package/dist/controller.d.ts.map +1 -0
  15. package/dist/controller.js +433 -0
  16. package/dist/controller.js.map +1 -0
  17. package/dist/dependency-version-check.d.ts +90 -0
  18. package/dist/dependency-version-check.d.ts.map +1 -0
  19. package/dist/dependency-version-check.js +121 -0
  20. package/dist/dependency-version-check.js.map +1 -0
  21. package/dist/env.d.ts +16 -0
  22. package/dist/env.d.ts.map +1 -0
  23. package/dist/env.js +18 -0
  24. package/dist/env.js.map +1 -0
  25. package/dist/environment-metadata.d.ts +23 -0
  26. package/dist/environment-metadata.d.ts.map +1 -0
  27. package/dist/environment-metadata.js +80 -0
  28. package/dist/environment-metadata.js.map +1 -0
  29. package/dist/environment-version-check.d.ts +83 -0
  30. package/dist/environment-version-check.d.ts.map +1 -0
  31. package/dist/environment-version-check.js +218 -0
  32. package/dist/environment-version-check.js.map +1 -0
  33. package/dist/execution-safety.d.ts +154 -0
  34. package/dist/execution-safety.d.ts.map +1 -0
  35. package/dist/execution-safety.js +194 -0
  36. package/dist/execution-safety.js.map +1 -0
  37. package/dist/index.d.ts +35 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +14 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/orchestration.d.ts +70 -0
  42. package/dist/orchestration.d.ts.map +1 -0
  43. package/dist/orchestration.js +255 -0
  44. package/dist/orchestration.js.map +1 -0
  45. package/dist/process-collector.d.ts +34 -0
  46. package/dist/process-collector.d.ts.map +1 -0
  47. package/dist/process-collector.js +148 -0
  48. package/dist/process-collector.js.map +1 -0
  49. package/dist/process-manager.d.ts +133 -0
  50. package/dist/process-manager.d.ts.map +1 -0
  51. package/dist/process-manager.js +333 -0
  52. package/dist/process-manager.js.map +1 -0
  53. package/dist/readiness.d.ts +120 -0
  54. package/dist/readiness.d.ts.map +1 -0
  55. package/dist/readiness.js +179 -0
  56. package/dist/readiness.js.map +1 -0
  57. package/dist/sandbox.d.ts +139 -0
  58. package/dist/sandbox.d.ts.map +1 -0
  59. package/dist/sandbox.js +271 -0
  60. package/dist/sandbox.js.map +1 -0
  61. package/package.json +29 -0
@@ -0,0 +1,218 @@
1
+ /**
2
+ * `DEC-NEXT-fault-layer-empirical-confirmation.md`'s `environment`
3
+ * mechanism: "declared/expected runner version (CI config) vs. observed
4
+ * runner version at execution." Same shape as `dependency-version-check.ts`'s
5
+ * comparison -- cheap, always available, no rerun needed.
6
+ *
7
+ * ## "Declared" is the project's own `.nvmrc`/`engines.node`, not a
8
+ * CI-workflow parser
9
+ *
10
+ * The ruling names "a CI config's pinned Node/language version" as one
11
+ * example of what "declared" can mean, not the only one -- its actual
12
+ * requirement is "whatever 'declared environment' already means elsewhere
13
+ * in this repo," checked directly rather than assumed (`environment-profile`
14
+ * turned out to mean something else entirely: DEC-270's safety level and
15
+ * credentials, not a version declaration). Nothing in this repo already
16
+ * parses a CI workflow file, and doing so first here would mean writing a
17
+ * YAML parser (or a regex sketch of one) for a signal that is, in every
18
+ * real Node project, already restated in `package.json` -- `.nvmrc` and
19
+ * `engines.node` are the actual, load-bearing declarations `nvm`/`npm`
20
+ * themselves read, checked directly against this repo's own root
21
+ * `package.json` (`engines: { node: ">=22.5" }`) and every fixture app
22
+ * under `fixture-apps/`, which all carry the same field. A future CI-file
23
+ * reader is a real, disclosed extension point, not a silent omission --
24
+ * this function's `declaredSource` is a closed union specifically so a
25
+ * third source can be added later without changing the shape callers
26
+ * already read.
27
+ *
28
+ * ## `satisfiesDeclaredNodeVersion`'s grammar is a real, narrower subset
29
+ *
30
+ * Supports `^x.y.z`, `~x.y.z`, `>=`/`<=`/`>`/`<` clauses, a bare version at
31
+ * any precision (`"20"`, `"20.11"`, `"20.11.5"`, each an x-range at that
32
+ * precision -- `"20"` means "any 20.x.y", matching what `actions/setup-node`
33
+ * itself does for a bare major), and space-separated AND clauses
34
+ * (`">=20.0.0 <21.0.0"`), npm's own shape for a bounded range. Does **not**
35
+ * support `||` (OR), hyphen ranges (`"1.2.3 - 2.3.4"`), prerelease/build
36
+ * tags, or the `x`/`*` wildcard tokens (`"20.x"`) -- an input using any of
37
+ * those parses to `null` ("cannot say") rather than a silently wrong
38
+ * true/false, the same "disclosed narrower scope, not a guess" pattern this
39
+ * repo already applies to `EnvironmentMetadata.toolchainVersions`' `null`
40
+ * entries.
41
+ */
42
+ import { existsSync, readFileSync } from "node:fs";
43
+ import { join } from "node:path";
44
+ export const DECLARED_VERSION_SOURCES = ["nvmrc", "package.json engines.node"];
45
+ /**
46
+ * Reads the declared Node version for the project rooted at `cwd` and
47
+ * compares it against `observedVersion` (the real, already-known runtime
48
+ * value -- `process.version`, or `EnvironmentMetadata.nodeVersion`, the same
49
+ * "always known, never probed" value this repo already captures elsewhere;
50
+ * this function never probes for it itself).
51
+ *
52
+ * `.nvmrc` takes precedence over `package.json engines.node` when both are
53
+ * present -- `.nvmrc` names one exact intended version a developer actually
54
+ * runs locally; `engines.node` is more often a permissive range meant to
55
+ * describe compatibility rather than pin a single runtime. The narrower,
56
+ * more specific declaration wins, the same precedence
57
+ * `classifyFaultLayer` (`descry-core`) already applies between `dependency`
58
+ * and `environment` themselves ("the narrower, more specific structural
59
+ * fact... is checked first").
60
+ */
61
+ export function checkEnvironmentVersion(cwd, observedVersion) {
62
+ const notApplicable = {
63
+ applicable: false,
64
+ mismatch: false,
65
+ declaredVersion: null,
66
+ declaredSource: null,
67
+ observedVersion,
68
+ };
69
+ const nvmrcPath = join(cwd, ".nvmrc");
70
+ let declaredVersion = null;
71
+ let declaredSource = null;
72
+ if (existsSync(nvmrcPath)) {
73
+ const raw = readFileSync(nvmrcPath, "utf8").trim();
74
+ if (raw.length > 0) {
75
+ declaredVersion = raw;
76
+ declaredSource = "nvmrc";
77
+ }
78
+ }
79
+ else {
80
+ const packageJsonPath = join(cwd, "package.json");
81
+ if (existsSync(packageJsonPath)) {
82
+ let parsed;
83
+ try {
84
+ parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
85
+ }
86
+ catch {
87
+ parsed = null;
88
+ }
89
+ const node = parsed?.engines?.node;
90
+ if (typeof node === "string" && node.trim().length > 0) {
91
+ declaredVersion = node;
92
+ declaredSource = "package.json engines.node";
93
+ }
94
+ }
95
+ }
96
+ if (declaredVersion === null || declaredSource === null) {
97
+ return notApplicable;
98
+ }
99
+ const satisfies = satisfiesDeclaredNodeVersion(declaredVersion, observedVersion);
100
+ if (satisfies === null) {
101
+ // Declared with a grammar this reader does not cover (or the observed
102
+ // string was not parseable), so honestly "not applicable" rather than
103
+ // implying a comparison was actually made -- see this module's header.
104
+ return { applicable: false, mismatch: false, declaredVersion, declaredSource, observedVersion };
105
+ }
106
+ return { applicable: true, mismatch: !satisfies, declaredVersion, declaredSource, observedVersion };
107
+ }
108
+ const VERSION_PATTERN = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?/;
109
+ function parseVersion(raw) {
110
+ const match = VERSION_PATTERN.exec(raw.trim());
111
+ if (match === null)
112
+ return null;
113
+ const major = Number(match[1]);
114
+ if (!Number.isFinite(major))
115
+ return null;
116
+ return {
117
+ major,
118
+ minor: match[2] !== undefined ? Number(match[2]) : 0,
119
+ minorSpecified: match[2] !== undefined,
120
+ patch: match[3] !== undefined ? Number(match[3]) : 0,
121
+ patchSpecified: match[3] !== undefined,
122
+ };
123
+ }
124
+ /** Positive when `a` > `b`, negative when `a` < `b`, zero when equal -- major.minor.patch only. */
125
+ function compareVersions(a, b) {
126
+ if (a.major !== b.major)
127
+ return a.major - b.major;
128
+ if (a.minor !== b.minor)
129
+ return a.minor - b.minor;
130
+ return a.patch - b.patch;
131
+ }
132
+ const CARET_PATTERN = /^\^(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
133
+ const TILDE_PATTERN = /^~(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
134
+ const COMPARATOR_PATTERN = /^(>=|<=|>|<|=)?(\d+)(?:\.(\d+))?(?:\.(\d+))?$/;
135
+ /**
136
+ * `null` means "this clause's grammar is not one of the ones documented in
137
+ * this module's header" -- propagated up as `null`, never coerced to a
138
+ * guessed `true`/`false`.
139
+ */
140
+ function satisfiesClause(clause, observed) {
141
+ const trimmed = clause.trim();
142
+ if (trimmed.length === 0)
143
+ return null;
144
+ const caret = CARET_PATTERN.exec(trimmed);
145
+ if (caret !== null) {
146
+ const base = {
147
+ major: Number(caret[1]),
148
+ minor: caret[2] !== undefined ? Number(caret[2]) : 0,
149
+ minorSpecified: true,
150
+ patch: caret[3] !== undefined ? Number(caret[3]) : 0,
151
+ patchSpecified: true,
152
+ };
153
+ return compareVersions(observed, base) >= 0 && observed.major === base.major;
154
+ }
155
+ const tilde = TILDE_PATTERN.exec(trimmed);
156
+ if (tilde !== null) {
157
+ const base = {
158
+ major: Number(tilde[1]),
159
+ minor: tilde[2] !== undefined ? Number(tilde[2]) : 0,
160
+ minorSpecified: true,
161
+ patch: tilde[3] !== undefined ? Number(tilde[3]) : 0,
162
+ patchSpecified: true,
163
+ };
164
+ return compareVersions(observed, base) >= 0 && observed.major === base.major && observed.minor === base.minor;
165
+ }
166
+ const comparator = COMPARATOR_PATTERN.exec(trimmed);
167
+ if (comparator === null)
168
+ return null;
169
+ const op = comparator[1];
170
+ const base = {
171
+ major: Number(comparator[2]),
172
+ minor: comparator[3] !== undefined ? Number(comparator[3]) : 0,
173
+ minorSpecified: comparator[3] !== undefined,
174
+ patch: comparator[4] !== undefined ? Number(comparator[4]) : 0,
175
+ patchSpecified: comparator[4] !== undefined,
176
+ };
177
+ const cmp = compareVersions(observed, base);
178
+ switch (op) {
179
+ case ">=":
180
+ return cmp >= 0;
181
+ case "<=":
182
+ return cmp <= 0;
183
+ case ">":
184
+ return cmp > 0;
185
+ case "<":
186
+ return cmp < 0;
187
+ case "=":
188
+ case undefined:
189
+ // Bare version: an x-range at whatever precision was actually given.
190
+ if (!base.minorSpecified)
191
+ return observed.major === base.major;
192
+ if (!base.patchSpecified)
193
+ return observed.major === base.major && observed.minor === base.minor;
194
+ return cmp === 0;
195
+ default:
196
+ return null;
197
+ }
198
+ }
199
+ /**
200
+ * Whether `observedVersion` satisfies `declaredRange` under the grammar
201
+ * documented in this module's header. `null` means the comparison could not
202
+ * be made at all (either string used a construct this reader does not
203
+ * cover) -- distinct from `false`, which is a real, positive "does not
204
+ * satisfy" answer.
205
+ */
206
+ export function satisfiesDeclaredNodeVersion(declaredRange, observedVersion) {
207
+ const observed = parseVersion(observedVersion);
208
+ if (observed === null)
209
+ return null;
210
+ const clauses = declaredRange.split(/\s+/).filter((clause) => clause.length > 0);
211
+ if (clauses.length === 0)
212
+ return null;
213
+ const results = clauses.map((clause) => satisfiesClause(clause, observed));
214
+ if (results.some((result) => result === null))
215
+ return null;
216
+ return results.every((result) => result === true);
217
+ }
218
+ //# sourceMappingURL=environment-version-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-version-check.js","sourceRoot":"","sources":["../src/environment-version-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * §21's execution boundary (RT-070): resource limits and privilege
3
+ * reporting for a runtime whose declared mode is trusted-local (see
4
+ * DECISIONS.md RT-070) -- every spawned process runs with the invoking
5
+ * user's own OS privileges, no filesystem or network restriction. That
6
+ * declaration does not make unrestricted execution safe; it names the
7
+ * boundary instead of leaving it absent, which is what §38's "arbitrary
8
+ * application code runs without a defined safety boundary" actually bans.
9
+ * Real isolation (containers/namespaces) is a separate architectural
10
+ * decision this module does not invent (plan §29's own instruction).
11
+ *
12
+ * What this module DOES enforce, for real, via the OS: CPU time, virtual
13
+ * memory, and process count, through `prlimit` -- an exec wrapper, never a
14
+ * shell. Reintroducing `sh -c 'ulimit ...; exec ...'` to get these would
15
+ * undo RT-036's `tokenizeCommand` refusal of shell metacharacters. `prlimit`
16
+ * is util-linux and absent on macOS, so it is reported as a capability,
17
+ * never assumed.
18
+ */
19
+ import type { CapabilityStatus, ResourceLimits } from "@descryy/runtime-contracts";
20
+ /**
21
+ * Whether `prlimit` is reachable on this platform's `PATH`. Takes `env` as
22
+ * a parameter (defaulting to `process.env`) rather than reading it
23
+ * internally so the "unavailable" branch is directly testable without
24
+ * mutating this process's own real environment or depending on running on
25
+ * a platform that genuinely lacks it.
26
+ */
27
+ export declare function resourceLimitCapability(env?: NodeJS.ProcessEnv): CapabilityStatus;
28
+ /**
29
+ * RT-193 originally made this always `unavailable` -- no platform had any
30
+ * enforcement mechanism wired up. The sandbox-isolation lane changes that
31
+ * on Linux: `networkIsolationCapability()` (`./sandbox.ts`) is real, backed
32
+ * by a bwrap network namespace. This function is kept as the stable name
33
+ * every existing caller (`ExecutionController.run()`, this package's own
34
+ * tests) already imports, delegating rather than duplicating the check --
35
+ * exactly the seam RT-193's own comment anticipated ("if a real mechanism
36
+ * is ever built, this one function changes and every caller keeps working
37
+ * unmodified"). Still unconditionally `unavailable` on macOS/Windows, where
38
+ * nothing enforces it yet.
39
+ */
40
+ export declare function networkPolicyCapability(env?: NodeJS.ProcessEnv): CapabilityStatus;
41
+ /**
42
+ * Wraps `command`/`args` so the OS enforces `limits`, via `prlimit`.
43
+ * `undefined` or an empty `limits` returns `command`/`args` unchanged --
44
+ * every execution before RT-070 keeps behaving exactly as before unless it
45
+ * opts in.
46
+ *
47
+ * **Throws rather than silently spawning unconstrained** when a limit was
48
+ * requested and `prlimit` is unavailable. `tokenizeCommand`'s own precedent
49
+ * (RT-036) is refuse rather than guess; the equivalent failure here would
50
+ * be a caller asking for a memory cap and getting a process that can
51
+ * allocate without bound, silently -- exactly the "sandbox that doesn't
52
+ * sandbox" shape §38 bans, one row up from this one.
53
+ */
54
+ export declare function applyResourceLimits(command: string, args: readonly string[], limits: ResourceLimits | undefined, env?: NodeJS.ProcessEnv): {
55
+ readonly command: string;
56
+ readonly args: readonly string[];
57
+ };
58
+ /**
59
+ * DEC-272's disclosure requirement: the sandbox boundary (RT-070) must be
60
+ * stated to the user directly in-product, not left as a silent gap in a
61
+ * doc. Fixed, not derived from `runningAsRoot` -- the boundary is the same
62
+ * regardless of which privilege level was found; only the observed fact
63
+ * changes.
64
+ *
65
+ * **Scope, corrected by the attach-mode-mitigations lane: this is the
66
+ * unconditional-unisolated text, not a universal one.** Written when
67
+ * RT-193/RT-070 landed, before `sandbox.ts` existed -- at that point it was
68
+ * accurate for every execution, because nothing anywhere in this runtime
69
+ * could isolate anything. That stopped being true the moment the
70
+ * sandbox-isolation lane gave spawn-mode real, bwrap-backed filesystem/
71
+ * network isolation (`applySandbox`, this same package): a spawned process
72
+ * that actually got a `FilesystemPolicy`/`NetworkPolicy` applied to it is
73
+ * genuinely more contained than "the same as if you started it yourself"
74
+ * says. Using this constant unconditionally for that process would be a
75
+ * real disclosure-accuracy bug -- understating a boundary is not the safe
76
+ * direction to be wrong in, it just happens to be a less alarming one.
77
+ * `sandboxBoundaryDisclosureFor()` below is the mode-aware replacement: it
78
+ * returns this exact string when no policy was applied (attach-mode always,
79
+ * and any spawn that declared none -- unchanged from before this lane), and
80
+ * a real, specific description of what is and is not contained when a
81
+ * policy genuinely was. This constant is kept, unchanged, as that function's
82
+ * own no-policy-applied return value and as attach-mode's own disclosure,
83
+ * not deprecated -- attach-mode never has anything more accurate to say.
84
+ */
85
+ export declare const SANDBOX_BOUNDARY_DISCLOSURE: string;
86
+ /**
87
+ * Whether a real `FilesystemPolicy`/`NetworkPolicy` was actually enforced on
88
+ * the process this disclosure describes -- not merely requested.
89
+ * `applySandbox` already refuses to spawn (throws) rather than proceed
90
+ * unconstrained when a declared policy cannot be enforced (see its own
91
+ * comment), so by the time a caller can observe a successfully spawned
92
+ * process, "declared" and "applied" are the same fact; this type exists so
93
+ * a caller states that fact explicitly rather than this function silently
94
+ * assuming it.
95
+ */
96
+ export interface AppliedIsolationPolicies {
97
+ readonly filesystemPolicyApplied: boolean;
98
+ readonly networkPolicyApplied: boolean;
99
+ }
100
+ /**
101
+ * The mode-aware replacement for reading `SANDBOX_BOUNDARY_DISCLOSURE`
102
+ * unconditionally. Defaults to reporting no policy applied -- so calling
103
+ * this with no argument at all is identical to the pre-existing constant,
104
+ * the same non-regression contract every other opt-in in this file
105
+ * (`applyResourceLimits`, `applySandbox`) already keeps.
106
+ *
107
+ * **Still only ever true for spawn-mode.** Attach-mode has no code path
108
+ * that can produce `filesystemPolicyApplied: true` or
109
+ * `networkPolicyApplied: true` for any process it did not itself spawn --
110
+ * see `sandbox.ts`'s own module doc for why that is structural, not a gap
111
+ * in this function. A caller in attach-mode should simply never pass
112
+ * anything but the default here, and the default's return value is this
113
+ * disclosure's original, unconditional "not isolated" text, unchanged.
114
+ */
115
+ export declare function sandboxBoundaryDisclosureFor(policies?: AppliedIsolationPolicies): string;
116
+ /**
117
+ * Attach mode's own contractual scope guarantee -- distinct from the
118
+ * sandbox-*boundary* disclosure above (what is or isn't contained) because
119
+ * this is a claim about what an attach-mode `Collector` is even permitted
120
+ * to *do*, not about what OS boundary wraps it. See `Collector` in
121
+ * `@descryy/runtime-contracts` for the interface-level statement this
122
+ * mirrors, and `sandbox.ts`'s own module doc for why attach-mode can never
123
+ * acquire the isolation `sandboxBoundaryDisclosureFor` describes for
124
+ * spawn-mode. Stated once, here, so every attach-mode caller surfaces the
125
+ * identical sentence rather than each writing its own paraphrase.
126
+ */
127
+ export declare const ATTACH_MODE_SCOPE_DISCLOSURE: string;
128
+ export interface PrivilegeReport {
129
+ /** Null when this platform has no uid concept at all (`process.getuid` is undefined on Windows) -- distinct from `false`, which is a real answer, not an absent one. */
130
+ readonly runningAsRoot: boolean | null;
131
+ /** DEC-272: plain-language statement of the sandbox boundary, carried on the same result the privilege level already travels on rather than a new return path. Always populated -- the disclosure is unconditional, not dependent on `runningAsRoot`. */
132
+ readonly disclosure: string;
133
+ }
134
+ /**
135
+ * Reports, never enforces. §21's privilege policy for a runtime declared
136
+ * trusted-local (RT-070) is not "refuse to run as root" -- CI containers
137
+ * commonly run as root by default, and a hard refusal there would turn a
138
+ * documented boundary into an undocumented outage. The policy is: the real
139
+ * privilege level becomes an observable fact instead of an invisible one,
140
+ * which is what nothing before RT-070 made it.
141
+ *
142
+ * A required parameter, not a defaulted one: `undefined` is the real,
143
+ * meaningful value a platform with no uid concept produces
144
+ * (`process.getuid` is itself `undefined` on Windows), and a *default*
145
+ * parameter cannot tell "caller explicitly passed undefined" apart from
146
+ * "caller omitted the argument" -- both look identical to the function.
147
+ * `currentPrivilegeReport()` below is the zero-argument convenience wrapper
148
+ * real callers use; this is what makes both branches -- root, and no uid
149
+ * concept at all -- directly testable without this process actually
150
+ * needing to run as root or on Windows.
151
+ */
152
+ export declare function privilegeReportFor(getuid: (() => number) | undefined): PrivilegeReport;
153
+ export declare function currentPrivilegeReport(): PrivilegeReport;
154
+ //# sourceMappingURL=execution-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-safety.d.ts","sourceRoot":"","sources":["../src/execution-safety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAGnF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAU9F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAE9F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAchE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,2BAA2B,QAGT,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;CACxC;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,GAAE,wBAA8C,GAAG,MAAM,CAwB7G;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,QAKoC,CAAC;AAE9E,MAAM,WAAW,eAAe;IAC9B,wKAAwK;IACxK,QAAQ,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IACvC,yPAAyP;IACzP,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,GAAG,eAAe,CAGtF;AAED,wBAAgB,sBAAsB,IAAI,eAAe,CAExD"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * §21's execution boundary (RT-070): resource limits and privilege
3
+ * reporting for a runtime whose declared mode is trusted-local (see
4
+ * DECISIONS.md RT-070) -- every spawned process runs with the invoking
5
+ * user's own OS privileges, no filesystem or network restriction. That
6
+ * declaration does not make unrestricted execution safe; it names the
7
+ * boundary instead of leaving it absent, which is what §38's "arbitrary
8
+ * application code runs without a defined safety boundary" actually bans.
9
+ * Real isolation (containers/namespaces) is a separate architectural
10
+ * decision this module does not invent (plan §29's own instruction).
11
+ *
12
+ * What this module DOES enforce, for real, via the OS: CPU time, virtual
13
+ * memory, and process count, through `prlimit` -- an exec wrapper, never a
14
+ * shell. Reintroducing `sh -c 'ulimit ...; exec ...'` to get these would
15
+ * undo RT-036's `tokenizeCommand` refusal of shell metacharacters. `prlimit`
16
+ * is util-linux and absent on macOS, so it is reported as a capability,
17
+ * never assumed.
18
+ */
19
+ import { execFileSync } from "node:child_process";
20
+ import { networkIsolationCapability } from "./sandbox.js";
21
+ /**
22
+ * Whether `prlimit` is reachable on this platform's `PATH`. Takes `env` as
23
+ * a parameter (defaulting to `process.env`) rather than reading it
24
+ * internally so the "unavailable" branch is directly testable without
25
+ * mutating this process's own real environment or depending on running on
26
+ * a platform that genuinely lacks it.
27
+ */
28
+ export function resourceLimitCapability(env = process.env) {
29
+ try {
30
+ execFileSync("prlimit", ["--version"], { env, stdio: "ignore" });
31
+ return { availability: "available", reason: null };
32
+ }
33
+ catch {
34
+ return {
35
+ availability: "unavailable",
36
+ reason: "prlimit (util-linux) is not on PATH -- CPU/memory/process limits cannot be enforced on this platform without reintroducing a shell, which this runtime refuses to do (RT-036)",
37
+ };
38
+ }
39
+ }
40
+ /**
41
+ * RT-193 originally made this always `unavailable` -- no platform had any
42
+ * enforcement mechanism wired up. The sandbox-isolation lane changes that
43
+ * on Linux: `networkIsolationCapability()` (`./sandbox.ts`) is real, backed
44
+ * by a bwrap network namespace. This function is kept as the stable name
45
+ * every existing caller (`ExecutionController.run()`, this package's own
46
+ * tests) already imports, delegating rather than duplicating the check --
47
+ * exactly the seam RT-193's own comment anticipated ("if a real mechanism
48
+ * is ever built, this one function changes and every caller keeps working
49
+ * unmodified"). Still unconditionally `unavailable` on macOS/Windows, where
50
+ * nothing enforces it yet.
51
+ */
52
+ export function networkPolicyCapability(env = process.env) {
53
+ return networkIsolationCapability(env);
54
+ }
55
+ /**
56
+ * Wraps `command`/`args` so the OS enforces `limits`, via `prlimit`.
57
+ * `undefined` or an empty `limits` returns `command`/`args` unchanged --
58
+ * every execution before RT-070 keeps behaving exactly as before unless it
59
+ * opts in.
60
+ *
61
+ * **Throws rather than silently spawning unconstrained** when a limit was
62
+ * requested and `prlimit` is unavailable. `tokenizeCommand`'s own precedent
63
+ * (RT-036) is refuse rather than guess; the equivalent failure here would
64
+ * be a caller asking for a memory cap and getting a process that can
65
+ * allocate without bound, silently -- exactly the "sandbox that doesn't
66
+ * sandbox" shape §38 bans, one row up from this one.
67
+ */
68
+ export function applyResourceLimits(command, args, limits, env = process.env) {
69
+ if (limits === undefined)
70
+ return { command, args };
71
+ const flags = [];
72
+ if (limits.maxMemoryBytes !== undefined)
73
+ flags.push(`--as=${limits.maxMemoryBytes}`);
74
+ if (limits.maxCpuSeconds !== undefined)
75
+ flags.push(`--cpu=${limits.maxCpuSeconds}`);
76
+ if (limits.maxProcesses !== undefined)
77
+ flags.push(`--nproc=${limits.maxProcesses}`);
78
+ if (flags.length === 0)
79
+ return { command, args };
80
+ const capability = resourceLimitCapability(env);
81
+ if (capability.availability !== "available") {
82
+ throw new Error(`resource limits were requested but cannot be enforced on this platform: ${capability.reason}`);
83
+ }
84
+ return { command: "prlimit", args: [...flags, "--", command, ...args] };
85
+ }
86
+ /**
87
+ * DEC-272's disclosure requirement: the sandbox boundary (RT-070) must be
88
+ * stated to the user directly in-product, not left as a silent gap in a
89
+ * doc. Fixed, not derived from `runningAsRoot` -- the boundary is the same
90
+ * regardless of which privilege level was found; only the observed fact
91
+ * changes.
92
+ *
93
+ * **Scope, corrected by the attach-mode-mitigations lane: this is the
94
+ * unconditional-unisolated text, not a universal one.** Written when
95
+ * RT-193/RT-070 landed, before `sandbox.ts` existed -- at that point it was
96
+ * accurate for every execution, because nothing anywhere in this runtime
97
+ * could isolate anything. That stopped being true the moment the
98
+ * sandbox-isolation lane gave spawn-mode real, bwrap-backed filesystem/
99
+ * network isolation (`applySandbox`, this same package): a spawned process
100
+ * that actually got a `FilesystemPolicy`/`NetworkPolicy` applied to it is
101
+ * genuinely more contained than "the same as if you started it yourself"
102
+ * says. Using this constant unconditionally for that process would be a
103
+ * real disclosure-accuracy bug -- understating a boundary is not the safe
104
+ * direction to be wrong in, it just happens to be a less alarming one.
105
+ * `sandboxBoundaryDisclosureFor()` below is the mode-aware replacement: it
106
+ * returns this exact string when no policy was applied (attach-mode always,
107
+ * and any spawn that declared none -- unchanged from before this lane), and
108
+ * a real, specific description of what is and is not contained when a
109
+ * policy genuinely was. This constant is kept, unchanged, as that function's
110
+ * own no-policy-applied return value and as attach-mode's own disclosure,
111
+ * not deprecated -- attach-mode never has anything more accurate to say.
112
+ */
113
+ export const SANDBOX_BOUNDARY_DISCLOSURE = "This app runs with your own user permissions, the same as if you started it yourself. " +
114
+ "The filesystem and network are not isolated -- anything your own account could read, write, " +
115
+ "or reach, this run can too.";
116
+ const NO_POLICIES_APPLIED = { filesystemPolicyApplied: false, networkPolicyApplied: false };
117
+ /**
118
+ * The mode-aware replacement for reading `SANDBOX_BOUNDARY_DISCLOSURE`
119
+ * unconditionally. Defaults to reporting no policy applied -- so calling
120
+ * this with no argument at all is identical to the pre-existing constant,
121
+ * the same non-regression contract every other opt-in in this file
122
+ * (`applyResourceLimits`, `applySandbox`) already keeps.
123
+ *
124
+ * **Still only ever true for spawn-mode.** Attach-mode has no code path
125
+ * that can produce `filesystemPolicyApplied: true` or
126
+ * `networkPolicyApplied: true` for any process it did not itself spawn --
127
+ * see `sandbox.ts`'s own module doc for why that is structural, not a gap
128
+ * in this function. A caller in attach-mode should simply never pass
129
+ * anything but the default here, and the default's return value is this
130
+ * disclosure's original, unconditional "not isolated" text, unchanged.
131
+ */
132
+ export function sandboxBoundaryDisclosureFor(policies = NO_POLICIES_APPLIED) {
133
+ const { filesystemPolicyApplied, networkPolicyApplied } = policies;
134
+ if (!filesystemPolicyApplied && !networkPolicyApplied)
135
+ return SANDBOX_BOUNDARY_DISCLOSURE;
136
+ if (filesystemPolicyApplied && networkPolicyApplied) {
137
+ return ("This process was spawned inside a real OS-level sandbox (bubblewrap): filesystem access is restricted " +
138
+ "to its declared allowedRoots (plus its own working directory) -- nothing else on this machine is visible " +
139
+ "to it, not merely unwritable -- and network access is fully denied, including this machine's own loopback. " +
140
+ "This is real isolation, not a name-only boundary.");
141
+ }
142
+ if (filesystemPolicyApplied) {
143
+ return ("This process was spawned inside a real OS-level sandbox (bubblewrap): filesystem access is restricted " +
144
+ "to its declared allowedRoots (plus its own working directory) -- nothing else on this machine is visible " +
145
+ "to it, not merely unwritable. Network access was NOT restricted for this run -- it can reach anything " +
146
+ "your own account's network could reach.");
147
+ }
148
+ return ("This process was spawned inside a real OS-level sandbox (bubblewrap): network access is fully denied " +
149
+ "for this run, including this machine's own loopback. Filesystem access was NOT restricted for this run -- " +
150
+ "it can read, write, or reach anything your own account's filesystem could.");
151
+ }
152
+ /**
153
+ * Attach mode's own contractual scope guarantee -- distinct from the
154
+ * sandbox-*boundary* disclosure above (what is or isn't contained) because
155
+ * this is a claim about what an attach-mode `Collector` is even permitted
156
+ * to *do*, not about what OS boundary wraps it. See `Collector` in
157
+ * `@descryy/runtime-contracts` for the interface-level statement this
158
+ * mirrors, and `sandbox.ts`'s own module doc for why attach-mode can never
159
+ * acquire the isolation `sandboxBoundaryDisclosureFor` describes for
160
+ * spawn-mode. Stated once, here, so every attach-mode caller surfaces the
161
+ * identical sentence rather than each writing its own paraphrase.
162
+ */
163
+ export const ATTACH_MODE_SCOPE_DISCLOSURE = "Attach mode observes an already-running process; it never executes code in it or applies changes to it. " +
164
+ "This is a permanent, documented architectural limitation, not a temporary gap -- there is no honest way to " +
165
+ "retroactively sandbox a process that was already running, unconfined, before this tool touched it. " +
166
+ "Respawn-and-supervise and the cgroup resource-limit move are real alternatives next to attach mode, not " +
167
+ "fixes to it; see their own documentation for what each actually gets you.";
168
+ /**
169
+ * Reports, never enforces. §21's privilege policy for a runtime declared
170
+ * trusted-local (RT-070) is not "refuse to run as root" -- CI containers
171
+ * commonly run as root by default, and a hard refusal there would turn a
172
+ * documented boundary into an undocumented outage. The policy is: the real
173
+ * privilege level becomes an observable fact instead of an invisible one,
174
+ * which is what nothing before RT-070 made it.
175
+ *
176
+ * A required parameter, not a defaulted one: `undefined` is the real,
177
+ * meaningful value a platform with no uid concept produces
178
+ * (`process.getuid` is itself `undefined` on Windows), and a *default*
179
+ * parameter cannot tell "caller explicitly passed undefined" apart from
180
+ * "caller omitted the argument" -- both look identical to the function.
181
+ * `currentPrivilegeReport()` below is the zero-argument convenience wrapper
182
+ * real callers use; this is what makes both branches -- root, and no uid
183
+ * concept at all -- directly testable without this process actually
184
+ * needing to run as root or on Windows.
185
+ */
186
+ export function privilegeReportFor(getuid) {
187
+ if (getuid === undefined)
188
+ return { runningAsRoot: null, disclosure: SANDBOX_BOUNDARY_DISCLOSURE };
189
+ return { runningAsRoot: getuid() === 0, disclosure: SANDBOX_BOUNDARY_DISCLOSURE };
190
+ }
191
+ export function currentPrivilegeReport() {
192
+ return privilegeReportFor(process.getuid?.bind(process));
193
+ }
194
+ //# sourceMappingURL=execution-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-safety.js","sourceRoot":"","sources":["../src/execution-safety.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export { withDefaultProcessEnv } from "./env.ts";
2
+ export type { SpawnProcessOptions, ProcessExitInfo, ManagedProcess, AttachProcessOptions } from "./process-manager.ts";
3
+ export { spawnProcess, attachManagedProcess, isProcessAlive } from "./process-manager.ts";
4
+ export type { ReadinessMechanism, HttpReadinessCheck, TcpPortReadinessCheck, LogPatternReadinessCheck, CommandReadinessCheck, CustomHookReadinessCheck, ReadinessCheck, ReadinessResult, AwaitReadinessOptions, } from "./readiness.ts";
5
+ export { READINESS_MECHANISMS, awaitReadiness } from "./readiness.ts";
6
+ export type { CreateExecutionInput, RunOptions, ProcessLifecycleEvent } from "./controller.ts";
7
+ export { ExecutionController } from "./controller.ts";
8
+ export type { ProcessCollector } from "./process-collector.ts";
9
+ export { createProcessCollector } from "./process-collector.ts";
10
+ export type { RegisteredCollectorCapabilities, CollectorCapabilityKey } from "./capability-registry.ts";
11
+ export { buildExecutionCapabilities, bestCapabilityStatus } from "./capability-registry.ts";
12
+ export type { PrivilegeReport, AppliedIsolationPolicies } from "./execution-safety.ts";
13
+ export { resourceLimitCapability, applyResourceLimits, networkPolicyCapability, privilegeReportFor, currentPrivilegeReport, SANDBOX_BOUNDARY_DISCLOSURE, sandboxBoundaryDisclosureFor, ATTACH_MODE_SCOPE_DISCLOSURE, } from "./execution-safety.ts";
14
+ export type { SandboxOptions } from "./sandbox.ts";
15
+ export { filesystemIsolationCapability, networkIsolationCapability, unsupportedNetworkPolicyReason, resolveExecutableDirectory, applySandbox, } from "./sandbox.ts";
16
+ export type { ContainerSandboxOptions } from "./container-sandbox.ts";
17
+ export { containerRuntimeCapability, containerFilesystemIsolationCapability, containerNetworkIsolationCapability, unsupportedContainerNetworkPolicyReason, resolveContainerImage, applyContainerSandbox, } from "./container-sandbox.ts";
18
+ export { captureEnvironmentMetadata } from "./environment-metadata.ts";
19
+ export type { DependencyVersionMismatch, DependencyVersionCheckResult } from "./dependency-version-check.ts";
20
+ export { checkDependencyVersions } from "./dependency-version-check.ts";
21
+ export type { DeclaredVersionSource, EnvironmentVersionCheckResult } from "./environment-version-check.ts";
22
+ export { DECLARED_VERSION_SOURCES, checkEnvironmentVersion, satisfiesDeclaredNodeVersion } from "./environment-version-check.ts";
23
+ /**
24
+ * RT-073: promoted from internal-only. `tokenizeCommand`'s POSIX-style
25
+ * "refuse rather than guess" tokenizer is exactly what `packages/test-runner`
26
+ * needs to validate a declared runner command before spawning it -- the
27
+ * same job it already does for `ServiceConfiguration.command` here.
28
+ * Re-implementing a second copy for one more caller would be the "two
29
+ * shapes that could drift" mistake this codebase has already named and
30
+ * avoided elsewhere (RT-068's `BrowserConfiguration`, RT-070's
31
+ * `ResourceLimits`).
32
+ */
33
+ export type { CommandTokenization, ServicesValidation } from "./orchestration.ts";
34
+ export { tokenizeCommand, validateServicesConfiguration, substitutePortPlaceholders } from "./orchestration.ts";
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEjD,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACvH,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1F,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,cAAc,EACd,eAAe,EACf,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEtE,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,YAAY,EAAE,+BAA+B,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACxG,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAE5F,YAAY,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,2BAA2B,EAC3B,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,8BAA8B,EAC9B,0BAA0B,EAC1B,YAAY,GACb,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EACL,0BAA0B,EAC1B,sCAAsC,EACtC,mCAAmC,EACnC,uCAAuC,EACvC,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAEvE,YAAY,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAC7G,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAExE,YAAY,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAC3G,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAEjI;;;;;;;;;GASG;AACH,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,6BAA6B,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ export { withDefaultProcessEnv } from "./env.js";
2
+ export { spawnProcess, attachManagedProcess, isProcessAlive } from "./process-manager.js";
3
+ export { READINESS_MECHANISMS, awaitReadiness } from "./readiness.js";
4
+ export { ExecutionController } from "./controller.js";
5
+ export { createProcessCollector } from "./process-collector.js";
6
+ export { buildExecutionCapabilities, bestCapabilityStatus } from "./capability-registry.js";
7
+ export { resourceLimitCapability, applyResourceLimits, networkPolicyCapability, privilegeReportFor, currentPrivilegeReport, SANDBOX_BOUNDARY_DISCLOSURE, sandboxBoundaryDisclosureFor, ATTACH_MODE_SCOPE_DISCLOSURE, } from "./execution-safety.js";
8
+ export { filesystemIsolationCapability, networkIsolationCapability, unsupportedNetworkPolicyReason, resolveExecutableDirectory, applySandbox, } from "./sandbox.js";
9
+ export { containerRuntimeCapability, containerFilesystemIsolationCapability, containerNetworkIsolationCapability, unsupportedContainerNetworkPolicyReason, resolveContainerImage, applyContainerSandbox, } from "./container-sandbox.js";
10
+ export { captureEnvironmentMetadata } from "./environment-metadata.js";
11
+ export { checkDependencyVersions } from "./dependency-version-check.js";
12
+ export { DECLARED_VERSION_SOURCES, checkEnvironmentVersion, satisfiesDeclaredNodeVersion } from "./environment-version-check.js";
13
+ export { tokenizeCommand, validateServicesConfiguration, substitutePortPlaceholders } from "./orchestration.js";
14
+ //# sourceMappingURL=index.js.map