Security review fixes (secure-exec)#76
Closed
NathanFlurry wants to merge 10 commits into
Closed
Conversation
…-pkgs rename + publish @agent-os-pkgs/* software packages
…limit
Guest inline JS (e.g. while(true){}) ran unbounded on the shared, slot-bounded
V8 runtime because register_v8_session hardcoded cpu_time_limit_ms: 0 at every
CreateSession (0 normalizes to None, so no TimeoutGuard was armed), letting a
guest pin a CPU core and starve peers.
Resolve a default CPU-time budget (V8_DEFAULT_CPU_TIME_LIMIT_MS = 30000ms,
overridable/disable-able via AGENT_OS_V8_CPU_TIME_LIMIT_MS, mirroring the heap
limit env) and thread it through register_v8_session into the CreateSession
frame so the existing watchdog pipeline arms a TimeoutGuard.
Adds bounded safeguard test javascript_infinite_loop_is_terminated_by_cpu_watchdog
(runs by default, fenced behind a wall-clock watchdog) asserting a runaway loop
is terminated with a nonzero exit.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The operator-configured AGENT_OS_WASM_MAX_STACK_BYTES budget was plumbed into the WASM runner env but never read by the engine, so the cap was silently ignored (dead): runaway recursion only ever tripped V8's generic default native stack guard with no attribution to the configured limit. The engine now parses the stack byte budget (rejecting malformed values like fuel/memory) and the WASM runner installs a stack-exhaustion guard around wasi.start: when a stack byte limit is configured and the guest exhausts the stack, the runner terminates nonzero and attributes the failure to the configured budget instead of leaking the engine's generic default-guard message. Adds the bounded SAFEGUARD test wasm_deep_recursion_respects_configured_stack_byte_limit (x-ref FAILURES.md#F-002), which now passes fast. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…minate isolate instead of fatal-aborting the shared process Hitting the operator-configured AGENT_OS_V8_HEAP_LIMIT_MB cap previously triggered V8's default fatal-OOM abort (SIGTRAP). Because the V8 runtime is process-global, one tenant's heap bomb crashed the whole process and every concurrent tenant (guest-triggerable cross-tenant host DoS). create_isolate now installs a near-heap-limit callback (via the new install_heap_limit_guard helper) on every isolate created with a heap cap, covering both the fresh-isolate and snapshot-restore paths. On approaching the cap the callback terminates the offending isolate's execution and returns a small headroom bump so V8 can propagate the uncatchable termination exception cleanly instead of aborting. The guest run ends with a nonzero exit. Adds the bounded SAFEGUARD test javascript_heap_allocation_bomb_is_capped_by_oom_guard to the javascript_v8 suite (runs by default, fenced behind a worker-thread wall-clock watchdog); the unbounded RAM-saturation variant stays env-gated. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… no fuel env is set
…100.64/10 as restricted egress in SSRF classifier
…) in SSRF classifier so ::169.254.169.254 is blocked
…ges in SSRF egress classifier
…apter so guest fetch cannot ride embedding page ambient cookies/HTTP-auth
…er so host-keyed egress denies (e.g. 169.254.169.254 metadata) are enforced
Member
Author
|
Superseded by #79 — re-applied cleanly on main; this branch was cut from the abandoned split/runtime-preview base (2247-file diff). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security review remediations for the secure-exec runtime. Each fix is its own jj/git revision and ships with a verifying test (bounded SAFEGUARD variants run by default; unbounded resource-saturation variants stay env-gated per the test-suite convention).
register_v8_sessionhardcodedcpu_time_limit_ms: 0, whichnon_zero_option/normalize_cpu_time_limit_msturned intoNone, so theTimeoutGuardwatchdog was never wired andwhile(true){}could pin a core forever on the shared V8 runtime. AddedV8_CPU_TIME_LIMIT_MS_ENV/V8_DEFAULT_CPU_TIME_LIMIT_MS(30s default, env-overridable, 0 disables) and ajavascript_cpu_time_limit_msresolver; passed the resolved value intoCreateSession. Verifying test:javascript_infinite_loop_is_terminated_by_cpu_watchdog(injavascript_v8_suite).AGENT_OS_WASM_MAX_STACK_BYTESwas plumbed into the runner env but never read. Addedwasm_stack_limit_bytes()(typedInvalidLimiton malformed budget) and a stack-exhaustion guard in the embedded WASM runner that terminates nonzero and attributes the termination to the configured cap. Verifying test:wasm_deep_recursion_respects_configured_stack_byte_limit(inwasm_suite).AGENT_OS_V8_HEAP_LIMIT_MBtriggered V8's default fatal-OOM abort (SIGTRAP), crashing every concurrent tenant on the process-global runtime. Addedinstall_heap_limit_guard(near-heap-limit callback that terminates the isolate cleanly with headroom) and wired it into bothcreate_isolateand the snapshot-restore path. Verifying test:javascript_heap_allocation_bomb_is_capped_by_oom_guard(injavascript_v8_suite).resolve_wasm_execution_timeoutreturnedNoneunlessAGENT_OS_WASM_MAX_FUELwas set, soWasmExecution::wait()never terminated a never-returning guest. AddedDEFAULT_WASM_EXECUTION_TIMEOUT_MS(30s) fallback so a timeout is always returned (exit 124). Verifying test:wasm::tests::wasm_execution_timeout_defaults_to_bounded_value_without_fuel_env.restricted_non_loopback_ip_rangereturnedNonefor0.0.0.0/::(routes to loopback on Linux, bypassing the port-ownership gate) and for100.64.0.0/10. Added unspecified and CGNAT arms for both IPv4 and IPv6. Verifying tests:ssrf_egress_classifier_tests(classifier labeling +filter_dns_safe_ip_addrsEACCES).::ffff:a.b.c.d) was canonicalized, so::169.254.169.254bypassed the169.254.0.0/16metadata block. Addedipv4_compatible_embeddedand recurse into the IPv4 classifier for IPv4-compatible addresses. Verifying test:classifier_denies_ipv6_spelled_metadata_addresses.224.0.0.0/4(multicast) and240.0.0.0/4(reserved, incl.255.255.255.255broadcast) arms; IPv6 spellings covered via the recursion. Verifying test:classifier_denies_reserved_and_multicast_targets.fetchwith default same-origin credentials, enabling credential exfil / CSRF via_networkFetchRaw. Pinnedcredentials: "omit"in both thefetch()andhttpRequest()paths ofcreateBrowserNetworkAdapter(). Verifying test:packages/browser/tests/runtime-driver/network-adapter.test.ts.wrapNetworkAdapteronly forwarded{url}to the policy callback, so a host-keyed deny (e.g.req.host === "169.254.169.254") never fired. Added acheckUrlhelper that parses hostname/port and passes{url, host, port}. Verifying test:packages/browser/tests/runtime-driver/network-permission-host.test.ts.Verification: full
cargo build --workspacegreen;javascript_v8_suite,wasm_suite,secure-exec-execution --lib(91 passed),secure-exec-sidecar --lib(61 passed) all green; browserruntime-drivervitest (37 passed) andtsc --noEmitclean.CARGO_TARGET_DIR=/home/nathan/secure-exec/target.Companion PR: the agent-os repo carries the matching consumer-side changes (agent-os → main).