@@ -51,10 +51,10 @@ permissions, and runtime resource limits.
5151
5252| Layer | Owns |
5353| ---| ---|
54- | ** ToolScriptSafetyFilter ** | Pre-execution static policy decision and redaction |
55- | ** SafetyCheckedExecutor ** | Applies the same decision before delegated execution |
54+ | ** SafetyWrappedCallable / SafetyCheckedExecutor ** | Current enforcement path: scan, await audit, then delegate only when allowed |
55+ | ** ToolScriptSafetyFilter ** | Normalizes and records decisions for wrappers and a future SDK terminal hook |
5656| ** Wrapper / Sandbox / Runtime** | Runtime isolation: CPU, memory, PID, FS, network hard limits |
57- | ** Audit / Telemetry** | Evidence that a decision occurred; ** not ** enforcement |
57+ | ** Audit / Telemetry** | Decision evidence; required audit persistence is part of the wrapper's fail-closed gate |
5858
5959## Quick start
6060
@@ -119,6 +119,13 @@ safe_run = SafetyWrappedCallable(
119119safe_run(" ls -la" ) # raises BlockedExecutionError if policy denies
120120```
121121
122+ Call `` await safe_run.call_async(...) `` instead when the delegate or caller
123+ already runs inside an event loop; this preserves the required-audit
124+ before-delegate guarantee.
125+ For a tool-style callable, set `` argv_kw `` , `` cwd_kw `` , `` env_kw `` ,
126+ `` metadata_kw `` , and `` output_bytes_kw `` to the corresponding argument
127+ names so the normalized request contains every available execution field.
128+
122129### Wrapping a code executor
123130
124131``` python
@@ -148,6 +155,7 @@ change between releases so policy overrides remain stable.
148155| ` FILE004_DOTENV_READ ` | file | deny | Reads of ` .env ` files |
149156| ` NET001_DOMAIN_NOT_ALLOWED ` | network | deny | Requests/curl/wget to non-allowlisted hosts |
150157| ` NET002_DYNAMIC_TARGET ` | network | review | Computed network destination |
158+ | ` NET003_IP_LITERAL ` | network | deny | IP literal when ` deny_ip_literals ` is enabled |
151159| ` PROC001_PROCESS_EXEC ` | process | review | Subprocess or command not on allow list |
152160| ` PROC002_SHELL_INJECTION ` | process | deny | ` shell=True ` with shell grammar |
153161| ` PROC003_SHELL_OPERATOR ` | process | review | ` ; ` , ` && ` , ` \| ` , ` & ` , command substitution |
@@ -156,13 +164,13 @@ change between releases so policy overrides remain stable.
156164| ` RES001_UNBOUNDED_LOOP ` | resource | deny | ` while True ` without break |
157165| ` RES002_FORK_BOMB ` | resource | deny | Classic ` :(){ :\|:& };: ` pattern |
158166| ` RES003_LONG_SLEEP ` | resource | deny | Sleeps exceeding policy limit |
159- | ` RES004_CONCURRENCY ` | resource | deny | Fan-out exceeding ` max_parallel_tasks ` |
167+ | ` RES004_CONCURRENCY ` | resource | deny | Fan-out exceeding ` max_parallel_tasks ` or ` max_processes ` |
160168| ` RES005_LARGE_WRITE ` | resource | deny | Writes exceeding ` max_file_write_bytes ` |
161169| ` SECRET001_LOG_SINK ` | secret | deny | Tainted value into print/log |
162170| ` SECRET002_FILE_SINK ` | secret | deny | Tainted value into file write |
163171| ` SECRET003_NETWORK_SINK ` | secret | deny | Tainted value into network payload |
164172| ` PARSE001_UNCERTAIN ` | analysis | review | Syntax error or unknown construct |
165- | ` OBF001_DYNAMIC_EXEC ` | analysis | review | ` eval ` , ` exec ` , ` importlib ` , reflective calls |
173+ | ` OBF001_DYNAMIC_EXEC ` | analysis | review | ` eval ` , ` exec ` , indirect Bash execution, interpreter payloads |
166174| ` SAFE000 ` | safe | allow | No findings |
167175| ` GUARD001_INTERNAL_ERROR ` | analysis | deny | Internal guard failure (fail closed) |
168176
@@ -272,19 +280,20 @@ never emitted as span attributes or metric labels.
272280| 0 | Final decision was `` allow `` |
273281| 2 | Final decision was `` deny `` |
274282| 3 | Final decision was `` needs_human_review `` |
275- | 4 | Invalid input / policy error |
283+ | 4 | Invalid input, policy, or required-audit error |
276284
277285## Integration with the SDK
278286
279- The standalone package is duck-typed to `` BaseFilter `` so it can be
280- composed with the framework's filter runner once the terminal-phase
281- ordering seam is exposed. Until then, wrap the executor or callable
282- explicitly via `` tool.wrapper `` .
287+ The current SDK does not expose a terminal filter phase after
288+ `` ToolCallbackFilter `` . A configured `` filters= `` instance can therefore
289+ scan arguments that a later callback changes; it is not a secure enforcement
290+ point. Use `` SafetyWrappedCallable `` or `` SafetyCheckedExecutor `` today:
291+ both scan, await the audit event, and only then invoke their delegate.
283292
284- The :class: ` ToolScriptSafetyFilter ` carries a `` terminal_before_handler ``
285- attribute (always `` True `` ). When the framework adopts the terminal
286- phase marker, the filter will automatically run after
287- `` ToolCallbackFilter `` and prevent TOCTOU mutations .
293+ `` ToolScriptSafetyFilter `` provides matching `` _before `` / `` _after `` hooks
294+ and a `` terminal_before_handler `` marker for a future SDK terminal phase.
295+ That marker is metadata only until the framework implements ordering after
296+ all argument-mutating callbacks. The wrapper remains mandatory until then .
288297
289298## Custom rules
290299
@@ -324,6 +333,10 @@ Rules must be pure: no file I/O, network access, or process creation.
324333* ** Runtime downloads.** A script that downloads and executes a payload
325334 in two stages defeats static analysis. Network egress policy and
326335 sandboxing are mandatory.
336+ * ** Runtime limits.** The wrapper validates the declared timeout and caps
337+ returned output, but it cannot impose CPU, memory, PID, file-size, or
338+ network limits on an arbitrary executor. Configure those in the sandbox
339+ or CodeExecutor runtime as well.
327340* ** Shell grammar holes.** The bash lexer-lite is conservative: any
328341 unbalanced quote or unsupported substitution becomes
329342 `` PARSE001_UNCERTAIN `` .
0 commit comments