CVE-2026-109444: Remote Code Execution via Ungated shell_run Tool Driven by Prompt Injection
A flaw in OpenSRE's action agent lets attacker-influenced alert or incident text trigger its shell_run tool with no human approval. An attacker can run arbitrary commands on the OpenSRE host and steal every secret in its environment, including production infrastructure credentials.

Overview
OpenSRE (Tracer-Cloud/opensre) is an open-source AI SRE agent that triages alerts and incidents from observability tools such as Grafana and Loki and can take remediation actions. Its action agent always has a shell_run tool that executes arbitrary shell commands on the host, under a shell policy that allows every command.
OpenSRE has a human approval gate, but shell_run never opts into it, and on non-interactive surfaces such as the gateway and scheduled runs the gate approves everything automatically. Because the same agent reads alert text that an attacker can influence, instructions hidden in a forwarded alert can make it run commands on the host. Those commands inherit the full process environment, which on an SRE agent typically holds production credentials.
Because exploitation can occur through nothing more than a crafted alert, with no authentication, no human approval and full access to the production credentials OpenSRE holds, affected deployments should require approval for shell execution immediately.
Affected Product
Affected Files
- tools/interactive_shell/shell/policy.py (alpha-mode policy, allow every non-empty command)
- tools/interactive_shell/shell/execution.py (_shell_argv builds [$SHELL or /bin/sh, "-lc", command], run via subprocess.run)
- tools/interactive_shell/actions/shell.py (shell_run tool, never sets requires_approval)
- tools/interactive_shell/actions/cli_command.py (cli_exec tool, same ungated pattern)
- core/tool/contracts.py (requires_approval default False)
- tools/interactive_shell/subprocess_presenter.py (execution_allowed returns True; headless_subprocess_presenter_factory discards confirm_fn)
- bootstrap/adapters.py (installs the auto-allow presenter process-wide for non-TTY surfaces)
- tools/interactive_shell/subprocess.py (child env built from dict(os.environ))
- integrations/messaging_security.py (fail-open "No allowlist configured, open access")
Root Cause
OpenSRE's action agent is given shell_run, a MUTATING tool that executes an arbitrary shell command on the host.
The command is run as [$SHELL or /bin/sh, "-lc", command] through subprocess.run, and the shell policy is in "alpha mode" that allows every command with no allow-list and no deny floor, rejecting only empty input.
The project implements a human approval gate keyed on a tool's requires_approval flag, but shell_run (and the sibling cli_exec) never set it, so they inherit the default False and are never gated, while tools such as slack_send_message and fix_sentry_issue do set it. On every non-interactive surface the gate is additionally bypassed: the headless presenter's execution_allowed returns True unconditionally, and its factory discards the host-supplied confirm_fn confirmation callback, and this presenter is installed process-wide for non-TTY surfaces (gateway, scheduled, embedded).
The same agent turn ingests incident and alert text relayed from observability sources (for example a forwarded Grafana or Loki alert), which is attacker-influenceable, so instructions embedded in that content can drive the model to emit a shell_run call that executes with no human confirmation. The child process environment is copied from the full os.environ, so an executed command can read every secret the deployment holds.
A fail-open messaging authorization branch (require_dm_pairing False with an empty allowed_user_ids) returns open access, providing an additional path for an unauthenticated messaging user to drive the ungated tool directly.
PoC
Environment: Python 3.12.
git clone --depth 1 https://github.com/Tracer-Cloud/opensre.git && cd opensreuv venv --python 3.12 .venvuv pip install --python .venv/bin/python -e .1. Preconditions, no LLM and no network (poc/incode_poc.py): shell policy returns allow for an arbitrary command; the command executes (executed_with_shell=True, exit_code=0, real uid=501 returned); shell_run.requires_approval is False; shell_run.is_available({}) is True; the headless presenter returns True from execution_allowed even for rm -rf /; the child environment is a superset of os.environ containing a planted canary variable.
2. Deterministic end to end, no external network (poc/deterministic_e2e.py): runs OpenSRE's real headless turn stack (AgentSession.run_headless_turn, the shared API used by the scheduled and gateway runners), the real adapter bootstrap, the real ReAct loop, real tool dispatch, the installed presenter, the real shell policy and the real subprocess.run; only the model is replaced by a loopback stub returning one shell_run tool call. Observed: shell_run executed, marker file written with the real uid line, no approval prompt, and the child read the planted environment canary.
3. End to end with a real local model via Ollama (poc/e2e_injection_driver.py --model qwen3:8b --runs 3): the untrusted input is a forwarded Grafana alert whose runbook_note field carries the injected instructions; shell_run was present in the 27 tools offered on every run. qwen3:8b emitted the injected command in runs 1 and 2 of 3 and it executed on the host with the real uid returned; qwen3:14b emitted 14 shell_run calls across 2 runs, all executed with no approval, including kubectl calls; llama3.1:8b emitted tool JSON as prose and did not use the tool channel.
Commands observed executing during testing, chosen by the model, with no approval prompt: kubectl config view; kubectl config set-cluster prod-cluster --server=... --insecure-skip-tls-verify=true; sudo systemctl restart <service>; curl -X POST https://<host>/... --header "Authorization: Bearer ...". The injected command was also persisted into OpenSRE's cross-session memory store under ~/.opensre/memory/ as a preference entry; automatic re-execution on a later clean alert was observed once and did not reproduce on two retries, so re-execution is reported as unconfirmed while the persistence is confirmed.
Impact
An attacker who can influence the text of an alert or incident that OpenSRE triages, for example the annotation, runbook note, or log payload of a forwarded Grafana or Loki alert, can drive the action agent to execute arbitrary shell or Python commands on the OpenSRE host as the OpenSRE process user, with no authentication to OpenSRE and no human approval.
Because the executed command inherits the full process environment, a single command discloses every configured secret, including LLM API keys, DATABASE_URL, Datadog, Grafana, and Slack tokens, and cloud credentials, and the host ships outbound network access for exfiltration. Since OpenSRE is an SRE remediation agent, those secrets are typically production infrastructure credentials, so host compromise extends to the monitored estate.
The ungated cli_exec and execute_python_code tools share the same path, and on a deployment with the fail-open messaging branch active an unauthenticated messaging user can drive the ungated tool directly.
Suggested Remediation
1. Set requires_approval=True on shell_run and cli_exec.
2. Stop discarding confirm_fn in headless_subprocess_presenter_factory, and make headless surfaces fail closed when no approver is available rather than returning True.
3. Add an allow-list or deny floor to the shell policy and remove the unconditional allow-every-command alpha mode from production builds.
4. Scope the child process environment instead of passing the full os.environ.
5. Treat content gathered from observability sources as untrusted data, keeping a separation between data and instructions so retrieved content cannot drive tool emission u6. guarded.
6. Close the no-allowlist-plus-pairing-disabled open-access branch and require a real OPENSRE_PAIRING_SECRET.
7. Separate low-severity issue: bind the opt-in local Grafana dev stack to 127.0.0.1 and drop anonymous Admin and the default admin/admin credentials.
Additional Resources
- CVE-2026-109444 record