forked from HAL9000/cleveragents-core
5e96b4bf80
Implement the spec's 6-level execution environment precedence chain (spec lines 19324-19386): 1. Plan override (priority=override) — always wins 2. Project override (priority=override) — wins over devcontainer 3. Nearest-ancestor devcontainer — auto-discovered 4. Plan fallback (priority=fallback) — defers to devcontainer 5. Project fallback (priority=fallback) — defers to closer scopes 6. Host default — final fallback - New resolve_with_precedence() API on ExecutionEnvironmentResolver - Added execution_env_priority field to ContextConfig (project model) - has_devcontainer() helper for devcontainer-instance detection - Legacy 4-level resolve() preserved for backward compatibility - _parse_priority() defaults missing priority to FALLBACK - 13 new Behave scenarios testing all 6 levels + edge cases - Updated CHANGELOG ISSUES CLOSED: #877
67 lines
2.1 KiB
Python
67 lines
2.1 KiB
Python
"""Security-oriented step definitions for actor run signature tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import click
|
|
import typer
|
|
from behave import then, when
|
|
|
|
with contextlib.suppress(ImportError, ModuleNotFoundError):
|
|
from cleveragents.cli.commands._resolve_actor import resolve_config_files
|
|
from cleveragents.core.exceptions import NotFoundError
|
|
|
|
|
|
@when("I call resolve_config_files with a control-character actor name")
|
|
def step_resolve_with_control_character_name(context: Any) -> None:
|
|
actor_name = "local/\x1b[31mevil\x1b[0m\x00actor"
|
|
|
|
mock_registry = MagicMock()
|
|
mock_registry.get.side_effect = NotFoundError(
|
|
resource_type="actor",
|
|
resource_id=actor_name,
|
|
)
|
|
|
|
mock_container = MagicMock()
|
|
mock_container.actor_registry.return_value = mock_registry
|
|
|
|
captured_stderr: list[str] = []
|
|
original_echo = typer.echo
|
|
|
|
def _capture_echo(message: str = "", err: bool = False, **kwargs: Any) -> None:
|
|
if err:
|
|
captured_stderr.append(message)
|
|
original_echo(message, err=err, **kwargs)
|
|
|
|
with (
|
|
patch(
|
|
"cleveragents.cli.commands._resolve_actor.get_container",
|
|
return_value=mock_container,
|
|
),
|
|
patch(
|
|
"cleveragents.cli.commands._resolve_actor.typer.echo",
|
|
side_effect=_capture_echo,
|
|
),
|
|
):
|
|
try:
|
|
resolve_config_files(actor_name, [])
|
|
context.resolve_exit_code = 0
|
|
except (SystemExit, click.exceptions.Exit) as exc:
|
|
context.resolve_exit_code = getattr(
|
|
exc, "exit_code", getattr(exc, "code", 1)
|
|
)
|
|
|
|
context.resolve_stderr = " ".join(captured_stderr)
|
|
|
|
|
|
@then("the not-found error should not include control characters")
|
|
def step_error_sanitises_control_characters(context: Any) -> None:
|
|
assert context.resolve_exit_code == 2
|
|
assert "not found in registry" in context.resolve_stderr
|
|
assert "\x1b" not in context.resolve_stderr
|
|
assert "\x00" not in context.resolve_stderr
|
|
assert "evil" in context.resolve_stderr
|