UNPKG

@sellable/install

Version:

One-command installer for Sellable MCP in Claude Code, Codex, and Hermes

473 lines (459 loc) • 15.3 kB
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, writeFileSync, } from "node:fs"; import { isAbsolute, join, relative, resolve } from "node:path"; import { runBoundedChildProcess } from "./provisioning-adapter.mjs"; const INPUT_KEYS = new Set([ "profileId", "workerUid", "runtimeUid", "credentialReferences", "oauthPrivateReference", ]); const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/; const FINGERPRINT = /^[a-f0-9]{64}$/; const EXACT_EGRESS = [ "https://app.sellable.dev", "https://slack.com", "https://api.openai.com", ]; export const PINNED_CONTAINMENT_IMAGE = "public.ecr.aws/supabase/postgres@sha256:af083ef64d0408c8f098ee6f5c364a59b26f36fbc0f3a334a62c5c1d57362e9b"; const CONTAINER_CHECK_KEYS = [ "runtimeUid", "profileRead", "controlDenied", "credentialDenied", "oauthDenied", "siblingDenied", "hostDenied", "dockerDenied", "rootReadOnly", "noNewPrivileges", "capabilitiesDropped", "networkDisabled", ]; function validPrivateReference(value, scheme) { return ( value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).every((key) => ["reference", "fingerprint", "purpose", "target", "owner"].includes(key) ) && typeof value.reference === "string" && value.reference.startsWith(`${scheme}://`) && FINGERPRINT.test(value.fingerprint) ); } export function buildContainmentContract(input) { if (!input || typeof input !== "object" || Array.isArray(input)) { return { ok: false, code: "invalid_input" }; } if (Object.keys(input).some((key) => !INPUT_KEYS.has(key))) { return { ok: false, code: "unknown_field" }; } if ( !SAFE_ID.test(input.profileId) || !Number.isSafeInteger(input.workerUid) || input.workerUid < 1 || !Number.isSafeInteger(input.runtimeUid) || input.runtimeUid < 1 || input.workerUid === input.runtimeUid || !Array.isArray(input.credentialReferences) || !input.credentialReferences.every( (reference) => validPrivateReference(reference, "secret") && ["service-credential", "worker-credential"].includes(reference.purpose) ) || !validPrivateReference(input.oauthPrivateReference, "oauth-private") ) { return { ok: false, code: "invalid_input" }; } const profileBase = `/var/lib/sellable-agents/${input.profileId}`; const manifest = { profileId: input.profileId, readOnlyRootFilesystem: true, noNewPrivileges: true, dockerSocket: false, capabilities: [], worker: { name: `sellable-worker-${input.profileId}`, uid: input.workerUid, }, runtime: { name: `sellable-runtime-${input.profileId}`, uid: input.runtimeUid, }, mounts: [ { profileId: input.profileId, source: `${profileBase}/control`, target: "/control", readOnly: false, owner: "worker", }, { profileId: input.profileId, source: `${profileBase}/profile`, target: "/profile", readOnly: false, owner: "runtime", }, ], credentials: input.credentialReferences.map((reference) => ({ purpose: reference.purpose, reference: reference.reference, fingerprint: reference.fingerprint, target: `/run/credentials/${reference.purpose}`, owner: "worker", })), oauthPrivateReference: { reference: input.oauthPrivateReference.reference, fingerprint: input.oauthPrivateReference.fingerprint, owner: "worker", }, oauth: { privateExchangeOwner: "worker", healthStatus: "NOT_PROBED", auditStatus: "REQUIRED", broaderRolloutBlocked: true, }, authority: { database: false, migrations: false, hostControl: false, }, resources: { pids: 128, memoryMb: 1024, cpuQuota: 1 }, egress: [...EXACT_EGRESS], }; const validated = validateContainmentContract(manifest); return validated.ok ? { ok: true, manifest } : validated; } export function validateContainmentContract(manifest) { if ( !manifest || typeof manifest !== "object" || !SAFE_ID.test(manifest.profileId) ) { return { ok: false, code: "invalid_manifest" }; } if ( manifest.readOnlyRootFilesystem !== true || manifest.noNewPrivileges !== true || manifest.dockerSocket !== false || !Array.isArray(manifest.capabilities) || manifest.capabilities.length !== 0 ) { return { ok: false, code: "privilege_boundary_failed" }; } if ( !Number.isSafeInteger(manifest.worker?.uid) || manifest.worker.uid < 1 || !Number.isSafeInteger(manifest.runtime?.uid) || manifest.runtime.uid < 1 || manifest.worker.uid === manifest.runtime.uid ) { return { ok: false, code: "identity_boundary_failed" }; } const profileBase = `/var/lib/sellable-agents/${manifest.profileId}/`; if ( !Array.isArray(manifest.mounts) || manifest.mounts.length !== 2 || manifest.mounts.some( (mount) => mount.profileId !== manifest.profileId || typeof mount.source !== "string" || !mount.source.startsWith(profileBase) || !["/control", "/profile"].includes(mount.target) || !["worker", "runtime"].includes(mount.owner) || mount.source.includes("docker.sock") ) || manifest.mounts.find((mount) => mount.target === "/control")?.owner !== "worker" || manifest.mounts.find((mount) => mount.target === "/profile")?.owner !== "runtime" ) { return { ok: false, code: "mount_boundary_failed" }; } if ( !Array.isArray(manifest.credentials) || !manifest.credentials.every( (reference) => validPrivateReference(reference, "secret") && typeof reference.target === "string" && reference.target.startsWith("/run/credentials/") && reference.owner === "worker" ) || !validPrivateReference(manifest.oauthPrivateReference, "oauth-private") || manifest.oauthPrivateReference.owner !== "worker" ) { return { ok: false, code: "secret_boundary_failed" }; } if ( manifest.oauth?.privateExchangeOwner !== "worker" || manifest.oauth?.healthStatus !== "NOT_PROBED" || manifest.oauth?.auditStatus !== "REQUIRED" || manifest.oauth?.broaderRolloutBlocked !== true ) { return { ok: false, code: "oauth_readiness_boundary_failed" }; } if ( manifest.authority?.database !== false || manifest.authority?.migrations !== false || manifest.authority?.hostControl !== false ) { return { ok: false, code: "authority_boundary_failed" }; } if ( manifest.resources?.pids !== 128 || manifest.resources?.memoryMb !== 1024 || manifest.resources?.cpuQuota !== 1 || JSON.stringify(manifest.egress) !== JSON.stringify(EXACT_EGRESS) ) { return { ok: false, code: "resource_or_egress_boundary_failed" }; } if ( /xox[bap]-|authorizationCode|authorization_code|accessToken|refreshToken/.test( JSON.stringify(manifest) ) ) { return { ok: false, code: "raw_secret_material" }; } return { ok: true, code: "contained" }; } export function isContainmentEgressAllowed(manifest, value) { if (!validateContainmentContract(manifest).ok) return false; try { const url = new URL(value); if (url.protocol !== "https:" || url.username || url.password) return false; return manifest.egress.includes(url.origin); } catch { return false; } } function containedRelative(root, target) { const rel = relative(root, target); return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel)); } export function materializeContainmentProfile(manifest, { root }) { if (!validateContainmentContract(manifest).ok) { return { ok: false, code: "invalid_manifest" }; } try { if (!isAbsolute(root) || !existsSync(root) || lstatSync(root).isSymbolicLink()) { return { ok: false, code: "invalid_root" }; } const realRoot = realpathSync(root); const profileRoot = join(realRoot, manifest.profileId); if (!containedRelative(realRoot, profileRoot)) { return { ok: false, code: "path_confinement_failed" }; } mkdirSync(join(profileRoot, "control"), { recursive: true, mode: 0o700 }); mkdirSync(join(profileRoot, "profile"), { recursive: true, mode: 0o750 }); mkdirSync(join(profileRoot, "credentials"), { recursive: true, mode: 0o700 }); writeFileSync(join(profileRoot, "profile", "probe.txt"), "sellable-agent-contained\n", { mode: 0o444, }); chmodSync(join(profileRoot, "profile", "probe.txt"), 0o444); writeFileSync( join(profileRoot, "containment.json"), `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 } ); return { ok: true, profileRoot }; } catch { return { ok: false, code: "materialization_failed" }; } } export function buildContainerContainmentProbeSpec( manifest, { root, runtimeUid, runtimeGid } ) { const validation = validateContainmentContract(manifest); if (!validation.ok) return { ok: false, code: validation.code }; try { if ( !Number.isSafeInteger(runtimeUid) || runtimeUid < 1 || !Number.isSafeInteger(runtimeGid) || runtimeGid < 1 || !isAbsolute(root) || String(root).includes(",") || String(root).includes("\n") ) { return { ok: false, code: "invalid_probe_runtime" }; } const realRoot = realpathSync(root); const materializedRoot = realpathSync(join(realRoot, manifest.profileId)); const profileData = realpathSync(join(materializedRoot, "profile")); if ( !containedRelative(realRoot, materializedRoot) || !containedRelative(materializedRoot, profileData) || lstatSync(profileData).isSymbolicLink() || readFileSync(join(profileData, "probe.txt"), "utf8") !== "sellable-agent-contained\n" ) { return { ok: false, code: "invalid_materialized_profile" }; } const script = [ `test "$(id -u)" = "${runtimeUid}"`, 'test "$(cat /profile/probe.txt)" = "sellable-agent-contained"', 'test ! -r /control/probe.txt', 'test ! -r /run/credentials/service-credential', 'test ! -r /run/credentials/oauth-private', 'test ! -r /siblings/profile-2/probe.txt', 'test ! -r /host/probe.txt', 'test ! -S /var/run/docker.sock', 'if touch /sellable-root-write-probe 2>/dev/null; then exit 31; fi', "grep -Eq '^NoNewPrivs:[[:space:]]+1$' /proc/self/status", "grep -Eq '^CapEff:[[:space:]]+0+$' /proc/self/status", "test ! -e /sys/class/net/eth0", `printf '%s\\n' '${JSON.stringify({ ok: true, checks: Object.fromEntries(CONTAINER_CHECK_KEYS.map((key) => [key, true])), })}'`, ].join("\n"); const args = [ "run", "--rm", "--label", "sellable.containment.probe=1", "--pull", "never", "--network", "none", "--read-only", "--cap-drop", "ALL", "--security-opt", "no-new-privileges", "--pids-limit", "128", "--memory", "1024m", "--cpus", "1", "--user", `${runtimeUid}:${runtimeGid}`, "--tmpfs", "/tmp:rw,noexec,nosuid,nodev,size=16m,mode=1777", "--mount", `type=bind,src=${profileData},dst=/profile,readonly`, PINNED_CONTAINMENT_IMAGE, "sh", "-ceu", script, ]; return { ok: true, command: "docker", image: PINNED_CONTAINMENT_IMAGE, args, shell: false, timeoutMs: 30_000, maxOutputBytes: 8 * 1024, }; } catch { return { ok: false, code: "invalid_materialized_profile" }; } } export async function runContainerContainmentProbe(manifest, runtime) { const spec = buildContainerContainmentProbeSpec(manifest, runtime); if (!spec.ok) return { ...spec, checks: {} }; const output = await runBoundedChildProcess(spec); if ( output.exitCode !== 0 || output.timedOut || output.outputLimitExceeded || output.terminated ) { return { ok: false, code: "container_probe_failed", checks: {} }; } try { const parsed = JSON.parse(output.stdout); if ( !parsed || parsed.ok !== true || Object.keys(parsed).sort().join("\0") !== ["checks", "ok"].sort().join("\0") || Object.keys(parsed.checks).sort().join("\0") !== [...CONTAINER_CHECK_KEYS].sort().join("\0") || CONTAINER_CHECK_KEYS.some((key) => parsed.checks[key] !== true) ) { throw new Error("invalid probe result"); } return { ok: true, code: "contained", checks: parsed.checks }; } catch { return { ok: false, code: "container_probe_failed", checks: {} }; } } function identityCanAccess(manifest, identity, target) { if (identity !== "worker" && identity !== "runtime") return false; if (target === "/") return false; if (target === "/var/run/docker.sock") return false; if (target.startsWith("/run/credentials/")) return identity === "worker"; const mount = manifest.mounts.find( (candidate) => target === candidate.target || target.startsWith(`${candidate.target}/`) ); return Boolean(mount && mount.owner === identity); } export function probeContainmentProfile(manifest, { root }) { const validation = validateContainmentContract(manifest); if (!validation.ok) return { ok: false, code: validation.code, checks: {} }; try { const realRoot = realpathSync(root); const profileRoot = realpathSync(join(realRoot, manifest.profileId)); const materializedManifest = JSON.parse( readFileSync(join(profileRoot, "containment.json"), "utf8") ); if (JSON.stringify(materializedManifest) !== JSON.stringify(manifest)) { return { ok: false, code: "materialized_manifest_mismatch", checks: {} }; } const checks = { runtimeProfile: identityCanAccess(manifest, "runtime", "/profile"), runtimeControlDenied: !identityCanAccess(manifest, "runtime", "/control"), runtimeCredentialDenied: !identityCanAccess( manifest, "runtime", "/run/credentials/service-credential" ), runtimeOauthDenied: !identityCanAccess( manifest, "runtime", "/run/credentials/oauth-private" ), siblingDenied: !identityCanAccess(manifest, "runtime", "/siblings/profile-2"), rootDenied: !identityCanAccess(manifest, "runtime", "/"), dockerDenied: !identityCanAccess(manifest, "runtime", "/var/run/docker.sock"), databaseDenied: manifest.authority.database === false, migrationDenied: manifest.authority.migrations === false, sellableAllowed: isContainmentEgressAllowed( manifest, "https://app.sellable.dev/api/v3/workspaces" ), slackAllowed: isContainmentEgressAllowed( manifest, "https://slack.com/api/auth.test" ), modelAllowed: isContainmentEgressAllowed( manifest, "https://api.openai.com/v1/responses" ), extraEndpointDenied: !isContainmentEgressAllowed( manifest, "https://evil.example/collect" ), }; return Object.values(checks).every(Boolean) ? { ok: true, code: "contained", checks } : { ok: false, code: "probe_failed", checks }; } catch { return { ok: false, code: "probe_failed", checks: {} }; } }