from __future__ import annotations import os from pathlib import Path import pytest from unified_agent import SandboxPolicy, UnifiedAgent from pentestgpt_agent.agents import ( EXECUTOR_INSTRUCTIONS, SUPERVISOR_INSTRUCTIONS, Executor, Supervisor, ) from pentestgpt_agent.loop import PentestLoop from pentestgpt_agent.memory import MemoryKernel, RunSpec, RunStatus from pentestgpt_agent.trace import EpisodeRunner, TraceStore from tests.support.local_target import local_template_target pytestmark = [pytest.mark.live] if os.getenv("PENTESTGPT_AGENT_LIVE") != "1": pytest.skip( "set PENTESTGPT_AGENT_LIVE=1 to run the live local-target smoke", allow_module_level=True, ) @pytest.mark.asyncio async def test_real_agents_capture_the_local_template_flag(tmp_path: Path) -> None: backend = os.getenv("PENTESTGPT_AGENT_BACKEND", "claude") model = os.getenv("PENTESTGPT_AGENT_MODEL") or None with local_template_target() as benchmark: traces = TraceStore(tmp_path / "runs") supervisor = Supervisor( EpisodeRunner( UnifiedAgent( backend, workspace=tmp_path / "supervisor", model=model, sandbox=SandboxPolicy.FULL_ACCESS, instructions=SUPERVISOR_INSTRUCTIONS, ), traces, ), max_turns=4, ) executor = Executor( EpisodeRunner( UnifiedAgent( backend, workspace=tmp_path / "executor", model=model, sandbox=SandboxPolicy.FULL_ACCESS, instructions=EXECUTOR_INSTRUCTIONS, ), traces, ), max_turns=12, ) result = await PentestLoop( memory=MemoryKernel(tmp_path / "state.sqlite3"), supervisor=supervisor, executor=executor, traces=traces, max_decisions=8, ).run( RunSpec( run_id="live-local-target", goal="Capture the flag from this authorized local web application.", allowed_targets=(benchmark.url,), ) ) assert result.status is RunStatus.COMPLETED assert any(benchmark.flag in observation.statement for observation in result.observations)