diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 000000000..dcd91fed8 --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,59 @@ +{ + "version": 1, + "skills": { + "api-security-testing": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/api-security-testing/SKILL.md", + "computedHash": "f1ae8b66cf139807a78dde2b50167ae628635a6c026c17edd864b6c6a65cec5c" + }, + "application-security-testing": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/application-security-testing/SKILL.md", + "computedHash": "3869b05bff8e91d2eee9ec98845e5a0fe0a73cca74ceef8c80b7dadc011431ff" + }, + "ci-security-scanning-with-strix": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/ci-security-scanning-with-strix/SKILL.md", + "computedHash": "ac3b8d18690ee68dcf178f3156caac1006da4377f4e3a02a55cb7ebe99566d9f" + }, + "find-security-vulnerabilities-in-code": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/find-security-vulnerabilities-in-code/SKILL.md", + "computedHash": "b9aaebedb74653bed108ad54790be164fc70e60ca6fbca844e379cb0408498cf" + }, + "fix-security-vulnerabilities-with-strix": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/fix-security-vulnerabilities-with-strix/SKILL.md", + "computedHash": "1f91d165671877f77b0c23f34a4596375dc425de388f5b17d2a4742194a8bfb8" + }, + "managed-pentesting-with-strix": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/managed-pentesting-with-strix/SKILL.md", + "computedHash": "64c0544b9c50750f3d417cae3fdc5cb8710ca0a0d6ac29ed3b81807e37d63ee1" + }, + "owasp-top-10-testing": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/owasp-top-10-testing/SKILL.md", + "computedHash": "82a5e167fd58e3641e61097f9206c8ecaab30e18bbf932499c85a4f58a6a49ba" + }, + "penetration-testing-with-strix": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/penetration-testing-with-strix/SKILL.md", + "computedHash": "a765131ca550714b55260cca234eeb5e09ae5dc32412d0aa6fc5d03860c13667" + }, + "web-app-penetration-testing": { + "source": "usestrix/strix", + "sourceType": "github", + "skillPath": "skills/web-app-penetration-testing/SKILL.md", + "computedHash": "4a26881a2e8d289611e12557aea27f729facfa4aef777ddcb43a6b6f7d6fe2ee" + } + } +} diff --git a/strix/interface/environment.py b/strix/interface/environment.py index fd389769e..c8ef2c62c 100644 --- a/strix/interface/environment.py +++ b/strix/interface/environment.py @@ -1,4 +1,4 @@ -"""Startup environment validation and Docker image management.""" +"""Startup environment validation and sandbox image management.""" import logging import os @@ -11,7 +11,7 @@ from rich.text import Text from strix.config import IntegrationSettings, codex, load_settings from strix.interface.utils import ( - check_docker_connection, + check_runtime_connection, image_exists, process_pull_line, ) @@ -164,7 +164,7 @@ def validate_environment() -> None: ) -def check_docker_installed() -> None: +def check_runtime_installed() -> None: backend = os.environ.get("STRIX_RUNTIME_BACKEND", "").strip() if not backend: try: @@ -205,7 +205,7 @@ def check_docker_installed() -> None: logger.debug("%s CLI present", display_name) -def pull_docker_image() -> None: +def pull_runtime_image() -> None: from docker.errors import DockerException console = Console() @@ -217,7 +217,7 @@ def pull_docker_image() -> None: backend = "docker" backend = (backend or "docker").lower() display_name = "Podman" if backend == "podman" else "Docker" - client = check_docker_connection(backend) + client = check_runtime_connection(backend) image = load_settings().runtime.image diff --git a/strix/interface/main.py b/strix/interface/main.py index c9bd55961..1404f15c4 100644 --- a/strix/interface/main.py +++ b/strix/interface/main.py @@ -17,8 +17,8 @@ from strix.config import codex, load_settings, persist_current from strix.core.paths import run_dir_for from strix.interface.cli_args import parse_arguments from strix.interface.environment import ( - check_docker_installed, - pull_docker_image, + check_runtime_installed, + pull_runtime_image, validate_environment, ) from strix.interface.interactive import ( @@ -469,8 +469,8 @@ def main() -> None: restart_after_update() sys.exit(0) - check_docker_installed() - pull_docker_image() + check_runtime_installed() + pull_runtime_image() validate_environment() # Everything below imports the scan engine; do not race the warm-up thread. diff --git a/strix/interface/scan_setup.py b/strix/interface/scan_setup.py index e68148ff0..6ce37915f 100644 --- a/strix/interface/scan_setup.py +++ b/strix/interface/scan_setup.py @@ -48,8 +48,6 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) -HOST_GATEWAY_HOSTNAME = "host.docker.internal" - class ModelConnectionError(RuntimeError): """An ordinary model preflight failure, annotated with its model route.""" diff --git a/strix/interface/utils.py b/strix/interface/utils.py index 4d8231e3c..41cfa5191 100644 --- a/strix/interface/utils.py +++ b/strix/interface/utils.py @@ -1597,11 +1597,11 @@ def clone_repository(repo_url: str, run_name: str, dest_name: str | None = None) ) from e -def check_docker_connection(backend: str | None = None) -> Any: +def check_runtime_connection(backend: str | None = None) -> Any: import os from strix.config import load_settings - from strix.runtime.backends import get_docker_client + from strix.runtime.backends import get_runtime_client if backend is None: backend = os.environ.get("STRIX_RUNTIME_BACKEND", "").strip() @@ -1614,7 +1614,7 @@ def check_docker_connection(backend: str | None = None) -> Any: display_name = "Podman" if resolved_backend == "podman" else "Docker" try: - client = get_docker_client(resolved_backend) + client = get_runtime_client(resolved_backend) client.ping() except Exception as exc: report_error(f"{resolved_backend}_unavailable", exc) diff --git a/strix/runtime/__init__.py b/strix/runtime/__init__.py index efba53c6a..a354c8ee0 100644 --- a/strix/runtime/__init__.py +++ b/strix/runtime/__init__.py @@ -3,9 +3,9 @@ from strix.runtime.backends import ( backend_supports_bind_mounts, get_backend, - get_docker_client, get_host_gateway, get_podman_socket_candidates, + get_runtime_client, parse_podman_machine_inspect, register_backend, resolve_runtime_socket, @@ -16,9 +16,9 @@ from strix.runtime.backends import ( __all__ = [ "backend_supports_bind_mounts", "get_backend", - "get_docker_client", "get_host_gateway", "get_podman_socket_candidates", + "get_runtime_client", "parse_podman_machine_inspect", "register_backend", "resolve_runtime_socket", diff --git a/strix/runtime/backends.py b/strix/runtime/backends.py index 58aff76c9..085eae166 100644 --- a/strix/runtime/backends.py +++ b/strix/runtime/backends.py @@ -298,8 +298,8 @@ def resolve_runtime_socket(backend: str = "docker") -> str | None: return None -def get_docker_client(backend: str = "docker") -> Any: - """Create a Docker client for ``backend`` using multi-layer socket fallthrough: +def get_runtime_client(backend: str = "docker") -> Any: + """Create a container runtime client for ``backend`` using multi-layer socket fallthrough: STRIX_RUNTIME_SOCKET → DOCKER_HOST → per-backend auto-detection → docker.from_env() default. Gracefully falls through to docker.from_env() on connection/ping failure. @@ -350,9 +350,10 @@ async def _docker_backend( from strix.runtime.docker_client import StrixDockerSandboxClient - raw_client = get_docker_client("docker") + raw_client = get_runtime_client("docker") client = StrixDockerSandboxClient(raw_client) client.host_gateway = get_host_gateway("docker") + client.backend_name = "docker" client.strix_bind_mounts = bind_mounts or [] options = DockerSandboxClientOptions(image=image, exposed_ports=exposed_ports) session = await client.create(options=options, manifest=manifest) @@ -376,9 +377,10 @@ async def _podman_backend( from strix.runtime.docker_client import StrixDockerSandboxClient - raw_client = get_docker_client("podman") + raw_client = get_runtime_client("podman") client = StrixDockerSandboxClient(raw_client) client.host_gateway = get_host_gateway("podman") + client.backend_name = "podman" client.strix_bind_mounts = bind_mounts or [] options = DockerSandboxClientOptions(image=image, exposed_ports=exposed_ports) session = await client.create(options=options, manifest=manifest) diff --git a/strix/runtime/docker_client.py b/strix/runtime/docker_client.py index c8be826a9..06550bfae 100644 --- a/strix/runtime/docker_client.py +++ b/strix/runtime/docker_client.py @@ -125,6 +125,7 @@ def _apply_run_labels(create_kwargs: dict[str, Any]) -> None: class StrixDockerSandboxSession(DockerSandboxSession): sandbox_network: str = "" + backend_name: str = "docker" async def _resolve_exposed_port(self, port: int) -> ExposedPortEndpoint: try: @@ -135,7 +136,7 @@ class StrixDockerSandboxSession(DockerSandboxSession): exposed_ports=self.state.exposed_ports, reason="backend_unavailable", context={ - "backend": "docker", + "backend": self.backend_name, "detail": "container_reload_failed", "network": self.sandbox_network, }, @@ -152,7 +153,7 @@ class StrixDockerSandboxSession(DockerSandboxSession): exposed_ports=self.state.exposed_ports, reason="backend_unavailable", context={ - "backend": "docker", + "backend": self.backend_name, "detail": "container_not_on_network", "network": self.sandbox_network, }, @@ -166,6 +167,7 @@ class StrixDockerSandboxClient(DockerSandboxClient): # backend before ``create()``. Each item is ``{source, target, read_only}``. strix_bind_mounts: list[dict[str, Any]] | None = None host_gateway: str = "host.docker.internal" + backend_name: str = "docker" async def _create_container( self, @@ -275,7 +277,9 @@ class StrixDockerSandboxClient(DockerSandboxClient): inner = session._inner if network and isinstance(inner, DockerSandboxSession): inner.__class__ = StrixDockerSandboxSession - cast("StrixDockerSandboxSession", inner).sandbox_network = network + strix_inner = cast("StrixDockerSandboxSession", inner) + strix_inner.sandbox_network = network + strix_inner.backend_name = self.backend_name return session async def delete(self, session: SandboxSession) -> SandboxSession: diff --git a/tests/test_podman.py b/tests/test_podman.py index d53715e02..708578b72 100644 --- a/tests/test_podman.py +++ b/tests/test_podman.py @@ -12,17 +12,17 @@ from unittest.mock import MagicMock, patch import pytest -from strix.interface.environment import check_docker_installed -from strix.interface.utils import check_docker_connection +from strix.interface.environment import check_runtime_installed +from strix.interface.utils import check_runtime_connection from strix.runtime.backends import ( _BACKENDS, _BIND_MOUNT_BACKENDS, auto_detect_podman_socket, backend_supports_bind_mounts, get_backend, - get_docker_client, get_host_gateway, get_podman_socket_candidates, + get_runtime_client, normalize_socket_url, parse_podman_machine_inspect, register_backend, @@ -307,7 +307,7 @@ def test_socket_fallthrough_graceful_on_missing_or_failed_socket( return_value="unix:///unreachable.sock", ), ): - client = get_docker_client("podman") + client = get_runtime_client("podman") assert client is mock_default_client mock_docker.from_env.assert_called_once() @@ -325,7 +325,7 @@ def test_socket_fallthrough_strix_runtime_socket_raw_path_normalization() -> Non # ============================================================================ -def test_check_docker_installed_podman_success(monkeypatch: pytest.MonkeyPatch) -> None: +def test_check_runtime_installed_podman_success(monkeypatch: pytest.MonkeyPatch) -> None: """When STRIX_RUNTIME_BACKEND=podman, succeeds if podman is in PATH even if docker is not.""" monkeypatch.setenv("STRIX_RUNTIME_BACKEND", "podman") @@ -336,20 +336,20 @@ def test_check_docker_installed_podman_success(monkeypatch: pytest.MonkeyPatch) monkeypatch.setattr("shutil.which", fake_which) # Should not raise or sys.exit - check_docker_installed() + check_runtime_installed() -def test_check_docker_installed_podman_missing(monkeypatch: pytest.MonkeyPatch) -> None: +def test_check_runtime_installed_podman_missing(monkeypatch: pytest.MonkeyPatch) -> None: """When STRIX_RUNTIME_BACKEND=podman and neither podman nor docker in PATH, exits with error.""" monkeypatch.setenv("STRIX_RUNTIME_BACKEND", "podman") monkeypatch.setattr("shutil.which", lambda _cmd: None) with pytest.raises(SystemExit) as exc_info: - check_docker_installed() + check_runtime_installed() assert exc_info.value.code == 1 -def test_check_docker_installed_docker_missing(monkeypatch: pytest.MonkeyPatch) -> None: +def test_check_runtime_installed_docker_missing(monkeypatch: pytest.MonkeyPatch) -> None: """When backend is docker and docker is missing from PATH, exits with error.""" monkeypatch.setenv("STRIX_RUNTIME_BACKEND", "docker") @@ -361,19 +361,19 @@ def test_check_docker_installed_docker_missing(monkeypatch: pytest.MonkeyPatch) monkeypatch.setattr("shutil.which", fake_which) with pytest.raises(SystemExit) as exc_info: - check_docker_installed() + check_runtime_installed() assert exc_info.value.code == 1 -def test_check_docker_connection_podman_uses_podman_backend( +def test_check_runtime_connection_podman_uses_podman_backend( monkeypatch: pytest.MonkeyPatch, ) -> None: - """check_docker_connection connects and pings backend client.""" + """check_runtime_connection connects and pings backend client.""" monkeypatch.setenv("STRIX_RUNTIME_BACKEND", "podman") mock_client = MagicMock() - with patch("strix.runtime.backends.get_docker_client", return_value=mock_client) as mock_get: - client = check_docker_connection() + with patch("strix.runtime.backends.get_runtime_client", return_value=mock_client) as mock_get: + client = check_runtime_connection() mock_get.assert_called_once_with("podman") mock_client.ping.assert_called_once() assert client is mock_client