You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Binary identity enforcement via `/proc/<pid>/exe` is nearly
impossible to make reliable. There is a reason that security
systems like SELinux assign security "domains" to a process
that are preserved across `execve()`.
One of the biggest is `LD_PRELOAD` and `LD_LIBRARY_PATH`:
the glibc dynamic linker will easily load arbitrary shared
libraries into the address space even for "fixed purpose"
binaries. And most uses of agents will have *some* writable
directory.
But a much bigger problem is that many binaries (including
`claude`) effectively include the ability to execute
arbitrary code inside that process - they are interpreters.
For example, `claude` is a Bun single-file executable.
A sandbox policy allowlisting only `claude` to reach
api.anthropic.com is bypassed with:
printf "process.stderr.write('INJECTED\n'" > /tmp/e.js
BUN_OPTIONS="--preload /tmp/e.js" claude --version
# => INJECTED
# => 2.1.146 (Claude Code)
Arbitrary JS runs inside the claude process before the app
starts — with full access to credentials and sockets — while
`/proc/<pid>/exe` still shows `claude`. The binary check
passes; the exfiltration is permitted.
It would absolutely be possible to try to craft an execution
environment for an agent that closed some of these loopholes,
but my opinion is that anyone doing that is already in
the business of minimizing what code goes into the container,
and at which point they are actually doing something better:
restricting what binaries *are present at all*.
That said, to do anything truly strong here gets into
things like [trusted_for](https://lwn.net/Articles/832959/)
etc.
Policy evaluation is now just enforced based on code running
within the sandbox - the same way that many other network
enforcement tools work. We do not claim that we can reliably
drill down into the binary-name level.
Signed-off-by: Colin Walters <walters@verbum.org>
Copy file name to clipboardExpand all lines: .agents/skills/generate-sandbox-policy/SKILL.md
+6-30Lines changed: 6 additions & 30 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -78,9 +78,6 @@ Regardless of tier, extract (or infer) these from the user's description:
78
78
|**Methods**| Specific HTTP methods to allow | Only for custom/fine-grained |
79
79
|**Paths**| Specific URL paths or patterns | Only for custom/fine-grained |
80
80
|**Enforcement**|`enforce` or `audit`? Default to `enforce`. | No — has a default |
81
-
|**Binary**| Which binary/process should have access | Yes — ask if not stated |
82
-
83
-
If the host and access level are clear but binaries are not specified, ask the user which binary or process will be making the requests. Suggest common defaults like `/usr/bin/curl`, `/usr/local/bin/claude`, etc.
84
81
85
82
## Step 2: Refine Scope (Clarification Loop)
86
83
@@ -92,7 +89,6 @@ Always ask about these if the user hasn't already specified them:
92
89
93
90
| Missing info | Question to ask |
94
91
|-------------|----------------|
95
-
|**Binary** not specified | "Which binary or process will make these requests? (e.g., `/usr/bin/curl`, `/usr/local/bin/claude`)" |
96
92
|**Port** not specified | "Which port does this API use? (443 for HTTPS is typical)" |
97
93
|**Enforcement** not stated | "Should policy violations be blocked (`enforce`) or just logged for review (`audit`)? I'll default to `enforce` if you're not sure." |
98
94
@@ -105,7 +101,6 @@ Ask these when the user's intent is broad and more specificity is possible:
105
101
| "Full access" / "allow everything" | "Do you actually need DELETE access, or would read-write (everything except DELETE) be enough?" |
106
102
| "Allow access to api.example.com" (no method/path detail) | "Do you know which specific API paths or operations you need? If so, I can lock the policy down to just those. Otherwise I'll use a broad preset." |
107
103
| L4-only / "just pass it through" | "L4-only means the proxy won't inspect HTTP traffic at all — any method and path will be allowed. Are you sure you don't want at least read-only or read-write restriction?" |
108
-
| Wildcard binary (`/usr/bin/*`) | "A wildcard binary pattern means any binary in that directory can use this policy. Can you narrow it to specific binaries?" |
109
104
| Multiple hosts in one policy | "Do all of these hosts need the same access level? If some need tighter restrictions, I can split them into separate policies." |
110
105
|`access: full` with `enforcement: audit`| "Full access in audit mode means nothing is actually restricted — all traffic flows through and violations are only logged. Is that intentional, or did you want to enforce restrictions?" |
111
106
|`**` path glob on all rules | "Using `**` on all paths allows any URL path. Do you know the specific API path prefixes you need (e.g., `/api/v1/`)?" |
@@ -262,10 +257,10 @@ network_policies:
262
257
# Optional: allow private IP destinations (CIDR or exact IP)
263
258
# allowed_ips:
264
259
# - "10.0.5.0/24"
265
-
binaries:
266
-
- { path: <binary_path> }
267
260
```
268
261
262
+
> **Note:** Binary allowlisting has been removed. The `binaries:` field is accepted in policy YAML for backward compatibility but is silently ignored — do not generate it in new policies.
263
+
269
264
### Deny Rules
270
265
271
266
Use `deny_rules` to block specific dangerous operations while allowing broad access. Deny rules are evaluated after allow rules and take precedence. This is the inverse of the `rules` approach — instead of enumerating every allowed operation, you grant broad access and block a small set of dangerous ones.
@@ -287,8 +282,6 @@ github_api:
287
282
path: "/repos/*/branches/*/protection"
288
283
- method: "*"
289
284
path: "/repos/*/rulesets"
290
-
binaries:
291
-
- { path: /usr/bin/curl }
292
285
```
293
286
294
287
Deny rules support the same matching capabilities as allow rules: `method`, `path`, `command` (SQL), and `query` parameter matchers. When generating policies, prefer deny rules when the user needs broad access with a small set of blocked operations — it produces a shorter, more maintainable policy than enumerating 60+ allow rules.
@@ -311,8 +304,6 @@ internal_api:
311
304
port: 8080
312
305
allowed_ips:
313
306
- "10.0.5.0/24"
314
-
binaries:
315
-
- { path: /usr/bin/curl }
316
307
```
317
308
318
309
### Policy Key Naming
@@ -345,9 +336,8 @@ Before presenting the policy to the user, verify correctness **and** flag breadt
345
336
346
337
### Structural Checks
347
338
348
-
- [ ] Every policy has `name`, `endpoints`, and `binaries`
339
+
- [ ] Every policy has `name`and `endpoints`
349
340
- [ ] Every endpoint has `host` and `port`
350
-
- [ ] Every binary has `path`
351
341
- [ ] Policy key matches `name` field
352
342
353
343
### Breadth Warnings
@@ -360,7 +350,6 @@ Evaluate the generated policy for overly broad access and **include warnings in
360
350
| **`access: full`** | "This policy allows all HTTP methods (including DELETE) on all paths. If you don't need DELETE, `read-write` is safer. If you only need to read, `read-only` is the most restrictive option." |
361
351
| **`access: full` + `enforcement: audit`** | "Full access in audit mode provides no actual restriction — all traffic flows through. This is effectively a monitoring-only policy." |
362
352
| **`access: read-write`** when user hasn't confirmed write need | "This policy allows POST, PUT, and PATCH on all paths. If you only need to read data, `read-only` is more restrictive." |
363
-
| **Wildcard binary** (`*` or `**` in binary path) | "This policy allows any binary matching the glob pattern. A compromised or unexpected binary in that directory could use this policy. Consider listing specific binary paths." |
364
353
| **`**` path glob** on all explicit rules | "All rules use `**` path patterns, which match any URL path. This is equivalent to a preset — consider using `access: read-only` (or similar) for clarity, or narrowing paths if you know the API structure." |
365
354
| **Multiple broad endpoints** in one policy | "This policy grants the same broad access to N different hosts. If any of these hosts needs tighter restrictions later, you'll need to split the policy." |
366
355
| **Hostless `allowed_ips`** (no `host` field) | "This endpoint has no `host` — any domain resolving to the allowed IP range on this port will be permitted. Consider adding a `host` field to restrict which domains can use this allowlist." |
@@ -396,12 +385,12 @@ The policy needs to go somewhere. Determine which mode applies:
396
385
- Whether the file uses compact (`{ host: ..., port: ... }`) or expanded YAML style
397
386
398
387
2.**Check for conflicts**:
399
-
- Does a policy with the same key already exist? If so, ask the user whether to **replace** it, **merge** new endpoints/binaries into it, or use a different key.
388
+
- Does a policy with the same key already exist? If so, ask the user whether to **replace** it, **merge** new endpoints into it, or use a different key.
400
389
- Does an existing policy already cover the same host:port? Warn the user — overlapping endpoint coverage across policies causes OPA evaluation errors (complete rule conflict).
401
390
402
391
3.**Apply the change**:
403
392
-**Adding a new policy**: Insert the new policy block under `network_policies`, maintaining the file's existing indentation and style.
404
-
-**Modifying an existing policy**: Edit the specific policy in place — add/remove endpoints, change access presets, update rules, add binaries, etc.
393
+
-**Modifying an existing policy**: Edit the specific policy in place — add/remove endpoints, change access presets, update rules, etc.
405
394
-**Removing a policy**: Delete the policy block if the user asks.
406
395
407
396
4.**Preserve everything else**: Do not modify `filesystem_policy`, `landlock`, `process`, or other policies unless the user explicitly asks.
@@ -458,7 +447,7 @@ Show the generated policy YAML with:
458
447
459
448
After presenting or applying the policy, ask if the user wants to:
460
449
- Tighten or loosen any rules
461
-
- Add more endpoints or binaries
450
+
- Add more endpoints
462
451
- Switch between enforce/audit mode
463
452
- Move from a preset to explicit rules (or vice versa)
464
453
- Apply the policy to a file (if presented only)
@@ -473,8 +462,6 @@ my_api:
473
462
name: my_api
474
463
endpoints:
475
464
- { host: api.example.com, port: 443 }
476
-
binaries:
477
-
- { path: /usr/bin/curl }
478
465
```
479
466
480
467
### HTTPS API with Read-Only Preset
@@ -489,8 +476,6 @@ my_api_readonly:
489
476
tls: terminate
490
477
enforcement: enforce
491
478
access: read-only
492
-
binaries:
493
-
- { path: /usr/bin/curl }
494
479
```
495
480
496
481
### HTTPS API with Explicit Rules
@@ -511,9 +496,6 @@ my_api_custom:
511
496
- allow:
512
497
method: POST
513
498
path: "/api/v1/data"
514
-
binaries:
515
-
- { path: /usr/bin/curl }
516
-
- { path: /usr/local/bin/myapp }
517
499
```
518
500
519
501
### HTTP (non-TLS) Internal API
@@ -533,8 +515,6 @@ internal_svc:
533
515
- allow:
534
516
method: POST
535
517
path: "/api/v1/jobs"
536
-
binaries:
537
-
- { path: /usr/bin/curl }
538
518
```
539
519
540
520
### Private IP Access (Host + Allowlist)
@@ -547,8 +527,6 @@ internal_db:
547
527
port: 5432
548
528
allowed_ips:
549
529
- "10.0.5.0/24"
550
-
binaries:
551
-
- { path: /usr/bin/curl }
552
530
```
553
531
554
532
### Private IP Access (Hostless — Any Domain in Range)
0 commit comments