@guilz-dev/belay 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -11
- package/dist/adapters/claude/runtime-entry.js +2 -0
- package/dist/adapters/codex/runtime-entry.js +2 -0
- package/dist/adapters/cursor/hooks.d.ts +5 -1
- package/dist/adapters/cursor/hooks.js +37 -2
- package/dist/adapters/cursor/runtime-entry.js +5 -0
- package/dist/adapters/shared/gate-runtime.d.ts +6 -0
- package/dist/adapters/shared/gate-runtime.js +260 -59
- package/dist/bundle/claude-runtime.mjs +7068 -3966
- package/dist/bundle/codex-runtime.mjs +7001 -3899
- package/dist/bundle/cursor-runtime.mjs +7004 -3899
- package/dist/commands/classify-for-report.js +5 -0
- package/dist/commands/doctor.js +88 -9
- package/dist/commands/explain.js +3 -2
- package/dist/commands/metrics.js +42 -0
- package/dist/commands/recovery-checkpoints.d.ts +8 -1
- package/dist/commands/recovery-checkpoints.js +44 -11
- package/dist/commands/session.d.ts +5 -0
- package/dist/commands/session.js +8 -1
- package/dist/config-io.js +2 -2
- package/dist/conformance/contained-execution-guarantee.d.ts +121 -0
- package/dist/conformance/contained-execution-guarantee.js +121 -0
- package/dist/conformance/guarantee-table.js +7 -0
- package/dist/core/audit-analysis.js +7 -4
- package/dist/core/audit-io.d.ts +1 -0
- package/dist/core/audit-io.js +16 -6
- package/dist/core/audit-legacy-archive.d.ts +6 -0
- package/dist/core/audit-legacy-archive.js +51 -0
- package/dist/core/audit-metrics.d.ts +15 -0
- package/dist/core/audit-metrics.js +16 -2
- package/dist/core/audit-query.d.ts +2 -0
- package/dist/core/audit-query.js +33 -9
- package/dist/core/audit-recovery-metrics.d.ts +33 -0
- package/dist/core/audit-recovery-metrics.js +146 -0
- package/dist/core/audit-serialize.d.ts +9 -0
- package/dist/core/audit-serialize.js +179 -0
- package/dist/core/audit-types.d.ts +8 -1
- package/dist/core/audit-types.js +1 -1
- package/dist/core/bounded-output.d.ts +11 -0
- package/dist/core/bounded-output.js +26 -0
- package/dist/core/capability/attestation.d.ts +56 -0
- package/dist/core/capability/attestation.js +110 -3
- package/dist/core/capability/boundary-session.d.ts +5 -0
- package/dist/core/capability/boundary-session.js +38 -1
- package/dist/core/capability/index.d.ts +1 -1
- package/dist/core/capability/index.js +1 -1
- package/dist/core/capability/policy-engine.js +32 -13
- package/dist/core/config.d.ts +18 -0
- package/dist/core/config.js +89 -10
- package/dist/core/contained-execution/docker-policy.d.ts +18 -0
- package/dist/core/contained-execution/docker-policy.js +97 -0
- package/dist/core/contained-execution/docker.d.ts +223 -0
- package/dist/core/contained-execution/docker.js +846 -0
- package/dist/core/contained-execution/eligibility.d.ts +9 -0
- package/dist/core/contained-execution/eligibility.js +103 -0
- package/dist/core/contained-execution/failure.d.ts +18 -0
- package/dist/core/contained-execution/failure.js +105 -0
- package/dist/core/contained-execution/mirror.d.ts +54 -0
- package/dist/core/contained-execution/mirror.js +587 -0
- package/dist/core/contained-execution/policy.d.ts +10 -0
- package/dist/core/contained-execution/policy.js +20 -0
- package/dist/core/decision-config-fingerprint.d.ts +3 -0
- package/dist/core/decision-config-fingerprint.js +6 -0
- package/dist/core/effect-ir/index.d.ts +1 -1
- package/dist/core/effect-ir/index.js +1 -1
- package/dist/core/effect-ir/normalize.d.ts +5 -0
- package/dist/core/effect-ir/normalize.js +17 -7
- package/dist/core/effect-ir/shell-build.js +2 -14
- package/dist/core/effect-ir/shell-lower.js +103 -25
- package/dist/core/gate-contract.d.ts +4 -1
- package/dist/core/gate-contract.js +2 -0
- package/dist/core/process-runner.d.ts +10 -3
- package/dist/core/process-runner.js +66 -10
- package/dist/core/recovery/checkpoint.d.ts +3 -0
- package/dist/core/recovery/checkpoint.js +33 -4
- package/dist/core/recovery/types.d.ts +1 -0
- package/dist/core/scrub.d.ts +17 -0
- package/dist/core/scrub.js +499 -5
- package/dist/core/standing-allow.d.ts +0 -20
- package/dist/core/standing-allow.js +0 -71
- package/dist/core/transactional/apply-observed-changes.d.ts +1 -0
- package/dist/core/transactional/apply-observed-changes.js +11 -0
- package/dist/core/transactional/backend-selector.d.ts +2 -1
- package/dist/core/transactional/backend-selector.js +32 -29
- package/dist/core/transactional/backend.d.ts +3 -0
- package/dist/core/transactional/file-checkpoint-backend.js +242 -54
- package/dist/core/transactional/file-checkpoint-git.d.ts +3 -0
- package/dist/core/transactional/file-checkpoint-git.js +53 -0
- package/dist/core/transactional/file-checkpoint-isolation.d.ts +3 -0
- package/dist/core/transactional/file-checkpoint-isolation.js +16 -0
- package/dist/core/transactional/git-worktree-backend.js +3 -0
- package/dist/core/transactional/git-worktree.d.ts +1 -0
- package/dist/core/transactional/git-worktree.js +1 -0
- package/dist/core/transactional/runner.js +59 -13
- package/dist/core/types.d.ts +13 -0
- package/dist/core/verdict/egress-classify.js +1 -0
- package/dist/core/verdict/parser.d.ts +10 -0
- package/dist/core/verdict/parser.js +271 -40
- package/dist/core/verdict/shell-semantics.js +21 -0
- package/dist/corpus/adversarial-probe.d.ts +1 -1
- package/dist/corpus/adversarial-probe.js +3 -3
- package/dist/corpus/benign-probe-cores.d.ts +6 -0
- package/dist/corpus/{must-allow-commands.js → benign-probe-cores.js} +8 -2
- package/dist/corpus/coverage-compare.d.ts +36 -0
- package/dist/corpus/coverage-compare.js +197 -0
- package/dist/corpus/coverage-contexts.d.ts +17 -0
- package/dist/corpus/coverage-contexts.js +88 -0
- package/dist/corpus/coverage-matrix.d.ts +37 -0
- package/dist/corpus/coverage-matrix.js +159 -0
- package/dist/corpus/coverage-probe.d.ts +91 -0
- package/dist/corpus/coverage-probe.js +341 -0
- package/dist/corpus/evaluate.d.ts +1 -1
- package/dist/corpus/evaluate.js +1 -1
- package/dist/corpus/structural-fixture-root.d.ts +2 -0
- package/dist/corpus/structural-fixture-root.js +5 -0
- package/dist/defaults.js +0 -8
- package/dist/egress-daemon.js +20 -4
- package/dist/installer.js +3 -0
- package/dist/runtime-provenance.d.ts +10 -0
- package/dist/runtime-provenance.js +63 -0
- package/dist/templates.js +12 -7
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -1
- package/skills/belay/SKILL.md +19 -0
- package/skills/belay/belay-status.md +5 -0
- package/dist/corpus/must-allow-commands.d.ts +0 -6
package/dist/core/config.d.ts
CHANGED
|
@@ -146,6 +146,20 @@ export interface BelaySandboxConfig {
|
|
|
146
146
|
enabled: boolean;
|
|
147
147
|
runtime: SandboxRuntime;
|
|
148
148
|
denyNetworkByDefault: boolean;
|
|
149
|
+
/** Optional in source config for backwards compatibility; normalization always supplies it. */
|
|
150
|
+
containedExecution?: BelayContainedExecutionConfig;
|
|
151
|
+
}
|
|
152
|
+
export interface BelayContainedExecutionConfig {
|
|
153
|
+
enabled: boolean;
|
|
154
|
+
image: string | null;
|
|
155
|
+
/** Required when enabled; optional in source objects for disabled backwards compatibility. */
|
|
156
|
+
dockerExecutable?: string | null;
|
|
157
|
+
/** Required when enabled; only local absolute unix:// endpoints are accepted. */
|
|
158
|
+
dockerHost?: string | null;
|
|
159
|
+
timeoutMs: number;
|
|
160
|
+
memoryMiB: number;
|
|
161
|
+
cpus: number;
|
|
162
|
+
pids: number;
|
|
149
163
|
}
|
|
150
164
|
export interface BelayClassifierConfig {
|
|
151
165
|
strictChains: boolean;
|
|
@@ -225,7 +239,9 @@ export declare const DEFAULT_REDACTION_V3: BelayRedactionConfig;
|
|
|
225
239
|
export declare const DEFAULT_CONTROL_PLANE_ISOLATION_V3: BelayControlPlaneIsolationConfig;
|
|
226
240
|
export declare const LEGACY_CONTROL_PLANE_V3: BelayControlPlaneConfig;
|
|
227
241
|
export declare const DEFAULT_CONTROL_PLANE_V3: BelayControlPlaneConfig;
|
|
242
|
+
export declare const DEFAULT_CONTAINED_EXECUTION: BelayContainedExecutionConfig;
|
|
228
243
|
export declare const DEFAULT_SANDBOX_V3: BelaySandboxConfig;
|
|
244
|
+
export declare function normalizeContainedExecutionConfig(raw: Partial<BelayContainedExecutionConfig> | undefined): BelayContainedExecutionConfig;
|
|
229
245
|
export declare const DEFAULT_NOTIFICATIONS_V3: BelayNotificationsConfig;
|
|
230
246
|
export declare const DEFAULT_APPROVAL_SIGNING_V3: BelayApprovalSigningConfig;
|
|
231
247
|
export declare const DEFAULT_APPROVAL_CONFIG: BelayApprovalConfig;
|
|
@@ -276,6 +292,8 @@ export declare function normalizeConfigV2(config: BelayConfigV2): BelayConfigV2;
|
|
|
276
292
|
export declare function normalizeConfig(config: BelayConfigV4): BelayConfigV4;
|
|
277
293
|
export declare function normalizeConfig(config: BelayConfigV2): BelayConfigV2;
|
|
278
294
|
export declare function isFreshConfigInput(loaded: unknown): boolean;
|
|
295
|
+
export declare function hasForbiddenShellOverrideLists(config: BelayConfigV4): boolean;
|
|
296
|
+
export declare function stripForbiddenShellOverrideLists(config: BelayConfigV4): BelayConfigV4;
|
|
279
297
|
export declare function mergeConfig(existing: unknown, defaults?: BelayConfigV4): BelayConfigV4;
|
|
280
298
|
export declare function scrubOptionsFromConfig(config: BelayConfigV4): ScrubOptions;
|
|
281
299
|
export declare function classifierOptionsFromConfig(config: BelayConfigV4): ClassifierOptions;
|
package/dist/core/config.js
CHANGED
|
@@ -148,11 +148,80 @@ export const DEFAULT_CONTROL_PLANE_V3 = {
|
|
|
148
148
|
integrity: 'hash-pinned',
|
|
149
149
|
isolation: { ...DEFAULT_CONTROL_PLANE_ISOLATION_V3 },
|
|
150
150
|
};
|
|
151
|
+
export const DEFAULT_CONTAINED_EXECUTION = {
|
|
152
|
+
enabled: false,
|
|
153
|
+
image: null,
|
|
154
|
+
dockerExecutable: null,
|
|
155
|
+
dockerHost: null,
|
|
156
|
+
timeoutMs: 30_000,
|
|
157
|
+
memoryMiB: 2048,
|
|
158
|
+
cpus: 2,
|
|
159
|
+
pids: 256,
|
|
160
|
+
};
|
|
151
161
|
export const DEFAULT_SANDBOX_V3 = {
|
|
152
162
|
enabled: false,
|
|
153
163
|
runtime: 'none',
|
|
154
164
|
denyNetworkByDefault: true,
|
|
165
|
+
containedExecution: { ...DEFAULT_CONTAINED_EXECUTION },
|
|
155
166
|
};
|
|
167
|
+
function positiveInteger(value, fallback) {
|
|
168
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : fallback;
|
|
169
|
+
}
|
|
170
|
+
function positiveNumber(value, fallback) {
|
|
171
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
172
|
+
}
|
|
173
|
+
export function normalizeContainedExecutionConfig(raw) {
|
|
174
|
+
return {
|
|
175
|
+
enabled: raw?.enabled === true,
|
|
176
|
+
image: typeof raw?.image === 'string' && raw.image.trim() ? raw.image.trim() : null,
|
|
177
|
+
dockerExecutable: typeof raw?.dockerExecutable === 'string' && raw.dockerExecutable.trim()
|
|
178
|
+
? raw.dockerExecutable.trim()
|
|
179
|
+
: null,
|
|
180
|
+
dockerHost: typeof raw?.dockerHost === 'string' && raw.dockerHost.trim() ? raw.dockerHost.trim() : null,
|
|
181
|
+
timeoutMs: positiveInteger(raw?.timeoutMs, DEFAULT_CONTAINED_EXECUTION.timeoutMs),
|
|
182
|
+
memoryMiB: positiveInteger(raw?.memoryMiB, DEFAULT_CONTAINED_EXECUTION.memoryMiB),
|
|
183
|
+
cpus: positiveNumber(raw?.cpus, DEFAULT_CONTAINED_EXECUTION.cpus),
|
|
184
|
+
pids: positiveInteger(raw?.pids, DEFAULT_CONTAINED_EXECUTION.pids),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function normalizeSandboxConfig(raw) {
|
|
188
|
+
const runtime = raw?.runtime === 'cursor-sandbox' ||
|
|
189
|
+
raw?.runtime === 'container' ||
|
|
190
|
+
raw?.runtime === 'seatbelt' ||
|
|
191
|
+
raw?.runtime === 'landlock'
|
|
192
|
+
? raw.runtime
|
|
193
|
+
: DEFAULT_SANDBOX_V3.runtime;
|
|
194
|
+
const containedExecution = normalizeContainedExecutionConfig(raw?.containedExecution);
|
|
195
|
+
if (containedExecution.enabled && (raw?.enabled !== true || runtime !== 'container')) {
|
|
196
|
+
throw new Error('contained execution requires sandbox.runtime=container with sandbox.enabled=true');
|
|
197
|
+
}
|
|
198
|
+
if (containedExecution.enabled && !containedExecution.image) {
|
|
199
|
+
throw new Error('contained execution requires an explicit image');
|
|
200
|
+
}
|
|
201
|
+
if (containedExecution.enabled && !containedExecution.dockerExecutable) {
|
|
202
|
+
throw new Error('contained execution requires an explicit Docker executable');
|
|
203
|
+
}
|
|
204
|
+
if (containedExecution.enabled &&
|
|
205
|
+
(!path.isAbsolute(containedExecution.dockerExecutable ?? '') ||
|
|
206
|
+
/[\0\n\r]/.test(containedExecution.dockerExecutable ?? ''))) {
|
|
207
|
+
throw new Error('contained execution requires an absolute executable path');
|
|
208
|
+
}
|
|
209
|
+
if (containedExecution.enabled && !containedExecution.dockerHost) {
|
|
210
|
+
throw new Error('contained execution requires an explicit Docker host');
|
|
211
|
+
}
|
|
212
|
+
if (containedExecution.enabled &&
|
|
213
|
+
(!containedExecution.dockerHost?.startsWith('unix:///') ||
|
|
214
|
+
!path.isAbsolute(containedExecution.dockerHost.slice('unix://'.length)) ||
|
|
215
|
+
/[\0\n\r]/.test(containedExecution.dockerHost))) {
|
|
216
|
+
throw new Error('contained execution requires a local unix Docker host');
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
enabled: raw?.enabled === true,
|
|
220
|
+
runtime,
|
|
221
|
+
denyNetworkByDefault: raw?.denyNetworkByDefault !== false,
|
|
222
|
+
containedExecution,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
156
225
|
export const DEFAULT_NOTIFICATIONS_V3 = {};
|
|
157
226
|
export const DEFAULT_APPROVAL_SIGNING_V3 = {
|
|
158
227
|
required: false,
|
|
@@ -829,16 +898,7 @@ export function normalizeConfig(config) {
|
|
|
829
898
|
: DEFAULT_EGRESS_V3.listenPort,
|
|
830
899
|
demoteL3External: v4.egress?.demoteL3External !== false,
|
|
831
900
|
},
|
|
832
|
-
sandbox:
|
|
833
|
-
enabled: v4.sandbox?.enabled === true,
|
|
834
|
-
runtime: v4.sandbox?.runtime === 'cursor-sandbox' ||
|
|
835
|
-
v4.sandbox?.runtime === 'container' ||
|
|
836
|
-
v4.sandbox?.runtime === 'seatbelt' ||
|
|
837
|
-
v4.sandbox?.runtime === 'landlock'
|
|
838
|
-
? v4.sandbox.runtime
|
|
839
|
-
: DEFAULT_SANDBOX_V3.runtime,
|
|
840
|
-
denyNetworkByDefault: v4.sandbox?.denyNetworkByDefault !== false,
|
|
841
|
-
},
|
|
901
|
+
sandbox: normalizeSandboxConfig(v4.sandbox),
|
|
842
902
|
audit: {
|
|
843
903
|
logPath: v4.audit?.logPath || DEFAULT_CONFIG_V4.audit.logPath,
|
|
844
904
|
includeAssessment: v4.audit?.includeAssessment !== false,
|
|
@@ -859,6 +919,21 @@ export function isFreshConfigInput(loaded) {
|
|
|
859
919
|
}
|
|
860
920
|
return Object.keys(loaded).length === 0;
|
|
861
921
|
}
|
|
922
|
+
export function hasForbiddenShellOverrideLists(config) {
|
|
923
|
+
return config.overrides.allow.length > 0 || config.overrides.external.length > 0;
|
|
924
|
+
}
|
|
925
|
+
export function stripForbiddenShellOverrideLists(config) {
|
|
926
|
+
if (!hasForbiddenShellOverrideLists(config)) {
|
|
927
|
+
return config;
|
|
928
|
+
}
|
|
929
|
+
return {
|
|
930
|
+
...config,
|
|
931
|
+
overrides: {
|
|
932
|
+
allow: [],
|
|
933
|
+
external: [],
|
|
934
|
+
},
|
|
935
|
+
};
|
|
936
|
+
}
|
|
862
937
|
export function mergeConfig(existing, defaults = DEFAULT_CONFIG_V4) {
|
|
863
938
|
const migrated = isFreshConfigInput(existing)
|
|
864
939
|
? normalizeConfig({ ...defaults, version: 4 })
|
|
@@ -914,6 +989,10 @@ export function mergeConfig(existing, defaults = DEFAULT_CONFIG_V4) {
|
|
|
914
989
|
sandbox: {
|
|
915
990
|
...defaults.sandbox,
|
|
916
991
|
...migrated.sandbox,
|
|
992
|
+
containedExecution: {
|
|
993
|
+
...(defaults.sandbox.containedExecution ?? DEFAULT_CONTAINED_EXECUTION),
|
|
994
|
+
...(migrated.sandbox.containedExecution ?? DEFAULT_CONTAINED_EXECUTION),
|
|
995
|
+
},
|
|
917
996
|
},
|
|
918
997
|
audit: {
|
|
919
998
|
...defaults.audit,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ContainedExecutionResourceLimits } from '../capability/attestation.js';
|
|
2
|
+
export declare const TMPFS_SIZE_BYTES: number;
|
|
3
|
+
export declare const SHM_SIZE_MIB = 64;
|
|
4
|
+
export declare const TMPFS_OPTIONS = "rw,nosuid,nodev,noexec,size=67108864,mode=1777";
|
|
5
|
+
export declare const PROXY_ENV_NAMES: readonly ["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "NO_PROXY", "http_proxy", "https_proxy", "all_proxy", "no_proxy"];
|
|
6
|
+
export declare const IMAGE_ID_PATTERN: RegExp;
|
|
7
|
+
export declare function assertContainedExecutionResourceLimits(limits: ContainedExecutionResourceLimits): void;
|
|
8
|
+
export interface BuildContainedDockerCreateArgsParams {
|
|
9
|
+
containerName: string;
|
|
10
|
+
imageId: string;
|
|
11
|
+
command: string;
|
|
12
|
+
hostMirrorRoot: string;
|
|
13
|
+
guestWorkspacePath: string;
|
|
14
|
+
guestCwd: string;
|
|
15
|
+
resourceLimits: ContainedExecutionResourceLimits;
|
|
16
|
+
user: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildContainedDockerCreateArgs(params: BuildContainedDockerCreateArgsParams): string[];
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { ContainedExecutionFailureError } from './failure.js';
|
|
3
|
+
export const TMPFS_SIZE_BYTES = 64 * 1024 * 1024;
|
|
4
|
+
export const SHM_SIZE_MIB = 64;
|
|
5
|
+
export const TMPFS_OPTIONS = `rw,nosuid,nodev,noexec,size=${TMPFS_SIZE_BYTES},mode=1777`;
|
|
6
|
+
export const PROXY_ENV_NAMES = [
|
|
7
|
+
'HTTP_PROXY',
|
|
8
|
+
'HTTPS_PROXY',
|
|
9
|
+
'ALL_PROXY',
|
|
10
|
+
'NO_PROXY',
|
|
11
|
+
'http_proxy',
|
|
12
|
+
'https_proxy',
|
|
13
|
+
'all_proxy',
|
|
14
|
+
'no_proxy',
|
|
15
|
+
];
|
|
16
|
+
export const IMAGE_ID_PATTERN = /^sha256:[a-f0-9]{64}$/;
|
|
17
|
+
const SAFE_CONTAINER_NAME = /^belay-contained-[0-9a-f-]{36}$/;
|
|
18
|
+
function assertSafeDockerPath(value, code) {
|
|
19
|
+
if (!path.isAbsolute(value) || /[\0\n\r,]/.test(value)) {
|
|
20
|
+
throw new ContainedExecutionFailureError(code);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function assertContainedExecutionResourceLimits(limits) {
|
|
24
|
+
if (!Number.isSafeInteger(limits.timeoutMs) ||
|
|
25
|
+
limits.timeoutMs <= 0 ||
|
|
26
|
+
!Number.isSafeInteger(limits.memoryMiB) ||
|
|
27
|
+
limits.memoryMiB <= 0 ||
|
|
28
|
+
!Number.isFinite(limits.cpus) ||
|
|
29
|
+
limits.cpus <= 0 ||
|
|
30
|
+
!Number.isSafeInteger(limits.pids) ||
|
|
31
|
+
limits.pids <= 0) {
|
|
32
|
+
throw new ContainedExecutionFailureError('contained_execution_resource_limits_invalid');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function buildContainedDockerCreateArgs(params) {
|
|
36
|
+
if (!IMAGE_ID_PATTERN.test(params.imageId)) {
|
|
37
|
+
throw new ContainedExecutionFailureError('contained_execution_invalid_image_id');
|
|
38
|
+
}
|
|
39
|
+
if (!SAFE_CONTAINER_NAME.test(params.containerName)) {
|
|
40
|
+
throw new ContainedExecutionFailureError('contained_execution_invalid_container_name');
|
|
41
|
+
}
|
|
42
|
+
assertSafeDockerPath(params.hostMirrorRoot, 'contained_execution_invalid_mount');
|
|
43
|
+
assertSafeDockerPath(params.guestWorkspacePath, 'contained_execution_invalid_mount');
|
|
44
|
+
assertSafeDockerPath(params.guestCwd, 'contained_execution_invalid_cwd');
|
|
45
|
+
assertContainedExecutionResourceLimits(params.resourceLimits);
|
|
46
|
+
if (!/^\d+:\d+$/.test(params.user)) {
|
|
47
|
+
throw new ContainedExecutionFailureError('contained_execution_invalid_user');
|
|
48
|
+
}
|
|
49
|
+
const memory = `${params.resourceLimits.memoryMiB}m`;
|
|
50
|
+
return [
|
|
51
|
+
'create',
|
|
52
|
+
'--name',
|
|
53
|
+
params.containerName,
|
|
54
|
+
'--network',
|
|
55
|
+
'none',
|
|
56
|
+
'--read-only',
|
|
57
|
+
'--tmpfs',
|
|
58
|
+
`/tmp:${TMPFS_OPTIONS}`,
|
|
59
|
+
'--cap-drop',
|
|
60
|
+
'ALL',
|
|
61
|
+
'--security-opt',
|
|
62
|
+
'no-new-privileges',
|
|
63
|
+
'--log-driver',
|
|
64
|
+
'none',
|
|
65
|
+
'--privileged=false',
|
|
66
|
+
'--publish-all=false',
|
|
67
|
+
'--restart',
|
|
68
|
+
'no',
|
|
69
|
+
'--memory',
|
|
70
|
+
memory,
|
|
71
|
+
'--memory-swap',
|
|
72
|
+
memory,
|
|
73
|
+
'--shm-size',
|
|
74
|
+
`${SHM_SIZE_MIB}m`,
|
|
75
|
+
'--cpus',
|
|
76
|
+
String(params.resourceLimits.cpus),
|
|
77
|
+
'--pids-limit',
|
|
78
|
+
String(params.resourceLimits.pids),
|
|
79
|
+
'--user',
|
|
80
|
+
params.user,
|
|
81
|
+
'--no-healthcheck',
|
|
82
|
+
'--ipc',
|
|
83
|
+
'none',
|
|
84
|
+
'--cgroupns',
|
|
85
|
+
'private',
|
|
86
|
+
...PROXY_ENV_NAMES.flatMap((name) => ['--env', `${name}=`]),
|
|
87
|
+
'--mount',
|
|
88
|
+
`type=bind,src=${params.hostMirrorRoot},dst=${params.guestWorkspacePath}`,
|
|
89
|
+
'--workdir',
|
|
90
|
+
params.guestCwd,
|
|
91
|
+
'--entrypoint',
|
|
92
|
+
'/bin/sh',
|
|
93
|
+
params.imageId,
|
|
94
|
+
'-c',
|
|
95
|
+
params.command,
|
|
96
|
+
];
|
|
97
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { BoundaryAttestation, ContainedExecutionAttestation, ContainedExecutionResourceLimits, DockerSubstrateIdentity } from '../capability/attestation.js';
|
|
2
|
+
import type { BelayContainedExecutionConfig } from '../config.js';
|
|
3
|
+
import { type ProcessOutputPolicy, type ShellRunResult } from '../process-runner.js';
|
|
4
|
+
import { type BuildContainedDockerCreateArgsParams, buildContainedDockerCreateArgs } from './docker-policy.js';
|
|
5
|
+
import { type ContainedExecutionFailureCode, ContainedExecutionFailureError } from './failure.js';
|
|
6
|
+
import { type ContainedExecutionMirrorBackend, type ContainedExecutionMirrorHandle } from './mirror.js';
|
|
7
|
+
export declare class ContainedDockerBoundaryUnavailableError extends ContainedExecutionFailureError {
|
|
8
|
+
readonly reason: ContainedExecutionFailureCode;
|
|
9
|
+
readonly receipt: undefined;
|
|
10
|
+
constructor(reason: ContainedExecutionFailureCode, options?: ErrorOptions);
|
|
11
|
+
}
|
|
12
|
+
export declare class ContainedDockerStartAttemptError extends ContainedExecutionFailureError {
|
|
13
|
+
constructor(options?: ErrorOptions);
|
|
14
|
+
}
|
|
15
|
+
export declare class ContainedDockerCleanupUnconfirmedError extends ContainedExecutionFailureError {
|
|
16
|
+
readonly containerName: string;
|
|
17
|
+
readonly executionStarted: boolean;
|
|
18
|
+
readonly cleanupConfirmed = false;
|
|
19
|
+
constructor(containerName: string, executionStarted: boolean, options?: ErrorOptions);
|
|
20
|
+
}
|
|
21
|
+
export interface ContainedDockerInspect {
|
|
22
|
+
Id: string;
|
|
23
|
+
Image: string;
|
|
24
|
+
Config: {
|
|
25
|
+
User: string;
|
|
26
|
+
Env: string[] | null;
|
|
27
|
+
Entrypoint: string[] | string | null;
|
|
28
|
+
Cmd: string[] | null;
|
|
29
|
+
WorkingDir: string;
|
|
30
|
+
Healthcheck: {
|
|
31
|
+
Test?: string[] | null;
|
|
32
|
+
} | null;
|
|
33
|
+
};
|
|
34
|
+
HostConfig: {
|
|
35
|
+
AutoRemove: boolean;
|
|
36
|
+
Privileged: boolean;
|
|
37
|
+
CapAdd: string[] | null;
|
|
38
|
+
CapDrop: string[] | null;
|
|
39
|
+
SecurityOpt: string[] | null;
|
|
40
|
+
LogConfig: {
|
|
41
|
+
Type: string;
|
|
42
|
+
Config: Record<string, string> | null;
|
|
43
|
+
} | null;
|
|
44
|
+
Devices: unknown[] | null;
|
|
45
|
+
DeviceRequests: unknown[] | null;
|
|
46
|
+
DeviceCgroupRules: string[] | null;
|
|
47
|
+
GroupAdd: string[] | null;
|
|
48
|
+
VolumesFrom: string[] | null;
|
|
49
|
+
Binds: string[] | null;
|
|
50
|
+
PortBindings: Record<string, unknown> | null;
|
|
51
|
+
PublishAllPorts: boolean;
|
|
52
|
+
Links: string[] | null;
|
|
53
|
+
ExtraHosts: string[] | null;
|
|
54
|
+
Dns: string[] | null;
|
|
55
|
+
DnsOptions: string[] | null;
|
|
56
|
+
DnsSearch: string[] | null;
|
|
57
|
+
NetworkMode: string;
|
|
58
|
+
ReadonlyRootfs: boolean;
|
|
59
|
+
Tmpfs: Record<string, string> | null;
|
|
60
|
+
Memory: number;
|
|
61
|
+
MemorySwap: number;
|
|
62
|
+
ShmSize: number;
|
|
63
|
+
NanoCpus: number;
|
|
64
|
+
PidsLimit: number | null;
|
|
65
|
+
RestartPolicy: {
|
|
66
|
+
Name: string;
|
|
67
|
+
MaximumRetryCount: number;
|
|
68
|
+
};
|
|
69
|
+
IpcMode: string;
|
|
70
|
+
PidMode: string;
|
|
71
|
+
UTSMode: string;
|
|
72
|
+
UsernsMode: string;
|
|
73
|
+
CgroupnsMode: string;
|
|
74
|
+
};
|
|
75
|
+
NetworkSettings: {
|
|
76
|
+
Ports: Record<string, unknown> | null;
|
|
77
|
+
};
|
|
78
|
+
Mounts: Array<{
|
|
79
|
+
Type: string;
|
|
80
|
+
Source: string;
|
|
81
|
+
Destination: string;
|
|
82
|
+
RW: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
85
|
+
export interface ContainedDockerDependencies {
|
|
86
|
+
resolveSubstrate(params: {
|
|
87
|
+
executable: string;
|
|
88
|
+
host: string;
|
|
89
|
+
repoRoot: string;
|
|
90
|
+
protectedRoots: readonly string[];
|
|
91
|
+
}): Promise<DockerSubstrateIdentity>;
|
|
92
|
+
runProcess(file: string, args: string[], env: Record<string, string>, timeoutMs: number, outputPolicy?: ProcessOutputPolicy): Promise<ShellRunResult>;
|
|
93
|
+
now(): number;
|
|
94
|
+
randomUUID(): string;
|
|
95
|
+
uid(): number;
|
|
96
|
+
gid(): number;
|
|
97
|
+
}
|
|
98
|
+
export type { BuildContainedDockerCreateArgsParams };
|
|
99
|
+
export { buildContainedDockerCreateArgs };
|
|
100
|
+
export declare function probeContainedDockerBoundary(params: {
|
|
101
|
+
repoRoot: string;
|
|
102
|
+
protectedRoots: readonly string[];
|
|
103
|
+
imageReference: string;
|
|
104
|
+
dockerExecutable: string;
|
|
105
|
+
dockerHost: string;
|
|
106
|
+
hostProbeRoot: string;
|
|
107
|
+
guestWorkspacePath: string;
|
|
108
|
+
resourceLimits: ContainedExecutionResourceLimits;
|
|
109
|
+
dependencies?: ContainedDockerDependencies;
|
|
110
|
+
}): Promise<ContainedExecutionAttestation>;
|
|
111
|
+
export interface ContainedExecutionReceipt {
|
|
112
|
+
version: 1;
|
|
113
|
+
inputFingerprint: string;
|
|
114
|
+
actionFingerprint: string;
|
|
115
|
+
attestationDigest: string;
|
|
116
|
+
executionStarted: true;
|
|
117
|
+
timeoutMs: number;
|
|
118
|
+
dockerSubstrate: DockerSubstrateIdentity;
|
|
119
|
+
imageId: string;
|
|
120
|
+
mirror: {
|
|
121
|
+
backend: ContainedExecutionMirrorBackend;
|
|
122
|
+
type: 'bind';
|
|
123
|
+
cardinality: 1;
|
|
124
|
+
readWrite: true;
|
|
125
|
+
sourceFingerprint: string;
|
|
126
|
+
guestWorkspacePathFingerprint: string;
|
|
127
|
+
guestCwdFingerprint: string;
|
|
128
|
+
};
|
|
129
|
+
logging: {
|
|
130
|
+
driver: 'none';
|
|
131
|
+
config: Record<string, never>;
|
|
132
|
+
};
|
|
133
|
+
network: {
|
|
134
|
+
mode: 'none';
|
|
135
|
+
publishAllPorts: false;
|
|
136
|
+
portBindings: Record<string, never>;
|
|
137
|
+
links: [];
|
|
138
|
+
extraHosts: [];
|
|
139
|
+
dns: [];
|
|
140
|
+
dnsOptions: [];
|
|
141
|
+
dnsSearch: [];
|
|
142
|
+
};
|
|
143
|
+
readOnlyRoot: true;
|
|
144
|
+
tmpfs: {
|
|
145
|
+
path: '/tmp';
|
|
146
|
+
sizeBytes: number;
|
|
147
|
+
mode: 0o1777;
|
|
148
|
+
exec: false;
|
|
149
|
+
nosuid: true;
|
|
150
|
+
nodev: true;
|
|
151
|
+
};
|
|
152
|
+
environment: {
|
|
153
|
+
hostForwarded: false;
|
|
154
|
+
proxyEnvironment: 'neutralized-empty';
|
|
155
|
+
neutralizedVariables: readonly string[];
|
|
156
|
+
imageEnvironmentDigest: string;
|
|
157
|
+
};
|
|
158
|
+
privilege: {
|
|
159
|
+
privileged: false;
|
|
160
|
+
noNewPrivileges: true;
|
|
161
|
+
capAdd: [];
|
|
162
|
+
capDrop: ['ALL'];
|
|
163
|
+
groupAdd: [];
|
|
164
|
+
};
|
|
165
|
+
devices: {
|
|
166
|
+
devices: [];
|
|
167
|
+
deviceRequests: [];
|
|
168
|
+
deviceCgroupRules: [];
|
|
169
|
+
};
|
|
170
|
+
storage: {
|
|
171
|
+
volumesFrom: [];
|
|
172
|
+
bindsOutsideMirror: [];
|
|
173
|
+
};
|
|
174
|
+
namespaces: {
|
|
175
|
+
ipc: 'none';
|
|
176
|
+
pid: 'private';
|
|
177
|
+
uts: 'private';
|
|
178
|
+
cgroup: 'private';
|
|
179
|
+
user: 'default';
|
|
180
|
+
};
|
|
181
|
+
healthcheckDisabled: true;
|
|
182
|
+
lifecycle: {
|
|
183
|
+
autoRemove: false;
|
|
184
|
+
restart: 'no';
|
|
185
|
+
maximumRetryCount: 0;
|
|
186
|
+
};
|
|
187
|
+
resources: {
|
|
188
|
+
memoryMiB: number;
|
|
189
|
+
memorySwapMiB: number;
|
|
190
|
+
shmSizeMiB: number;
|
|
191
|
+
cpus: number;
|
|
192
|
+
nanoCpus: number;
|
|
193
|
+
pids: number;
|
|
194
|
+
};
|
|
195
|
+
user: string;
|
|
196
|
+
entrypoint: '/bin/sh';
|
|
197
|
+
exitCode: number | null;
|
|
198
|
+
timedOut: boolean;
|
|
199
|
+
}
|
|
200
|
+
export interface ContainedDockerExecutionResult extends ShellRunResult {
|
|
201
|
+
executionStarted: true;
|
|
202
|
+
receipt: ContainedExecutionReceipt;
|
|
203
|
+
receiptHash: string;
|
|
204
|
+
}
|
|
205
|
+
export interface ExecuteContainedDockerParams {
|
|
206
|
+
repoRoot: string;
|
|
207
|
+
controlPlaneDir: string;
|
|
208
|
+
protectedRoots: readonly string[];
|
|
209
|
+
config: BelayContainedExecutionConfig;
|
|
210
|
+
mirror: ContainedExecutionMirrorHandle;
|
|
211
|
+
guestCwd: string;
|
|
212
|
+
command: string;
|
|
213
|
+
inputFingerprint: string;
|
|
214
|
+
signedAttestation: unknown;
|
|
215
|
+
dependencies?: ContainedDockerDependencies;
|
|
216
|
+
}
|
|
217
|
+
export declare function executeContainedDocker(params: ExecuteContainedDockerParams): Promise<ContainedDockerExecutionResult>;
|
|
218
|
+
export declare function probeContainedDockerForSession(params: {
|
|
219
|
+
repoRoot: string;
|
|
220
|
+
controlPlaneDir: string;
|
|
221
|
+
config: BelayContainedExecutionConfig;
|
|
222
|
+
dependencies?: ContainedDockerDependencies;
|
|
223
|
+
}): Promise<BoundaryAttestation>;
|