contextos-mcp 0.4.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/LICENSE +21 -0
- package/README.md +260 -0
- package/bin/mcp.mjs +61 -0
- package/bin/swarm.mjs +45 -0
- package/dist/agents/aider.d.ts +12 -0
- package/dist/agents/aider.js +192 -0
- package/dist/agents/aider.js.map +1 -0
- package/dist/agents/claude-code.d.ts +9 -0
- package/dist/agents/claude-code.js +236 -0
- package/dist/agents/claude-code.js.map +1 -0
- package/dist/agents/codex.d.ts +14 -0
- package/dist/agents/codex.js +203 -0
- package/dist/agents/codex.js.map +1 -0
- package/dist/agents/direct-llm.d.ts +22 -0
- package/dist/agents/direct-llm.js +254 -0
- package/dist/agents/direct-llm.js.map +1 -0
- package/dist/agents/mock.d.ts +10 -0
- package/dist/agents/mock.js +170 -0
- package/dist/agents/mock.js.map +1 -0
- package/dist/agents/opencode.d.ts +23 -0
- package/dist/agents/opencode.js +631 -0
- package/dist/agents/opencode.js.map +1 -0
- package/dist/agents/provider.d.ts +11 -0
- package/dist/agents/provider.js +31 -0
- package/dist/agents/provider.js.map +1 -0
- package/dist/compression/compressor.d.ts +28 -0
- package/dist/compression/compressor.js +268 -0
- package/dist/compression/compressor.js.map +1 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.js +172 -0
- package/dist/config.js.map +1 -0
- package/dist/contextos/loader.d.ts +46 -0
- package/dist/contextos/loader.js +120 -0
- package/dist/contextos/loader.js.map +1 -0
- package/dist/contextos/selector.d.ts +46 -0
- package/dist/contextos/selector.js +80 -0
- package/dist/contextos/selector.js.map +1 -0
- package/dist/core/action-dispatcher.d.ts +15 -0
- package/dist/core/action-dispatcher.js +28 -0
- package/dist/core/action-dispatcher.js.map +1 -0
- package/dist/core/action-schema.d.ts +72 -0
- package/dist/core/action-schema.js +86 -0
- package/dist/core/action-schema.js.map +1 -0
- package/dist/core/repl-interface.d.ts +48 -0
- package/dist/core/repl-interface.js +5 -0
- package/dist/core/repl-interface.js.map +1 -0
- package/dist/core/repl.d.ts +59 -0
- package/dist/core/repl.js +353 -0
- package/dist/core/repl.js.map +1 -0
- package/dist/core/rlm.d.ts +65 -0
- package/dist/core/rlm.js +564 -0
- package/dist/core/rlm.js.map +1 -0
- package/dist/core/runtime.py +470 -0
- package/dist/core/types.d.ts +265 -0
- package/dist/core/types.js +19 -0
- package/dist/core/types.js.map +1 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.js +75 -0
- package/dist/env.js.map +1 -0
- package/dist/hooks/runner.d.ts +43 -0
- package/dist/hooks/runner.js +173 -0
- package/dist/hooks/runner.js.map +1 -0
- package/dist/labs/cli.d.ts +15 -0
- package/dist/labs/cli.js +295 -0
- package/dist/labs/cli.js.map +1 -0
- package/dist/labs/episodic-memory.d.ts +132 -0
- package/dist/labs/episodic-memory.js +391 -0
- package/dist/labs/episodic-memory.js.map +1 -0
- package/dist/labs/interactive-swarm.d.ts +20 -0
- package/dist/labs/interactive-swarm.js +1173 -0
- package/dist/labs/interactive-swarm.js.map +1 -0
- package/dist/labs/interactive.d.ts +10 -0
- package/dist/labs/interactive.js +2099 -0
- package/dist/labs/interactive.js.map +1 -0
- package/dist/labs/ts-analyzer.d.ts +74 -0
- package/dist/labs/ts-analyzer.js +379 -0
- package/dist/labs/ts-analyzer.js.map +1 -0
- package/dist/labs/viewer.d.ts +12 -0
- package/dist/labs/viewer.js +1284 -0
- package/dist/labs/viewer.js.map +1 -0
- package/dist/main.d.ts +15 -0
- package/dist/main.js +247 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp/server.d.ts +16 -0
- package/dist/mcp/server.js +192775 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session.d.ts +117 -0
- package/dist/mcp/session.js +450 -0
- package/dist/mcp/session.js.map +1 -0
- package/dist/mcp/state.d.ts +42 -0
- package/dist/mcp/state.js +537 -0
- package/dist/mcp/state.js.map +1 -0
- package/dist/mcp/tools/contextos.d.ts +17 -0
- package/dist/mcp/tools/contextos.js +583 -0
- package/dist/mcp/tools/contextos.js.map +1 -0
- package/dist/mcp/tools.d.ts +15 -0
- package/dist/mcp/tools.js +393 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/orchestration/reviewer-gate.d.ts +43 -0
- package/dist/orchestration/reviewer-gate.js +280 -0
- package/dist/orchestration/reviewer-gate.js.map +1 -0
- package/dist/orchestration/reviewer-prompt.d.ts +18 -0
- package/dist/orchestration/reviewer-prompt.js +67 -0
- package/dist/orchestration/reviewer-prompt.js.map +1 -0
- package/dist/orchestration/verification-runner.d.ts +53 -0
- package/dist/orchestration/verification-runner.js +352 -0
- package/dist/orchestration/verification-runner.js.map +1 -0
- package/dist/plugins/adapter.d.ts +11 -0
- package/dist/plugins/adapter.js +11 -0
- package/dist/plugins/adapter.js.map +1 -0
- package/dist/plugins/core/archive.d.ts +9 -0
- package/dist/plugins/core/archive.js +128 -0
- package/dist/plugins/core/archive.js.map +1 -0
- package/dist/plugins/core/digest.d.ts +11 -0
- package/dist/plugins/core/digest.js +70 -0
- package/dist/plugins/core/digest.js.map +1 -0
- package/dist/plugins/core/grants.d.ts +22 -0
- package/dist/plugins/core/grants.js +90 -0
- package/dist/plugins/core/grants.js.map +1 -0
- package/dist/plugins/core/index.d.ts +10 -0
- package/dist/plugins/core/index.js +11 -0
- package/dist/plugins/core/index.js.map +1 -0
- package/dist/plugins/core/pinning.d.ts +15 -0
- package/dist/plugins/core/pinning.js +86 -0
- package/dist/plugins/core/pinning.js.map +1 -0
- package/dist/plugins/core/types.d.ts +105 -0
- package/dist/plugins/core/types.js +22 -0
- package/dist/plugins/core/types.js.map +1 -0
- package/dist/plugins/core/updater.d.ts +16 -0
- package/dist/plugins/core/updater.js +64 -0
- package/dist/plugins/core/updater.js.map +1 -0
- package/dist/plugins/loader.d.ts +65 -0
- package/dist/plugins/loader.js +110 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/prompts/orchestrator.d.ts +6 -0
- package/dist/prompts/orchestrator.js +114 -0
- package/dist/prompts/orchestrator.js.map +1 -0
- package/dist/routing/model-resolver.d.ts +27 -0
- package/dist/routing/model-resolver.js +265 -0
- package/dist/routing/model-resolver.js.map +1 -0
- package/dist/routing/model-router.d.ts +130 -0
- package/dist/routing/model-router.js +515 -0
- package/dist/routing/model-router.js.map +1 -0
- package/dist/runtime/attestations.cjs +260 -0
- package/dist/runtime/reviewer-pipeline.cjs +319 -0
- package/dist/runtime/sandbox.cjs +468 -0
- package/dist/runtime/state-machine.cjs +885 -0
- package/dist/runtime/thread-store.cjs +175 -0
- package/dist/runtime/transactional-git.cjs +315 -0
- package/dist/runtime/verification-pipeline.cjs +435 -0
- package/dist/sandbox/execution.d.ts +165 -0
- package/dist/sandbox/execution.js +497 -0
- package/dist/sandbox/execution.js.map +1 -0
- package/dist/security/file-policy.d.ts +22 -0
- package/dist/security/file-policy.js +53 -0
- package/dist/security/file-policy.js.map +1 -0
- package/dist/security/repository-boundary.d.ts +21 -0
- package/dist/security/repository-boundary.js +119 -0
- package/dist/security/repository-boundary.js.map +1 -0
- package/dist/security/secret-filter.d.ts +36 -0
- package/dist/security/secret-filter.js +149 -0
- package/dist/security/secret-filter.js.map +1 -0
- package/dist/swarm.d.ts +14 -0
- package/dist/swarm.js +624 -0
- package/dist/swarm.js.map +1 -0
- package/dist/threads/cache.d.ts +64 -0
- package/dist/threads/cache.js +241 -0
- package/dist/threads/cache.js.map +1 -0
- package/dist/threads/manager.d.ts +94 -0
- package/dist/threads/manager.js +914 -0
- package/dist/threads/manager.js.map +1 -0
- package/dist/ui/banner.d.ts +14 -0
- package/dist/ui/banner.js +42 -0
- package/dist/ui/banner.js.map +1 -0
- package/dist/ui/dashboard.d.ts +33 -0
- package/dist/ui/dashboard.js +135 -0
- package/dist/ui/dashboard.js.map +1 -0
- package/dist/ui/index.d.ts +11 -0
- package/dist/ui/index.js +12 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/log.d.ts +39 -0
- package/dist/ui/log.js +126 -0
- package/dist/ui/log.js.map +1 -0
- package/dist/ui/onboarding.d.ts +14 -0
- package/dist/ui/onboarding.js +755 -0
- package/dist/ui/onboarding.js.map +1 -0
- package/dist/ui/run-log.d.ts +50 -0
- package/dist/ui/run-log.js +60 -0
- package/dist/ui/run-log.js.map +1 -0
- package/dist/ui/spinner.d.ts +41 -0
- package/dist/ui/spinner.js +172 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/ui/streaming-feed.d.ts +72 -0
- package/dist/ui/streaming-feed.js +386 -0
- package/dist/ui/streaming-feed.js.map +1 -0
- package/dist/ui/summary.d.ts +18 -0
- package/dist/ui/summary.js +113 -0
- package/dist/ui/summary.js.map +1 -0
- package/dist/ui/text-input.d.ts +19 -0
- package/dist/ui/text-input.js +266 -0
- package/dist/ui/text-input.js.map +1 -0
- package/dist/ui/theme.d.ts +63 -0
- package/dist/ui/theme.js +97 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/utils/command-exists.d.ts +11 -0
- package/dist/utils/command-exists.js +41 -0
- package/dist/utils/command-exists.js.map +1 -0
- package/dist/utils/process-security.d.ts +19 -0
- package/dist/utils/process-security.js +90 -0
- package/dist/utils/process-security.js.map +1 -0
- package/dist/utils/rolling-buffer.d.ts +16 -0
- package/dist/utils/rolling-buffer.js +30 -0
- package/dist/utils/rolling-buffer.js.map +1 -0
- package/dist/utils/safe-spawn.d.ts +13 -0
- package/dist/utils/safe-spawn.js +49 -0
- package/dist/utils/safe-spawn.js.map +1 -0
- package/dist/worktree/manager.d.ts +69 -0
- package/dist/worktree/manager.js +593 -0
- package/dist/worktree/manager.js.map +1 -0
- package/dist/worktree/merge.d.ts +58 -0
- package/dist/worktree/merge.js +389 -0
- package/dist/worktree/merge.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextOS — Real Execution Sandbox & OCI Container Profile Engine
|
|
3
|
+
*
|
|
4
|
+
* Implements Section 19 of CONTEXTOS_IMPLEMENTATION_PLAN.md & Milestones W5.3 and W5.4:
|
|
5
|
+
* - Execution modes: oci-required, oci-preferred, host-unsafe
|
|
6
|
+
* - Fail-closed fallback: zero silent fallbacks from OCI to host
|
|
7
|
+
* - Auto-merge governance: auto-merge permanently blocked by default in host-unsafe mode
|
|
8
|
+
* - Repository config is prohibited from enabling unsafe auto-merge override
|
|
9
|
+
* - Hardened container profile (read-only root, cap-drop ALL, no-new-privileges, non-root user 1000:1000)
|
|
10
|
+
* - Network isolation: default network=none
|
|
11
|
+
* - Ephemeral isolated TMPFS mounts (no real HOME mounted)
|
|
12
|
+
* - Direct OCI engine execution (Docker/Podman/mock) without shell
|
|
13
|
+
* - Engine/image digest verification and runner evidence recording
|
|
14
|
+
* - Adversarial attack validation (traversal, git hooks, secret staging, config tampering)
|
|
15
|
+
*/
|
|
16
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
17
|
+
import * as crypto from "node:crypto";
|
|
18
|
+
import * as path from "node:path";
|
|
19
|
+
import { getSanitizedEnv, killProcessTree, redactSensitiveOutput } from "../utils/process-security.js";
|
|
20
|
+
export class ExecutionSandbox {
|
|
21
|
+
mode;
|
|
22
|
+
containerEngine;
|
|
23
|
+
image;
|
|
24
|
+
expectedImageDigest;
|
|
25
|
+
network;
|
|
26
|
+
limits;
|
|
27
|
+
mockExecutor;
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.mode = options.mode || "oci-preferred";
|
|
30
|
+
this.containerEngine = options.containerEngine || null;
|
|
31
|
+
this.image = options.image || "node:20-alpine";
|
|
32
|
+
this.expectedImageDigest = options.expectedImageDigest;
|
|
33
|
+
this.network = options.network || "deny";
|
|
34
|
+
this.mockExecutor = options.mockExecutor;
|
|
35
|
+
this.limits = {
|
|
36
|
+
cpus: options.limits?.cpus || "2.0",
|
|
37
|
+
memoryMb: Math.max(256, options.limits?.memoryMb || 2048),
|
|
38
|
+
pidsLimit: Math.max(32, options.limits?.pidsLimit || 128),
|
|
39
|
+
timeoutMs: Math.max(1000, options.limits?.timeoutMs || 30000),
|
|
40
|
+
maxOutputBytes: options.limits?.maxOutputBytes || 512 * 1024,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Evaluates if auto-merge is permitted based on execution outcome and mode.
|
|
45
|
+
* Section 19.1 & W5.4:
|
|
46
|
+
* - oci-required: permitted after gates pass
|
|
47
|
+
* - oci-preferred: permitted only if OCI container was actually used
|
|
48
|
+
* - host-unsafe: blocked by default; requires explicit user-local safety override
|
|
49
|
+
* - Repository config is strictly forbidden from enabling unsafe override
|
|
50
|
+
*/
|
|
51
|
+
canAutoMerge(executionRecord = {}) {
|
|
52
|
+
const { runnerMode = "host-unsafe", userOverride = false, isFromRepoConfig = false } = executionRecord;
|
|
53
|
+
if (runnerMode === "oci") {
|
|
54
|
+
return {
|
|
55
|
+
allowed: true,
|
|
56
|
+
reasonCode: "OCI_SANDBOX_VERIFIED",
|
|
57
|
+
reason: "Execution was verified inside isolated OCI container sandbox.",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (userOverride) {
|
|
61
|
+
if (isFromRepoConfig) {
|
|
62
|
+
return {
|
|
63
|
+
allowed: false,
|
|
64
|
+
reasonCode: "REPO_CONFIG_OVERRIDE_PROHIBITED",
|
|
65
|
+
reason: "Repository configuration cannot enable host-unsafe auto-merge override. Only user-local safety override is permitted.",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
allowed: true,
|
|
70
|
+
reasonCode: "HOST_UNSAFE_EXPLICIT_OVERRIDE",
|
|
71
|
+
reason: "Auto-merge permitted via explicit user-local safety override for host-unsafe execution.",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
allowed: false,
|
|
76
|
+
reasonCode: "HOST_UNSAFE_BLOCKED",
|
|
77
|
+
reason: "Auto-merge is blocked for host-unsafe execution. Requires isolated OCI container or explicit manual confirmation.",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Generates hardened OCI container CLI arguments adhering strictly to Section 19.2.
|
|
82
|
+
*/
|
|
83
|
+
buildContainerArgs(workspacePath, commandArgs, overrides = {}) {
|
|
84
|
+
const resolvedWorkspace = path.resolve(workspacePath);
|
|
85
|
+
const network = overrides.network || this.network;
|
|
86
|
+
const limits = { ...this.limits, ...overrides.limits };
|
|
87
|
+
const args = [
|
|
88
|
+
"run",
|
|
89
|
+
"--rm",
|
|
90
|
+
"--read-only",
|
|
91
|
+
"--cap-drop=ALL",
|
|
92
|
+
"--security-opt=no-new-privileges",
|
|
93
|
+
"--user=1000:1000",
|
|
94
|
+
`--cpus=${limits.cpus}`,
|
|
95
|
+
`--memory=${limits.memoryMb}m`,
|
|
96
|
+
`--pids-limit=${limits.pidsLimit}`,
|
|
97
|
+
"--tmpfs=/tmp:rw,noexec,nosuid,size=64m",
|
|
98
|
+
"--tmpfs=/home/sandbox:rw,noexec,nosuid,size=64m",
|
|
99
|
+
"-e",
|
|
100
|
+
"HOME=/home/sandbox",
|
|
101
|
+
"-e",
|
|
102
|
+
"TMPDIR=/tmp",
|
|
103
|
+
"-e",
|
|
104
|
+
"NODE_ENV=test",
|
|
105
|
+
];
|
|
106
|
+
if (network === "deny") {
|
|
107
|
+
args.push("--network=none");
|
|
108
|
+
}
|
|
109
|
+
const mountPath = process.platform === "win32" ? resolvedWorkspace.replace(/\\/g, "/") : resolvedWorkspace;
|
|
110
|
+
// Mount workspace directory read-write
|
|
111
|
+
args.push("-v", `${mountPath}:/workspace:rw`);
|
|
112
|
+
args.push("-w", "/workspace");
|
|
113
|
+
args.push(this.image);
|
|
114
|
+
args.push(...commandArgs);
|
|
115
|
+
return args;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Scans a proposed action or file write for adversarial attacks (Section 19.6).
|
|
119
|
+
*/
|
|
120
|
+
validateAdversarialAttempt(action = {}) {
|
|
121
|
+
const { targetPath, commandArgs } = action;
|
|
122
|
+
// 1. Path traversal escape checks
|
|
123
|
+
if (targetPath) {
|
|
124
|
+
const normalized = targetPath.replace(/\\/g, "/");
|
|
125
|
+
if (normalized.startsWith("../") ||
|
|
126
|
+
normalized.includes("/../") ||
|
|
127
|
+
normalized.endsWith("/..") ||
|
|
128
|
+
normalized === "..") {
|
|
129
|
+
return {
|
|
130
|
+
safe: false,
|
|
131
|
+
rule: "SEC-SANDBOX-001",
|
|
132
|
+
violation: `Path traversal detected: "${targetPath}" attempts to escape workspace root.`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// 2. Git internal tampering checks
|
|
136
|
+
if (normalized === ".git" ||
|
|
137
|
+
normalized.startsWith(".git/") ||
|
|
138
|
+
normalized.includes("/.git/") ||
|
|
139
|
+
normalized.includes("core.hookspath") ||
|
|
140
|
+
normalized.includes("hooks/")) {
|
|
141
|
+
return {
|
|
142
|
+
safe: false,
|
|
143
|
+
rule: "SEC-SANDBOX-002",
|
|
144
|
+
violation: `Tampering with Git internals or hooks detected: "${targetPath}".`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// 3. Staged secret file check
|
|
148
|
+
const base = path.basename(normalized).toLowerCase();
|
|
149
|
+
if (base === ".env" ||
|
|
150
|
+
base.startsWith(".env.") ||
|
|
151
|
+
base.endsWith(".pem") ||
|
|
152
|
+
base.endsWith(".key") ||
|
|
153
|
+
base === "id_rsa" ||
|
|
154
|
+
base === "id_ed25519") {
|
|
155
|
+
return {
|
|
156
|
+
safe: false,
|
|
157
|
+
rule: "SEC-SANDBOX-003",
|
|
158
|
+
violation: `Writing blocked secret/credential file detected: "${targetPath}".`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// 4. Command arguments inspection
|
|
163
|
+
if (Array.isArray(commandArgs)) {
|
|
164
|
+
const fullCmd = commandArgs.join(" ");
|
|
165
|
+
if (/core\.hooksPath/i.test(fullCmd)) {
|
|
166
|
+
return {
|
|
167
|
+
safe: false,
|
|
168
|
+
rule: "SEC-SANDBOX-004",
|
|
169
|
+
violation: "Command attempts to alter Git core.hooksPath configuration.",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (/git\s+config\s+--global/i.test(fullCmd) || /git\s+config\s+--system/i.test(fullCmd)) {
|
|
173
|
+
return {
|
|
174
|
+
safe: false,
|
|
175
|
+
rule: "SEC-SANDBOX-005",
|
|
176
|
+
violation: "Command attempts to modify global or system git configuration.",
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { safe: true };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Plans execution runner and validates against fail-closed policy.
|
|
184
|
+
*/
|
|
185
|
+
planExecution(isEngineAvailable) {
|
|
186
|
+
if (this.mode === "oci-required") {
|
|
187
|
+
if (!isEngineAvailable) {
|
|
188
|
+
const err = new Error('OCI sandbox is required by policy (mode="oci-required"), but no container engine (docker/podman) is available. Silent fallback is prohibited.');
|
|
189
|
+
err.code = "CTX_SANDBOX_OCI_UNAVAILABLE";
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
return { runner: "oci", autoMergeBlocked: false };
|
|
193
|
+
}
|
|
194
|
+
if (this.mode === "oci-preferred") {
|
|
195
|
+
if (isEngineAvailable) {
|
|
196
|
+
return { runner: "oci", autoMergeBlocked: false };
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
runner: "host-unsafe",
|
|
200
|
+
autoMergeBlocked: true,
|
|
201
|
+
warning: "Container engine not detected. Falling back to host-unsafe execution. Auto-merge is blocked until manually verified.",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
runner: "host-unsafe",
|
|
206
|
+
autoMergeBlocked: true,
|
|
207
|
+
warning: "Running in host-unsafe mode. Auto-merge is blocked by default.",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Detects whether a functional container engine (docker/podman/mock) is available.
|
|
212
|
+
*/
|
|
213
|
+
detectContainerEngine() {
|
|
214
|
+
if (this.containerEngine === "mock") {
|
|
215
|
+
return "mock";
|
|
216
|
+
}
|
|
217
|
+
if (this.containerEngine === "docker" || this.containerEngine === "podman") {
|
|
218
|
+
try {
|
|
219
|
+
execFileSync(this.containerEngine, ["--version"], { stdio: "ignore" });
|
|
220
|
+
return this.containerEngine;
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Probe docker first, then podman
|
|
227
|
+
try {
|
|
228
|
+
execFileSync("docker", ["--version"], { stdio: "ignore" });
|
|
229
|
+
return "docker";
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// probe podman
|
|
233
|
+
try {
|
|
234
|
+
execFileSync("podman", ["--version"], { stdio: "ignore" });
|
|
235
|
+
return "podman";
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Resolves image digest for evidence and pin verification.
|
|
244
|
+
*/
|
|
245
|
+
getImageDigest(engine, image) {
|
|
246
|
+
if (engine === "mock") {
|
|
247
|
+
return `sha256:${crypto.createHash("sha256").update(image).digest("hex")}`;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const out = execFileSync(engine, ["image", "inspect", "--format", "{{range .RepoDigests}}{{.}}{{end}}", image], {
|
|
251
|
+
encoding: "utf8",
|
|
252
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
253
|
+
timeout: 5000,
|
|
254
|
+
}).trim();
|
|
255
|
+
if (out?.includes("@sha256:")) {
|
|
256
|
+
const digestPart = out.split("@")[1];
|
|
257
|
+
return digestPart || null;
|
|
258
|
+
}
|
|
259
|
+
return out || null;
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Executes a command inside the hardened sandbox or host fallback according to policy.
|
|
267
|
+
*/
|
|
268
|
+
async execute(workspacePath, commandArgs, options = {}) {
|
|
269
|
+
// 1. Validate adversarial attempts
|
|
270
|
+
const advCheck = this.validateAdversarialAttempt({
|
|
271
|
+
targetPath: workspacePath,
|
|
272
|
+
commandArgs,
|
|
273
|
+
});
|
|
274
|
+
if (!advCheck.safe) {
|
|
275
|
+
const secErr = new Error(`Sandbox security violation: ${advCheck.violation}`);
|
|
276
|
+
secErr.code = advCheck.rule || "CTX_SECURITY_VIOLATION";
|
|
277
|
+
throw secErr;
|
|
278
|
+
}
|
|
279
|
+
const engine = this.detectContainerEngine();
|
|
280
|
+
const plan = this.planExecution(Boolean(engine));
|
|
281
|
+
const startTime = Date.now();
|
|
282
|
+
if (plan.runner === "oci" && engine) {
|
|
283
|
+
return this.executeInContainer(engine, workspacePath, commandArgs, options, startTime);
|
|
284
|
+
}
|
|
285
|
+
return this.executeInHost(workspacePath, commandArgs, options, startTime, plan.warning);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Executes the command directly inside an isolated OCI container without shell.
|
|
289
|
+
*/
|
|
290
|
+
async executeInContainer(engine, workspacePath, commandArgs, options, startTime) {
|
|
291
|
+
const digest = this.getImageDigest(engine, this.image);
|
|
292
|
+
if (this.expectedImageDigest && digest) {
|
|
293
|
+
const normalizedExpected = this.expectedImageDigest.replace(/^sha256:/, "");
|
|
294
|
+
const normalizedActual = digest.replace(/^sha256:/, "");
|
|
295
|
+
if (normalizedExpected !== normalizedActual) {
|
|
296
|
+
const err = new Error(`Image digest mismatch for image "${this.image}": expected "${this.expectedImageDigest}", got "${digest}". Execution aborted fail-closed.`);
|
|
297
|
+
err.code = "CTX_SANDBOX_DIGEST_MISMATCH";
|
|
298
|
+
throw err;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const containerArgs = this.buildContainerArgs(workspacePath, commandArgs, {
|
|
302
|
+
network: options.network || this.network,
|
|
303
|
+
});
|
|
304
|
+
const timeoutMs = options.timeoutMs || this.limits.timeoutMs;
|
|
305
|
+
let stdout = "";
|
|
306
|
+
let stderr = "";
|
|
307
|
+
let exitCode = null;
|
|
308
|
+
let signal = null;
|
|
309
|
+
if (engine === "mock" && this.mockExecutor) {
|
|
310
|
+
const mockRes = await this.mockExecutor("mock", containerArgs, {
|
|
311
|
+
cwd: workspacePath,
|
|
312
|
+
timeoutMs,
|
|
313
|
+
signal: options.signal,
|
|
314
|
+
});
|
|
315
|
+
stdout = mockRes.stdout;
|
|
316
|
+
stderr = mockRes.stderr;
|
|
317
|
+
exitCode = mockRes.exitCode;
|
|
318
|
+
}
|
|
319
|
+
else if (engine === "mock") {
|
|
320
|
+
stdout = `[mock-oci-container: ${this.image}] executed: ${commandArgs.join(" ")}\n`;
|
|
321
|
+
exitCode = 0;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
// Real container execution (Docker or Podman) without shell
|
|
325
|
+
const child = spawn(engine, containerArgs, {
|
|
326
|
+
cwd: workspacePath,
|
|
327
|
+
shell: false,
|
|
328
|
+
windowsHide: true,
|
|
329
|
+
});
|
|
330
|
+
let timedOut = false;
|
|
331
|
+
const timer = setTimeout(() => {
|
|
332
|
+
timedOut = true;
|
|
333
|
+
killProcessTree(child.pid);
|
|
334
|
+
}, timeoutMs);
|
|
335
|
+
const abortHandler = () => {
|
|
336
|
+
killProcessTree(child.pid);
|
|
337
|
+
};
|
|
338
|
+
if (options.signal) {
|
|
339
|
+
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
340
|
+
}
|
|
341
|
+
const maxBytes = this.limits.maxOutputBytes;
|
|
342
|
+
child.stdout.on("data", (chunk) => {
|
|
343
|
+
if (stdout.length < maxBytes) {
|
|
344
|
+
stdout += chunk.toString("utf8");
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
child.stderr.on("data", (chunk) => {
|
|
348
|
+
if (stderr.length < maxBytes) {
|
|
349
|
+
stderr += chunk.toString("utf8");
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
const [code, sig] = await new Promise((resolve) => {
|
|
353
|
+
child.on("close", (c, s) => {
|
|
354
|
+
clearTimeout(timer);
|
|
355
|
+
if (options.signal) {
|
|
356
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
357
|
+
}
|
|
358
|
+
resolve([c, s]);
|
|
359
|
+
});
|
|
360
|
+
child.on("error", (_err) => {
|
|
361
|
+
clearTimeout(timer);
|
|
362
|
+
if (options.signal) {
|
|
363
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
364
|
+
}
|
|
365
|
+
resolve([1, null]);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
exitCode = code;
|
|
369
|
+
signal = sig;
|
|
370
|
+
if (timedOut) {
|
|
371
|
+
signal = "SIGKILL";
|
|
372
|
+
exitCode = 124;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const durationMs = Date.now() - startTime;
|
|
376
|
+
const rawCombined = stdout + (stderr ? `\n${stderr}` : "");
|
|
377
|
+
const outputSha256 = crypto.createHash("sha256").update(rawCombined).digest("hex");
|
|
378
|
+
const redactedOutput = redactSensitiveOutput(rawCombined);
|
|
379
|
+
const evidence = {
|
|
380
|
+
engine,
|
|
381
|
+
image: this.image,
|
|
382
|
+
imageDigest: digest || undefined,
|
|
383
|
+
exitCode,
|
|
384
|
+
durationMs,
|
|
385
|
+
outputSha256,
|
|
386
|
+
runnerMode: "oci",
|
|
387
|
+
network: options.network || this.network,
|
|
388
|
+
timestamp: Date.now(),
|
|
389
|
+
};
|
|
390
|
+
return {
|
|
391
|
+
success: exitCode === 0,
|
|
392
|
+
exitCode,
|
|
393
|
+
signal,
|
|
394
|
+
durationMs,
|
|
395
|
+
stdout,
|
|
396
|
+
stderr,
|
|
397
|
+
outputSha256,
|
|
398
|
+
redactedOutput,
|
|
399
|
+
runnerMode: "oci",
|
|
400
|
+
containerEngine: engine,
|
|
401
|
+
image: this.image,
|
|
402
|
+
imageDigest: digest || undefined,
|
|
403
|
+
autoMergeBlocked: false,
|
|
404
|
+
evidence,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Executes on host in host-unsafe fallback mode with full process isolation and secret redaction.
|
|
409
|
+
*/
|
|
410
|
+
async executeInHost(workspacePath, commandArgs, options, startTime, warning) {
|
|
411
|
+
const timeoutMs = options.timeoutMs || this.limits.timeoutMs;
|
|
412
|
+
const env = getSanitizedEnv();
|
|
413
|
+
let stdout = "";
|
|
414
|
+
let stderr = "";
|
|
415
|
+
const child = spawn(commandArgs[0], commandArgs.slice(1), {
|
|
416
|
+
cwd: workspacePath,
|
|
417
|
+
env,
|
|
418
|
+
shell: false,
|
|
419
|
+
windowsHide: true,
|
|
420
|
+
});
|
|
421
|
+
let timedOut = false;
|
|
422
|
+
const timer = setTimeout(() => {
|
|
423
|
+
timedOut = true;
|
|
424
|
+
killProcessTree(child.pid);
|
|
425
|
+
}, timeoutMs);
|
|
426
|
+
const abortHandler = () => {
|
|
427
|
+
killProcessTree(child.pid);
|
|
428
|
+
};
|
|
429
|
+
if (options.signal) {
|
|
430
|
+
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
431
|
+
}
|
|
432
|
+
const maxBytes = this.limits.maxOutputBytes;
|
|
433
|
+
child.stdout.on("data", (chunk) => {
|
|
434
|
+
if (stdout.length < maxBytes) {
|
|
435
|
+
stdout += chunk.toString("utf8");
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
child.stderr.on("data", (chunk) => {
|
|
439
|
+
if (stderr.length < maxBytes) {
|
|
440
|
+
stderr += chunk.toString("utf8");
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
const [exitCode, sig] = await new Promise((resolve) => {
|
|
444
|
+
child.on("close", (code, signalStr) => {
|
|
445
|
+
clearTimeout(timer);
|
|
446
|
+
if (options.signal) {
|
|
447
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
448
|
+
}
|
|
449
|
+
resolve([code, signalStr]);
|
|
450
|
+
});
|
|
451
|
+
child.on("error", (err) => {
|
|
452
|
+
clearTimeout(timer);
|
|
453
|
+
if (options.signal) {
|
|
454
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
455
|
+
}
|
|
456
|
+
stderr += `\nFailed to start process: ${err.message}`;
|
|
457
|
+
resolve([1, null]);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
const durationMs = Date.now() - startTime;
|
|
461
|
+
const rawCombined = stdout + (stderr ? `\n${stderr}` : "");
|
|
462
|
+
const outputSha256 = crypto.createHash("sha256").update(rawCombined).digest("hex");
|
|
463
|
+
const redactedOutput = redactSensitiveOutput(rawCombined);
|
|
464
|
+
const decision = this.canAutoMerge({
|
|
465
|
+
runnerMode: "host-unsafe",
|
|
466
|
+
userOverride: options.userOverride,
|
|
467
|
+
isFromRepoConfig: options.isFromRepoConfig,
|
|
468
|
+
});
|
|
469
|
+
const evidence = {
|
|
470
|
+
engine: "none",
|
|
471
|
+
image: "host",
|
|
472
|
+
exitCode: timedOut ? 124 : exitCode,
|
|
473
|
+
durationMs,
|
|
474
|
+
outputSha256,
|
|
475
|
+
runnerMode: "host-unsafe",
|
|
476
|
+
network: "allow",
|
|
477
|
+
timestamp: Date.now(),
|
|
478
|
+
};
|
|
479
|
+
return {
|
|
480
|
+
success: exitCode === 0 && !timedOut,
|
|
481
|
+
exitCode: timedOut ? 124 : exitCode,
|
|
482
|
+
signal: timedOut ? "SIGKILL" : sig,
|
|
483
|
+
durationMs,
|
|
484
|
+
stdout,
|
|
485
|
+
stderr,
|
|
486
|
+
outputSha256,
|
|
487
|
+
redactedOutput,
|
|
488
|
+
runnerMode: "host-unsafe",
|
|
489
|
+
containerEngine: "none",
|
|
490
|
+
image: "host",
|
|
491
|
+
autoMergeBlocked: !decision.allowed,
|
|
492
|
+
evidence,
|
|
493
|
+
warning,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
//# sourceMappingURL=execution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution.js","sourceRoot":"","sources":["../../src/sandbox/execution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Policy — restricts agent file access to the worktree boundary.
|
|
3
|
+
*
|
|
4
|
+
* Prevents agents from reading/writing files outside their designated
|
|
5
|
+
* worktree directory. Also blocks access to .git/ internals.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Validate that a file path is within the allowed worktree boundary.
|
|
9
|
+
*
|
|
10
|
+
* @param filePath - The path to validate (absolute or relative)
|
|
11
|
+
* @param worktreeRoot - The worktree root directory (absolute)
|
|
12
|
+
* @returns true if access is ALLOWED
|
|
13
|
+
*/
|
|
14
|
+
export declare function isPathAllowed(filePath: string, worktreeRoot: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Sanitize a list of file paths, removing any that violate the file policy.
|
|
17
|
+
*
|
|
18
|
+
* @param filePaths - List of file paths to filter
|
|
19
|
+
* @param worktreeRoot - The worktree root directory
|
|
20
|
+
* @returns Filtered list containing only allowed paths
|
|
21
|
+
*/
|
|
22
|
+
export declare function filterAllowedPaths(filePaths: string[], worktreeRoot: string): string[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Policy — restricts agent file access to the worktree boundary.
|
|
3
|
+
*
|
|
4
|
+
* Prevents agents from reading/writing files outside their designated
|
|
5
|
+
* worktree directory. Also blocks access to .git/ internals.
|
|
6
|
+
*/
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
/** Directories that agents must NEVER access, even within a worktree. */
|
|
9
|
+
const FORBIDDEN_DIRS = [
|
|
10
|
+
".git",
|
|
11
|
+
"node_modules", // Too large, not useful for agents
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Validate that a file path is within the allowed worktree boundary.
|
|
15
|
+
*
|
|
16
|
+
* @param filePath - The path to validate (absolute or relative)
|
|
17
|
+
* @param worktreeRoot - The worktree root directory (absolute)
|
|
18
|
+
* @returns true if access is ALLOWED
|
|
19
|
+
*/
|
|
20
|
+
export function isPathAllowed(filePath, worktreeRoot) {
|
|
21
|
+
const normalizedRoot = path.resolve(worktreeRoot);
|
|
22
|
+
const absPath = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(normalizedRoot, filePath);
|
|
23
|
+
// Must be strictly within worktree root (using path.relative to prevent sibling prefix bypass)
|
|
24
|
+
const relativePath = path.relative(normalizedRoot, absPath);
|
|
25
|
+
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
// Check for forbidden directories in the relative path
|
|
29
|
+
const segments = relativePath.split(path.sep);
|
|
30
|
+
for (const segment of segments) {
|
|
31
|
+
if (FORBIDDEN_DIRS.includes(segment)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Sanitize a list of file paths, removing any that violate the file policy.
|
|
39
|
+
*
|
|
40
|
+
* @param filePaths - List of file paths to filter
|
|
41
|
+
* @param worktreeRoot - The worktree root directory
|
|
42
|
+
* @returns Filtered list containing only allowed paths
|
|
43
|
+
*/
|
|
44
|
+
export function filterAllowedPaths(filePaths, worktreeRoot) {
|
|
45
|
+
return filePaths.filter((fp) => {
|
|
46
|
+
const allowed = isPathAllowed(fp, worktreeRoot);
|
|
47
|
+
if (!allowed) {
|
|
48
|
+
process.stderr.write(`[file-policy] Blocked path: ${fp}\n`);
|
|
49
|
+
}
|
|
50
|
+
return allowed;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=file-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-policy.js","sourceRoot":"","sources":["../../src/security/file-policy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,yEAAyE;AACzE,MAAM,cAAc,GAAa;IAChC,MAAM;IACN,cAAc,EAAE,mCAAmC;CACnD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,YAAoB;IACnE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IAE5G,+FAA+F;IAC/F,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAC5D,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACpE,OAAO,KAAK,CAAC;IACd,CAAC;IAED,uDAAuD;IACvD,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE9C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAChC,IAAI,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAmB,EAAE,YAAoB;IAC3E,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;QAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,IAAI,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,OAAO,CAAC;IAChB,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository boundary enforcement with nearest-parent realpath resolution.
|
|
3
|
+
*
|
|
4
|
+
* Prevents arbitrary file reads/writes, path traversals (../),
|
|
5
|
+
* sibling-prefix escapes (e.g. /repo-sibling attempting to match /repo),
|
|
6
|
+
* UNC paths, null bytes, and symlink/junction escapes.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SecurityBoundaryException extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Checks whether targetPath is contained within repoRoot.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isWithinRepository(targetPath: string, repoRoot: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Asserts that targetPath is strictly within repoRoot.
|
|
17
|
+
*
|
|
18
|
+
* If valid, returns the canonical resolved path.
|
|
19
|
+
* If targetPath or repoRoot escapes boundaries, throws SecurityBoundaryException.
|
|
20
|
+
*/
|
|
21
|
+
export declare function assertWithinRepository(targetPath: string, repoRoot: string): string;
|