Files
temp/features/steps/actor_run_signature_security_steps.py
hamza.khyari 5e96b4bf80 feat(resource): implement 6-level execution environment precedence chain
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
2026-03-31 16:01:58 +00:00

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