Skip to content
Open
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
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ name: strix-penetration-test
on:
pull_request:

permissions:
security-events: write
actions: read
contents: read

jobs:
security-scan:
runs-on: ubuntu-latest
Expand All @@ -214,13 +219,23 @@ jobs:
STRIX_LLM: ${{ secrets.STRIX_LLM }}
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}

run: strix -n -t ./ --scan-mode quick
run: strix -n -t ./ --scan-mode quick --sarif-output results.sarif

- name: Upload SARIF to GitHub code scanning
if: always()
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: results.sarif
```

> [!TIP]
> In CI pull request runs, Strix automatically scopes quick reviews to changed files.
> If diff-scope cannot resolve, ensure checkout uses full history (`fetch-depth: 0`) or pass
> `--diff-base` explicitly.
> `--sarif-output` writes GitHub-compatible SARIF before Strix exits with code `2` for
> confirmed vulnerabilities, so `if: always()` preserves code scanning upload on findings.
> Use `--sarif` to write `strix_runs/<run-name>/results.sarif`, or
> `--sarif-output <path>` to choose the upload path.

### Configuration

Expand Down
40 changes: 36 additions & 4 deletions strix/interface/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
import os
import shutil
import sys
from importlib.metadata import version
from pathlib import Path
from typing import Any

import litellm
from docker.errors import DockerException
from docker.errors import DockerException # type: ignore[import-untyped]
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
Expand Down Expand Up @@ -44,6 +45,7 @@
)
from strix.runtime.docker_runtime import HOST_GATEWAY_HOSTNAME # noqa: E402
from strix.telemetry import posthog # noqa: E402
from strix.telemetry.sarif import write_sarif_report # noqa: E402
from strix.telemetry.tracer import get_global_tracer # noqa: E402


Expand Down Expand Up @@ -257,8 +259,6 @@ async def warm_up_llm() -> None:

def get_version() -> str:
try:
from importlib.metadata import version

return version("strix-agent")
except Exception: # noqa: BLE001
return "unknown"
Expand Down Expand Up @@ -387,6 +387,21 @@ def parse_arguments() -> argparse.Namespace:
help="Path to a custom config file (JSON) to use instead of ~/.strix/cli-config.json",
)

parser.add_argument(
"--sarif",
action="store_true",
help="Write GitHub code scanning SARIF results after the scan completes.",
)

parser.add_argument(
"--sarif-output",
type=str,
help=(
"Path for SARIF output. Defaults to strix_runs/<run-name>/results.sarif "
"when --sarif is enabled."
),
)

args = parser.parse_args()

if args.instruction and args.instruction_file:
Expand Down Expand Up @@ -453,7 +468,7 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) ->

stats_text = build_final_stats_text(tracer)

panel_parts = [completion_text, "\n\n", target_text]
panel_parts: list[str | Text] = [completion_text, "\n\n", target_text]

if stats_text.plain:
panel_parts.extend(["\n", stats_text])
Expand Down Expand Up @@ -484,6 +499,22 @@ def display_completion_message(args: argparse.Namespace, results_path: Path) ->
console.print()


def write_requested_sarif_output(args: argparse.Namespace, results_path: Path) -> Path | None:
"""Write SARIF output when requested and report write failures without crashing."""
if not args.sarif and not args.sarif_output:
return None

output_path = Path(args.sarif_output) if args.sarif_output else results_path / "results.sarif"
tracer = get_global_tracer()
vulnerability_reports = tracer.vulnerability_reports if tracer else []
try:
write_sarif_report(output_path, vulnerability_reports, tool_version=get_version())
except OSError as error:
Console().print(f"[yellow]Failed to write SARIF:[/] {error}")
return None
return output_path


def pull_docker_image() -> None:
console = Console()
client = check_docker_connection()
Expand Down Expand Up @@ -632,6 +663,7 @@ def main() -> None: # noqa: PLR0912, PLR0915
posthog.end(tracer, exit_reason=exit_reason)

results_path = Path("strix_runs") / args.run_name
write_requested_sarif_output(args, results_path)
display_completion_message(args, results_path)

if args.non_interactive:
Expand Down
Loading