Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions backend/secuscan/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,15 +601,20 @@ async def read_stream():
return f"Execution error: {str(e)}", -1

def _resolve_execution_timeout(self, inputs: Dict[str, Any]) -> int:
"""Resolve per-task process timeout from plugin inputs."""
"""Resolve per-task process timeout from plugin inputs.

The caller may request a shorter timeout than the operator cap, but
never a longer one. ``settings.sandbox_timeout`` is the hard ceiling
and is always enforced regardless of what the client supplies.
"""
for key in ("max_scan_time", "timeout"):
raw_value = inputs.get(key)
try:
timeout = int(raw_value)
except (TypeError, ValueError):
continue
if timeout > 0:
return timeout
return min(timeout, settings.sandbox_timeout)
return settings.sandbox_timeout

def _classify_command_result(self, plugin, output: str, exit_code: int) -> tuple[str, Optional[str]]:
Expand Down
32 changes: 32 additions & 0 deletions testing/backend/unit/test_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,38 @@ def test_classify_command_result_fails_on_undefined_flag_even_with_zero_exit(set
assert error is not None


def test_resolve_execution_timeout_clamps_requested_timeout(monkeypatch):
monkeypatch.setattr(settings, "sandbox_timeout", 600)

executor = TaskExecutor()

assert executor._resolve_execution_timeout({"timeout": 9999}) == 600


def test_resolve_execution_timeout_allows_shorter_requested_timeout(monkeypatch):
monkeypatch.setattr(settings, "sandbox_timeout", 600)

executor = TaskExecutor()

assert executor._resolve_execution_timeout({"timeout": 120}) == 120


def test_resolve_execution_timeout_ignores_invalid_values(monkeypatch):
monkeypatch.setattr(settings, "sandbox_timeout", 600)

executor = TaskExecutor()

assert executor._resolve_execution_timeout({"timeout": "invalid"}) == 600


def test_resolve_execution_timeout_prefers_max_scan_time(monkeypatch):
monkeypatch.setattr(settings, "sandbox_timeout", 600)

executor = TaskExecutor()

assert executor._resolve_execution_timeout({"max_scan_time": 90, "timeout": 120}) == 90


@pytest.mark.asyncio
async def test_execute_task_sets_cancelled_status_in_db(setup_test_environment):
"""
Expand Down
Loading