@theokit/sdk 4.19.4 → 4.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sandbox/bwrap.d.cts +75 -0
- package/dist/sandbox/bwrap.d.ts +75 -0
- package/dist/sandbox/index.cjs +506 -12
- package/dist/sandbox/index.cjs.map +1 -1
- package/dist/sandbox/index.d.cts +3 -0
- package/dist/sandbox/index.d.ts +3 -0
- package/dist/sandbox/index.js +487 -14
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/linux-sandbox.d.cts +115 -0
- package/dist/sandbox/linux-sandbox.d.ts +115 -0
- package/dist/sandbox/seccomp.d.cts +10 -0
- package/dist/sandbox/seccomp.d.ts +10 -0
- package/package.json +2 -2
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { BwrapDetection, SandboxMode } from "./bwrap.js";
|
|
2
|
+
import { LocalSandbox } from "./local-sandbox.js";
|
|
3
|
+
import type { SandboxBackend, SandboxConfig } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* M57 — the single source of truth for the sandbox command wrap. Turns `command` into
|
|
6
|
+
* `<bin> <bwrap flags> [--seccomp 3] -- /bin/sh -c '<command>' [3< <bpf>]`, or `null` when the policy
|
|
7
|
+
* skips the sandbox (`danger-full-access`). Extracted from `LinuxSandbox.wrapCommand` so the interactive
|
|
8
|
+
* PTY backend (M57) can reuse the EXACT wrap the one-shot `run_shell` already uses (DRY) — faithful to
|
|
9
|
+
* Codex, where the sandbox transforms the argv before the PTY spawns it (`sandboxing/src/manager.rs:321`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function wrapCommandForSandbox(mode: SandboxMode, opts: {
|
|
12
|
+
cwd: string;
|
|
13
|
+
network?: boolean;
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
bin?: string;
|
|
16
|
+
seccompPath?: string;
|
|
17
|
+
}, command: string): string | null;
|
|
18
|
+
export declare function allowlistedEnv(source?: NodeJS.ProcessEnv): Record<string, string>;
|
|
19
|
+
export declare class LinuxSandbox extends LocalSandbox {
|
|
20
|
+
private readonly mode;
|
|
21
|
+
private readonly network;
|
|
22
|
+
private readonly cwd;
|
|
23
|
+
private readonly bin;
|
|
24
|
+
private readonly env;
|
|
25
|
+
/** M63 — path to the cBPF seccomp program written host-side; passed to `bwrap --seccomp 3` via a
|
|
26
|
+
* shell redirect. `undefined` when the network is unrestricted OR generation failed (honest fallback). */
|
|
27
|
+
private readonly seccompPath;
|
|
28
|
+
constructor(config: SandboxConfig, opts: {
|
|
29
|
+
mode: SandboxMode;
|
|
30
|
+
network?: boolean;
|
|
31
|
+
bin?: string;
|
|
32
|
+
env?: Record<string, string>;
|
|
33
|
+
});
|
|
34
|
+
/** Extracted for test visibility — delegates to the pure `wrapCommandForSandbox` (M57, single wrap SoT). */
|
|
35
|
+
wrapCommand(command: string): string | null;
|
|
36
|
+
execute(command: string, opts?: {
|
|
37
|
+
timeoutMs?: number;
|
|
38
|
+
}): Promise<import("./types.js").ExecuteResult>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* M63 — the restricted-network seccomp program is DETERMINISTIC, so write it ONCE per process and
|
|
42
|
+
* reuse the path across every LinuxSandbox (no per-instance temp accumulation).
|
|
43
|
+
*
|
|
44
|
+
* ARCH GUARD (review HIGH): `buildSeccompFilter` emits an x86_64 program whose arch guard KILLs every
|
|
45
|
+
* syscall whose `seccomp_data.arch != AUDIT_ARCH_X86_64`. On a non-x86_64 host that would brick EVERY
|
|
46
|
+
* sandboxed command (the first execve is killed) — and silently, because generation succeeds and bwrap
|
|
47
|
+
* accepts it. So we REFUSE to install on non-x64 and WARN through the honest-downgrade channel (bwrap
|
|
48
|
+
* FS/network confinement still applies), exactly like the bwrap-missing fallback. `arch` is injectable
|
|
49
|
+
* for tests. Cleaned on exit AND on SIGINT/SIGTERM (TUI Ctrl+C would otherwise leak the temp dir).
|
|
50
|
+
*/
|
|
51
|
+
export declare function seccompPathForArch(arch: string, warn: (m: string) => void): string | undefined;
|
|
52
|
+
/** M57 — exported so the interactive PTY backend reuses the SAME memoized x64-gated seccomp program. */
|
|
53
|
+
export declare function restrictedSeccompPath(): string | undefined;
|
|
54
|
+
/** Test seam: reset the WARN-once latch. */
|
|
55
|
+
export declare function resetSandboxWarnLatch(): void;
|
|
56
|
+
/** Durable sandbox posture for the UI — the honest answer to "am I kernel-enforced right now?". */
|
|
57
|
+
export interface SandboxPosture {
|
|
58
|
+
mode: SandboxMode;
|
|
59
|
+
enforced: boolean;
|
|
60
|
+
detail: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* MEDIUM-2: compute the posture so a surface (TUI footer) can show enforcement DURABLY instead of a
|
|
64
|
+
* one-shot warn. `danger-full-access` is honestly reported as unenforced; an unavailable bwrap reports
|
|
65
|
+
* the downgrade reason so the user never believes they are confined when they are not.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveSandboxPosture(opts: {
|
|
68
|
+
mode: SandboxMode;
|
|
69
|
+
detect?: () => BwrapDetection;
|
|
70
|
+
}): SandboxPosture;
|
|
71
|
+
export interface CreateSandboxBackendOptions {
|
|
72
|
+
mode: SandboxMode;
|
|
73
|
+
workDir?: string;
|
|
74
|
+
network?: boolean;
|
|
75
|
+
timeoutMs?: number;
|
|
76
|
+
/** Injectable for tests; defaults to the real 3-probe detection. */
|
|
77
|
+
detect?: () => BwrapDetection;
|
|
78
|
+
/** Injectable for tests; defaults to console.warn. */
|
|
79
|
+
warn?: (message: string) => void;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Honest factory: bwrap available + mode wants confinement → `LinuxSandbox` (kernel enforcement,
|
|
83
|
+
* running the VALIDATED absolute bin); `danger-full-access` → plain `LocalSandbox` silently (explicit
|
|
84
|
+
* opt-out, `bwrap.rs:245-252`); bwrap unavailable → WARN once + `LocalSandbox` (the declarative M23
|
|
85
|
+
* gating remains the guard). NEVER pretends to sandbox — the fallback is loud, mirroring Codex's
|
|
86
|
+
* MISSING_BWRAP_WARNING. The durable posture lives in `resolveSandboxPosture` for the UI.
|
|
87
|
+
*/
|
|
88
|
+
export declare function createSandboxBackend(opts: CreateSandboxBackendOptions): SandboxBackend;
|
|
89
|
+
/** Reset para testes — o latch é estado de módulo e testes precisam de isolamento. */
|
|
90
|
+
export declare function resetInteractiveWarnLatch(): void;
|
|
91
|
+
export interface InteractiveWrapOptions {
|
|
92
|
+
mode: SandboxMode;
|
|
93
|
+
/** `true` mantém a rede. Default `false`, igual ao `run_shell` não-interativo. */
|
|
94
|
+
network?: boolean;
|
|
95
|
+
/** Injetável para testes; default é a detecção real memoizada. */
|
|
96
|
+
detect?: () => BwrapDetection;
|
|
97
|
+
/** Injetável para testes; default é `console.warn` com redação. */
|
|
98
|
+
warn?: (message: string) => void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A composição que o caminho interativo precisa — o par de `createSandboxBackend`.
|
|
102
|
+
*
|
|
103
|
+
* `createSandboxBackend` resolve isto para o caminho não-interativo devolvendo um BACKEND pronto. O
|
|
104
|
+
* PTY não aceita um backend: ele é dono do spawn e só admite transformar o comando. Esta função
|
|
105
|
+
* entrega a MESMA decisão na forma que o PTY aceita — `(command, cwd) => string | null` —, pronta
|
|
106
|
+
* para `new PtyInteractiveBackend({ wrapCommand: interactiveWrapCommand({ mode }) })`.
|
|
107
|
+
*
|
|
108
|
+
* A detecção é consultada **a cada wrap**, não congelada na construção: uma sessão interativa vive
|
|
109
|
+
* por horas, e uma detecção positiva obsoleta continuaria afirmando confinamento depois de o binário
|
|
110
|
+
* sumir (a revalidação por `existsSync` vive dentro de `detectBwrapMemoizado`).
|
|
111
|
+
*
|
|
112
|
+
* As duas rotas que devolvem `null` são semanticamente diferentes e o código não as funde:
|
|
113
|
+
* `danger-full-access` é opt-out explícito e NÃO avisa; bwrap indisponível é falha e avisa uma vez.
|
|
114
|
+
*/
|
|
115
|
+
export declare function interactiveWrapCommand(opts: InteractiveWrapOptions): (command: string, cwd: string) => string | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface SeccompOptions {
|
|
2
|
+
/** When true (network off), also deny the socket set + non-AF_UNIX socket(). */
|
|
3
|
+
networkRestricted: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Build the cBPF seccomp program as a `Buffer` (each `sock_filter` = 8 bytes). Jump targets are
|
|
7
|
+
* expressed against labels and back-patched to relative offsets, so the layout is deterministic and
|
|
8
|
+
* unit-testable against the authoritative syscall list.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildSeccompFilter(opts: SeccompOptions): Buffer;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface SeccompOptions {
|
|
2
|
+
/** When true (network off), also deny the socket set + non-AF_UNIX socket(). */
|
|
3
|
+
networkRestricted: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Build the cBPF seccomp program as a `Buffer` (each `sock_filter` = 8 bytes). Jump targets are
|
|
7
|
+
* expressed against labels and back-patched to relative offsets, so the layout is deterministic and
|
|
8
|
+
* unit-testable against the authoritative syscall list.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildSeccompFilter(opts: SeccompOptions): Buffer;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theokit/sdk",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "TypeScript SDK for the Theo agent harness
|
|
3
|
+
"version": "4.21.0",
|
|
4
|
+
"description": "TypeScript SDK for the Theo agent harness \u2014 same surface, local or cloud.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/usetheodev/theokit-sdk#readme",
|
|
7
7
|
"bugs": "https://github.com/usetheodev/theokit-sdk/issues",
|