mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-04 07:20:10 +00:00
security: tighten permission handling
This commit is contained in:
+43
-1
@@ -12,7 +12,7 @@ def test_defaults_when_no_files(tmp_path):
|
||||
assert cfg.model == "gpt-5.6-sol"
|
||||
assert cfg.mode == "interactive"
|
||||
assert cfg.max_iterations == 150
|
||||
assert "pytest" in cfg.allowed_commands
|
||||
assert cfg.allowed_commands == []
|
||||
|
||||
|
||||
def test_global_and_workspace_override(tmp_path):
|
||||
@@ -31,6 +31,48 @@ def test_global_and_workspace_override(tmp_path):
|
||||
assert cfg.mode == "plan" # from workspace
|
||||
|
||||
|
||||
def test_workspace_cannot_grant_its_own_permissions(tmp_path):
|
||||
g = tmp_path / "global.toml"
|
||||
g.write_text(
|
||||
'allowed_commands = ["git status"]\nauto_allow = ["write_file"]\n'
|
||||
)
|
||||
ws = tmp_path / "ws"
|
||||
(ws / ".coworker").mkdir(parents=True)
|
||||
(ws / ".coworker" / "config.toml").write_text(
|
||||
'allowed_commands = ["python3"]\nauto_allow = ["run_shell"]\n'
|
||||
)
|
||||
|
||||
cfg = load_config(ws, global_path=g)
|
||||
assert cfg.allowed_commands == ["git status"]
|
||||
assert cfg.auto_allow == ["write_file"]
|
||||
|
||||
|
||||
def test_build_engine_honors_explicit_empty_command_allowlist(tmp_path):
|
||||
from coworker.agent import build_code_engine
|
||||
from coworker.config import global_config_path
|
||||
|
||||
global_config_path().parent.mkdir(parents=True)
|
||||
global_config_path().write_text('allowed_commands = ["pytest"]\n')
|
||||
|
||||
class _Stub:
|
||||
def complete(self, **k): # pragma: no cover
|
||||
raise NotImplementedError
|
||||
|
||||
def capabilities(self, m): # pragma: no cover
|
||||
raise NotImplementedError
|
||||
|
||||
engine = build_code_engine(
|
||||
workspace=tmp_path, provider=_Stub(), allowed_commands=[]
|
||||
)
|
||||
try:
|
||||
decision = engine.permissions.evaluate(
|
||||
"run_shell", {"command": "pytest -q"}, None
|
||||
)
|
||||
assert not decision.allowed and decision.needs_user
|
||||
finally:
|
||||
engine.executor.close()
|
||||
|
||||
|
||||
def test_build_engine_respects_max_iterations(tmp_path):
|
||||
(tmp_path / ".coworker").mkdir()
|
||||
(tmp_path / ".coworker" / "config.toml").write_text("max_iterations = 3\n")
|
||||
|
||||
@@ -131,13 +131,21 @@ def test_allowlist_prefix_is_argv_boundary(tmp_path):
|
||||
assert eng.evaluate("run_shell", {"command": "lsof"}, None).needs_user
|
||||
|
||||
|
||||
def test_interpreters_not_auto_allowed_by_default(tmp_path):
|
||||
# The default allowlist must not auto-run interpreters (arbitrary code execution).
|
||||
def test_shell_commands_not_auto_allowed_by_default(tmp_path):
|
||||
# There is no generally safe executable: these examples cover code execution,
|
||||
# environment disclosure, reads outside the workspace, and helper execution.
|
||||
from coworker.config import DEFAULT_ALLOWED_COMMANDS
|
||||
|
||||
eng = PermissionEngine(
|
||||
workspace_root=tmp_path, allowed_commands=list(DEFAULT_ALLOWED_COMMANDS)
|
||||
)
|
||||
for cmd in ("python3 -c 'import os'", "node -e 1", "npm run x", "npx foo"):
|
||||
for cmd in (
|
||||
"python3 -c 'import os'",
|
||||
"pytest /tmp/attacker_test.py",
|
||||
"find . -exec sh -c 'echo arbitrary' {} +",
|
||||
"cat ~/.config/coworker/secrets.json",
|
||||
"echo $OPENAI_API_KEY",
|
||||
"git status",
|
||||
):
|
||||
d = eng.evaluate("run_shell", {"command": cmd}, None)
|
||||
assert not d.allowed and d.needs_user, cmd
|
||||
|
||||
Reference in New Issue
Block a user