Skip to content

bug(ocsf): process events omit command arguments #4563

Open

Description

@prekshivyas

User Story

As an operator using OpenShell OCSF events for audit review, I need process records to include the command that ran so that I can investigate activity without relying on incomplete network event fields.

Problem Statement

OpenShell process launch records include the program name and PID but omit command arguments. Network activity records populate cmd_line from absolute paths found in the process chain rather than the actual argument vector. As a result, the audit trail cannot show the command that produced the activity.

Related downstream report: NVIDIA/NemoClaw#13175

Related broader work: #4268 concerns launch and exit coverage for every workload process; this issue is specifically about the contents of cmd_line in existing records.

Impact / Why This Matters

Auditors cannot reconstruct the executed command from current OCSF process records, and the network record's path-only value can be mistaken for a complete command line. Host logs or manual process inspection are incomplete workarounds after the process exits.

Acceptance Criteria

  • Process launch records include the executed command and arguments in the OCSF command-line field when available.
  • Network activity records do not present an absolute-path-only list as if it were the actual command line.
  • Audit output has a documented bound and redacts known sensitive arguments before emission.
  • Regression coverage includes an argument that is not an absolute path and a sensitive argument that must be redacted.

Reproduction Steps

  1. Enable OpenShell OCSF JSONL logging for a sandbox.
  2. Launch a workload with a visible non-path argument and make an allowed network request from that process.
  3. Inspect the process launch record and the corresponding network activity record. The launch record lacks the arguments; the network cmd_line contains only selected absolute paths, not the executed command.

Environment

  • Reported with OpenShell 0.0.116 on Ubuntu 24.04 with Docker; the downstream report notes internal reproduction.
  • The process and network event construction is also present in OpenShell v0.1.2, v0.1.3, v0.1.4, and current main as checked in source.

Agent Investigation

  • crates/openshell-sandbox/src/delegated.rs constructs the process event with OcsfProcess::new(program, pid) and does not set cmd_line.
  • crates/openshell-supervisor-network/src/proxy.rs builds the network event's command-line string from decision.cmdline_paths.

Activity

  1. added
    state:acceptedA maintainer decided OpenShell should pursue this issue
    and removed
    state:triage-neededOpened without agent diagnostics and needs triage
    on Oct 23, 2026
  2. krishicks commented on Oct 23, 2026

    @krishicks
    Collaborator

    Accepting. The bug reproduces from the code; analysis and open decisions below.

    Where it reproduces

    • Launch event. crates/openshell-sandbox/src/delegated.rs:131 builds the process with OcsfProcess::new(program, pid) and never calls .with_cmd_line(..). args is in scope and is dropped.
    • Network events. crates/openshell-supervisor-network/src/proxy.rs (around lines 869, 2688, 5513) fills cmd_line with absolute paths collected from the whole process ancestry (procfs::collect_cmdline_paths, openshell-binary-identity), joined with ", ". It is not the socket owner's argv. When the list is empty it emits the sentinel "-".
    • Docs. docs/observability/ocsf-json-export.mdx:256 shows "cmd_line": "curl https://github.com" for a network event, which the code does not emit.
    • crates/openshell-supervisor-process emits no Process Activity events; exec and SSH children emitting none is feat(sandbox): emit process launch and exit events for every workload process, with the parent pid #4268.

    Small fix

    1. Add a shared audit sanitizer in openshell-ocsf (next to sanitize_reason_for_audit) that redacts sensitive arguments, quotes arguments containing whitespace, escapes control characters, and caps argument count and total length with a truncation marker.
    2. Use it at delegated.rs:131. Keep argv out of message. Move event construction into a pure function so it is testable without a Linux launcher.
    3. Harden crates/openshell-ocsf/src/format/shorthand.rs:520, which prints [cmd:..] raw. Once real argv flows, a newline or ] could forge or split log lines.
    4. Stop passing cmdline_paths to with_cmd_line on network events. If operators still want the path list, put it under unmapped with an honest key. Leave cmdline_paths as-is in the OPA input and protocol; it is diagnostic and never authorizes.
    5. Update ocsf-json-export.mdx and logging.mdx (PROC:LAUNCH example), and document the bound and redaction rules.

    Estimate: about 1.5-2 engineer-days with tests and docs.

    Large fix

    Real argv on network events. This needs a raw-argv field next to cmdline_paths in procfs.rs, openshell-binary-identity, ResolvedBinaryIdentity, proto/sandbox_protocol.proto, and the sandbox-backend conversion. The value must stay out of PolicyIdentityKey and the OPA input, because a workload can rewrite its own argv, and it must be capped at the protocol boundary (read_process_cmdline currently reads up to roughly ARG_MAX). Estimate: another 3-5 days.

    Decisions needed

    1. Argv by default or omitted? The MXC driver deliberately omits arguments (etw_consumer.rs:1785, regression test at :2122) because they may contain credentials, signed URLs, or PII. The issue asks Linux to include them. Emit redacted argv, or follow MXC? If argv is emitted, should MXC align?
    2. Scope of the network side. Is removing or relocating the misleading path list enough, or is real per-connection argv in scope (protocol change)?
    3. Redaction contract and bound. Redaction by denylist is best-effort and cannot catch a positional secret. The rules and the size bound become published behavior. AGENTS.md says never to log secrets in OCSF messages.
    4. Path list. Move to unmapped.cmdline_paths, or drop from OCSF.
    5. "-" sentinel. OCSF guidance is empty or omitted; fixing cmd_line is in scope here, the other fields are separate.

    Sequencing

    #4288 touches the same lines (adds .actor_process(..) right after delegated.rs:131 and makes the process builder require an actor before build()). Landing it first avoids a rebase.

  3. krishicks commented on Oct 23, 2026

    @krishicks
    Collaborator

    Follow-up on decisions 1 and 3: proposing that command-line capture be opt-in, off by default.

    Why

    Redaction by denylist only catches arguments that look like secrets. A workload that deliberately encodes a secret into a command (for example a path such as /tmp/<secret>/run) passes any pattern match. Because OCSF events leave the sandbox through the gateway log push, SIEM export, and OTLP, outside the sandbox's network policy, workload-chosen argv is a covert channel for anything the agent can read.

    How much this matters depends on the event:

    Agent-chosen strings already appear in OCSF today (destination hosts on denied CONNECTs, L7 request paths, the cmdline_paths ancestry list), so the channel is partly open already. That is worth auditing separately.

    Proposal

    • Default is to omit cmd_line, matching the MXC driver (etw_consumer.rs:1785). Linux and Windows then agree.
    • When enabled, emit the bounded, redacted, control-character-escaped value from the small fix. The docs state that redaction is best-effort and that cmd_line on workload-spawned processes is attacker-controlled data and not secret-safe.
    • Removing the misleading path list from network cmd_line is a bug fix and is not gated by the setting.
    • For agent-spawned processes (network events, feat(sandbox): emit process launch and exit events for every workload process, with the parent pid #4268), prefer names-only (argv[0] plus flag names, no values) over full argv even when enabled.

    New decision: where the setting lives

    This adds public configuration, and the supervisor emits the events, so the value has to reach it. Candidates: a gateway-level setting, a per-sandbox or policy setting, or a supervisor environment variable. Gateway-level is simplest for operators; per-sandbox allows enabling it for one trusted workload. This should follow the existing supervisor-side setting plumbing and proto/README.md. It also needs deciding whether the switch covers only launch events or also the child-process events from #4268.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:acceptedA maintainer decided OpenShell should pursue this issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions