@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.
- package/dist/capability-registry.d.ts +24 -0
- package/dist/capability-registry.d.ts.map +1 -0
- package/dist/capability-registry.js +42 -0
- package/dist/capability-registry.js.map +1 -0
- package/dist/collector-version.d.ts +10 -0
- package/dist/collector-version.d.ts.map +1 -0
- package/dist/collector-version.js +12 -0
- package/dist/collector-version.js.map +1 -0
- package/dist/container-sandbox.d.ts +188 -0
- package/dist/container-sandbox.d.ts.map +1 -0
- package/dist/container-sandbox.js +233 -0
- package/dist/container-sandbox.js.map +1 -0
- package/dist/controller.d.ts +162 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +433 -0
- package/dist/controller.js.map +1 -0
- package/dist/dependency-version-check.d.ts +90 -0
- package/dist/dependency-version-check.d.ts.map +1 -0
- package/dist/dependency-version-check.js +121 -0
- package/dist/dependency-version-check.js.map +1 -0
- package/dist/env.d.ts +16 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +18 -0
- package/dist/env.js.map +1 -0
- package/dist/environment-metadata.d.ts +23 -0
- package/dist/environment-metadata.d.ts.map +1 -0
- package/dist/environment-metadata.js +80 -0
- package/dist/environment-metadata.js.map +1 -0
- package/dist/environment-version-check.d.ts +83 -0
- package/dist/environment-version-check.d.ts.map +1 -0
- package/dist/environment-version-check.js +218 -0
- package/dist/environment-version-check.js.map +1 -0
- package/dist/execution-safety.d.ts +154 -0
- package/dist/execution-safety.d.ts.map +1 -0
- package/dist/execution-safety.js +194 -0
- package/dist/execution-safety.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestration.d.ts +70 -0
- package/dist/orchestration.d.ts.map +1 -0
- package/dist/orchestration.js +255 -0
- package/dist/orchestration.js.map +1 -0
- package/dist/process-collector.d.ts +34 -0
- package/dist/process-collector.d.ts.map +1 -0
- package/dist/process-collector.js +148 -0
- package/dist/process-collector.js.map +1 -0
- package/dist/process-manager.d.ts +133 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +333 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/readiness.d.ts +120 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +179 -0
- package/dist/readiness.js.map +1 -0
- package/dist/sandbox.d.ts +139 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +271 -0
- package/dist/sandbox.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability registry (plan §16): assembles per-execution capabilities
|
|
3
|
+
* from the collectors actually present, so `distributedTrace: unavailable`
|
|
4
|
+
* is a first-class answer the AI planner can read rather than a silent
|
|
5
|
+
* gap. Does not know how to *produce* a `CollectorCapabilities` value —
|
|
6
|
+
* that's each collector's own `capabilities()` (Agents 2-4's concern) —
|
|
7
|
+
* only how to assemble and query what's been registered.
|
|
8
|
+
*/
|
|
9
|
+
import type { CapabilityStatus, CollectorCapabilities, EnvironmentTier, ExecutionCapabilities, FidelityLevel } from "@descryy/runtime-contracts";
|
|
10
|
+
export interface RegisteredCollectorCapabilities {
|
|
11
|
+
readonly collectorId: string;
|
|
12
|
+
readonly capabilities: CollectorCapabilities;
|
|
13
|
+
}
|
|
14
|
+
export declare function buildExecutionCapabilities(environmentTier: EnvironmentTier, fidelityLevel: FidelityLevel, collectors: readonly RegisteredCollectorCapabilities[]): ExecutionCapabilities;
|
|
15
|
+
export type CollectorCapabilityKey = keyof CollectorCapabilities;
|
|
16
|
+
/**
|
|
17
|
+
* The best (most available) status for one capability across every
|
|
18
|
+
* registered collector — "is this observable *anywhere* in this
|
|
19
|
+
* execution," not per-collector. An execution with two network collectors
|
|
20
|
+
* where one degraded and one is fully available should read as available,
|
|
21
|
+
* not degraded.
|
|
22
|
+
*/
|
|
23
|
+
export declare function bestCapabilityStatus(capabilities: ExecutionCapabilities, key: CollectorCapabilityKey): CapabilityStatus;
|
|
24
|
+
//# sourceMappingURL=capability-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-registry.d.ts","sourceRoot":"","sources":["../src/capability-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,aAAa,EACd,MAAM,4BAA4B,CAAC;AAEpC,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;CAC9C;AAED,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,SAAS,+BAA+B,EAAE,GACrD,qBAAqB,CAEvB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,qBAAqB,CAAC;AAajE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,qBAAqB,EACnC,GAAG,EAAE,sBAAsB,GAC1B,gBAAgB,CAalB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability registry (plan §16): assembles per-execution capabilities
|
|
3
|
+
* from the collectors actually present, so `distributedTrace: unavailable`
|
|
4
|
+
* is a first-class answer the AI planner can read rather than a silent
|
|
5
|
+
* gap. Does not know how to *produce* a `CollectorCapabilities` value —
|
|
6
|
+
* that's each collector's own `capabilities()` (Agents 2-4's concern) —
|
|
7
|
+
* only how to assemble and query what's been registered.
|
|
8
|
+
*/
|
|
9
|
+
export function buildExecutionCapabilities(environmentTier, fidelityLevel, collectors) {
|
|
10
|
+
return { environmentTier, fidelityLevel, collectors };
|
|
11
|
+
}
|
|
12
|
+
const AVAILABILITY_RANK = {
|
|
13
|
+
unavailable: 0,
|
|
14
|
+
degraded: 1,
|
|
15
|
+
available: 2,
|
|
16
|
+
};
|
|
17
|
+
const NO_COLLECTOR_STATUS = {
|
|
18
|
+
availability: "unavailable",
|
|
19
|
+
reason: "no collector registered for this execution",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The best (most available) status for one capability across every
|
|
23
|
+
* registered collector — "is this observable *anywhere* in this
|
|
24
|
+
* execution," not per-collector. An execution with two network collectors
|
|
25
|
+
* where one degraded and one is fully available should read as available,
|
|
26
|
+
* not degraded.
|
|
27
|
+
*/
|
|
28
|
+
export function bestCapabilityStatus(capabilities, key) {
|
|
29
|
+
// Seeded from the first collector actually seen, not from
|
|
30
|
+
// NO_COLLECTOR_STATUS -- seeding from the placeholder would make a
|
|
31
|
+
// single collector's genuine "unavailable" tie the placeholder's rank
|
|
32
|
+
// and lose its specific reason to the generic one.
|
|
33
|
+
let best = null;
|
|
34
|
+
for (const collector of capabilities.collectors) {
|
|
35
|
+
const status = collector.capabilities[key];
|
|
36
|
+
if (best === null || AVAILABILITY_RANK[status.availability] > AVAILABILITY_RANK[best.availability]) {
|
|
37
|
+
best = status;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return best ?? NO_COLLECTOR_STATUS;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=capability-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-registry.js","sourceRoot":"","sources":["../src/capability-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,MAAM,UAAU,0BAA0B,CACxC,eAAgC,EAChC,aAA4B,EAC5B,UAAsD;IAEtD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AACxD,CAAC;AAID,MAAM,iBAAiB,GAAqD;IAC1E,WAAW,EAAE,CAAC;IACd,QAAQ,EAAE,CAAC;IACX,SAAS,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,mBAAmB,GAAqB;IAC5C,YAAY,EAAE,aAAa;IAC3B,MAAM,EAAE,4CAA4C;CACrD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAmC,EACnC,GAA2B;IAE3B,0DAA0D;IAC1D,mEAAmE;IACnE,sEAAsE;IACtE,mDAAmD;IACnD,IAAI,IAAI,GAA4B,IAAI,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,IAAI,KAAK,IAAI,IAAI,iBAAiB,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACnG,IAAI,GAAG,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,IAAI,mBAAmB,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Evidence.collectorVersion`'s source for `ProcessCollector`: this
|
|
3
|
+
* package's own `package.json` "version", read from the artifact itself
|
|
4
|
+
* rather than hand-duplicated into a second string that can drift from
|
|
5
|
+
* what actually shipped. Same idea as `nodeVersion` elsewhere in this repo
|
|
6
|
+
* (`process.version`, always known, never probed) applied to a package
|
|
7
|
+
* instead of the Node runtime.
|
|
8
|
+
*/
|
|
9
|
+
export declare const COLLECTOR_VERSION: string;
|
|
10
|
+
//# sourceMappingURL=collector-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collector-version.d.ts","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,eAAO,MAAM,iBAAiB,EAAE,MAA6E,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Evidence.collectorVersion`'s source for `ProcessCollector`: this
|
|
3
|
+
* package's own `package.json` "version", read from the artifact itself
|
|
4
|
+
* rather than hand-duplicated into a second string that can drift from
|
|
5
|
+
* what actually shipped. Same idea as `nodeVersion` elsewhere in this repo
|
|
6
|
+
* (`process.version`, always known, never probed) applied to a package
|
|
7
|
+
* instead of the Node runtime.
|
|
8
|
+
*/
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
export const COLLECTOR_VERSION = require("../package.json").version;
|
|
12
|
+
//# sourceMappingURL=collector-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collector-version.js","sourceRoot":"","sources":["../src/collector-version.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,iBAAiB,GAAY,OAAO,CAAC,iBAAiB,CAAkC,CAAC,OAAO,CAAC"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §21's execution boundary, container backend -- the macOS/Windows half of
|
|
3
|
+
* the sandbox-isolation lane. `sandbox.ts` closed this gap for Linux via
|
|
4
|
+
* bubblewrap; this module closes it for macOS and Windows, but NOT by
|
|
5
|
+
* building a second and third native OS sandbox. That path was researched
|
|
6
|
+
* (`sandbox.ts`'s own original module doc named `sandbox-exec`/Seatbelt on
|
|
7
|
+
* macOS and Job Objects/AppContainer on Windows as candidates) and
|
|
8
|
+
* explicitly rejected as an architecture decision: `sandbox-exec` is
|
|
9
|
+
* deprecated (no public API replacement), the Endpoint Security Framework
|
|
10
|
+
* needs a special Apple entitlement this project does not have, and
|
|
11
|
+
* hand-building Job Objects + AppContainer + WFP network enforcement on
|
|
12
|
+
* Windows is a second and third bespoke native sandbox to build and
|
|
13
|
+
* maintain for the same problem already solved once.
|
|
14
|
+
*
|
|
15
|
+
* **Mechanism: route the spawned process through a real container instead.**
|
|
16
|
+
* This runtime's actual targets -- JVM/Spring, Node, Python backends -- are
|
|
17
|
+
* already containerizable, so this reuses the *Linux* sandbox mechanism
|
|
18
|
+
* bwrap already proved, by running the target process inside a `docker run`
|
|
19
|
+
* invocation. The container's own rootfs IS the "outside allowedRoots is
|
|
20
|
+
* unreachable" boundary; nothing inside the container needs bwrap. On
|
|
21
|
+
* macOS, `docker run` targets Docker Desktop's Linux VM; on Windows, WSL2 /
|
|
22
|
+
* Docker Desktop's Windows backend. Both ultimately run the same Linux
|
|
23
|
+
* container runtime this module already drives directly.
|
|
24
|
+
*
|
|
25
|
+
* **What was actually verified, and what was not -- stated precisely,
|
|
26
|
+
* matching `sandbox.ts`'s own "researched and disclosed rather than
|
|
27
|
+
* guessed" discipline:**
|
|
28
|
+
*
|
|
29
|
+
* REAL-TESTED, on this Linux development machine, via a real installed
|
|
30
|
+
* Docker daemon (server 29.6.1) and real `docker run` invocations -- not
|
|
31
|
+
* mocked, not asserted from documentation: a file outside a container's
|
|
32
|
+
* bind-mounts is genuinely unreachable (`ENOENT` from inside the running
|
|
33
|
+
* container); a network call under `--network none` is genuinely refused
|
|
34
|
+
* against a real host listener; both together on one spawn; the same
|
|
35
|
+
* boundary through the full `ExecutionController.run()` path, not just this
|
|
36
|
+
* module's own function called directly. See `container-sandbox.test.ts`
|
|
37
|
+
* and `controller.test.ts`'s container-backend end-to-end test.
|
|
38
|
+
*
|
|
39
|
+
* **IMPLEMENTATION-NOT-DONE, not a "gap" and not a coverage caveat: the
|
|
40
|
+
* macOS-Docker-Desktop-VM-specific and Windows-WSL2-specific integration
|
|
41
|
+
* paths are UNVERIFIED on real macOS/Windows hardware.** No such hardware
|
|
42
|
+
* is available in this environment. Concretely unverified: detecting
|
|
43
|
+
* whether Docker Desktop is actually running before attempting a spawn;
|
|
44
|
+
* path translation across the Docker Desktop VM boundary on macOS; WSL2
|
|
45
|
+
* path translation on Windows (a Windows host path and the path Linux
|
|
46
|
+
* containers see are not the same string). This module's bind-mount
|
|
47
|
+
* strategy assumes the host path and the in-container path are identical
|
|
48
|
+
* (`-v hostPath:hostPath`, mirroring `sandbox.ts`'s own `--bind root root`
|
|
49
|
+
* convention) -- true on Linux, true for Docker Desktop's Linux VM when the
|
|
50
|
+
* path is already inside the VM's shared-drive mapping, but the exact
|
|
51
|
+
* translation rules for an arbitrary Windows path are not exercised here at
|
|
52
|
+
* all. This is "the container mechanism exists and is tested where it can
|
|
53
|
+
* be, but full cross-platform verification needs hardware nobody here has"
|
|
54
|
+
* -- a distinct category from a real bug or an intentionally out-of-scope
|
|
55
|
+
* capability, and this module does not claim otherwise.
|
|
56
|
+
*
|
|
57
|
+
* **Explicit scope boundary, same treatment attach-mode already gets in
|
|
58
|
+
* this codebase:** a target that cannot run containerized at all --deep
|
|
59
|
+
* native OS integration, GUI-dependent processes, OS-specific native
|
|
60
|
+
* dependencies that don't exist inside a Linux container image-- is out of
|
|
61
|
+
* scope for this approach, on any platform. There is no fallback to a
|
|
62
|
+
* native sandbox for such a target; it is simply not isolatable by this
|
|
63
|
+
* mechanism, and that is disclosed rather than silently unsupported.
|
|
64
|
+
*
|
|
65
|
+
* **Real escape testing on the container boundary is still required and
|
|
66
|
+
* was done** -- "the container starts successfully" is not treated as
|
|
67
|
+
* sufficient evidence here, the same bar `sandbox.ts`'s bwrap path was held
|
|
68
|
+
* to. A mutation check on the first version of that testing (temporarily
|
|
69
|
+
* removing `--network none`, rerunning) surfaced a genuine behavioral
|
|
70
|
+
* difference from bwrap worth stating precisely rather than glossing over:
|
|
71
|
+
* **Docker gives every container its own network namespace by default,
|
|
72
|
+
* with or without `--network none`** -- unlike bwrap, which shares the
|
|
73
|
+
* host's network unless `networkPolicy` is declared. A containerized
|
|
74
|
+
* process can therefore never reach the HOST's own loopback either way
|
|
75
|
+
* (that alone is not evidence `--network none` specifically did anything),
|
|
76
|
+
* but it CAN still reach the public internet via Docker's default bridge
|
|
77
|
+
* NAT when no `networkPolicy` is declared -- only `--network none`
|
|
78
|
+
* additionally blocks that. `container-sandbox.test.ts`'s dedicated
|
|
79
|
+
* mutation-sensitive test isolates exactly this variable (an external
|
|
80
|
+
* host, not the host's own loopback) to prove the flag itself is what is
|
|
81
|
+
* being tested, not mere containerization.
|
|
82
|
+
*
|
|
83
|
+
* **What this module does NOT attempt to solve, disclosed rather than
|
|
84
|
+
* silently dropped:** composition with `ResourceLimits` (`prlimit`) --
|
|
85
|
+
* `sandbox.ts` composes bwrap with `applyResourceLimits` by wrapping
|
|
86
|
+
* outside it; this module does not attempt the equivalent composition with
|
|
87
|
+
* Docker's own `--memory`/`--cpus`/`--pids-limit` flags in this pass. A
|
|
88
|
+
* `resourceLimits`-declared execution routed through the container backend
|
|
89
|
+
* runs without OS-enforced resource limits inside the container. Selective
|
|
90
|
+
* network allow-/deny-listing (`NetworkPolicy` shapes other than full
|
|
91
|
+
* denial) is unimplemented here for the same reason `sandbox.ts` doesn't
|
|
92
|
+
* implement it: it needs DNS interception and IP filtering this iteration
|
|
93
|
+
* does not build -- `unsupportedContainerNetworkPolicyReason` reports this
|
|
94
|
+
* the same way `sandbox.ts`'s own `unsupportedNetworkPolicyReason` does,
|
|
95
|
+
* deliberately matching posture rather than silently claiming broader
|
|
96
|
+
* support than the underlying mechanism has.
|
|
97
|
+
*/
|
|
98
|
+
import type { CapabilityStatus, FilesystemPolicy, NetworkPolicy } from "@descryy/runtime-contracts";
|
|
99
|
+
/**
|
|
100
|
+
* Real capability check: is a working Docker CLI + reachable daemon
|
|
101
|
+
* actually present, not merely "does a `docker` binary exist on PATH."
|
|
102
|
+
* `docker version` talks to the daemon; a CLI with no running daemon behind
|
|
103
|
+
* it (Docker Desktop not started, dockerd not running) fails this the same
|
|
104
|
+
* way a missing binary does -- both are "cannot enforce," and the caller
|
|
105
|
+
* does not need to tell them apart to make the right decision (refuse).
|
|
106
|
+
*/
|
|
107
|
+
export declare function containerRuntimeCapability(env?: NodeJS.ProcessEnv): CapabilityStatus;
|
|
108
|
+
/**
|
|
109
|
+
* Mirrors `sandbox.ts`'s `filesystemIsolationCapability`/
|
|
110
|
+
* `networkIsolationCapability` split: one real mechanism underneath
|
|
111
|
+
* (a container boundary), two named capabilities because the two policies
|
|
112
|
+
* are declared, refused, and reasoned about independently at the call
|
|
113
|
+
* site.
|
|
114
|
+
*/
|
|
115
|
+
export declare function containerFilesystemIsolationCapability(env?: NodeJS.ProcessEnv): CapabilityStatus;
|
|
116
|
+
export declare function containerNetworkIsolationCapability(env?: NodeJS.ProcessEnv): CapabilityStatus;
|
|
117
|
+
/**
|
|
118
|
+
* Same posture as `sandbox.ts`'s `unsupportedNetworkPolicyReason`, matched
|
|
119
|
+
* deliberately rather than reinvented: only full denial
|
|
120
|
+
* (`{ mode: "allow", hosts: [] }`, `docker run --network none`) is
|
|
121
|
+
* enforced. A shape bwrap already discloses as unsupported is not silently
|
|
122
|
+
* claimed as supported here just because the underlying mechanism changed.
|
|
123
|
+
*/
|
|
124
|
+
export declare function unsupportedContainerNetworkPolicyReason(policy: NetworkPolicy): string | null;
|
|
125
|
+
export declare function resolveContainerImage(interpreterCommand: string): string | null;
|
|
126
|
+
export interface ContainerSandboxOptions {
|
|
127
|
+
/** Used only to resolve a default base image via `resolveContainerImage` when `containerImage` is not given. */
|
|
128
|
+
readonly interpreterCommand: string;
|
|
129
|
+
/** The command to exec *inside* the container -- resolved against the image's own PATH, not the host's (see `processEnv`'s own comment for why `PATH` is never forwarded). */
|
|
130
|
+
readonly command: string;
|
|
131
|
+
readonly args: readonly string[];
|
|
132
|
+
/** Bind-mounted into the container at the identical path (`-v cwd:cwd`), always -- mirrors `sandbox.ts`'s "cwd always bound" default, and is the one directory the target process is guaranteed to need. */
|
|
133
|
+
readonly cwd: string;
|
|
134
|
+
readonly filesystemPolicy?: FilesystemPolicy;
|
|
135
|
+
readonly networkPolicy?: NetworkPolicy;
|
|
136
|
+
/**
|
|
137
|
+
* Env vars the CONTAINERIZED PROCESS itself needs (e.g. `PORT`, a
|
|
138
|
+
* service's declared `env`) -- forwarded into the container via `-e
|
|
139
|
+
* KEY=VALUE`. **`PATH` is deliberately never forwarded**: the container
|
|
140
|
+
* must resolve `command` against its own image's filesystem layout
|
|
141
|
+
* (`/usr/local/bin/node` in `node:22-slim`, not wherever the host's
|
|
142
|
+
* interpreter happens to live), and forwarding the host's `PATH` would
|
|
143
|
+
* silently break that resolution or -- if it named a path that happens to
|
|
144
|
+
* exist for a different binary inside the image -- run the wrong thing.
|
|
145
|
+
* Every other variable is forwarded unfiltered, matching the same
|
|
146
|
+
* unfiltered-inheritance precedent `process-manager.ts` already
|
|
147
|
+
* established for the bwrap path (bwrap does not `--clearenv` either).
|
|
148
|
+
*/
|
|
149
|
+
readonly processEnv?: Readonly<Record<string, string | undefined>>;
|
|
150
|
+
/** Env used only to run the `docker` CLI itself (PATH lookup for `docker`, `DOCKER_HOST`, etc.) -- plays the same role `SandboxOptions.env` plays for bwrap's own capability check. Defaults to `process.env`. */
|
|
151
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
152
|
+
/** Explicit override -- bypasses `resolveContainerImage`'s name-based guess entirely. */
|
|
153
|
+
readonly containerImage?: string;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Wraps `command`/`args` with `docker run` so a real container boundary
|
|
157
|
+
* enforces `filesystemPolicy`/`networkPolicy`. Neither declared returns
|
|
158
|
+
* `command`/`args` unchanged and never invokes docker at all -- the same
|
|
159
|
+
* non-regression contract `sandbox.ts`'s `applySandbox` established for
|
|
160
|
+
* bwrap, applied here too: a caller who never opts into a policy is
|
|
161
|
+
* completely unaffected by this module's existence.
|
|
162
|
+
*
|
|
163
|
+
* **Throws rather than silently spawning unconstrained** when a policy is
|
|
164
|
+
* requested and cannot actually be backed -- no working Docker, or a
|
|
165
|
+
* `networkPolicy` shape this mechanism doesn't implement -- matching
|
|
166
|
+
* `applySandbox`'s own refuse-rather-than-guess precedent exactly.
|
|
167
|
+
*
|
|
168
|
+
* **Returns a real, unique `containerName` whenever it wraps.** This was
|
|
169
|
+
* added after a genuine finding from this module's own real escape tests,
|
|
170
|
+
* not assumed up front: `process-manager.ts`'s existing group-kill
|
|
171
|
+
* (`process.kill(-pid, signal)`) targets the *local* `docker` CLI process's
|
|
172
|
+
* group -- exactly right for bwrap, which execs the sandboxed process
|
|
173
|
+
* directly and never creates a second process group. Docker is different:
|
|
174
|
+
* the container runs under `dockerd`, not as a child of the local `docker`
|
|
175
|
+
* CLI, so killing the CLI's process group does not reliably stop the
|
|
176
|
+
* container -- observed directly (a `kill()` call left a real orphaned
|
|
177
|
+
* `node:22-slim` container running after both the CLI process and its
|
|
178
|
+
* grace period were gone). `containerName` lets the caller
|
|
179
|
+
* (`process-manager.ts`) issue a real, explicit `docker stop <name>`
|
|
180
|
+
* against the container itself as the actual mechanism, not the CLI
|
|
181
|
+
* process, for the container backend.
|
|
182
|
+
*/
|
|
183
|
+
export declare function applyContainerSandbox(options: ContainerSandboxOptions): {
|
|
184
|
+
readonly command: string;
|
|
185
|
+
readonly args: readonly string[];
|
|
186
|
+
readonly containerName?: string;
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=container-sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-sandbox.d.ts","sourceRoot":"","sources":["../src/container-sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEpG;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAUjG;AAED;;;;;;GAMG;AACH,wBAAgB,sCAAsC,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAE7G;AAED,wBAAgB,mCAAmC,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAE1G;AAED;;;;;;GAMG;AACH,wBAAgB,uCAAuC,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAO5F;AAkBD,wBAAgB,qBAAqB,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE/E;AAED,MAAM,WAAW,uBAAuB;IACtC,gHAAgH;IAChH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,8KAA8K;IAC9K,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,4MAA4M;IAC5M,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IACnE,kNAAkN;IAClN,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,yFAAyF;IACzF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,uBAAuB,GAC/B;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAkDjG"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §21's execution boundary, container backend -- the macOS/Windows half of
|
|
3
|
+
* the sandbox-isolation lane. `sandbox.ts` closed this gap for Linux via
|
|
4
|
+
* bubblewrap; this module closes it for macOS and Windows, but NOT by
|
|
5
|
+
* building a second and third native OS sandbox. That path was researched
|
|
6
|
+
* (`sandbox.ts`'s own original module doc named `sandbox-exec`/Seatbelt on
|
|
7
|
+
* macOS and Job Objects/AppContainer on Windows as candidates) and
|
|
8
|
+
* explicitly rejected as an architecture decision: `sandbox-exec` is
|
|
9
|
+
* deprecated (no public API replacement), the Endpoint Security Framework
|
|
10
|
+
* needs a special Apple entitlement this project does not have, and
|
|
11
|
+
* hand-building Job Objects + AppContainer + WFP network enforcement on
|
|
12
|
+
* Windows is a second and third bespoke native sandbox to build and
|
|
13
|
+
* maintain for the same problem already solved once.
|
|
14
|
+
*
|
|
15
|
+
* **Mechanism: route the spawned process through a real container instead.**
|
|
16
|
+
* This runtime's actual targets -- JVM/Spring, Node, Python backends -- are
|
|
17
|
+
* already containerizable, so this reuses the *Linux* sandbox mechanism
|
|
18
|
+
* bwrap already proved, by running the target process inside a `docker run`
|
|
19
|
+
* invocation. The container's own rootfs IS the "outside allowedRoots is
|
|
20
|
+
* unreachable" boundary; nothing inside the container needs bwrap. On
|
|
21
|
+
* macOS, `docker run` targets Docker Desktop's Linux VM; on Windows, WSL2 /
|
|
22
|
+
* Docker Desktop's Windows backend. Both ultimately run the same Linux
|
|
23
|
+
* container runtime this module already drives directly.
|
|
24
|
+
*
|
|
25
|
+
* **What was actually verified, and what was not -- stated precisely,
|
|
26
|
+
* matching `sandbox.ts`'s own "researched and disclosed rather than
|
|
27
|
+
* guessed" discipline:**
|
|
28
|
+
*
|
|
29
|
+
* REAL-TESTED, on this Linux development machine, via a real installed
|
|
30
|
+
* Docker daemon (server 29.6.1) and real `docker run` invocations -- not
|
|
31
|
+
* mocked, not asserted from documentation: a file outside a container's
|
|
32
|
+
* bind-mounts is genuinely unreachable (`ENOENT` from inside the running
|
|
33
|
+
* container); a network call under `--network none` is genuinely refused
|
|
34
|
+
* against a real host listener; both together on one spawn; the same
|
|
35
|
+
* boundary through the full `ExecutionController.run()` path, not just this
|
|
36
|
+
* module's own function called directly. See `container-sandbox.test.ts`
|
|
37
|
+
* and `controller.test.ts`'s container-backend end-to-end test.
|
|
38
|
+
*
|
|
39
|
+
* **IMPLEMENTATION-NOT-DONE, not a "gap" and not a coverage caveat: the
|
|
40
|
+
* macOS-Docker-Desktop-VM-specific and Windows-WSL2-specific integration
|
|
41
|
+
* paths are UNVERIFIED on real macOS/Windows hardware.** No such hardware
|
|
42
|
+
* is available in this environment. Concretely unverified: detecting
|
|
43
|
+
* whether Docker Desktop is actually running before attempting a spawn;
|
|
44
|
+
* path translation across the Docker Desktop VM boundary on macOS; WSL2
|
|
45
|
+
* path translation on Windows (a Windows host path and the path Linux
|
|
46
|
+
* containers see are not the same string). This module's bind-mount
|
|
47
|
+
* strategy assumes the host path and the in-container path are identical
|
|
48
|
+
* (`-v hostPath:hostPath`, mirroring `sandbox.ts`'s own `--bind root root`
|
|
49
|
+
* convention) -- true on Linux, true for Docker Desktop's Linux VM when the
|
|
50
|
+
* path is already inside the VM's shared-drive mapping, but the exact
|
|
51
|
+
* translation rules for an arbitrary Windows path are not exercised here at
|
|
52
|
+
* all. This is "the container mechanism exists and is tested where it can
|
|
53
|
+
* be, but full cross-platform verification needs hardware nobody here has"
|
|
54
|
+
* -- a distinct category from a real bug or an intentionally out-of-scope
|
|
55
|
+
* capability, and this module does not claim otherwise.
|
|
56
|
+
*
|
|
57
|
+
* **Explicit scope boundary, same treatment attach-mode already gets in
|
|
58
|
+
* this codebase:** a target that cannot run containerized at all --deep
|
|
59
|
+
* native OS integration, GUI-dependent processes, OS-specific native
|
|
60
|
+
* dependencies that don't exist inside a Linux container image-- is out of
|
|
61
|
+
* scope for this approach, on any platform. There is no fallback to a
|
|
62
|
+
* native sandbox for such a target; it is simply not isolatable by this
|
|
63
|
+
* mechanism, and that is disclosed rather than silently unsupported.
|
|
64
|
+
*
|
|
65
|
+
* **Real escape testing on the container boundary is still required and
|
|
66
|
+
* was done** -- "the container starts successfully" is not treated as
|
|
67
|
+
* sufficient evidence here, the same bar `sandbox.ts`'s bwrap path was held
|
|
68
|
+
* to. A mutation check on the first version of that testing (temporarily
|
|
69
|
+
* removing `--network none`, rerunning) surfaced a genuine behavioral
|
|
70
|
+
* difference from bwrap worth stating precisely rather than glossing over:
|
|
71
|
+
* **Docker gives every container its own network namespace by default,
|
|
72
|
+
* with or without `--network none`** -- unlike bwrap, which shares the
|
|
73
|
+
* host's network unless `networkPolicy` is declared. A containerized
|
|
74
|
+
* process can therefore never reach the HOST's own loopback either way
|
|
75
|
+
* (that alone is not evidence `--network none` specifically did anything),
|
|
76
|
+
* but it CAN still reach the public internet via Docker's default bridge
|
|
77
|
+
* NAT when no `networkPolicy` is declared -- only `--network none`
|
|
78
|
+
* additionally blocks that. `container-sandbox.test.ts`'s dedicated
|
|
79
|
+
* mutation-sensitive test isolates exactly this variable (an external
|
|
80
|
+
* host, not the host's own loopback) to prove the flag itself is what is
|
|
81
|
+
* being tested, not mere containerization.
|
|
82
|
+
*
|
|
83
|
+
* **What this module does NOT attempt to solve, disclosed rather than
|
|
84
|
+
* silently dropped:** composition with `ResourceLimits` (`prlimit`) --
|
|
85
|
+
* `sandbox.ts` composes bwrap with `applyResourceLimits` by wrapping
|
|
86
|
+
* outside it; this module does not attempt the equivalent composition with
|
|
87
|
+
* Docker's own `--memory`/`--cpus`/`--pids-limit` flags in this pass. A
|
|
88
|
+
* `resourceLimits`-declared execution routed through the container backend
|
|
89
|
+
* runs without OS-enforced resource limits inside the container. Selective
|
|
90
|
+
* network allow-/deny-listing (`NetworkPolicy` shapes other than full
|
|
91
|
+
* denial) is unimplemented here for the same reason `sandbox.ts` doesn't
|
|
92
|
+
* implement it: it needs DNS interception and IP filtering this iteration
|
|
93
|
+
* does not build -- `unsupportedContainerNetworkPolicyReason` reports this
|
|
94
|
+
* the same way `sandbox.ts`'s own `unsupportedNetworkPolicyReason` does,
|
|
95
|
+
* deliberately matching posture rather than silently claiming broader
|
|
96
|
+
* support than the underlying mechanism has.
|
|
97
|
+
*/
|
|
98
|
+
import { execFileSync } from "node:child_process";
|
|
99
|
+
import { randomUUID } from "node:crypto";
|
|
100
|
+
import { basename } from "node:path";
|
|
101
|
+
/**
|
|
102
|
+
* Real capability check: is a working Docker CLI + reachable daemon
|
|
103
|
+
* actually present, not merely "does a `docker` binary exist on PATH."
|
|
104
|
+
* `docker version` talks to the daemon; a CLI with no running daemon behind
|
|
105
|
+
* it (Docker Desktop not started, dockerd not running) fails this the same
|
|
106
|
+
* way a missing binary does -- both are "cannot enforce," and the caller
|
|
107
|
+
* does not need to tell them apart to make the right decision (refuse).
|
|
108
|
+
*/
|
|
109
|
+
export function containerRuntimeCapability(env = process.env) {
|
|
110
|
+
try {
|
|
111
|
+
execFileSync("docker", ["version", "--format", "{{.Server.Version}}"], { env, stdio: "ignore" });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return {
|
|
115
|
+
availability: "unavailable",
|
|
116
|
+
reason: "docker is not on PATH, or no Docker daemon is reachable -- container-based isolation cannot be enforced without a real, running Docker",
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return { availability: "available", reason: null };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Mirrors `sandbox.ts`'s `filesystemIsolationCapability`/
|
|
123
|
+
* `networkIsolationCapability` split: one real mechanism underneath
|
|
124
|
+
* (a container boundary), two named capabilities because the two policies
|
|
125
|
+
* are declared, refused, and reasoned about independently at the call
|
|
126
|
+
* site.
|
|
127
|
+
*/
|
|
128
|
+
export function containerFilesystemIsolationCapability(env = process.env) {
|
|
129
|
+
return containerRuntimeCapability(env);
|
|
130
|
+
}
|
|
131
|
+
export function containerNetworkIsolationCapability(env = process.env) {
|
|
132
|
+
return containerRuntimeCapability(env);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Same posture as `sandbox.ts`'s `unsupportedNetworkPolicyReason`, matched
|
|
136
|
+
* deliberately rather than reinvented: only full denial
|
|
137
|
+
* (`{ mode: "allow", hosts: [] }`, `docker run --network none`) is
|
|
138
|
+
* enforced. A shape bwrap already discloses as unsupported is not silently
|
|
139
|
+
* claimed as supported here just because the underlying mechanism changed.
|
|
140
|
+
*/
|
|
141
|
+
export function unsupportedContainerNetworkPolicyReason(policy) {
|
|
142
|
+
if (policy.mode === "allow" && policy.hosts.length === 0)
|
|
143
|
+
return null;
|
|
144
|
+
return ('only full network denial ({ mode: "allow", hosts: [] }) is enforced -- selective allow- or deny-listing of specific ' +
|
|
145
|
+
"hosts would need a custom docker network with DNS interception and IP filtering inside it, not built in this " +
|
|
146
|
+
"iteration (the same posture the bwrap backend already declares for the same shapes)");
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Minimal, official, `-slim`/`-jre` base images for this runtime's actual
|
|
150
|
+
* fixture targets (JVM/Spring, Node, Python backends -- see
|
|
151
|
+
* `packages/orchestrator/test/fixtures`), keyed by the interpreter's
|
|
152
|
+
* basename so a full interpreter path (`/home/x/.nvm/versions/node/vX/bin/node`)
|
|
153
|
+
* still resolves. Not a general-purpose image registry -- a caller with a
|
|
154
|
+
* different runtime need passes `containerImage` explicitly instead of
|
|
155
|
+
* extending this table with more special cases.
|
|
156
|
+
*/
|
|
157
|
+
const KNOWN_RUNTIME_IMAGES = {
|
|
158
|
+
node: "node:22-slim",
|
|
159
|
+
python: "python:3.12-slim",
|
|
160
|
+
python3: "python:3.12-slim",
|
|
161
|
+
java: "eclipse-temurin:21-jre",
|
|
162
|
+
};
|
|
163
|
+
export function resolveContainerImage(interpreterCommand) {
|
|
164
|
+
return KNOWN_RUNTIME_IMAGES[basename(interpreterCommand)] ?? null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Wraps `command`/`args` with `docker run` so a real container boundary
|
|
168
|
+
* enforces `filesystemPolicy`/`networkPolicy`. Neither declared returns
|
|
169
|
+
* `command`/`args` unchanged and never invokes docker at all -- the same
|
|
170
|
+
* non-regression contract `sandbox.ts`'s `applySandbox` established for
|
|
171
|
+
* bwrap, applied here too: a caller who never opts into a policy is
|
|
172
|
+
* completely unaffected by this module's existence.
|
|
173
|
+
*
|
|
174
|
+
* **Throws rather than silently spawning unconstrained** when a policy is
|
|
175
|
+
* requested and cannot actually be backed -- no working Docker, or a
|
|
176
|
+
* `networkPolicy` shape this mechanism doesn't implement -- matching
|
|
177
|
+
* `applySandbox`'s own refuse-rather-than-guess precedent exactly.
|
|
178
|
+
*
|
|
179
|
+
* **Returns a real, unique `containerName` whenever it wraps.** This was
|
|
180
|
+
* added after a genuine finding from this module's own real escape tests,
|
|
181
|
+
* not assumed up front: `process-manager.ts`'s existing group-kill
|
|
182
|
+
* (`process.kill(-pid, signal)`) targets the *local* `docker` CLI process's
|
|
183
|
+
* group -- exactly right for bwrap, which execs the sandboxed process
|
|
184
|
+
* directly and never creates a second process group. Docker is different:
|
|
185
|
+
* the container runs under `dockerd`, not as a child of the local `docker`
|
|
186
|
+
* CLI, so killing the CLI's process group does not reliably stop the
|
|
187
|
+
* container -- observed directly (a `kill()` call left a real orphaned
|
|
188
|
+
* `node:22-slim` container running after both the CLI process and its
|
|
189
|
+
* grace period were gone). `containerName` lets the caller
|
|
190
|
+
* (`process-manager.ts`) issue a real, explicit `docker stop <name>`
|
|
191
|
+
* against the container itself as the actual mechanism, not the CLI
|
|
192
|
+
* process, for the container backend.
|
|
193
|
+
*/
|
|
194
|
+
export function applyContainerSandbox(options) {
|
|
195
|
+
const { filesystemPolicy, networkPolicy } = options;
|
|
196
|
+
if (filesystemPolicy === undefined && networkPolicy === undefined) {
|
|
197
|
+
return { command: options.command, args: options.args };
|
|
198
|
+
}
|
|
199
|
+
const env = options.env ?? process.env;
|
|
200
|
+
if (networkPolicy !== undefined) {
|
|
201
|
+
const shapeReason = unsupportedContainerNetworkPolicyReason(networkPolicy);
|
|
202
|
+
if (shapeReason !== null) {
|
|
203
|
+
throw new Error(`networkPolicy was configured but cannot be enforced: ${shapeReason}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const capability = containerRuntimeCapability(env);
|
|
207
|
+
if (capability.availability !== "available") {
|
|
208
|
+
throw new Error(`container sandbox was requested but cannot be enforced: ${capability.reason}`);
|
|
209
|
+
}
|
|
210
|
+
const image = options.containerImage ?? resolveContainerImage(options.interpreterCommand);
|
|
211
|
+
if (image === null) {
|
|
212
|
+
throw new Error(`container sandbox was requested but no base image is known for interpreter "${options.interpreterCommand}" -- pass containerImage explicitly`);
|
|
213
|
+
}
|
|
214
|
+
const containerName = `descry-sandbox-${randomUUID()}`;
|
|
215
|
+
const dockerArgs = ["run", "--rm", "--name", containerName];
|
|
216
|
+
if (networkPolicy !== undefined) {
|
|
217
|
+
dockerArgs.push("--network", "none");
|
|
218
|
+
}
|
|
219
|
+
dockerArgs.push("-v", `${options.cwd}:${options.cwd}`, "-w", options.cwd);
|
|
220
|
+
if (filesystemPolicy !== undefined) {
|
|
221
|
+
for (const root of filesystemPolicy.allowedRoots) {
|
|
222
|
+
dockerArgs.push("-v", `${root}:${root}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for (const [key, value] of Object.entries(options.processEnv ?? {})) {
|
|
226
|
+
if (key === "PATH" || value === undefined)
|
|
227
|
+
continue;
|
|
228
|
+
dockerArgs.push("-e", `${key}=${value}`);
|
|
229
|
+
}
|
|
230
|
+
dockerArgs.push(image, options.command, ...options.args);
|
|
231
|
+
return { command: "docker", args: dockerArgs, containerName };
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=container-sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-sandbox.js","sourceRoot":"","sources":["../src/container-sandbox.ts"],"names":[],"mappings":"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"}
|