forward_handler_preserves_ssrf_response_and_denial_stage's ssrf_denied
check was the only assertion in the test without a failure message,
unlike its sibling assertions. This test has failed intermittently in
CI with no way to see what response was actually returned, since the
message is where that gets surfaced.
Signed-off-by: politerealism <burdcat17@gmail.com>
* feat(mxc): ETW->OCSF audit consumer + Windows OCSF JSONL parity (cp6 P1)
Add a Windows MXC ETW->OCSF audit trail in openshell-driver-mxc: a real-time
Sandboxing-provider ETW consumer that decodes events (TDH), attributes each to
an OpenShell sandbox_id, and maps them to OCSF (lifecycle 6002, config 5019,
process 1007, finding 2004).
cp6 Phase 1 - durable OCSF JSONL audit-file parity with Linux:
- openshell-ocsf: add emit_ocsf_event_routed (populates the event-bridge
thread-local AND stamps sandbox_id+message in one dispatch) plus public
set/clear_current_event; OS-aware device (Device::windows/for_current_os) so
device.os.name reflects the host instead of a hardcoded Linux stub.
- etw_consumer: emit via the routed emit (previously fired a bare info! that
never populated the bridge, so the structured event was dropped).
- openshell-server: install OcsfJsonlLayer over a synchronous daily-rotated
appender (durable under force-kill), gated by OPENSHELL_OCSF_JSON, path via
%PROGRAMDATA%\OpenShell\logs (override OPENSHELL_OCSF_LOG_DIR).
- device.hostname now resolves to the real gateway machine name.
Box-proven on 7F203-MXC-001: JSONL lines == shorthand OCSF rows, all valid
OCSF JSON, per-sandbox attribution intact, disabled state writes nothing.
Signed-off-by: Akber Raza <akberr@nvidia.com>
* feat(mxc): map remaining Sandboxing ETW events to OCSF
Close the last three ETW->OCSF gaps so the audit trail covers the full
set of events the Sandboxing provider emits (12/12):
- ProcessLaunched -> Process Activity [1007] "Launch" (confirmed start;
carries the real processId/threadId, the twin of CreateProcessInSandbox
which only has the request + command line).
- SandboxProxyConfigured -> Device Config State Change [5019] (the one
network-plane setup event; surfaces proxyPort, "no proxy" when 0).
- SandboxConsoleReferencePlumbed -> Device Config State Change [5019]
(console-handle plumbing).
map_config_state now handles the full config/hardening/setup family and
carries proxyPort/hasConsoleReference/creationFlags as unmapped fields.
Verified on 7F203-MXC-001: 11/12 event types emit OCSF without a proxy
(SandboxProxyConfigured requires proxy config to fire).
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(mxc): seed ETW attribution under registry lock + Device tests
Address CodeRabbit review on !31:
- Prevent stale ETW attribution on a delete/launch race: register the
wxc-exec pid while holding the registry lock, and bail if the sandbox
entry is already gone. Previously the attribution key could be seeded
after `delete` had removed the sandbox, leaving a stale key that could
misroute later Sandboxing ETW events to a dead sandbox_id. Lock order
(registry -> attribution) matches the delete path, so no deadlock.
- Add unit tests for the new Device::windows and Device::for_current_os
constructors to harden Windows/Linux OCSF device parity.
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(mxc-etw): buffer+replay racing events and harden attribution keys
Addresses two ETW->OCSF attribution review items (Shailendra #1, #2).
#2 early-event loss: ETW delivers the sandbox create/config burst the instant wxc-exec starts, which can beat the driver's register_launch (now under the registry lock post-Ready). process_event previously dropped anything unresolved, losing the racing burst. Add a bounded, time-bounded pending buffer (PENDING_MAX=4096, PENDING_TTL=5s): unresolved events are held and replayed once attribution lands, aged-out ones dropped. Consumer switched to a timed recv_timeout(200ms) so the buffer is re-driven after each event and on a tick. Emit path factored into shared emit_resolved().
#1 attribution collisions: a Windows PID is recycled after exit and a command line is commonly identical across sandboxes. register_launch now rebinds by_pid on reuse and clears the stale last_pid_sid hint (warns if the PID still pointed at a different, leaked sandbox); command line is held in by_cmd only while unique and demoted to a new ambiguous_cmds set on a second owner, so a duplicate command refuses to resolve rather than misroute.
Unit tests: buffer replay (direct + cross-link), buffer bound, PID-reuse rebind, duplicate-cmd non-resolution. Box-verified on 7F203-MXC-001 (5 sandboxes, identical cmd -> 5 isolated sandbox_ids, 50/50 OCSF/JSONL, BuffersLost=0).
Signed-off-by: Akber Raza <akberr@nvidia.com>
* docs(mxc-etw): note cmd_line is captured raw with no privacy filtering
Review item #3 (Shailendra): add a PRIVACY NOTE on map_process_launch stating cmd_line is copied verbatim into OCSF process.cmd_line with no redaction, so secrets/PII on a command line land unredacted in the durable audit trail (deliberate audit-fidelity trade-off; treat the log as sensitive). Redaction is owned by an upstream privacy layer, not this path; no general audit-output PII scrubber exists today (openshell_core::secrets [CREDENTIAL] redaction is scoped to the proxy HTTP-target logging, a separate egress path).
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(mxc-etw): open ETW trace on caller thread so start_session reports real status
Review item #4 (Shailendra): start_session previously returned Ok(EtwSession) as soon as the pump thread was spawned, but OpenTraceW ran later inside that thread; if it failed we still handed back a live-looking session and logged 'consumer started' (silent failure = false audit coverage).
Split the two Win32 calls instead of adding a channel handshake (avoids any lost-wakeup/hang risk): the quick, synchronous OpenTraceW now runs on the caller thread (open_trace), and only the blocking ProcessTrace runs on the pump thread (run_trace). start_session returns Err if OpenTraceW fails (reclaiming the boxed Sender so the consumer disconnects, stopping the session, joining the consumer) and returns Ok/logs 'started' only once capture is genuinely open. Opened handle + LoggerName buffer + boxed Sender are carried to the pump via a Send OpenedTrace so they outlive ProcessTrace.
Box-verified on 7F203-MXC-001: consumer started=True, failed-to-start=False, 50 OCSF rows / 50 JSONL, BuffersLost=0 (no regression to capture/emit).
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(mxc-etw): guard pending-event replay against PID recycling
CodeRabbit flagged that drain_resolved() re-resolved buffered events
against the live by_pid map, so if Windows recycled a wxc-exec PID within
PENDING_TTL a stale event from the dead sandbox could be emitted under the
new owner.
Stamp each by_pid registration with its Instant and add resolve_replay(),
used only on the buffered/replay path. It (a) never falls back to the
recycle-/ambiguity-prone by_cmd or last_pid_sid keys, and (b) trusts a PID
match only when the registration is not newer than the buffered event by
more than REPLAY_PID_GRACE (2s) - a recycled PID's registration lands well
outside that window, so the stale event ages out instead of misattributing.
The legitimate #2 seed race (registration lands ~immediately) still replays.
Adds unit tests for the recycle-refusal, in-grace acceptance, and
weak-fallback exclusion.
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(mxc-etw): surface unexpected ProcessTrace termination (review #4)
start_session already returns Err on OpenTraceW failure (runs on the
caller thread since e41a7701), closing the first half of Shailendra's #4.
This closes the second half: ProcessTrace's result was discarded, so if
capture died mid-run the backend had no way to know.
Add a shared CaptureHealth (stopped/stopping/exit_code) between the pump
thread and EtwSession. run_trace now records ProcessTrace's WIN32_ERROR
and, when the pump returns without a deliberate stop, logs at ERROR that
MXC OCSF capture is no longer running. EtwSession::stop() sets `stopping`
before teardown so a normal shutdown isn't misreported, and
EtwSession::is_capture_alive() exposes the state for status/diagnostics.
Box-verified on 7F203-MXC-001: 5 sandboxes, 50 attributed OCSF rows,
JSONL parity 50/50, BuffersLost=0, clean start/stop (no false failure).
Signed-off-by: Akber Raza <akberr@nvidia.com>
* feat(mxc-ocsf): add ETW->OCSF audit-trail example kit; fix proxy-configured message
Add a runnable OCSF audit-trail example under examples/ (run-ocsf-audit.ps1,
mxc-ocsf-audit.toml, ocsf-audit.yaml, README) that spins up sandboxes with the
in-process ETW consumer and egress proxy on, emitting a full OCSF JSONL audit
trail across all four classes (6002/5019/1007/2004).
Fix SandboxProxyConfigured mapping to log "MXC sandbox proxy configured" instead
of a misleading "(no proxy)" when the provider reports proxyPort=0; the event's
presence already indicates proxy configuration. Verified on-box: 26 events, all
mapped ETW event types present.
Signed-off-by: Akber Raza <akberr@nvidia.com>
* feat(mxc-ocsf): clearer audit report + client-safe run-ocsf-audit.ps1
Improve the ETW to OCSF audit-trail example output and make it safe to ship.
Report:
- Add an event-type coverage count ("N of M expected event types fired");
the denominator auto-adjusts (8 with proxy on, 7 with -NoProxy).
- Split the checklist into expected event types vs anomaly findings
(ActivityError/FallbackError), which are reported separately and not
counted toward coverage (a clean run may emit none).
- Verdict is now coverage-based (all expected types must fire) instead of
the looser "at least 3 OCSF classes".
- Call out the absolute path to the durable OCSF JSONL log prominently.
Client-safety:
- Default -ShareOut to empty (no auto-copy); pass -ShareOut a UNC path to
opt in. Removes a hardcoded internal share path from a published example.
- Drop internal-team wording ("Hand that zip back for evaluation", "BUNDLE:")
in favor of neutral "Results bundle:".
- Update README-ocsf-audit.txt to match the opt-in -ShareOut behavior.
Verified on both MXC boxes: 7F203-MXC-001 (base-container) -> PASS, 8 of 8
event types, 26 OCSF events across 4 classes; 7F203-MXC-003 (AppContainer
fallback) -> reduced set as expected, clean output.
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(mxc): configure OCSF audit workloads per sandbox
- remove unsupported gateway-scoped workload fields from the shipped MXC audit example.
- build the command, working directory, and filesystem grant from each run's ShareDir
- pass the workload through --driver-config-json.
- preserve the host CONNECT proxy configuration and conditional audit coverage for the future host_connect_proxy merge
- require the workload output when determining the audit verdict.
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(mxc): omit command arguments from OCSF audit logs
- record only the executable basename for MXC CreateProcessInSandbox audit events
- leave process.cmd_line unset so workload arguments cannot reach shorthand or JSONL logs
- cover tokens, passwords, signed URLs, and PII with a secret-leak regression test
- update the audit example, architecture guidance, and published logging documentation
- preserve ETW attribution and future host_connect_proxy enforcement behavior
Signed-off-by: Akber Raza <akberr@nvidia.com>
* feat(etw): enhance ETW session management with distinct naming for concurrent gateways
* fix(etw): bound the audit queue during overload
- replace the unbounded ETW callback channel with count- and byte-bounded buffering
- keep the ETW callback non-blocking and count records rejected during overload
- emit immediate, rate-limited warnings that identify resulting audit coverage gaps
- make the audit example fail when queue overload causes dropped ETW records
- cover stalled consumers, oversized events, and warning throttling with unit tests
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(etw): harden sandbox audit attribution
- remove command-line and persistent per-PID fallback keys from live and replay resolution
- retire the driver-owned wxc-exec PID before publishing child completion
- retain established identity, activity, and correlation-vector links only for the five-second late-event window
- prevent buffered records from crossing rapid PID retirement and reuse boundaries
- add resolver and lifecycle coverage and document the attribution trust boundary
Signed-off-by: Akber Raza <akberr@nvidia.com>
* chore(mxc): address rebase follow-ups
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
* fix(mxc): align OCSF audit example with driver config
- remove unsupported egress proxy settings
- stop requiring the unavailable proxy audit event
- update example documentation for supported event coverage
Signed-off-by: Akber Raza <akberr@nvidia.com>
* fix(etw): redact command-line secrets in DecodedEtwEvent summary
* fix(etw): enhance PID resolution and event attribution logic for ETW records
* fix(ocsf): restrict gateway-local JSONL sink to Windows/MXC path with opt-in configuration
* address rebase issues
* fix(mxc): address ETW audit review feedback
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
* fix(mxc): fail closed across ambiguous PID reuse
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
* fix(mxc): bind ETW attribution to process generation
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
---------
Signed-off-by: Akber Raza <akberr@nvidia.com>
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
Co-authored-by: Jamie King <jamiek@nvidia.com>
Co-authored-by: Drew Newberry <anewberry@nvidia.com>
* fix(supervisor): canonicalize L7 protocols before OPA
Normalize recognized protocol spellings before supervisor validation and Rego authorization so mixed-case MCP policies cannot take the REST rule path. Reject null MCP configuration instead of silently defaulting it, and cover both behaviors with regression tests.
Signed-off-by: Shiju <shiju@nvidia.com>
* test(policy): cover normalization for every supported protocol
Exercise TCP, REST, WebSocket, GraphQL, SQL, JSON-RPC and MCP through YAML and protobuf loading with lowercase, uppercase and mixed-case names.
Assert canonical names in raw OPA data and returned L7 configuration, preserve TCP L4 behavior, and verify that normalization leaves unrelated data unchanged and is idempotent.
Signed-off-by: Shiju <shiju@nvidia.com>
---------
Signed-off-by: Shiju <shiju@nvidia.com>
Previously, every event from a sandbox reused the sandbox ID as its event
ID. Consumers deduplicating security records could mistake separate events
for the same record, and missing device types or empty image objects could
prevent schema validation.
Give each event its own ID, retain the sandbox association separately, and
classify the environment as Other/Sandbox while keeping the OS separate.
Omit unknown container details instead of emitting empty objects.
Security tooling can now distinguish events from the same sandbox and read
their identity consistently after serialization.
Refs #1055
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Keep watching briefly when a compute driver reports ContainerExited without a canonical main-process result. This prevents --no-keep cleanup from deleting the sandbox before the supervisor publishes the authoritative exit status.
Signed-off-by: Shiju <shiju@nvidia.com>
* refactor(inference): remove managed inference routes
Closes#3172
Remove the inference route control plane, inference.local data path, built-in router crate, and SDK surface. Move inference workloads to explicitly imported provider profiles and native endpoints, with migration cleanup and updated tests and documentation.
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
* fix(policy): preserve alternate upstream isolation
Restore the provider policy activation guard so legacy OpenAI and Anthropic providers configured for alternate base URLs do not grant egress to the built-in public vendor endpoints.
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
---------
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
* chore(deps): replace ring with AWS-LC
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* fix(lint): address warnings after dependency upgrades
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* fix(tls): limit provider initialization to reqwest clients
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
---------
Signed-off-by: Simon Scatton <sscatton@nvidia.com>
* feat(sandbox): support rootfs tar as --from source for VM driver
Accept flat rootfs tar archives (.tar, .tar.gz, .tgz) via the --from
flag for VM-backed gateways. The CLI detects the archive extension,
validates that the gateway uses the VM compute driver, and passes the
tar path through driver_config. The VM driver copies the tar into its
staging area and feeds it into the existing rootfs extraction and ext4
disk creation pipeline, skipping the container image pull/export steps.
Closes#2175
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(sandbox): validate rootfs tar path at the VM driver boundary
The rootfs_tar_path field in driver_config was passed from the API
caller directly to tokio::fs::copy without validation. An authenticated
user bypassing the CLI could supply arbitrary host paths (e.g.
/dev/zero for disk exhaustion, or readable host files for data
exfiltration).
Introduce a trusted staging directory that the VM driver creates on
startup and advertises via GetCapabilities. The CLI now copies the tar
into the staging directory before creating the sandbox, and the driver
validates that the received path is a regular file inside the staging
root and within a configurable size limit (default 10 GiB) before any
I/O.
New VmDriverConfig options:
- rootfs_tar_staging_dir: override the staging directory
(default: <state_dir>/rootfs-tar-staging)
- rootfs_tar_max_bytes: override the size limit (default: 10 GiB)
Addresses GATOR-28b5152e-01.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(sandbox): request-scoped staging, size pre-check, and cleanup for rootfs tar
Tighten the rootfs tar staging flow to address the remaining GATOR-01
obligations:
- Request-scoped staging: the CLI creates a unique per-request
subdirectory (req-<pid>) under the staging root instead of placing
files directly in the shared directory. The driver enforces that the
tar path is at depth 2 (staging_root/<subdir>/<file>), preventing
cross-request path selection.
- Size pre-check: the driver advertises rootfs_tar_max_bytes via
GetCapabilities. The CLI reads this limit and rejects oversized files
before copying, avoiding disk exhaustion in the staging directory.
- Cleanup: the driver removes the request staging subdirectory after
consuming the tar (on cache hit, copy success, or copy failure),
ensuring staged data does not persist beyond the request.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(vm): restore rootfs-tar sandboxes from persisted image identity
On restore or restart, the one-shot staged tar archive has already been
cleaned up. Reading the persisted image identity from the sandbox state
directory and resolving the cached disk path directly avoids re-accessing
the deleted staging path.
Addresses GATOR-168b9210-01.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(cli): use random staging dirs and enforce byte limit during rootfs tar copy
Replace PID-based request staging directories with tempfile-generated
random names to prevent collisions and make paths unpredictable.
Replace bare tokio::fs::copy with a streaming copy loop that enforces
the advertised max_bytes limit during transfer, closing the TOCTOU gap
between the pre-copy size check and the actual copy.
Signed-off-by: Philippe Martin <phmartin@nvidia.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(sandbox): issue rootfs tar staging slots from the gateway
A caller could name any host path in `driver_config.vm.rootfs_tar_path`,
which the privileged VM driver then read. The CLI-side locality check did
not apply to direct API requests.
The gateway now owns staging. `BeginRootfsTarStaging` allocates a
request-scoped directory under the driver-advertised staging root and
returns an opaque single-use token; `CreateSandbox` carries the token, and
the gateway substitutes the path it allocated before dispatching to the
driver. `template.driver_config.<driver>.rootfs_tar_path` is rejected
outright in request validation, so a caller-supplied path never reaches
privileged I/O.
Tokens are bound to the issuing workspace and subject, consumed once, and
expire after 30 minutes. Outstanding slots are capped per caller and
overall, so one caller can neither exhaust the staging filesystem nor
starve others. An RAII guard reclaims the directory on every failure path
after consumption, and an age-gated sweep runs at startup and on each
reconcile pass for directories whose driver died before its own cleanup.
The token is stripped from the public sandbox before persistence: the
stored copy is returned verbatim by GetSandbox, ListSandboxes and
WatchSandbox to every member of the workspace.
Also fixes two defects this exposed:
- The CLI wrote `rootfs_tar_path` at the top level of `driver_config`, but
the gateway forwards only `driver_config.<driver_name>`, silently
dropping unmatched keys. The archive never reached the VM driver, so the
documented `--from ./rootfs.tar` flow did not work at all. Config is now
nested under `vm` and deep-merged, so a caller's existing VM settings
survive instead of being clobbered by a shallow extend.
- Staging previously required `GetGatewayInfo`, which is restricted to
`platform_admin`, making the feature unusable for ordinary users on any
RBAC-enabled gateway. The new RPC matches CreateSandbox at
`sandbox:write` / `workspace_role: user`.
`compute_driver.proto` is unchanged; the gateway reads the staging root
from the capabilities it already stores.
Refs #2175
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(vm): derive rootfs tar cache identity from archive contents
The prepared-disk cache key combined the archive's full path with an mtime
truncated to seconds, then mapped punctuation to `-`. Distinct paths such as
`/tmp/a/b.tar` and `/tmp/a-b.tar` collapsed onto the same key and reused each
other's disk, a rewrite within the same second kept stale contents, and a long
path could exceed filesystem component limits.
Identity is now a SHA-256 of the archive contents. This is also what makes the
cache work at all now that the gateway allocates a fresh staging directory per
request: a path-derived key would miss on every create.
The archive is hashed, the cache checked, and only on a miss copied — so a hit
skips writing a multi-gigabyte file. The copy is hashed as it is written and
rejected if the digest differs from the first pass, which closes the window
where the source changes during staging rather than approximating it with a
re-stat.
Refs #2175
Signed-off-by: Philippe Martin <phmartin@redhat.com>
* fix(vm): decompress gzip rootfs tar archives during staging
`--from` accepts `.tar.gz` and `.tgz`, but the driver staged whatever bytes
it was given and the guest image-prep VM extracts the staged file with a
plain `tar -xpf`. Compressed sources therefore depended on the guest tar
auto-detecting gzip, and the prepared disk was sized from the compressed
length, which is far too small for the expanded rootfs.
Staging now detects gzip from the archive's magic bytes -- the driver only
ever sees a gateway-issued staging path, never the caller's file name -- and
writes an uncompressed tar. The digest still covers the source bytes, so the
"archive changed while staging" check is unaffected, and expansion is bounded
by `rootfs_tar_max_bytes` so a compression bomb cannot fill the host disk.
`extract_rootfs_archive_to` sniffs gzip as well, so the host-side extraction
path matches.
Adds unit coverage for gzip staging, bounded expansion, and gzip extraction,
plus an e2e sandbox created from a gzip-compressed export.
Signed-off-by: Philippe Martin <phmartin@redhat.com>
---------
Signed-off-by: Philippe Martin <phmartin@redhat.com>
Signed-off-by: Philippe Martin <phmartin@nvidia.com>
* fix(supervisor): preserve MCP versions in runtime config
Carry canonical MCP version allowlists through both policy-loading paths into the supervisor's typed L7 endpoint configuration. Reject malformed, ambiguous, or cross-protocol version data during policy activation so invalid configuration cannot remove MCP inspection from a selected route.
Signed-off-by: Shiju <shiju@nvidia.com>
* fix(supervisor): accept mixed-case MCP version protocols
Validate materialized MCP version metadata through the parsed L7 protocol so supported mixed-case spellings remain valid. Cover YAML and protobuf ingress with pinned-default regression tests.
Signed-off-by: Shiju <shiju@nvidia.com>
---------
Signed-off-by: Shiju <shiju@nvidia.com>
Evaluate proposal coverage once per observed installed policy snapshot while preserving replacement and deadline behavior.
Signed-off-by: Shiju <shiju@nvidia.com>
Build protobuf-derived allow objects from selectors that are actually present. This keeps protobuf and YAML policy validation aligned for protocols such as GraphQL over WebSocket.
Signed-off-by: Shiju <shiju@nvidia.com>
* feat(middleware): define HTTP response pre-return interface
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): clarify HTTP response interface
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): align response result actions
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): expose response reason codes
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): share session end reasons
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(middleware)!: finalize HTTP response pre-return contract
Replace the separate body_end event with HttpResponseBodyUnit.end_of_stream.
Every body-inspecting stage receives exactly one flagged unit, which may be
empty; a zero-byte body is one empty flagged unit and OpenShell never reads
ahead to set the flag.
Defer response trailers from V1 and reserve their field numbers. HTTP/1.0
clients and Content-Length bodies cannot carry trailers and that behavior was
undefined.
Add HttpResponsePreflight.permitted_body_modes, computed once from the
original upstream head so every stage sees the same list, and make an
unlisted selection a failure rather than a downgrade. Add the block_delivery
preflight action as a successful decision enforced regardless of on_error.
Expose Content-Length, Content-Encoding, and Content-Range read-only in
preflight. Cap STREAM_BYTES input units at half of max_payload_bytes and
permit deferring bytes across replacements only for fail_closed bindings,
surfaced as deferral_permitted.
Split PEER_DISCONNECT into DOWNSTREAM_DISCONNECT and UPSTREAM_DISCONNECT and
attribute WebSocket relay failures by direction instead of a generic peer
error. Compile the content-guard example in lint and branch checks so proto
renames cannot break it silently.
BREAKING CHANGE: WebSocketSessionEndReason and WebSocketSessionEnd are
replaced by the shared MiddlewareSessionEndReason and MiddlewareSessionEnd.
NORMAL_CLOSE is now NORMAL, UPSTREAM_REJECTED is now UPSTREAM_FAILURE, and
PEER_DISCONNECT is split into DOWNSTREAM_DISCONNECT and UPSTREAM_DISCONNECT.
Enum numbers are unchanged so binary wire compatibility is preserved;
generated symbols and JSON names change.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): describe skip as opting out of inspection
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(middleware): add body-phase block_delivery and skip_remaining actions
Body results may now stop delivery or opt out of inspecting the rest of the
response after a prefix. One HttpResponseBlockDelivery message is shared by
preflight and body results and documents the difference between blocking
before and after head commitment. Drop the field reservations, since nothing
in this contract has shipped, and renumber session_end to close the gap.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): share HTTP body leaf messages across directions
HttpBodyUnit, HttpBodyPassThrough, HttpBodyTransform, HttpBodySkipRemaining,
and HttpBodyMode carry no response-specific semantics, so name them for reuse
by the streaming request hook. Envelopes, results, preflight, and
block_delivery stay response-specific because commitment semantics differ.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): keep HTTP body leaf messages response-specific
Reverts the shared HttpBody* naming. A direction-specific payload such as a
response-only semantic mode would otherwise add unreachable variants to the
other direction or force a source-breaking fork after 0.1.0. The streaming
request hook defines its own HttpRequestBody* messages and copies the shape;
SDKs present a direction-neutral body handler over both.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): simplify response proto comments
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(middleware): reject undispatched response bindings
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): simplify phase field comment
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* refactor(middleware): rename HTTP response preflight result
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(middleware): add HTTP response trailer results
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define response block delivery
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define stage-local response body modes
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define final response body units
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): define streaming response deferral
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): defer whole-body accumulation timeout
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): align response result diagnostics
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* test(middleware): cover upstream WebSocket disconnect
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(middleware): keep response streams unit-local
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* docs(middleware): trim disconnect compatibility note
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
---------
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* feat(ocsf): configurable schema version for SIEM backward compatibility
Add a gateway-configurable OCSF schema version target that downgrades
JSONL output for SIEMs that only support older schema versions. AWS
Security Lake requires v1.1.0, Splunk CIM Add-On targets v1.1-v1.3.
The downgrade filter strips profile-gated fields (ai_model, container,
observation_point_id), removes unknown profiles from metadata.profiles,
rewrites metadata.version, and adds an unmapped.downgraded_from
breadcrumb so auditors can distinguish "no model involved" from "model
attribution stripped."
Supported target versions (1.1, 1.3) are enforced by an allow-list in
the settings registry. Invalid values are rejected with a clear error.
The setting flows to sandboxes via the settings bundle and takes effect
on the next poll cycle. The shorthand log output is unaffected.
Closes#2662
Signed-off-by: Adel Zaalouk <zanetworker@gmail.com>
Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
* fix(ocsf): align downgrade with schema version
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
---------
Signed-off-by: Adel Zaalouk <zanetworker@gmail.com>
Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
Signed-off-by: John Myers <9696606+johntmyers@users.noreply.github.com>
Co-authored-by: John Myers <9696606+johntmyers@users.noreply.github.com>
Keep a dedicated SQLite connection alive so pool connection replacement
retains the shared in-memory schema and objects.
Closes#3173
Signed-off-by: Emilien Macchi <emacchi@redhat.com>
* docs(gateway-config): fix stale community sandbox image path
Signed-off-by: Yuedong Wu <dwcn22@outlook.com>
* docs(sandbox-image): purge remaining stale image references
Rebasing onto main surfaced four more instances of the same dead
ghcr.io/nvidia/openshell/sandbox path, introduced by commits merged
after this branch was opened: three test fixtures (driver-docker,
openshell-ocsf, compute::mod) and one user-facing default in the
SPIFFE token-exchange Podman demo README. Correct all four to
ghcr.io/nvidia/openshell-community/sandboxes/base, consistent with
the rest of this fix.
Signed-off-by: Yuedong Wu <dwcn22@outlook.com>
---------
Signed-off-by: Yuedong Wu <dwcn22@outlook.com>
* fix(sandbox): detect an available login shell instead of hardcoding /bin/bash
The built-in default sandbox command and the interactive SSH session
hardcoded /bin/bash. Minimal images such as Alpine ship only /bin/sh
(BusyBox ash), so sandbox startup failed with an opaque "No such file or
directory (os error 2)" that never named the missing binary.
Add openshell-core::shell with shell-path constants and a runtime
detect_login_shell() that resolves a shell present in the sandbox image
($SHELL if executable, then bash, then /bin/sh). Use it for:
- the built-in default command (only the default is remapped; explicit
user commands are never rewritten), resolved in the supervisor so it
inspects the sandbox filesystem rather than the gateway's
- the SSH interactive shell
- the SHELL environment variable
Also name the program in the spawn error so a missing shell/binary is
diagnosable instead of a bare ENOENT.
Refs #3146
Signed-off-by: Akram <akram.benaissi@gmail.com>
* fix(sandbox): drop $SHELL preference in shell detection
$SHELL is image/user-controlled and the detected shell is later invoked
with `-lc`, so an executable that is not a compatible shell (e.g.
SHELL=/bin/false) would pass the executable check and then break command
execution even when /bin/sh is available. Resolve only from known shell
paths instead.
Also add a USR_BASH constant for /usr/bin/bash rather than a string
literal in SHELL_CANDIDATES.
Refs #3146
Signed-off-by: Akram <akram.benaissi@gmail.com>
* fix(sandbox): resolve the default login shell in the supervisor (empty command = default)
Addresses review: interactive PTY SSH now uses the detected shell, the shell
tests are portable across the Windows lane, and default-shell provenance is
carried without a new spec field.
An omitted command is left empty end to end and resolved in the supervisor,
which is the only place that sees the sandbox image:
- The CLI forwards the command as-is; the gateway persists an omitted command
as empty (no baked /bin/bash -l) and requests a TTY.
- MainProcessConfig carries the command empty (the transport now allows it);
the supervisor resolves a login shell that exists in the sandbox image (bash
when present, otherwise /bin/sh on minimal images like Alpine) and logs the
resolved shell.
- Interactive PTY SSH (spawn_pty_shell) uses the detected shell; a shared
build_ssh_shell_command helper covers the PTY and non-PTY paths, with a
deterministic sh-only regression test.
- Unix-only shell tests are gated with cfg(unix).
An explicit command is always run verbatim.
Refs #3146
Signed-off-by: Akram <akram.benaissi@gmail.com>
---------
Signed-off-by: Akram <akram.benaissi@gmail.com>
Move all provider-related functions, helpers, constants, and tests from
the monolithic run.rs (~2,700 lines) into a dedicated
commands/provider.rs module. This is PR3 of the CLI refactor series
(issue #2304).
The extraction follows the same pattern established in PR2 (gateway):
- Self-contained module with own imports
- pub use re-exports in run.rs so callers (main.rs) are unchanged
- Inline #[cfg(test)] mod tests
Signed-off-by: Varsha Prasad Narsing <vnarsing@nvidia.com>
Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
Closes#3155
Add the missing authenticate_sandbox method to the ComputeDriver impl
for the MXC driver. The method returns Status::unimplemented, matching
the Docker and Podman drivers, since the MXC driver advertises
supports_sandbox_authentication: false.
Signed-off-by: Jeff MAURY <jmaury@nvidia.com>
Signed-off-by: Jeff MAURY <jmaury@redhat.com>
Centralize compute-driver RPC descriptors, stream instrumentation, provider
routing, and standalone installation in openshell-otel. Use typed RPC
constants so gateway and in-process driver paths cannot panic on unknown
operation strings or repeat runtime method parsing.
Emit semantic-convention rpc.service and rpc.method attributes, preserve
trace context and resource identity across deployment modes, and route both
RPC boundary and backend crate spans to each selected driver provider. Leave
consumer-dropped watch spans unset while recording observed terminal status,
and avoid reboxing untraced external-driver streams.
Derive each driver tracing identity from Cargo package and crate metadata and
attach its descriptor to the compute-driver registration, keeping provider
selection and target routing tied to the registered implementation. Share
tracing setup and round-trip test support across Docker, Podman, Kubernetes,
and VM, and update the gateway tracing documentation.
Signed-off-by: Kris Hicks <khicks@nvidia.com>
* feat(middleware): broaden HTTP header mutation authority
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(middleware): protect response credential headers from mutation
Response middleware could write or remove Set-Cookie, WWW-Authenticate,
Authentication-Info, and Proxy-Authentication-Info, letting a stage plant
or strip credentials the sandbox client acts on. Protect them in both
directions, matching the request profile's treatment of Authorization and
Cookie, and reserve the x-openshell-credential prefix for responses too.
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(middleware): reject credential placeholder writes
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
---------
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
* fix(cli): preserve provider type on credential updates
provider_update built every UpdateProviderRequest with an empty type and
profile workspace. Policy interceptors evaluate the request before the
gateway merges it with stored state, so they cannot tell the target is a
profile-managed provider and deny credential-only updates. Creating a
provider from an interceptor-vended profile works; rotating its
credentials does not.
Look up the stored provider and forward its type and profile workspace
verbatim. The gateway treats profile_workspace as immutable, so the
stored value is passed through rather than recomputed from the request
workspace.
The read is best-effort: a caller holding provider:write without
provider:read must still be able to rotate credentials, so a denied read
falls back to the previous empty metadata rather than failing the
update. --from-existing and --from-oidc-token need the stored type, so
they surface the error instead.
This also removes a duplicate GetProvider call, since the oidc-token and
from-existing paths each fetched the same provider separately.
Signed-off-by: Nathan DeMoss <ndemoss28@gmail.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Signed-off-by: Nathan DeMoss <ndemoss28@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>