项目文件夹

文件
Gelei Deng e238d701f2 feat: 🎸 version 1.0 agentic workflow (#325)
* feat: 🎸 version 1.0 agentic workflow

Major rewrite of PentestGPT to use an agentic pipeline architecture:
Core Changes: - New event-driven architecture with EventBus for
TUI-agent decoupling - Implemented AgentController with 5-state
lifecycle (IDLE->RUNNING->PAUSED->COMPLETED->ERROR) - Added AgentBackend
interface with ClaudeCodeBackend implementation - Session management
with file-based persistence for resumable pentests - Langfuse
integration for observability and tracing Interface: - New Textual-based
TUI with real-time activity feed - Keyboard shortcuts: F1 help, Ctrl+P
pause, Ctrl+Q quit - Enhanced CLI with --target, --instruction,
--non-interactive, --debug flags Project Structure: - Moved legacy
multi-LLM version (v0.15) to legacy/ directory - New pentestgpt/core/
for agent, controller, events, session modules - New
pentestgpt/interface/ for TUI and CLI components - New
pentestgpt/benchmark/ for xbow benchmark integration - Comprehensive
test suite in tests/ with unit and integration tests DevOps: - Docker
support with Ubuntu 24.04 container - GitHub Actions CI/CD pipeline -
Makefile with dev commands (test, lint, format, typecheck) - Added
xbow-validation-benchmarks as submodule

* style: format code with Black

This commit fixes the style issues introduced in abe3be0 according to the output
from Black.

Details: https://github.com/GreyDGL/PentestGPT/pull/325

* fix: 🐛 fix test pipeline

* feat: 🎸 update format

* feat: 🎸 update

---------

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2025-12-13 01:57:24 +08:00

280 行
9.0 KiB
Python

"""Tests for benchmark CLI commands.
Integration tests for the benchmark CLI command handlers.
"""
import json
import tempfile
from argparse import Namespace
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
from pentestgpt.benchmark.cli import cmd_list, cmd_start, cmd_status, cmd_stop
@pytest.mark.integration
class TestBenchmarkCLI:
"""Tests for benchmark CLI commands."""
@pytest.fixture
def sample_benchmark_dir(self) -> Path:
"""Create sample benchmark structure."""
with tempfile.TemporaryDirectory() as tmpdir:
benchmarks_dir = Path(tmpdir)
# Create benchmark 1
bench1 = benchmarks_dir / "XBEN-001-24"
bench1.mkdir()
(bench1 / "benchmark.json").write_text(
json.dumps(
{
"name": "Test SQL Injection",
"description": "A simple SQL injection challenge",
"level": 1,
"tags": ["sqli", "web"],
}
)
)
# Create benchmark 2
bench2 = benchmarks_dir / "XBEN-002-24"
bench2.mkdir()
(bench2 / "benchmark.json").write_text(
json.dumps(
{
"name": "Advanced XSS",
"description": "Cross-site scripting challenge",
"level": 2,
"tags": ["xss", "web"],
}
)
)
# Create benchmark 3
bench3 = benchmarks_dir / "XBEN-003-24"
bench3.mkdir()
(bench3 / "benchmark.json").write_text(
json.dumps(
{
"name": "File Inclusion",
"description": "LFI/RFI challenge",
"level": 3,
"tags": ["lfi", "rfi"],
}
)
)
yield benchmarks_dir
def test_cmd_list_all(self, sample_benchmark_dir: Path, capsys):
"""Test listing all benchmarks."""
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
tags=None,
levels=None,
show_tags=False,
)
result = cmd_list(args)
assert result == 0
captured = capsys.readouterr()
assert "XBEN-001-24" in captured.out
assert "XBEN-002-24" in captured.out
assert "XBEN-003-24" in captured.out
assert "Total: 3 benchmarks" in captured.out
def test_cmd_list_filter_by_tag(self, sample_benchmark_dir: Path, capsys):
"""Test listing benchmarks filtered by tag."""
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
tags=["sqli"],
levels=None,
show_tags=False,
)
result = cmd_list(args)
assert result == 0
captured = capsys.readouterr()
assert "XBEN-001-24" in captured.out
assert "XBEN-002-24" not in captured.out
assert "Total: 1 benchmarks" in captured.out
def test_cmd_list_filter_by_level(self, sample_benchmark_dir: Path, capsys):
"""Test listing benchmarks filtered by level."""
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
tags=None,
levels=[1, 2],
show_tags=False,
)
result = cmd_list(args)
assert result == 0
captured = capsys.readouterr()
assert "XBEN-001-24" in captured.out
assert "XBEN-002-24" in captured.out
assert "XBEN-003-24" not in captured.out
assert "Total: 2 benchmarks" in captured.out
def test_cmd_list_show_tags(self, sample_benchmark_dir: Path, capsys):
"""Test showing all tags."""
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
tags=None,
levels=None,
show_tags=True,
)
result = cmd_list(args)
assert result == 0
captured = capsys.readouterr()
assert "Available tags:" in captured.out
assert "sqli:" in captured.out
assert "web:" in captured.out
assert "xss:" in captured.out
def test_cmd_list_invalid_dir(self, capsys):
"""Test listing from non-existent directory."""
args = Namespace(
benchmarks_dir="/nonexistent/path",
tags=None,
levels=None,
show_tags=False,
)
result = cmd_list(args)
assert result == 1
captured = capsys.readouterr()
assert "Error:" in captured.out
@patch("pentestgpt.benchmark.cli.start_benchmark")
def test_cmd_start_success(self, mock_start: MagicMock, sample_benchmark_dir: Path, capsys):
"""Test starting a benchmark successfully."""
mock_start.return_value = {
"success": True,
"target_url": "http://localhost:8080",
}
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
benchmark_id="XBEN-001-24",
)
result = cmd_start(args)
assert result == 0
mock_start.assert_called_once()
captured = capsys.readouterr()
assert "Benchmark started successfully!" in captured.out
assert "http://localhost:8080" in captured.out
@patch("pentestgpt.benchmark.cli.start_benchmark")
def test_cmd_start_failure(self, mock_start: MagicMock, sample_benchmark_dir: Path, capsys):
"""Test handling failed benchmark start."""
mock_start.return_value = {
"success": False,
"message": "Docker error",
}
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
benchmark_id="XBEN-001-24",
)
result = cmd_start(args)
assert result == 1
captured = capsys.readouterr()
assert "Failed to start benchmark" in captured.out
assert "Docker error" in captured.out
def test_cmd_start_not_found(self, sample_benchmark_dir: Path, capsys):
"""Test starting non-existent benchmark."""
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
benchmark_id="XBEN-999-24",
)
result = cmd_start(args)
assert result == 1
captured = capsys.readouterr()
assert "not found" in captured.out
@patch("pentestgpt.benchmark.cli.stop_benchmark")
def test_cmd_stop_success(self, mock_stop: MagicMock, sample_benchmark_dir: Path, capsys):
"""Test stopping a benchmark successfully."""
mock_stop.return_value = {"success": True}
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
benchmark_id="XBEN-001-24",
)
result = cmd_stop(args)
assert result == 0
mock_stop.assert_called_once()
captured = capsys.readouterr()
assert "stopped successfully" in captured.out
@patch("pentestgpt.benchmark.cli.stop_benchmark")
def test_cmd_stop_failure(self, mock_stop: MagicMock, sample_benchmark_dir: Path, capsys):
"""Test handling failed benchmark stop."""
mock_stop.return_value = {
"success": False,
"message": "Container not running",
}
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
benchmark_id="XBEN-001-24",
)
result = cmd_stop(args)
assert result == 1
captured = capsys.readouterr()
assert "Failed to stop" in captured.out
def test_cmd_stop_not_found(self, sample_benchmark_dir: Path, capsys):
"""Test stopping non-existent benchmark."""
args = Namespace(
benchmarks_dir=str(sample_benchmark_dir),
benchmark_id="XBEN-999-24",
)
result = cmd_stop(args)
assert result == 1
captured = capsys.readouterr()
assert "not found" in captured.out
@patch("pentestgpt.benchmark.cli.get_running_benchmarks")
def test_cmd_status_running(self, mock_running: MagicMock, capsys):
"""Test showing running benchmarks."""
mock_running.return_value = [
{
"name": "xben-001-24",
"ports": "0.0.0.0:8080->80/tcp",
"status": "Up 5 minutes",
}
]
args = Namespace()
result = cmd_status(args)
assert result == 0
captured = capsys.readouterr()
assert "Running benchmark containers:" in captured.out
assert "xben-001-24" in captured.out
assert "8080" in captured.out
@patch("pentestgpt.benchmark.cli.get_running_benchmarks")
def test_cmd_status_none_running(self, mock_running: MagicMock, capsys):
"""Test showing status when no benchmarks running."""
mock_running.return_value = []
args = Namespace()
result = cmd_status(args)
assert result == 0
captured = capsys.readouterr()
assert "No benchmark containers currently running" in captured.out