GHSA-p8rw-8qj3-hf33: Path Traversal
Summary
An authenticated, non-admin user can obtain arbitrary host-filesystem read/write (and host environment-secret disclosure) on an Omnigent runner by uploading an agent bundle whose osenv.cwd points outside any intended workspace (e.g. / or /home/<victim>). The cwd field is taken verbatim from the bundle with no validation, normalization, or boundary check anywhere in the spec pipeline.
This is a different sink from GHSA-jrrm-9hc7-2v3h (CWE-94, shared-agent bundle overwrite -> stdio MCP RCE). It shares the bundle-upload vector but is reached through the user's own session-scoped agent and is not addressed by that advisory's proposed shared-agent guard.
Preconditions
- Runner realizes a session-scoped uploaded bundle without OMNIGENTRUNNERWORKSPACE set. When that env var is set (CLI- and host-launched sessions set it), the spec cwd is overridden and the attack is neutralized — so this is deployment-gated, not universal. - Attacker is any authenticated user (no admin scope; requireuser only checks identity). No shared-agent overwrite needed.
Details (verified against code)
1. Parse — no validation. omnigent/spec/parser.py:696 stores cwd=str(cwdraw) verbatim. Absolute paths (/, /etc), ../.., etc. are all accepted. The sandbox.type is likewise author-chosen and "none" is legal. 2. Validate — cwd unconstrained. omnigent/spec/validator.py validateosenv checks only fork/scratch/egress combinations; it never references cwd (the sole mention, line ~526, is a comment). No boundary is applied to the cwd itself. The boundary in server/schemas.py validates a caller-supplied workspace against the spec cwd (treating the author cwd as trusted) and only for host-launched sessions — it does not bound the cwd. 3. Sink. omnigent/inner/osenv.py:890 sets cwd = Path(spec.cwd or os.getcwd()).resolve(strict=False) as the environment root; osenv.py:934 does shutil.copytree(src=cwd, ...) when fork=true. All agent file/shell tools are bounded by assertwithincwd (osenv.py:1040), which checks resolved.relativeto(cwd) — but since cwd is attacker-controlled, cwd=/ makes the entire host filesystem in-bounds for read and write; fork=true with cwd=/home/victim copies that tree into the agent-readable workspace. 4. Decisive gate. omnigent/runner/resourceregistry.py:648-654: cwd = defaultcwd only when self.runnerworkspace is not None or specosenv.cwd in (None, ".", "./"); otherwise cwd = specosenv.cwd (the attacker's absolute path). So OMNIGENTRUNNERWORKSPACE is the only thing standing between the spec and the host FS — and it is an operational control, not an in-code guard. A code comment at tooldispatch.py:~4207 claims cwd "is treated as a boundary at session-create time," which is not true on this path.
Attack path
1. Authenticated user sends POST /v1/sessions (multipart) with an agent bundle whose config.yaml contains: yaml osenv: cwd: "/" # or /home/<victim>, with fork: true for one-shot exfil sandbox: { type: none } 2. On a runner without OMNIGENTRUNNERWORKSPACE, the agent's sysosread/write/edit/shell tools now operate over the whole host filesystem, and (sandbox inactive) inherit the runner's full environment — exposing host secrets via e.g. sysosshell("env").
Impact
Arbitrary host-filesystem read/write and runner-environment secret disclosure, available to any authenticated user on an affected deployment. High severity (deployment-conditional).
Suggested fix
Add a control on the cwd field itself in omnigent/spec/validateosenv (and/or at parse): reject absolute paths and .. traversal, and require cwd to resolve within the runner workspace / an allow-listed root. Do not rely on OMNIGENTRUNNERWORKSPACE being set as the sole defense. Consider also disallowing bundle-author sandbox.type: none for server-realized (non-CLI) sessions.
Related
GHSA-jrrm-9hc7-2v3h (same bundle-upload vector, different sink; that fix does not cover this).
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/omnigentto a version that resolves this vulnerability.Fixed in 0.3.0 - Configuration
Add validation for the `cwd` field in `omnigent/spec/_validate_os_env` (and/or during `omnigent/spec/parser.py` parsing) so that `os_env.cwd` cannot be an absolute path (e.g., starting with `/`), cannot contain `..` traversal, and is required to resolve within the runner workspace or a configured allow-listed root. This prevents `omnigent/inner/os_env.py` from setting an attacker-chosen filesystem root via `cwd = Path(spec.cwd or os.getcwd()).resolve(strict=False)` and from copying it with `shutil.copytree(..., fork=true)`.
omnigent/spec/_validate_os_env os_env.cwd = reject absolute paths and '..' traversal; require cwd to resolve within the runner workspace or an allow-listed root - Configuration
Modify server-side validation to reject `sandbox.type: none` for server-realized (non-CLI) sessions, since the attacker can choose `sandbox.type: none` and thereby cause the host environment to be inherited when the attacker-controlled `cwd` is used.
omnigent/spec/validator (sandbox validation) os_env.sandbox.type = disallow 'none' for server-realized (non-CLI) sessions
Event History
Frequently Asked Questions
Which deployments are exposed?
Exposure is limited to runners that realize a session-scoped uploaded bundle without OMNIGENT_RUNNER_WORKSPACE set. CLI-launched and host-launched sessions set this variable and override the bundle-provided cwd, neutralizing this attack path.
What level of access does an attacker need?
An attacker only needs an authenticated user account; no administrator scope is required. The attack uses the attacker’s own session-scoped agent and does not require overwriting a shared agent bundle.
What can be done if patching is not immediately possible?
Ensure OMNIGENT_RUNNER_WORKSPACE is set for every runner session that can realize uploaded session-scoped bundles. This causes the runner to override the bundle’s cwd value and prevents the described path from being used.
How can I determine whether a runner is affected?
Check whether the runner can realize uploaded session-scoped bundles while OMNIGENT_RUNNER_WORKSPACE is absent. Runners where that environment variable is consistently set are not affected by this specific issue.