Skip to content

tool-bash: strict profile skips quoted payloads (bash -c "sudo id" passes) #408

Open

Description

Summary

The bash tool in microsoft/amplifier-module-tool-bash (that repo has issues disabled, so filing here) applies a strict safety profile (the default) that matches literal spellings at command position, and command positions inside quoted strings are excluded by design (to avoid false positives like git commit -m 'rm -rf cleanup'). The side effect: any blocked command wrapped in an interpreter payload is never examined, because it sits inside quotes:

'sudo id'                                 -> allowed=False  reason='Privilege escalation not allowed in strict mode'
'rm -rf /'                                -> allowed=False  reason='Prevents root filesystem deletion'
'bash -c "sudo id"'                       -> allowed=True
"sh -c 'rm -rf /'"                        -> allowed=True
"eval 'sudo id'"                          -> allowed=True
'echo sudo | sh'                          -> allowed=True
'python -c "import os; os.system(\'rm -rf /\')"' -> allowed=True

Combined with validate() ending in "Default: allow" for everything not on the list, the profile only stops commands typed exactly the way the patterns spell them.

Where it happens

amplifier_module_tool_bash/safety.py:

  • _is_in_command_position() returns False for any position _in_quoted_region() — so an occurrence of sudo, rm -rf /, etc. inside '…' or "…" is skipped entirely.
  • STRICT_PROFILE blocks the literal tokens sudo, su -, rm -rf /, rm -rf ~, rm -fr …, mkfs, passwd, dd if=/dev/zero, the fork bomb — but nothing about interpreter wrappers (bash -c, sh -c, eval, python -c, env …) that carry the payload as a quoted argument.

Repro (offline, shipped bytes at HEAD)

Against amplifier_module_tool_bash/safety.py (blob a15c7fba):

import amplifier_module_tool_bash.safety as safety

v = safety.SafetyValidator(profile="strict")
for cmd in ["sudo id", "rm -rf /", 'bash -c "sudo id"', "sh -c 'rm -rf /'",
            "eval 'sudo id'", "echo sudo | sh",
            'python -c "import os; os.system(\'rm -rf /\')"']:
    r = v.validate(cmd)
    print(f"{cmd!r:58s} -> allowed={r.allowed}" + (f"  reason={r.reason!r}" if not r.allowed else ""))

The verbatim output is the table above.

Context

This tool does default require_approval = True, so the approval layer — not the pattern list — is the real control, and this report is about the gap between what the README §Security tells operators the profiles do and what they can do:

Use strict profile (default) for shared/workstation environments … Never execute untrusted user input

A pattern list that can be defeated by quoting cannot carry "never execute untrusted input" on a workstation; the two bullets promise different strengths. The same text-matching mechanism is reported against the pwsh sibling module in #407 (microsoft/amplifier-bundle-windows-shell), which additionally ships with no approval surface at all.

Suggested direction

  • At strict, reject interpreter wrappers carrying quoted payloads (bash -c, sh -c, eval, python -c, …) rather than skipping their contents — the false-positive goal (commit messages, echo strings) is about string arguments to ordinary commands, not about spawning a nested interpreter whose entire payload is quoted command text.
  • Block eval outright at strict, the way sudo is blocked.
  • Longer term, decide on parsed structure rather than text: tokenize the command (e.g. shlex) and evaluate the resulting argv tree, including recursing into -c payloads, instead of substring positions.

Verified at HEAD aa363e9b (default branch main), 2026-09-20.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions