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: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ jobs:
if: steps.changed_paths.outputs.docs_only != 'true'
run: python3 scripts/report_twin_drift.py

- name: Cache Playwright browser download
if: steps.changed_paths.outputs.docs_only != 'true'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.cache/ms-playwright
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('lyrashield/interface/viewer/frontend/package-lock.json') }}
restore-keys: |
playwright-chromium-${{ runner.os }}-

- name: Verify owned viewer source and committed build
if: steps.changed_paths.outputs.docs_only != 'true'
working-directory: lyrashield/interface/viewer/frontend
Expand Down
12 changes: 7 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
repos:
# Ruff for fast linting and formatting
# Ruff for fast linting and formatting.
# rev is the version uv.lock and CI resolve, so a local hook cannot pass
# formatting or lint that the locked gate rejects.
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.13
rev: v0.15.20
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down Expand Up @@ -32,12 +34,12 @@ repos:
- id: check-case-conflict
- id: check-docstring-first

# Security checks with bandit
# Security checks with bandit, pinned to the locked version.
- repo: https://github.com/PyCQA/bandit
rev: 1.8.3
rev: 1.9.4
hooks:
- id: bandit
args: [-c, pyproject.toml]
args: [-c, pyproject.toml, -l]

# Additional Python code quality checks
- repo: https://github.com/asottile/pyupgrade
Expand Down
1 change: 0 additions & 1 deletion .trivyignore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ misconfigurations:
- id: AVD-DS-0002
paths:
- containers/Dockerfile
- Dockerfile
statement: Container starts as root then immediately drops to unprivileged pentester user via setpriv; no sudo or docker socket is available to the agent.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type-check:

security:
@echo "🔒 Running security checks with bandit..."
uv run bandit -r strix lyrashield_adapter lyrashield -q -c pyproject.toml
uv run bandit -r strix lyrashield_adapter lyrashield -q -c pyproject.toml -l
@echo "✅ Security checks complete!"

check-all:
Expand Down
11 changes: 2 additions & 9 deletions lyrashield/artifacts/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import threading
from collections.abc import Callable
from datetime import UTC, datetime
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
from typing import Any, Optional, cast

Expand Down Expand Up @@ -149,6 +148,7 @@
from lyrashield.runtime.session_manager import CLEANUP_FAILED, CLEANUP_REMOVED
from lyrashield.telemetry import posthog, scarf
from lyrashield.utils.redaction import redact_text
from lyrashield.version import engine_version
from strix.config import codex
from strix.config.loader import load_settings
from strix.core.paths import run_dir_for, runtime_state_dir
Expand Down Expand Up @@ -192,10 +192,7 @@

def _strix_version() -> str | None:
"""Best-effort package version for the SARIF tool.driver.version field."""
try:
return version("strix-agent")
except PackageNotFoundError:
return None
return engine_version()


def get_global_report_state() -> Optional["ReportState"]:
Expand Down Expand Up @@ -630,10 +627,6 @@ def set_terminal_reason(self, reason: str) -> None:
if self.run_record.get("status") != "completed":
self.run_record["terminal_reason"] = reason

def set_sandbox_cleanup_status(self, sandbox_removed: bool) -> None:
"""Backward-compatible boolean wrapper around :meth:`set_cleanup_outcome`."""
self.set_cleanup_outcome(CLEANUP_REMOVED if sandbox_removed else CLEANUP_FAILED)

def set_cleanup_outcome(
self,
outcome: str,
Expand Down
4 changes: 0 additions & 4 deletions lyrashield/artifacts/usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,6 @@ def record_ancillary_cost(self, category: str, cost: Any) -> None:
self._ancillary_costs.get(category, 0.0) + numeric_cost
)

@property
def ancillary_cost_total(self) -> float:
return _round_cost(sum(self._ancillary_costs.values()))

@property
def total_cost(self) -> float:
return _round_cost(self._total_cost + sum(self._ancillary_costs.values()))
Expand Down
8 changes: 2 additions & 6 deletions lyrashield/interface/tui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import threading
import webbrowser
from collections.abc import Callable
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as pkg_version
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar

Expand Down Expand Up @@ -51,17 +49,15 @@
from lyrashield.lifecycle.runner import run_strix_scan
from lyrashield.policy.models import is_recommended_or_frontier_model
from lyrashield.runtime import session_manager
from lyrashield.version import engine_version
from strix.config import load_settings


logger = logging.getLogger(__name__)


def get_package_version() -> str:
try:
return pkg_version("strix-agent")
except PackageNotFoundError:
return "dev"
return engine_version() or "dev"


class ChatTextArea(TextArea):
Expand Down
8 changes: 0 additions & 8 deletions lyrashield/interface/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,14 +583,6 @@ def update_layer_status(layers_info: dict[str, str], layer_id: str, layer_status
_update_layer_status(layers_info, layer_id, layer_status)


def process_pull_line(
line: dict[str, Any], layers_info: dict[str, str], status: Any, last_update: str
) -> str:
from lyrashield.interface.image_pull import process_pull_line as _process_pull_line

return _process_pull_line(line, layers_info, status, last_update)


def validate_config_file(config_path: str) -> Path:
console = Console()
path = Path(config_path)
Expand Down
4 changes: 0 additions & 4 deletions lyrashield/lifecycle/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,6 @@ async def park_waiting(self, agent_id: str, *, wait_kind: WaitKind) -> None:
self.wait_kinds[agent_id] = wait_kind
await self.set_status(agent_id, "waiting")

async def wait_kind_of(self, agent_id: str) -> WaitKind | None:
async with self._lock:
return self.wait_kinds.get(agent_id)

async def record_recovery(self, agent_id: str) -> int:
"""Count a turn that ended without a lifecycle tool call; return the new total.

Expand Down
11 changes: 6 additions & 5 deletions lyrashield/telemetry/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import logging
import platform
import sys
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
from typing import Any, Protocol
from uuid import uuid4

from lyrashield.version import engine_version


logger = logging.getLogger(__name__)

Expand All @@ -33,11 +34,11 @@ def get_total_llm_usage(self) -> dict[str, Any]: ...


def get_version() -> str:
try:
return version("strix-agent")
except PackageNotFoundError:
logger.debug("strix-agent version lookup failed", exc_info=True)
resolved = engine_version()
if resolved is None:
logger.debug("engine version lookup failed", exc_info=True)
return "unknown"
return resolved


def is_first_run() -> bool:
Expand Down
28 changes: 28 additions & 0 deletions lyrashield/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Single source of truth for the engine's own package version.

The distribution is named ``lyrashield-engine`` in ``pyproject.toml``. Looking
up any other identifier raises ``PackageNotFoundError`` in every environment that
installed this project, which silently degrades the SARIF
``tool.driver.version`` field and the TUI header.

``engine_version`` never raises: a frozen PyInstaller build or a source checkout
that was never installed has no distribution metadata at all, and a version
string is informational rather than load-bearing.
"""

from __future__ import annotations

from importlib.metadata import PackageNotFoundError, version


DISTRIBUTION_NAME = "lyrashield-engine"


def engine_version() -> str | None:
"""Return the installed engine version, or ``None`` when metadata is absent."""
try:
return version(DISTRIBUTION_NAME)
except PackageNotFoundError:
return None
except Exception: # noqa: BLE001 - metadata backends can fail in frozen builds
return None
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -433,4 +433,11 @@ line-ending = "auto"
[tool.bandit]
exclude_dirs = ["docs", "build", "dist"]
skips = ["B101", "B105", "B601", "B404", "B603", "B607"] # Skip assert, hardcoded-password false positives, shell injection, subprocess import and partial path checks
severity = "medium"
# Bandit reads no severity floor from this file. Only the `-l/--level` CLI flag
# filters, so a `severity = "medium"` key here was inert and misdescribed the
# gate. It is deliberately absent: the enforced floor is LOW, and the
# invocations (Makefile `security`, .pre-commit-config.yaml, and
# scripts/verify-controlled-derivative.sh) carry the explicit `-l` flag that
# enforces it. A medium floor would drop B311, B403 and B405-B409, and the ruff
# equivalents of B403/B405-B409 (S403, S405-S409) are preview-only and inactive
# in this repo, so enforcing medium would silently weaken the security gate.
4 changes: 0 additions & 4 deletions scripts/customer-branding-allowlist.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@
"# Modifications \u00a9 2026 LyraShield; based on upstream Strix (Apache-2.0)",
"from lyrashield.lifecycle.runner import run_strix_scan",
"from strix.config import load_settings",
" return pkg_version(\"strix-agent\")",
" self._app_reference: StrixTUIApp | None = None",
" def set_app_reference(self, app: \"StrixTUIApp\") -> None:",
" if event.button.id == \"stop_agent\" and isinstance(app, StrixTUIApp):",
Expand Down Expand Up @@ -331,7 +330,6 @@
],
"lyrashield/artifacts/state.py": [
" tool_version=_strix_version(),",
" return version(\"strix-agent\")",
" persistence. This store keeps only Strix-owned scan artifacts and",
" scan streams, so the OpenRouter streaming handler (see strix.config.models)",
"# Modifications \u00a9 2026 LyraShield; based on upstream Strix (Apache-2.0)",
Expand Down Expand Up @@ -517,8 +515,6 @@
],
"lyrashield/telemetry/_common.py": [
" \"strix_version\": get_version(),",
" logger.debug(\"strix-agent version lookup failed\", exc_info=True)",
" return version(\"strix-agent\")",
" marker = Path.home() / \".strix\" / \".seen\"",
"# Modifications \u00a9 2026 LyraShield; based on upstream Strix (Apache-2.0)"
],
Expand Down
16 changes: 0 additions & 16 deletions scripts/docker.sh

This file was deleted.

2 changes: 1 addition & 1 deletion scripts/verify-controlled-derivative.sh
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,4 @@ uv run ruff check .
uv run ruff format --check .
uv run pytest --durations=25 -W error::pydantic.PydanticDeprecatedSince211
uv run mypy strix lyrashield_adapter lyrashield
uv run bandit -c pyproject.toml -r strix lyrashield_adapter lyrashield -q
uv run bandit -c pyproject.toml -r strix lyrashield_adapter lyrashield -q -l
3 changes: 0 additions & 3 deletions strix.spec
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,6 @@ hiddenimports = [
'tiktoken_ext',
'tiktoken_ext.openai_public',

# Tenacity retry
'tenacity',

# CVSS scoring
'cvss',

Expand Down
Loading
Loading