Investigate in your agent.

Turn this advisory into a focused investigation. Ask your agent to check your project’s versions, explain the evidence, and suggest a compatible fix.

Your agent needs remote MCP support and access to https://cve.new/mcp.

Read the setup guide

Skip to content
Check your stack
CVE-2026-113663

AG2: Newline injection in SandboxShellTool bypasses allowed=[...] and readonly=True, enabling arbitrary command execution

Products / packages ag2ai ag2 · ag2ai autogen · ag2 · 1 more

Published

Share

Severity and exploitation

Not in CISA KEVSource: HIGH

KEV coverage does not rule out exploitation.

KEV checked 15/11/2026, 02:05:34 UTC
GitHub_M · CVSS:3.1 · 8.8
Assessment vectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
OSV · CVSS:3.1 · 8.8
Assessment vectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
The headline uses the highest imported catalog score. Assessments can differ by source and CVSS version.

Severity

high8.8

CVSS measures technical severity, not the likelihood of an attack.

Known exploitation

Not listed in the checked CISA KEV catalog

Absence from CISA KEV does not rule out attacks.

Public exploit

Not established by this record

Missing evidence does not mean no public exploit exists.

Remediation

Some package fixes recorded

Fix versions are recorded for 1 of 2 mapped packages. Check each release branch and any separate product guidance.

What this means

SandboxShellTool's restricted modes — allowed=[...] (explicit command allowlist) and readonly=True (implicit READONLY_COMMANDS allowlist) — can both be bypassed by embedding a newline character inside the command string. The filter only inspects a single-line set of shell metacharacters, but execution runs the raw string through sh -c, where a shell treats an embedded newline exactly like a ; statement separator. An agent (or content that steers the agent, e.g. via prompt injection) can therefore run arbitrary shell commands even when the tool was configured to allow only a specific…

Source excerpt · OSV

Attack conditions

No attack-condition excerpt has been identified here. Review the full source description and references for authentication, network access, and configuration requirements.

Review supporting sources →

Affected versions

Check the exact model, release, and any platform requirements against your installation.

ag2ai · GitHub_M

ag2

1 range

Compare with this publisher’s ranges. This does not inspect your installation.

affected

>= 0.13.2, < 1.1.1

unaffected

unknown

Other versions: unknown

ag2ai · GitHub_M

autogen

1 range

Compare with this publisher’s ranges. This does not inspect your installation.

affected

>= 0.13.2, <= 0.14.1

unaffected

unknown

Other versions: unknown

Packages

Compare each installed version with the publisher’s affected ranges. Where a fix is recorded, choose the appropriate release branch and test compatibility before upgrading.

ag2 PyPI
Affected range Recorded fix
From 0.13.2 before 1.1.1Ecosystem versions1.1.1

Version evidence: OSV · GHSA-2h4w-29cv-2cmv

View original version evidence (13 explicit versions)
Affected range Recorded fix
From 0.13.2 through 0.14.1 (inclusive)Ecosystem versionsNot recorded

Version evidence: OSV · GHSA-2h4w-29cv-2cmv

View original version evidence (6 explicit versions)

What to do

Compare your installed product and version with the affected versions above. A matching product name alone does not confirm exposure.

Use the recorded fix for your release branch. Review the publisher’s instructions and test compatibility before upgrading.

cve.new guidance unless attributed to a publisher. This page does not inspect your installation.

Public exploit evidence

No public exploit reference is identified in the imported evidence. This is not a claim that no exploit exists.

Original source description

Read the full publisher description

Summary

SandboxShellTool's restricted modes — allowed=[...] (explicit command allowlist) and readonly=True (implicit READONLY_COMMANDS allowlist) — can both be bypassed by embedding a newline character inside the command string. The filter only inspects a single-line set of shell metacharacters, but execution runs the raw string through sh -c, where a shell treats an embedded newline exactly like a ; statement separator. An agent (or content that steers the agent, e.g. via prompt injection) can therefore run arbitrary shell commands even when the tool was configured to allow only a specific command prefix or only read-only commands. With the default LocalEnvironment() backend this is a real host subprocess (its own docstring: "Commands run via subprocess on the host, so there is no real isolation"), not an isolated container, so this is full host command execution, not merely an escape from an already-sandboxed environment.

Details

Both restricted modes are enforced by ShellAdapter._filter() in ag2/tools/sandbox/adapter/shell.py:

def _filter(self, command: str) -> str | None:
    if self._allowed is not None:
        if not any(matches(p, command) for p in self._allowed):
            return f"Command not allowed: {command!r}"
        if contains_shell_operator(command):
            return f"Command not allowed (shell operators are not permitted in restricted mode): {command!r}"
    ...

readonly=True reuses this exact same branch: SandboxShellTool.__init__ / ShellAdapter.__init__ sets self._allowed = list(READONLY_COMMANDS) when readonly is set and no explicit allowed is given, so both modes share one filter and one bypass.

contains_shell_operator() in ag2/tools/sandbox/filter.py checks the command against:

_SHELL_OPERATORS: tuple[str, ...] = (">", ">>", "|", ";", "&&", "||", "`", "$(")

This tuple has no entry for \n (or \r). matches() only checks that the command starts with an allowed prefix followed by a space or end-of-string; it never inspects the remainder of the string. Execution is:

argv = ["sh", "-c", command]
...
return await sandbox.exec(argv, env=self._env, timeout=self._timeout)

A POSIX shell splits sh -c "<line1>\n<line2>" into two sequential statements, the same as if they were separated by ;. So a command such as "cat allowed_file.txt\nrm -rf /anything":

  1. Passes matches("cat", command) — the string starts with "cat ".
    1. Passes contains_shell_operator(command) — \n is not a recognized operator.
    1. Reaches sh -c "cat allowed_file.txt\nrm -rf /anything", which runs both statements.

Proof of Concept

Verified directly against the current main branch (github.com/ag2ai/ag2, commit a05ff1cb6ff4955ef252a4e0d4a27d3a32ad57f0, cloned 2026-09-21), by importing the real ag2/tools/sandbox/filter.py module (stdlib-only, no other AG2 dependencies needed) and reproducing ShellAdapter._filter()'s allowlist branch verbatim:

=== allowed=['cat'] ===
'cat /etc/passwd; rm -rf /tmp/x'   -> Command not allowed (shell operators are not permitted in restricted mode): ...
'cat /etc/passwd && rm -rf /tmp/x' -> Command not allowed (shell operators are not permitted in restricted mode): ...
'cat /etc/passwd | rm -rf /tmp/x'  -> Command not allowed (shell operators are not permitted in restricted mode): ...
'cat /etc/passwd\nrm -rf /tmp/x'   -> None   <-- BYPASS: newline variant is NOT rejected

=== readonly=True (uses READONLY_COMMANDS as the allowed list) ===
'echo hi\nid'                      -> None   <-- BYPASS in readonly mode too

=== sh -c really treats the embedded newline as a second statement (mirrors ShellAdapter._exec_async) ===
$ sh -c 'echo first-command-output\necho SECOND-STATEMENT-RAN-$((21*2))'
first-command-output
SECOND-STATEMENT-RAN-42

The three standard chaining attempts (;, &&, |) are correctly rejected by the real filter, confirming it does real work; the newline-separated variant, expressing the same intent, is accepted by the identical filter in both restricted modes, and a real sh -c call confirms the shell executes both statements.

Impact

Any agent configured with SandboxShellTool(allowed=[...]) or SandboxShellTool(readonly=True) — the two configurations the tool's own docstring presents as actual security boundaries, as opposed to blocked, which the same docstring explicitly says "is not a security boundary" — can be made to execute arbitrary shell commands via a single embedded newline in the command argument. Because tool arguments are LLM-generated and can be steered by content the agent processes (a fetched document, tool output, or any other text reaching the model — i.e. indirect prompt injection), this is reachable without the operator of the agent writing the malicious command themselves. With the default LocalEnvironment() backend, this is command execution on the host process running the agent, not confined to a container.

Suggested Remediation

  • Add "\n" and "\r" to _SHELL_OPERATORS in ag2/tools/sandbox/filter.py so contains_shell_operator() rejects embedded newlines the same way it rejects ;.
    • More robustly, execute allowlisted/readonly commands without invoking a shell in restricted mode (shlex.split + exec with no sh -c wrapper), so redirection, chaining, substitution, and every kind of statement separator are inert by construction instead of relying on an enumerated denylist.

Environment Tested

  • Repository: github.com/ag2ai/ag2, commit a05ff1cb6ff4955ef252a4e0d4a27d3a32ad57f0 (main), cloned 2026-09-21.
    • PyPI package ag2, version 1.0.5 per pyproject.toml on this commit.
    • Python 3, stdlib only (subprocess, importlib) against the real, unmodified ag2/tools/sandbox/filter.py — no AG2 runtime dependencies required to reproduce.

References

5 sources

Inside the record

Evidence available

Package identity
Version evidence
Remediation guidance · not supplied
Weakness classification
Additional publishers

Describes the evidence in this view, not whether the product is safe.

Original records

CVE-2026-113663

View the latest unmodified CVE JSON from the official source. It may be newer than this imported page.

cve.new JSON →

Underlying weaknesses

The source’s classification of what caused the vulnerability.

CWE-78 ↗

CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

GitHub_M

Related →
CWE-184 ↗

CWE-184: Incomplete List of Disallowed Inputs

GitHub_M

Related →
Contributing organizations · 2
GitHub_M

Assigning organization

2026-11-05
CISA-ADP

Additional data publisher

2026-11-09

Timeline

All times UTC
  1. Advisory published

    Publication date supplied by the source.

  2. Source updated

    Latest modification date supplied by the source.

  3. Last imported by cve.new

    Latest catalog import, not the date we first saw this advisory.

What changed

Follow changes
  1. CVE · CVE-2026-113663

    Observed 2026-11-09T17:51:16.994Z · upstream updated 2026-11-09T17:40:51.743Z

    Prioritization updated

    before

    []

    after

    [
      {
        "source": "CISA-ADP",
        "type": "SSVC",
        "role": "CISA Coordinator",
        "timestamp": "2026-11-09T17:37:11.466545Z",
        "decisions": [
          {
            "name": "Exploitation",
            "value": "none"
          },
          {
            "name": "Automatable",
            "value": "no"
          },
          {
            "name": "Technical Impact",
            "value": "total"
          }
        ]
      }
    ]

Large field values are marked as truncated previews. Original source snapshots remain retained.

Showing up to 50 most recent observed changes.