@sellable/install 0.1.356 → 0.1.357-wip.20260720060000.4

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.
@@ -0,0 +1,472 @@
1
+ import {
2
+ chmodSync,
3
+ existsSync,
4
+ lstatSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ realpathSync,
8
+ writeFileSync,
9
+ } from "node:fs";
10
+ import { isAbsolute, join, relative, resolve } from "node:path";
11
+ import { runBoundedChildProcess } from "./provisioning-adapter.mjs";
12
+
13
+ const INPUT_KEYS = new Set([
14
+ "profileId",
15
+ "workerUid",
16
+ "runtimeUid",
17
+ "credentialReferences",
18
+ "oauthPrivateReference",
19
+ ]);
20
+ const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/;
21
+ const FINGERPRINT = /^[a-f0-9]{64}$/;
22
+ const EXACT_EGRESS = [
23
+ "https://app.sellable.dev",
24
+ "https://slack.com",
25
+ "https://api.openai.com",
26
+ ];
27
+
28
+ export const PINNED_CONTAINMENT_IMAGE =
29
+ "public.ecr.aws/supabase/postgres@sha256:af083ef64d0408c8f098ee6f5c364a59b26f36fbc0f3a334a62c5c1d57362e9b";
30
+
31
+ const CONTAINER_CHECK_KEYS = [
32
+ "runtimeUid",
33
+ "profileRead",
34
+ "controlDenied",
35
+ "credentialDenied",
36
+ "oauthDenied",
37
+ "siblingDenied",
38
+ "hostDenied",
39
+ "dockerDenied",
40
+ "rootReadOnly",
41
+ "noNewPrivileges",
42
+ "capabilitiesDropped",
43
+ "networkDisabled",
44
+ ];
45
+
46
+ function validPrivateReference(value, scheme) {
47
+ return (
48
+ value &&
49
+ typeof value === "object" &&
50
+ !Array.isArray(value) &&
51
+ Object.keys(value).every((key) =>
52
+ ["reference", "fingerprint", "purpose", "target", "owner"].includes(key)
53
+ ) &&
54
+ typeof value.reference === "string" &&
55
+ value.reference.startsWith(`${scheme}://`) &&
56
+ FINGERPRINT.test(value.fingerprint)
57
+ );
58
+ }
59
+
60
+ export function buildContainmentContract(input) {
61
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
62
+ return { ok: false, code: "invalid_input" };
63
+ }
64
+ if (Object.keys(input).some((key) => !INPUT_KEYS.has(key))) {
65
+ return { ok: false, code: "unknown_field" };
66
+ }
67
+ if (
68
+ !SAFE_ID.test(input.profileId) ||
69
+ !Number.isSafeInteger(input.workerUid) ||
70
+ input.workerUid < 1 ||
71
+ !Number.isSafeInteger(input.runtimeUid) ||
72
+ input.runtimeUid < 1 ||
73
+ input.workerUid === input.runtimeUid ||
74
+ !Array.isArray(input.credentialReferences) ||
75
+ !input.credentialReferences.every(
76
+ (reference) =>
77
+ validPrivateReference(reference, "secret") &&
78
+ ["service-credential", "worker-credential"].includes(reference.purpose)
79
+ ) ||
80
+ !validPrivateReference(input.oauthPrivateReference, "oauth-private")
81
+ ) {
82
+ return { ok: false, code: "invalid_input" };
83
+ }
84
+ const profileBase = `/var/lib/sellable-agents/${input.profileId}`;
85
+ const manifest = {
86
+ profileId: input.profileId,
87
+ readOnlyRootFilesystem: true,
88
+ noNewPrivileges: true,
89
+ dockerSocket: false,
90
+ capabilities: [],
91
+ worker: {
92
+ name: `sellable-worker-${input.profileId}`,
93
+ uid: input.workerUid,
94
+ },
95
+ runtime: {
96
+ name: `sellable-runtime-${input.profileId}`,
97
+ uid: input.runtimeUid,
98
+ },
99
+ mounts: [
100
+ {
101
+ profileId: input.profileId,
102
+ source: `${profileBase}/control`,
103
+ target: "/control",
104
+ readOnly: false,
105
+ owner: "worker",
106
+ },
107
+ {
108
+ profileId: input.profileId,
109
+ source: `${profileBase}/profile`,
110
+ target: "/profile",
111
+ readOnly: false,
112
+ owner: "runtime",
113
+ },
114
+ ],
115
+ credentials: input.credentialReferences.map((reference) => ({
116
+ purpose: reference.purpose,
117
+ reference: reference.reference,
118
+ fingerprint: reference.fingerprint,
119
+ target: `/run/credentials/${reference.purpose}`,
120
+ owner: "worker",
121
+ })),
122
+ oauthPrivateReference: {
123
+ reference: input.oauthPrivateReference.reference,
124
+ fingerprint: input.oauthPrivateReference.fingerprint,
125
+ owner: "worker",
126
+ },
127
+ oauth: {
128
+ privateExchangeOwner: "worker",
129
+ healthStatus: "NOT_PROBED",
130
+ auditStatus: "REQUIRED",
131
+ broaderRolloutBlocked: true,
132
+ },
133
+ authority: {
134
+ database: false,
135
+ migrations: false,
136
+ hostControl: false,
137
+ },
138
+ resources: { pids: 128, memoryMb: 1024, cpuQuota: 1 },
139
+ egress: [...EXACT_EGRESS],
140
+ };
141
+ const validated = validateContainmentContract(manifest);
142
+ return validated.ok ? { ok: true, manifest } : validated;
143
+ }
144
+
145
+ export function validateContainmentContract(manifest) {
146
+ if (
147
+ !manifest ||
148
+ typeof manifest !== "object" ||
149
+ !SAFE_ID.test(manifest.profileId)
150
+ ) {
151
+ return { ok: false, code: "invalid_manifest" };
152
+ }
153
+ if (
154
+ manifest.readOnlyRootFilesystem !== true ||
155
+ manifest.noNewPrivileges !== true ||
156
+ manifest.dockerSocket !== false ||
157
+ !Array.isArray(manifest.capabilities) ||
158
+ manifest.capabilities.length !== 0
159
+ ) {
160
+ return { ok: false, code: "privilege_boundary_failed" };
161
+ }
162
+ if (
163
+ !Number.isSafeInteger(manifest.worker?.uid) ||
164
+ manifest.worker.uid < 1 ||
165
+ !Number.isSafeInteger(manifest.runtime?.uid) ||
166
+ manifest.runtime.uid < 1 ||
167
+ manifest.worker.uid === manifest.runtime.uid
168
+ ) {
169
+ return { ok: false, code: "identity_boundary_failed" };
170
+ }
171
+ const profileBase = `/var/lib/sellable-agents/${manifest.profileId}/`;
172
+ if (
173
+ !Array.isArray(manifest.mounts) ||
174
+ manifest.mounts.length !== 2 ||
175
+ manifest.mounts.some(
176
+ (mount) =>
177
+ mount.profileId !== manifest.profileId ||
178
+ typeof mount.source !== "string" ||
179
+ !mount.source.startsWith(profileBase) ||
180
+ !["/control", "/profile"].includes(mount.target) ||
181
+ !["worker", "runtime"].includes(mount.owner) ||
182
+ mount.source.includes("docker.sock")
183
+ ) ||
184
+ manifest.mounts.find((mount) => mount.target === "/control")?.owner !==
185
+ "worker" ||
186
+ manifest.mounts.find((mount) => mount.target === "/profile")?.owner !==
187
+ "runtime"
188
+ ) {
189
+ return { ok: false, code: "mount_boundary_failed" };
190
+ }
191
+ if (
192
+ !Array.isArray(manifest.credentials) ||
193
+ !manifest.credentials.every(
194
+ (reference) =>
195
+ validPrivateReference(reference, "secret") &&
196
+ typeof reference.target === "string" &&
197
+ reference.target.startsWith("/run/credentials/") &&
198
+ reference.owner === "worker"
199
+ ) ||
200
+ !validPrivateReference(manifest.oauthPrivateReference, "oauth-private") ||
201
+ manifest.oauthPrivateReference.owner !== "worker"
202
+ ) {
203
+ return { ok: false, code: "secret_boundary_failed" };
204
+ }
205
+ if (
206
+ manifest.oauth?.privateExchangeOwner !== "worker" ||
207
+ manifest.oauth?.healthStatus !== "NOT_PROBED" ||
208
+ manifest.oauth?.auditStatus !== "REQUIRED" ||
209
+ manifest.oauth?.broaderRolloutBlocked !== true
210
+ ) {
211
+ return { ok: false, code: "oauth_readiness_boundary_failed" };
212
+ }
213
+ if (
214
+ manifest.authority?.database !== false ||
215
+ manifest.authority?.migrations !== false ||
216
+ manifest.authority?.hostControl !== false
217
+ ) {
218
+ return { ok: false, code: "authority_boundary_failed" };
219
+ }
220
+ if (
221
+ manifest.resources?.pids !== 128 ||
222
+ manifest.resources?.memoryMb !== 1024 ||
223
+ manifest.resources?.cpuQuota !== 1 ||
224
+ JSON.stringify(manifest.egress) !== JSON.stringify(EXACT_EGRESS)
225
+ ) {
226
+ return { ok: false, code: "resource_or_egress_boundary_failed" };
227
+ }
228
+ if (
229
+ /xox[bap]-|authorizationCode|authorization_code|accessToken|refreshToken/.test(
230
+ JSON.stringify(manifest)
231
+ )
232
+ ) {
233
+ return { ok: false, code: "raw_secret_material" };
234
+ }
235
+ return { ok: true, code: "contained" };
236
+ }
237
+
238
+ export function isContainmentEgressAllowed(manifest, value) {
239
+ if (!validateContainmentContract(manifest).ok) return false;
240
+ try {
241
+ const url = new URL(value);
242
+ if (url.protocol !== "https:" || url.username || url.password) return false;
243
+ return manifest.egress.includes(url.origin);
244
+ } catch {
245
+ return false;
246
+ }
247
+ }
248
+
249
+ function containedRelative(root, target) {
250
+ const rel = relative(root, target);
251
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
252
+ }
253
+
254
+ export function materializeContainmentProfile(manifest, { root }) {
255
+ if (!validateContainmentContract(manifest).ok) {
256
+ return { ok: false, code: "invalid_manifest" };
257
+ }
258
+ try {
259
+ if (!isAbsolute(root) || !existsSync(root) || lstatSync(root).isSymbolicLink()) {
260
+ return { ok: false, code: "invalid_root" };
261
+ }
262
+ const realRoot = realpathSync(root);
263
+ const profileRoot = join(realRoot, manifest.profileId);
264
+ if (!containedRelative(realRoot, profileRoot)) {
265
+ return { ok: false, code: "path_confinement_failed" };
266
+ }
267
+ mkdirSync(join(profileRoot, "control"), { recursive: true, mode: 0o700 });
268
+ mkdirSync(join(profileRoot, "profile"), { recursive: true, mode: 0o750 });
269
+ mkdirSync(join(profileRoot, "credentials"), { recursive: true, mode: 0o700 });
270
+ writeFileSync(join(profileRoot, "profile", "probe.txt"), "sellable-agent-contained\n", {
271
+ mode: 0o444,
272
+ });
273
+ chmodSync(join(profileRoot, "profile", "probe.txt"), 0o444);
274
+ writeFileSync(
275
+ join(profileRoot, "containment.json"),
276
+ `${JSON.stringify(manifest, null, 2)}\n`,
277
+ { mode: 0o600 }
278
+ );
279
+ return { ok: true, profileRoot };
280
+ } catch {
281
+ return { ok: false, code: "materialization_failed" };
282
+ }
283
+ }
284
+
285
+ export function buildContainerContainmentProbeSpec(
286
+ manifest,
287
+ { root, runtimeUid, runtimeGid }
288
+ ) {
289
+ const validation = validateContainmentContract(manifest);
290
+ if (!validation.ok) return { ok: false, code: validation.code };
291
+ try {
292
+ if (
293
+ !Number.isSafeInteger(runtimeUid) ||
294
+ runtimeUid < 1 ||
295
+ !Number.isSafeInteger(runtimeGid) ||
296
+ runtimeGid < 1 ||
297
+ !isAbsolute(root) ||
298
+ String(root).includes(",") ||
299
+ String(root).includes("\n")
300
+ ) {
301
+ return { ok: false, code: "invalid_probe_runtime" };
302
+ }
303
+ const realRoot = realpathSync(root);
304
+ const materializedRoot = realpathSync(join(realRoot, manifest.profileId));
305
+ const profileData = realpathSync(join(materializedRoot, "profile"));
306
+ if (
307
+ !containedRelative(realRoot, materializedRoot) ||
308
+ !containedRelative(materializedRoot, profileData) ||
309
+ lstatSync(profileData).isSymbolicLink() ||
310
+ readFileSync(join(profileData, "probe.txt"), "utf8") !==
311
+ "sellable-agent-contained\n"
312
+ ) {
313
+ return { ok: false, code: "invalid_materialized_profile" };
314
+ }
315
+ const script = [
316
+ `test "$(id -u)" = "${runtimeUid}"`,
317
+ 'test "$(cat /profile/probe.txt)" = "sellable-agent-contained"',
318
+ 'test ! -r /control/probe.txt',
319
+ 'test ! -r /run/credentials/service-credential',
320
+ 'test ! -r /run/credentials/oauth-private',
321
+ 'test ! -r /siblings/profile-2/probe.txt',
322
+ 'test ! -r /host/probe.txt',
323
+ 'test ! -S /var/run/docker.sock',
324
+ 'if touch /sellable-root-write-probe 2>/dev/null; then exit 31; fi',
325
+ "grep -Eq '^NoNewPrivs:[[:space:]]+1$' /proc/self/status",
326
+ "grep -Eq '^CapEff:[[:space:]]+0+$' /proc/self/status",
327
+ "test ! -e /sys/class/net/eth0",
328
+ `printf '%s\\n' '${JSON.stringify({
329
+ ok: true,
330
+ checks: Object.fromEntries(CONTAINER_CHECK_KEYS.map((key) => [key, true])),
331
+ })}'`,
332
+ ].join("\n");
333
+ const args = [
334
+ "run",
335
+ "--rm",
336
+ "--label",
337
+ "sellable.containment.probe=1",
338
+ "--pull",
339
+ "never",
340
+ "--network",
341
+ "none",
342
+ "--read-only",
343
+ "--cap-drop",
344
+ "ALL",
345
+ "--security-opt",
346
+ "no-new-privileges",
347
+ "--pids-limit",
348
+ "128",
349
+ "--memory",
350
+ "1024m",
351
+ "--cpus",
352
+ "1",
353
+ "--user",
354
+ `${runtimeUid}:${runtimeGid}`,
355
+ "--tmpfs",
356
+ "/tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777",
357
+ "--mount",
358
+ `type=bind,src=${profileData},dst=/profile,readonly`,
359
+ PINNED_CONTAINMENT_IMAGE,
360
+ "sh",
361
+ "-ceu",
362
+ script,
363
+ ];
364
+ return {
365
+ ok: true,
366
+ command: "docker",
367
+ image: PINNED_CONTAINMENT_IMAGE,
368
+ args,
369
+ shell: false,
370
+ timeoutMs: 30_000,
371
+ maxOutputBytes: 8 * 1024,
372
+ };
373
+ } catch {
374
+ return { ok: false, code: "invalid_materialized_profile" };
375
+ }
376
+ }
377
+
378
+ export async function runContainerContainmentProbe(manifest, runtime) {
379
+ const spec = buildContainerContainmentProbeSpec(manifest, runtime);
380
+ if (!spec.ok) return { ...spec, checks: {} };
381
+ const output = await runBoundedChildProcess(spec);
382
+ if (
383
+ output.exitCode !== 0 ||
384
+ output.timedOut ||
385
+ output.outputLimitExceeded ||
386
+ output.terminated
387
+ ) {
388
+ return { ok: false, code: "container_probe_failed", checks: {} };
389
+ }
390
+ try {
391
+ const parsed = JSON.parse(output.stdout);
392
+ if (
393
+ !parsed ||
394
+ parsed.ok !== true ||
395
+ Object.keys(parsed).sort().join("\0") !== ["checks", "ok"].sort().join("\0") ||
396
+ Object.keys(parsed.checks).sort().join("\0") !==
397
+ [...CONTAINER_CHECK_KEYS].sort().join("\0") ||
398
+ CONTAINER_CHECK_KEYS.some((key) => parsed.checks[key] !== true)
399
+ ) {
400
+ throw new Error("invalid probe result");
401
+ }
402
+ return { ok: true, code: "contained", checks: parsed.checks };
403
+ } catch {
404
+ return { ok: false, code: "container_probe_failed", checks: {} };
405
+ }
406
+ }
407
+
408
+ function identityCanAccess(manifest, identity, target) {
409
+ if (identity !== "worker" && identity !== "runtime") return false;
410
+ if (target === "/") return false;
411
+ if (target === "/var/run/docker.sock") return false;
412
+ if (target.startsWith("/run/credentials/")) return identity === "worker";
413
+ const mount = manifest.mounts.find(
414
+ (candidate) => target === candidate.target || target.startsWith(`${candidate.target}/`)
415
+ );
416
+ return Boolean(mount && mount.owner === identity);
417
+ }
418
+
419
+ export function probeContainmentProfile(manifest, { root }) {
420
+ const validation = validateContainmentContract(manifest);
421
+ if (!validation.ok) return { ok: false, code: validation.code, checks: {} };
422
+ try {
423
+ const realRoot = realpathSync(root);
424
+ const profileRoot = realpathSync(join(realRoot, manifest.profileId));
425
+ const materializedManifest = JSON.parse(
426
+ readFileSync(join(profileRoot, "containment.json"), "utf8")
427
+ );
428
+ if (JSON.stringify(materializedManifest) !== JSON.stringify(manifest)) {
429
+ return { ok: false, code: "materialized_manifest_mismatch", checks: {} };
430
+ }
431
+ const checks = {
432
+ runtimeProfile: identityCanAccess(manifest, "runtime", "/profile"),
433
+ runtimeControlDenied: !identityCanAccess(manifest, "runtime", "/control"),
434
+ runtimeCredentialDenied: !identityCanAccess(
435
+ manifest,
436
+ "runtime",
437
+ "/run/credentials/service-credential"
438
+ ),
439
+ runtimeOauthDenied: !identityCanAccess(
440
+ manifest,
441
+ "runtime",
442
+ "/run/credentials/oauth-private"
443
+ ),
444
+ siblingDenied: !identityCanAccess(manifest, "runtime", "/siblings/profile-2"),
445
+ rootDenied: !identityCanAccess(manifest, "runtime", "/"),
446
+ dockerDenied: !identityCanAccess(manifest, "runtime", "/var/run/docker.sock"),
447
+ databaseDenied: manifest.authority.database === false,
448
+ migrationDenied: manifest.authority.migrations === false,
449
+ sellableAllowed: isContainmentEgressAllowed(
450
+ manifest,
451
+ "https://app.sellable.dev/api/v3/workspaces"
452
+ ),
453
+ slackAllowed: isContainmentEgressAllowed(
454
+ manifest,
455
+ "https://slack.com/api/auth.test"
456
+ ),
457
+ modelAllowed: isContainmentEgressAllowed(
458
+ manifest,
459
+ "https://api.openai.com/v1/responses"
460
+ ),
461
+ extraEndpointDenied: !isContainmentEgressAllowed(
462
+ manifest,
463
+ "https://evil.example/collect"
464
+ ),
465
+ };
466
+ return Object.values(checks).every(Boolean)
467
+ ? { ok: true, code: "contained", checks }
468
+ : { ok: false, code: "probe_failed", checks };
469
+ } catch {
470
+ return { ok: false, code: "probe_failed", checks: {} };
471
+ }
472
+ }