#107

Scope-restriction bypass via the terminal tool's free-text command argument

Opencarfeiiopened 18d ago

Affected versions: confirmed on commit 87d763a9c830597e9b8c1d56293300f6961beec2 (v0.2.0, main branch).

Summary

The workspace scope guard that is supposed to block any tool call touching a host outside the operator-configured target list only inspects a fixed, shallow set of dict keys (target, host, hostname, ip, address, url, hosts, targets). The terminal tool's schema has no such key (only command, timeout, working_dir), so any out-of-scope host embedded inside a shell command string, e.g. curl http://out-of-scope-host.com/... or nmap out-of-scope-host.com, is never extracted, never checked, and the command always executes regardless of the configured scope.

Details

pentestagent/workspaces/validation.py defines gather_candidate_targets, whose own docstring says it is "shallow, non-recursive" and collects values only for a fixed list of key names. pentestagent/agents/base_agent.py calls this function on the raw arguments dict of every tool call before execution, and only blocks the call if any extracted candidate fails is_target_in_scope.

The browser tool's schema declares a url parameter, one of the recognized key names, so an out-of-scope URL is correctly extracted and blocked.

The terminal tool's schema declares only command (required), timeout, and working_dir. None of these key names match the fixed list gather_candidate_targets checks, so for any terminal call the candidate list is always empty and the scope guard never fires, no matter what host is embedded in the free-text command string.

The project's own unit test (tests/unit/workspaces/test_validation.py::test_irrelevant_key_ignored) documents this exact behavior directly: gather_candidate_targets({"command": "nmap -sV 10.0.0.1", "port": "80"}) is asserted to return []. The shallow extraction is fine as a unit-level contract, but the terminal tool is the primary action surface of the agent (arbitrary shell commands, including any CLI tools installed in the runtime), so the scope guard currently provides no coverage at all for the tool it most needs to cover.

Steps To Reproduce

(available upon request)

Impact

An operator configures a workspace scope restricting the agent to specific authorized targets. The scope guard exists to stop the agent, whether driven by its own reasoning, a misconfigured task, or content that manipulates its tool-call choices, from touching anything outside that authorization. Because the terminal tool is unguarded, any call embedding an out-of-scope host in the command string bypasses the restriction entirely and executes against hosts the operator never authorized. For a tool whose core purpose is enforcing scope during authorized security testing, this is a complete bypass of that guarantee for its most commonly used capability, with real legal/liability exposure for the operator if the agent reaches an unauthorized third-party host.

CWE-863: Incorrect Authorization. CVSS 3.1: 6.3 (Medium), AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N.

Activity

0

No activity yet.