Skip to content

feat(supervisor): record the command line of each process - #5254

Draft
krishicks wants to merge 1 commit into
feat/4268-exec-process-events/krishicksfrom
feat/4268-process-command-lines/krishicks
Draft

krishicks wants to merge 1 commit into
feat/4268-exec-process-events/krishicksfrom
feat/4268-process-command-lines/krishicks

Conversation

@krishicks

@krishicks krishicks commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Process Activity named each process only by its executable, so /usr/bin/bash or /usr/bin/python3 didn't say what it was asked to run. With the separate, off-by-default ocsf_process_cmdline_enabled setting on, Launch records carry process.cmd_line.

Last of a stack of four for #4268 (based on #5253):

  1. feat(supervisor): identify the process behind each network event #5251 Identify the process behind each network event
  2. feat(supervisor): record launch and exit of sandbox sessions #5252 Record launch and exit of sandbox sessions
  3. feat(supervisor): record each exec in the workload and its exit #5253 Record each exec in the workload and its exit
  4. feat(supervisor): record the command line of each process #5254 This PR: record the command line of each process

Related Issue

Closes #4268

Changes

  • The runtime records argv with each launch: the spawn's own program and args for the entrypoint and sessions, and the argv each exec passes, read from the caller's memory. It keeps at most 256 args and 4 KiB per process, and a shortened list ends with ….
  • ReadProcessEvents.command_lines is an optional bool, applied alongside exec recording. The supervisor withholds command lines unless the current setting asks for them, which covers takeover and turning the setting off.
  • cmd_line quotes arguments the way a shell would read them, and ANSI-C quotes control and bidi characters. The shorthand [cmd:…] escapes control characters and ], so a workload can't forge log lines.
  • New setting ocsf_process_cmdline_enabled; docs in ocsf-json-export.mdx ("Record command lines") and logging.mdx.

Before: no process.cmd_line on Launch records.

After (585/585 Launches carry cmd_line):

{"launch_type":"Exec","pid":81,"name":"/usr/bin/dash","cmd_line":"sh -c 'echo \"in-sandbox: sh pid=$$\"; /bin/true; /bin/sleep 0.01; echo done'"}
{"launch_type":"Exec","pid":86,"name":"/usr/bin/sleep","cmd_line":"/bin/sleep 0.01"}
{"launch_type":"Exec","pid":91,"name":"/usr/bin/bash","cmd_line":"bash -c 'exec 3<>/dev/tcp/example.com/443 && echo connected'"}

Log-injection attempt, sh -c '/bin/echo "$(printf "x\nOCSF NET:OPEN [INFO] ALLOWED forged -> evil.example:443")"':

OCSF PROC:LAUNCH [INFO] /usr/bin/echo(98) [cmd:/bin/echo $'x\nOCSF NET:OPEN [INFO\] ALLOWED forged -> evil.example:443']

No forged NET line appears.

Known limitations:

  • Arguments can hold secrets, and with the setting on they reach every OCSF destination: shorthand logs, openshell logs, and JSON export. There is no redaction, which is why the setting is opt-in and separate from exec recording.
  • An exec's argv is read before the kernel runs it, so another thread can change it first (audit only).
  • An older runtime ignores command_lines without a warning.
  • Terminate records don't carry cmd_line. The network actor's cmd_line is still the policy-matched paths.
  • argv is read before the rate-limit check, so the read cost is paid even for suppressed execs.

Release Notes

Set ocsf_process_cmdline_enabled to include each process's command line in OCSF Process Activity Launch events. Command lines can contain secrets and appear in every OCSF destination.

Testing

  • Lockfile checks, rust:format:check, and every CI clippy invocation (--locked, including perf-harness) pass on macOS and the aarch64 Linux target for this commit
  • Unit tests added/updated (argv limits and truncation, quoting, shorthand escaping, withholding); sandbox (305), sandbox-backend (111), and supervisor (300) suites pass in an Ubuntu 24.04 guest (kernel 6.8); openshell-ocsf (180) passes on macOS
  • Manual end-to-end: gateway + Docker sandbox (kernel 7.0), before/after OCSF output above

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

Process Activity named each process by its executable only, so a record
of /usr/bin/bash or /usr/bin/python3 did not say what it was asked to
run.

When the ocsf_process_cmdline_enabled setting is on, the runtime records
each process's arguments with its launch: the argv it started the
entrypoint or a session with, and the argv each exec in the workload
passes, read from the caller's memory. It keeps up to 256 arguments and
4 KiB per process and marks a shortened list. The supervisor applies the
setting through ReadProcessEvents alongside ocsf_process_exec_enabled,
reports command lines only while the setting asks for them, and sets
process.cmd_line on Launch records with the arguments quoted as a shell
would read them, control characters ANSI-C quoted, and a trailing …
when shortened. The shorthand log also escapes control characters and
`]` in the command line so a workload cannot forge log lines.

Arguments can hold secrets, so the setting is off by default and
separate from exec recording. An exec's arguments are read before the
kernel runs it, so another thread can change them first.

Refs #4268

Signed-off-by: Kris Hicks <[email protected]>
@copy-pr-bot

copy-pr-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant