@agentsh/secure-sandbox 0.1.5 → 0.1.7
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 +45 -11
- package/dist/adapters/blaxel.d.ts +1 -1
- package/dist/adapters/cloudflare.d.ts +1 -1
- package/dist/adapters/daytona.d.ts +1 -1
- package/dist/adapters/e2b.d.ts +1 -1
- package/dist/adapters/index.d.ts +2 -1
- package/dist/adapters/index.js +11 -1
- package/dist/adapters/vercel.d.ts +1 -1
- package/dist/chunk-4FJHYLAB.js +251 -0
- package/dist/chunk-4FJHYLAB.js.map +1 -0
- package/dist/chunk-5IG6ABIZ.js +268 -0
- package/dist/chunk-5IG6ABIZ.js.map +1 -0
- package/dist/{chunk-GFPHTJLU.js → chunk-LNDICGZU.js} +3 -243
- package/dist/chunk-LNDICGZU.js.map +1 -0
- package/dist/index-TyzWAIUD.d.ts +60 -0
- package/dist/index.d.ts +5 -11
- package/dist/index.js +209 -43
- package/dist/index.js.map +1 -1
- package/dist/policies/index.d.ts +1 -1
- package/dist/policies/index.js +5 -3
- package/dist/testing/index.d.ts +1 -1
- package/dist/{types-CUqsllMs.d.ts → types-DFMGk2GV.d.ts} +127 -2
- package/package.json +19 -1
- package/dist/chunk-GFPHTJLU.js.map +0 -1
- package/dist/chunk-L4KFLVNU.js +0 -33
- package/dist/chunk-L4KFLVNU.js.map +0 -1
- package/dist/index-aQ1TVPtG.d.ts +0 -16
- package/dist/{index-Nmlhw9oj.d.ts → index-CedRtlB6.d.ts} +22 -22
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @agentsh/secure-sandbox
|
|
2
2
|
|
|
3
|
-
Runtime security for AI agent sandboxes. Drop-in protection against prompt injection, secret exfiltration, and sandbox escape — works with [Vercel](https://vercel.com/sandbox), [E2B](https://e2b.dev/), [Daytona](https://www.daytona.io/), [Cloudflare Containers](https://developers.cloudflare.com/containers/),
|
|
3
|
+
Runtime security for AI agent sandboxes. Drop-in protection against prompt injection, secret exfiltration, and sandbox escape — works with [Vercel](https://vercel.com/sandbox), [E2B](https://e2b.dev/), [Daytona](https://www.daytona.io/), [Cloudflare Containers](https://developers.cloudflare.com/containers/), [Blaxel](https://blaxel.ai/sandbox), [Sprites](https://sprites.dev), and [Modal](https://modal.com). Powered by [agentsh](https://www.agentsh.org).
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install @agentsh/secure-sandbox
|
|
@@ -94,25 +94,46 @@ When you call `secureSandbox()`, the library:
|
|
|
94
94
|
3. **Writes your policy** as YAML and starts the agentsh server
|
|
95
95
|
4. **Returns a `SecuredSandbox`** where every `exec()`, `writeFile()`, and `readFile()` is mediated
|
|
96
96
|
|
|
97
|
-
Enforcement happens at the **
|
|
97
|
+
Enforcement happens at the **kernel level** — Landlock restricts filesystem access, a network proxy filters outbound connections, and the shell shim mediates every command. On platforms that support it, **seccomp** intercepts process execution and **FUSE** intercepts file I/O at the syscall level. On gVisor-based platforms (like Modal), **ptrace** provides equivalent enforcement by intercepting `execve`, `openat`, `connect`, and signal syscalls. There's no way for the agent to bypass it from userspace.
|
|
98
98
|
|
|
99
99
|
| Capability | What It Does |
|
|
100
100
|
|------------|-------------|
|
|
101
|
-
| **seccomp** | Intercepts process execution at the syscall level — blocks `sudo`, `env`, `nc` before they run |
|
|
102
101
|
| **Landlock** | Kernel-level filesystem restrictions — denies access to paths like `~/.ssh`, `~/.aws` |
|
|
103
|
-
| **FUSE** | Virtual filesystem layer — intercepts every file open/read/write, enables soft-delete quarantine |
|
|
104
102
|
| **Network Proxy** | Filters outbound connections by domain and port — blocks exfiltration to unauthorized hosts |
|
|
103
|
+
| **Shell Shim** | Replaces `/bin/bash` — mediates every command through the policy engine |
|
|
104
|
+
| **seccomp** | Intercepts process execution at the syscall level — blocks `sudo`, `env`, `nc` before they run (opt-in) |
|
|
105
|
+
| **ptrace** | Syscall-level interception via `PTRACE_SEIZE` — enforces exec, file, network, and signal policies on gVisor platforms where seccomp user-notify is unavailable |
|
|
106
|
+
| **FUSE** | Virtual filesystem layer — intercepts every file open/read/write, enables soft-delete quarantine (opt-in) |
|
|
105
107
|
| **DLP** | Detects and redacts secrets (API keys, tokens) in command output |
|
|
106
108
|
|
|
107
109
|
## Supported Platforms
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
|
112
|
-
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
111
|
+
Every provider gets the same protections — the enforcement mechanism adapts to what the kernel supports:
|
|
112
|
+
|
|
113
|
+
| Protection | Vercel | E2B | Daytona | Cloudflare | Blaxel | Sprites | Modal |
|
|
114
|
+
|------------|--------|-----|---------|------------|--------|---------|-------|
|
|
115
|
+
| **File access control** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
116
|
+
| **Network filtering** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
117
|
+
| **Command mediation** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
118
|
+
| **Secret filtering** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
119
|
+
| **Threat intelligence** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
120
|
+
| **DLP** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
121
|
+
|
|
122
|
+
Different platforms use different kernel mechanisms to achieve these protections:
|
|
123
|
+
|
|
124
|
+
| Provider | Primary Enforcement | Security Mode |
|
|
125
|
+
|----------|-------------------|---------------|
|
|
126
|
+
| [**Vercel**](https://vercel.com/sandbox) | Landlock + network proxy + shell shim | `landlock` |
|
|
127
|
+
| [**E2B**](https://e2b.dev/) | Landlock + network proxy + shell shim | `full` |
|
|
128
|
+
| [**Daytona**](https://www.daytona.io/) | Landlock + network proxy + shell shim | `full` |
|
|
129
|
+
| [**Cloudflare**](https://developers.cloudflare.com/containers/) | Landlock + network proxy + shell shim | `landlock` |
|
|
130
|
+
| [**Blaxel**](https://blaxel.ai/sandbox) | Landlock + network proxy + shell shim | `full` |
|
|
131
|
+
| [**Sprites**](https://sprites.dev) | Landlock + network proxy + shell shim | `full` |
|
|
132
|
+
| [**Modal**](https://modal.com) | ptrace (execve + openat + connect + signal) + network proxy | `ptrace` |
|
|
133
|
+
|
|
134
|
+
> **Optional hardening:** seccomp and FUSE are available but disabled by default for compatibility. seccomp adds syscall-level command interception; FUSE adds a virtual filesystem layer with soft-delete quarantine. Enable via `serverConfig: { seccompDetails: { execve: true } }` or `serverConfig: { fuse: { deferred: true } }`.
|
|
135
|
+
>
|
|
136
|
+
> **Modal:** gVisor doesn't support seccomp user-notify or Landlock. ptrace provides equivalent enforcement by intercepting syscalls via `PTRACE_SEIZE`.
|
|
116
137
|
|
|
117
138
|
```typescript
|
|
118
139
|
// E2B
|
|
@@ -131,6 +152,19 @@ const sandbox = await secureSandbox(adapters.cloudflare(getSandbox(env.Sandbox,
|
|
|
131
152
|
// Blaxel
|
|
132
153
|
import { SandboxInstance } from '@blaxel/core';
|
|
133
154
|
const sandbox = await secureSandbox(adapters.blaxel(await SandboxInstance.create({ name: 'my-sandbox' })));
|
|
155
|
+
|
|
156
|
+
// Sprites (Fly.io Firecracker microVMs)
|
|
157
|
+
import { SpritesClient } from '@fly/sprites';
|
|
158
|
+
import { sprites } from '@agentsh/secure-sandbox/adapters/sprites';
|
|
159
|
+
const client = new SpritesClient(process.env.SPRITES_TOKEN);
|
|
160
|
+
const sandbox = await secureSandbox(sprites(client.sprite('my-sprite')));
|
|
161
|
+
|
|
162
|
+
// Modal (gVisor sandboxes with ptrace enforcement)
|
|
163
|
+
import { modal, modalDefaults } from '@agentsh/secure-sandbox/adapters/modal';
|
|
164
|
+
const sandbox = await secureSandbox(modal(modalSandbox), {
|
|
165
|
+
...modalDefaults(),
|
|
166
|
+
// your overrides
|
|
167
|
+
});
|
|
134
168
|
```
|
|
135
169
|
|
|
136
170
|
## Default Policy
|
package/dist/adapters/e2b.d.ts
CHANGED
package/dist/adapters/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { e2b } from './e2b.js';
|
|
|
3
3
|
export { daytona } from './daytona.js';
|
|
4
4
|
export { cloudflare } from './cloudflare.js';
|
|
5
5
|
export { blaxel } from './blaxel.js';
|
|
6
|
-
|
|
6
|
+
export { m as modal, a as modalDefaults, s as sprites, b as spritesDefaults } from '../index-TyzWAIUD.js';
|
|
7
|
+
import '../types-DFMGk2GV.js';
|
package/dist/adapters/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
modal,
|
|
3
|
+
modalDefaults,
|
|
4
|
+
sprites,
|
|
5
|
+
spritesDefaults
|
|
6
|
+
} from "../chunk-5IG6ABIZ.js";
|
|
2
7
|
import {
|
|
3
8
|
blaxel
|
|
4
9
|
} from "../chunk-UYEAO27E.js";
|
|
@@ -15,12 +20,17 @@ import "../chunk-OANLKSOD.js";
|
|
|
15
20
|
import {
|
|
16
21
|
vercel
|
|
17
22
|
} from "../chunk-JY5ERJTX.js";
|
|
23
|
+
import "../chunk-LNDICGZU.js";
|
|
18
24
|
import "../chunk-PZ5AY32C.js";
|
|
19
25
|
export {
|
|
20
26
|
blaxel,
|
|
21
27
|
cloudflare,
|
|
22
28
|
daytona,
|
|
23
29
|
e2b,
|
|
30
|
+
modal,
|
|
31
|
+
modalDefaults,
|
|
32
|
+
sprites,
|
|
33
|
+
spritesDefaults,
|
|
24
34
|
vercel
|
|
25
35
|
};
|
|
26
36
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PolicyDefinitionSchema,
|
|
3
|
+
agentDefault,
|
|
4
|
+
agentSandbox,
|
|
5
|
+
ciStrict,
|
|
6
|
+
devSafe,
|
|
7
|
+
merge,
|
|
8
|
+
mergePrepend,
|
|
9
|
+
validatePolicy
|
|
10
|
+
} from "./chunk-LNDICGZU.js";
|
|
11
|
+
import {
|
|
12
|
+
__export
|
|
13
|
+
} from "./chunk-PZ5AY32C.js";
|
|
14
|
+
|
|
15
|
+
// src/policies/index.ts
|
|
16
|
+
var policies_exports = {};
|
|
17
|
+
__export(policies_exports, {
|
|
18
|
+
PolicyDefinitionSchema: () => PolicyDefinitionSchema,
|
|
19
|
+
agentDefault: () => agentDefault,
|
|
20
|
+
agentSandbox: () => agentSandbox,
|
|
21
|
+
ciStrict: () => ciStrict,
|
|
22
|
+
devSafe: () => devSafe,
|
|
23
|
+
merge: () => merge,
|
|
24
|
+
mergePrepend: () => mergePrepend,
|
|
25
|
+
serializePolicy: () => serializePolicy,
|
|
26
|
+
systemPolicyYaml: () => systemPolicyYaml,
|
|
27
|
+
validatePolicy: () => validatePolicy
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// src/policies/serialize.ts
|
|
31
|
+
import yaml from "js-yaml";
|
|
32
|
+
function toArray(value) {
|
|
33
|
+
return Array.isArray(value) ? value : [value];
|
|
34
|
+
}
|
|
35
|
+
var FILE_DECISION_KEYS = [
|
|
36
|
+
"allow",
|
|
37
|
+
"deny",
|
|
38
|
+
"redirect",
|
|
39
|
+
"audit",
|
|
40
|
+
"softDelete"
|
|
41
|
+
];
|
|
42
|
+
var SIMPLE_DECISION_KEYS = ["allow", "deny", "redirect"];
|
|
43
|
+
function findDecision(rule, keys) {
|
|
44
|
+
for (const k of keys) {
|
|
45
|
+
if (k in rule) {
|
|
46
|
+
return { key: k, value: rule[k] };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`No decision key found in rule: ${JSON.stringify(rule)}`);
|
|
50
|
+
}
|
|
51
|
+
function yamlDecision(key) {
|
|
52
|
+
return key === "softDelete" ? "soft_delete" : key;
|
|
53
|
+
}
|
|
54
|
+
function serializeFileRules(rules) {
|
|
55
|
+
return rules.map((rule, i) => {
|
|
56
|
+
const r = rule;
|
|
57
|
+
const { key, value } = findDecision(r, FILE_DECISION_KEYS);
|
|
58
|
+
const paths = toArray(value);
|
|
59
|
+
const out = {
|
|
60
|
+
name: `file-rule-${i}`,
|
|
61
|
+
paths
|
|
62
|
+
};
|
|
63
|
+
if ("ops" in r && r.ops) {
|
|
64
|
+
out.operations = r.ops;
|
|
65
|
+
}
|
|
66
|
+
out.decision = yamlDecision(key);
|
|
67
|
+
if (key === "redirect" && "to" in r) {
|
|
68
|
+
out.redirect_to = r.to;
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function serializeNetworkRules(rules) {
|
|
74
|
+
return rules.map((rule, i) => {
|
|
75
|
+
const r = rule;
|
|
76
|
+
const { key, value } = findDecision(r, SIMPLE_DECISION_KEYS);
|
|
77
|
+
const domains = toArray(value);
|
|
78
|
+
const out = {
|
|
79
|
+
name: `network-rule-${i}`,
|
|
80
|
+
domains,
|
|
81
|
+
decision: key
|
|
82
|
+
};
|
|
83
|
+
if ("ports" in r && r.ports) {
|
|
84
|
+
out.ports = r.ports;
|
|
85
|
+
}
|
|
86
|
+
if (key === "redirect" && "to" in r) {
|
|
87
|
+
out.redirect_to = r.to;
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function serializeCommandRules(rules) {
|
|
93
|
+
return rules.map((rule, i) => {
|
|
94
|
+
const r = rule;
|
|
95
|
+
const { key, value } = findDecision(r, SIMPLE_DECISION_KEYS);
|
|
96
|
+
const commands = toArray(value);
|
|
97
|
+
const out = {
|
|
98
|
+
name: `command-rule-${i}`,
|
|
99
|
+
commands,
|
|
100
|
+
decision: key
|
|
101
|
+
};
|
|
102
|
+
if (key === "redirect" && "to" in r) {
|
|
103
|
+
const to = r.to;
|
|
104
|
+
if (typeof to === "string") {
|
|
105
|
+
out.redirect_to = to;
|
|
106
|
+
} else if (typeof to === "object" && to !== null) {
|
|
107
|
+
const target = to;
|
|
108
|
+
out.redirect_to = { command: target.cmd, args: target.args };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function serializeEnvRules(rules) {
|
|
115
|
+
return rules.map((rule, i) => {
|
|
116
|
+
const out = {
|
|
117
|
+
name: `env-rule-${i}`,
|
|
118
|
+
commands: rule.commands
|
|
119
|
+
};
|
|
120
|
+
if (rule.allow) {
|
|
121
|
+
out.allow = rule.allow;
|
|
122
|
+
}
|
|
123
|
+
if (rule.deny) {
|
|
124
|
+
out.deny = rule.deny;
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function serializeDnsRedirects(redirects) {
|
|
130
|
+
return redirects.map((r) => ({
|
|
131
|
+
match: r.match,
|
|
132
|
+
resolve_to: r.resolveTo
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
function serializeConnectRedirects(redirects) {
|
|
136
|
+
return redirects.map((r) => ({
|
|
137
|
+
match: r.match,
|
|
138
|
+
redirect_to: r.redirectTo
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
function serializePackageRules(rules) {
|
|
142
|
+
return rules.map((rule) => {
|
|
143
|
+
const match = {};
|
|
144
|
+
if (rule.match.packages) {
|
|
145
|
+
match.packages = rule.match.packages;
|
|
146
|
+
}
|
|
147
|
+
if (rule.match.namePatterns) {
|
|
148
|
+
match.name_patterns = rule.match.namePatterns;
|
|
149
|
+
}
|
|
150
|
+
if (rule.match.findingType) {
|
|
151
|
+
match.finding_type = rule.match.findingType;
|
|
152
|
+
}
|
|
153
|
+
if (rule.match.severity !== void 0) {
|
|
154
|
+
match.severity = rule.match.severity;
|
|
155
|
+
}
|
|
156
|
+
if (rule.match.reasons) {
|
|
157
|
+
match.reasons = rule.match.reasons;
|
|
158
|
+
}
|
|
159
|
+
if (rule.match.licenseSpdx) {
|
|
160
|
+
match.license_spdx = rule.match.licenseSpdx;
|
|
161
|
+
}
|
|
162
|
+
if (rule.match.ecosystem) {
|
|
163
|
+
match.ecosystem = rule.match.ecosystem;
|
|
164
|
+
}
|
|
165
|
+
if (rule.match.options) {
|
|
166
|
+
match.options = rule.match.options;
|
|
167
|
+
}
|
|
168
|
+
const out = {
|
|
169
|
+
match,
|
|
170
|
+
action: rule.action
|
|
171
|
+
};
|
|
172
|
+
if (rule.reason) {
|
|
173
|
+
out.reason = rule.reason;
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function serializePolicy(policy) {
|
|
179
|
+
const doc = {
|
|
180
|
+
version: 1,
|
|
181
|
+
name: "secure-sandbox-policy"
|
|
182
|
+
};
|
|
183
|
+
if (policy.file && policy.file.length > 0) {
|
|
184
|
+
doc.file_rules = serializeFileRules(policy.file);
|
|
185
|
+
}
|
|
186
|
+
if (policy.network && policy.network.length > 0) {
|
|
187
|
+
doc.network_rules = serializeNetworkRules(policy.network);
|
|
188
|
+
}
|
|
189
|
+
if (policy.commands && policy.commands.length > 0) {
|
|
190
|
+
doc.command_rules = serializeCommandRules(policy.commands);
|
|
191
|
+
}
|
|
192
|
+
if (policy.env && policy.env.length > 0) {
|
|
193
|
+
doc.env_rules = serializeEnvRules(policy.env);
|
|
194
|
+
}
|
|
195
|
+
if (policy.dns && policy.dns.length > 0) {
|
|
196
|
+
doc.dns_redirects = serializeDnsRedirects(policy.dns);
|
|
197
|
+
}
|
|
198
|
+
if (policy.connect && policy.connect.length > 0) {
|
|
199
|
+
doc.connect_redirects = serializeConnectRedirects(policy.connect);
|
|
200
|
+
}
|
|
201
|
+
if (policy.packageRules && policy.packageRules.length > 0) {
|
|
202
|
+
doc.package_rules = serializePackageRules(policy.packageRules);
|
|
203
|
+
}
|
|
204
|
+
return yaml.dump(doc, { lineWidth: -1 });
|
|
205
|
+
}
|
|
206
|
+
function systemPolicyYaml() {
|
|
207
|
+
const doc = {
|
|
208
|
+
version: 1,
|
|
209
|
+
name: "_system-protection",
|
|
210
|
+
file_rules: [
|
|
211
|
+
{
|
|
212
|
+
name: "_system-protect-config",
|
|
213
|
+
paths: ["/etc/agentsh/**"],
|
|
214
|
+
operations: ["write", "create", "delete"],
|
|
215
|
+
decision: "deny",
|
|
216
|
+
message: "Policy files are immutable during agent execution"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "_system-protect-binary",
|
|
220
|
+
paths: ["/usr/local/bin/agentsh*", "/usr/bin/agentsh*"],
|
|
221
|
+
operations: ["write", "create", "delete"],
|
|
222
|
+
decision: "deny",
|
|
223
|
+
message: "agentsh binary is immutable during agent execution"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "_system-protect-shim-files",
|
|
227
|
+
paths: ["/usr/bin/agentsh-shell-shim", "/bin/bash", "/bin/sh"],
|
|
228
|
+
operations: ["write", "create", "delete"],
|
|
229
|
+
decision: "deny",
|
|
230
|
+
message: "Shell and shim binaries are immutable during agent execution"
|
|
231
|
+
}
|
|
232
|
+
],
|
|
233
|
+
command_rules: [
|
|
234
|
+
{
|
|
235
|
+
name: "_system-protect-process",
|
|
236
|
+
commands: ["kill", "killall", "pkill"],
|
|
237
|
+
args_match: ["agentsh"],
|
|
238
|
+
decision: "deny",
|
|
239
|
+
message: "Cannot terminate agentsh processes"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
};
|
|
243
|
+
return yaml.dump(doc, { lineWidth: -1 });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export {
|
|
247
|
+
serializePolicy,
|
|
248
|
+
systemPolicyYaml,
|
|
249
|
+
policies_exports
|
|
250
|
+
};
|
|
251
|
+
//# sourceMappingURL=chunk-4FJHYLAB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/policies/index.ts","../src/policies/serialize.ts"],"sourcesContent":["export { PolicyDefinitionSchema, validatePolicy } from './schema.js';\nexport type { PolicyDefinition, FileRule, NetworkRule, CommandRule, EnvRule, DnsRedirect, ConnectRedirect } from './schema.js';\nexport { agentDefault, devSafe, ciStrict, agentSandbox } from './presets.js';\nexport { merge, mergePrepend } from './merge.js';\nexport { serializePolicy, systemPolicyYaml } from './serialize.js';\n","import yaml from 'js-yaml';\nimport type {\n PolicyDefinition,\n FileRule,\n NetworkRule,\n CommandRule,\n EnvRule,\n DnsRedirect,\n ConnectRedirect,\n PackageRule,\n} from './schema.js';\n\n// ─── Helpers ────────────────────────────────────────────────\n\n/** Normalize a string-or-array value to always be an array. */\nfunction toArray(value: string | string[]): string[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/** Detect the decision key from a rule object. */\ntype DecisionKey = 'allow' | 'deny' | 'redirect' | 'audit' | 'softDelete';\n\nconst FILE_DECISION_KEYS: DecisionKey[] = [\n 'allow',\n 'deny',\n 'redirect',\n 'audit',\n 'softDelete',\n];\n\nconst SIMPLE_DECISION_KEYS: DecisionKey[] = ['allow', 'deny', 'redirect'];\n\nfunction findDecision(\n rule: Record<string, unknown>,\n keys: DecisionKey[],\n): { key: DecisionKey; value: unknown } {\n for (const k of keys) {\n if (k in rule) {\n return { key: k, value: rule[k] };\n }\n }\n throw new Error(`No decision key found in rule: ${JSON.stringify(rule)}`);\n}\n\n/** Map softDelete → soft_delete for YAML output. */\nfunction yamlDecision(key: DecisionKey): string {\n return key === 'softDelete' ? 'soft_delete' : key;\n}\n\n// ─── File rules ─────────────────────────────────────────────\n\nfunction serializeFileRules(rules: FileRule[]): Record<string, unknown>[] {\n return rules.map((rule, i) => {\n const r = rule as Record<string, unknown>;\n const { key, value } = findDecision(r, FILE_DECISION_KEYS);\n const paths = toArray(value as string | string[]);\n\n const out: Record<string, unknown> = {\n name: `file-rule-${i}`,\n paths,\n };\n\n if ('ops' in r && r.ops) {\n out.operations = r.ops;\n }\n\n out.decision = yamlDecision(key);\n\n if (key === 'redirect' && 'to' in r) {\n out.redirect_to = r.to;\n }\n\n return out;\n });\n}\n\n// ─── Network rules ──────────────────────────────────────────\n\nfunction serializeNetworkRules(\n rules: NetworkRule[],\n): Record<string, unknown>[] {\n return rules.map((rule, i) => {\n const r = rule as Record<string, unknown>;\n const { key, value } = findDecision(r, SIMPLE_DECISION_KEYS);\n const domains = toArray(value as string | string[]);\n\n const out: Record<string, unknown> = {\n name: `network-rule-${i}`,\n domains,\n decision: key,\n };\n\n if ('ports' in r && r.ports) {\n out.ports = r.ports;\n }\n\n if (key === 'redirect' && 'to' in r) {\n out.redirect_to = r.to;\n }\n\n return out;\n });\n}\n\n// ─── Command rules ──────────────────────────────────────────\n\nfunction serializeCommandRules(\n rules: CommandRule[],\n): Record<string, unknown>[] {\n return rules.map((rule, i) => {\n const r = rule as Record<string, unknown>;\n const { key, value } = findDecision(r, SIMPLE_DECISION_KEYS);\n const commands = toArray(value as string | string[]);\n\n const out: Record<string, unknown> = {\n name: `command-rule-${i}`,\n commands,\n decision: key,\n };\n\n if (key === 'redirect' && 'to' in r) {\n const to = r.to;\n if (typeof to === 'string') {\n out.redirect_to = to;\n } else if (typeof to === 'object' && to !== null) {\n const target = to as { cmd: string; args: string[] };\n out.redirect_to = { command: target.cmd, args: target.args };\n }\n }\n\n return out;\n });\n}\n\n// ─── Env rules ──────────────────────────────────────────────\n\nfunction serializeEnvRules(rules: EnvRule[]): Record<string, unknown>[] {\n return rules.map((rule, i) => {\n const out: Record<string, unknown> = {\n name: `env-rule-${i}`,\n commands: rule.commands,\n };\n if (rule.allow) {\n out.allow = rule.allow;\n }\n if (rule.deny) {\n out.deny = rule.deny;\n }\n return out;\n });\n}\n\n// ─── DNS redirects ──────────────────────────────────────────\n\nfunction serializeDnsRedirects(\n redirects: DnsRedirect[],\n): Record<string, unknown>[] {\n return redirects.map((r) => ({\n match: r.match,\n resolve_to: r.resolveTo,\n }));\n}\n\n// ─── Connect redirects ──────────────────────────────────────\n\nfunction serializeConnectRedirects(\n redirects: ConnectRedirect[],\n): Record<string, unknown>[] {\n return redirects.map((r) => ({\n match: r.match,\n redirect_to: r.redirectTo,\n }));\n}\n\n// ─── Package rules ───────────────────────────────────────────\n\nfunction serializePackageRules(\n rules: PackageRule[],\n): Record<string, unknown>[] {\n return rules.map((rule) => {\n const match: Record<string, unknown> = {};\n\n if (rule.match.packages) {\n match.packages = rule.match.packages;\n }\n if (rule.match.namePatterns) {\n match.name_patterns = rule.match.namePatterns;\n }\n if (rule.match.findingType) {\n match.finding_type = rule.match.findingType;\n }\n if (rule.match.severity !== undefined) {\n match.severity = rule.match.severity;\n }\n if (rule.match.reasons) {\n match.reasons = rule.match.reasons;\n }\n if (rule.match.licenseSpdx) {\n match.license_spdx = rule.match.licenseSpdx;\n }\n if (rule.match.ecosystem) {\n match.ecosystem = rule.match.ecosystem;\n }\n if (rule.match.options) {\n match.options = rule.match.options;\n }\n\n const out: Record<string, unknown> = {\n match,\n action: rule.action,\n };\n\n if (rule.reason) {\n out.reason = rule.reason;\n }\n\n return out;\n });\n}\n\n// ─── Public API ─────────────────────────────────────────────\n\n/**\n * Converts a PolicyDefinition to agentsh YAML format.\n *\n * Omits empty categories from output.\n */\nexport function serializePolicy(policy: PolicyDefinition): string {\n const doc: Record<string, unknown> = {\n version: 1,\n name: 'secure-sandbox-policy',\n };\n\n if (policy.file && policy.file.length > 0) {\n doc.file_rules = serializeFileRules(policy.file);\n }\n\n if (policy.network && policy.network.length > 0) {\n doc.network_rules = serializeNetworkRules(policy.network);\n }\n\n if (policy.commands && policy.commands.length > 0) {\n doc.command_rules = serializeCommandRules(policy.commands);\n }\n\n if (policy.env && policy.env.length > 0) {\n doc.env_rules = serializeEnvRules(policy.env);\n }\n\n if (policy.dns && policy.dns.length > 0) {\n doc.dns_redirects = serializeDnsRedirects(policy.dns);\n }\n\n if (policy.connect && policy.connect.length > 0) {\n doc.connect_redirects = serializeConnectRedirects(policy.connect);\n }\n\n if (policy.packageRules && policy.packageRules.length > 0) {\n doc.package_rules = serializePackageRules(policy.packageRules);\n }\n\n return yaml.dump(doc, { lineWidth: -1 });\n}\n\n/**\n * Returns the fixed system policy YAML from the spec (Section 9.4).\n *\n * This static set of rules protects agentsh's own configuration, binaries,\n * and processes from tampering by the agent. These rules are written to a\n * separate system policy directory evaluated before user policy.\n */\nexport function systemPolicyYaml(): string {\n const doc = {\n version: 1,\n name: '_system-protection',\n file_rules: [\n {\n name: '_system-protect-config',\n paths: ['/etc/agentsh/**'],\n operations: ['write', 'create', 'delete'],\n decision: 'deny',\n message: 'Policy files are immutable during agent execution',\n },\n {\n name: '_system-protect-binary',\n paths: ['/usr/local/bin/agentsh*', '/usr/bin/agentsh*'],\n operations: ['write', 'create', 'delete'],\n decision: 'deny',\n message: 'agentsh binary is immutable during agent execution',\n },\n {\n name: '_system-protect-shim-files',\n paths: ['/usr/bin/agentsh-shell-shim', '/bin/bash', '/bin/sh'],\n operations: ['write', 'create', 'delete'],\n decision: 'deny',\n message: 'Shell and shim binaries are immutable during agent execution',\n },\n ],\n command_rules: [\n {\n name: '_system-protect-process',\n commands: ['kill', 'killall', 'pkill'],\n args_match: ['agentsh'],\n decision: 'deny',\n message: 'Cannot terminate agentsh processes',\n },\n ],\n };\n\n return yaml.dump(doc, { lineWidth: -1 });\n}\n"],"mappings":"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","names":[]}
|