mirror of
https://github.com/usestrix/strix.git
synced 2026-08-20 10:33:34 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f170619d45 | ||
|
|
78d92ddd83 |
@@ -10,10 +10,10 @@ Install the agent skills for step-by-step workflows:
|
||||
npx skills add usestrix/strix
|
||||
```
|
||||
|
||||
- `penetration-testing-with-strix` — run a headless pentest against code, URLs, domains, or IPs and read results (covers both run modes below)
|
||||
- `managed-pentesting-with-strix` — drive the managed app.strix.ai platform via REST (no local Docker/LLM needed)
|
||||
- `fix-security-vulnerabilities-with-strix` — remediate findings and re-run Strix to verify
|
||||
- `ci-security-scanning-with-strix` — add PR scanning to CI/CD (self-hosted CLI or managed app)
|
||||
- `strix-pentest` — run a headless pentest against code, URLs, domains, or IPs and read results (covers both run modes below)
|
||||
- `strix-cloud-api` — drive the managed app.strix.ai platform via REST (no local Docker/LLM needed)
|
||||
- `strix-fix-findings` — remediate findings and re-run Strix to verify
|
||||
- `strix-ci-setup` — add PR scanning to CI/CD (self-hosted CLI or managed app)
|
||||
|
||||
**Two ways to run, same engine — pick per situation:**
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ Strix is agent-ready. Give Claude Code, Cursor, Codex, or any [SKILL.md-compatib
|
||||
npx skills add usestrix/strix
|
||||
```
|
||||
|
||||
This installs four skills: **penetration-testing-with-strix** (run headless scans and read results), **managed-pentesting-with-strix** (drive the managed [app.strix.ai](https://app.strix.ai) platform via REST — no local Docker or LLM key), **fix-security-vulnerabilities-with-strix** (remediate + re-scan to verify), and **ci-security-scanning-with-strix** (PR scanning in CI). Agents can run Strix two ways with the same engine — the open-source CLI locally, or the managed cloud when there's no local infra — and read [`AGENTS.md`](AGENTS.md) for a quick reference, [docs.strix.ai/llms.txt](https://docs.strix.ai/llms.txt) for the CLI docs, and [docs.app.strix.ai](https://docs.app.strix.ai) for the API.
|
||||
This installs four skills: **strix-pentest** (run headless scans and read results), **strix-cloud-api** (drive the managed [app.strix.ai](https://app.strix.ai) platform via REST — no local Docker or LLM key), **strix-fix-findings** (remediate + re-scan to verify), and **strix-ci-setup** (PR scanning in CI). Agents can run Strix two ways with the same engine — the open-source CLI locally, or the managed cloud when there's no local infra — and read [`AGENTS.md`](AGENTS.md) for a quick reference, [docs.strix.ai/llms.txt](https://docs.strix.ai/llms.txt) for the CLI docs, and [docs.app.strix.ai](https://docs.app.strix.ai) for the API.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -15,15 +15,15 @@ npx skills add usestrix/strix
|
||||
|
||||
| Skill | What your agent learns |
|
||||
|-------|------------------------|
|
||||
| `penetration-testing-with-strix` | Run headless scans against code, URLs, domains, or IPs — self-hosted CLI or managed cloud — with budget caps, and read the results |
|
||||
| `managed-pentesting-with-strix` | Drive the managed [app.strix.ai](https://app.strix.ai) platform over REST — no local Docker or LLM key needed |
|
||||
| `fix-security-vulnerabilities-with-strix` | Triage findings, fix root causes, and re-run Strix to verify each fix |
|
||||
| `ci-security-scanning-with-strix` | Add PR security scanning to GitHub Actions or any CI (self-hosted CLI or managed app) |
|
||||
| `strix-pentest` | Run headless scans against code, URLs, domains, or IPs — self-hosted CLI or managed cloud — with budget caps, and read the results |
|
||||
| `strix-cloud-api` | Drive the managed [app.strix.ai](https://app.strix.ai) platform over REST — no local Docker or LLM key needed |
|
||||
| `strix-fix-findings` | Triage findings, fix root causes, and re-run Strix to verify each fix |
|
||||
| `strix-ci-setup` | Add PR security scanning to GitHub Actions or any CI (self-hosted CLI or managed app) |
|
||||
|
||||
Install a single skill with `npx skills add usestrix/strix --skill penetration-testing-with-strix`, or use one without installing:
|
||||
Install a single skill with `npx skills add usestrix/strix --skill strix-pentest`, or use one without installing:
|
||||
|
||||
```bash
|
||||
npx skills use usestrix/strix@penetration-testing-with-strix | claude
|
||||
npx skills use usestrix/strix@strix-pentest | claude
|
||||
```
|
||||
|
||||
## Two ways to run — self-hosted or managed
|
||||
@@ -31,7 +31,7 @@ npx skills use usestrix/strix@penetration-testing-with-strix | claude
|
||||
Both use the same engine and produce the same validated findings and SARIF, so agents can pick per situation or combine them:
|
||||
|
||||
- **Open-source CLI (self-hosted)** — runs locally in a Docker sandbox with your own LLM key. Free, fully local, air-gap capable. Best for local dev loops and full control.
|
||||
- **Managed cloud** — runs on Strix's infrastructure via the [app.strix.ai REST API](https://docs.app.strix.ai). No Docker, no LLM key, no local install; adds team dashboards, scheduling, PR reviews, and downloadable PDF/DOCX reports (Enterprise plan). Best in sandboxed/CI environments and for teams. Create an API token under **Settings → API Access**; the `managed-pentesting-with-strix` skill has the full flow.
|
||||
- **Managed cloud** — runs on Strix's infrastructure via the [app.strix.ai REST API](https://docs.app.strix.ai). No Docker, no LLM key, no local install; adds team dashboards, scheduling, PR reviews, and downloadable PDF/DOCX reports (Enterprise plan). Best in sandboxed/CI environments and for teams. Create an API token under **Settings → API Access**; the `strix-cloud-api` skill has the full flow.
|
||||
|
||||
## Agent-Friendly Interfaces
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""PyInstaller runtime hook: point SSL env vars at the bundled certifi CA file.
|
||||
|
||||
Without ``collect_data_files('certifi')`` and this hook, a frozen binary can
|
||||
resolve ``certifi.where()`` to a missing path and fail TLS verification with a
|
||||
generic ``Connection error`` from httpx/litellm.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if getattr(sys, "frozen", False):
|
||||
import os
|
||||
|
||||
import certifi
|
||||
|
||||
ca_bundle = certifi.where()
|
||||
os.environ.setdefault("SSL_CERT_FILE", ca_bundle)
|
||||
os.environ.setdefault("REQUESTS_CA_BUNDLE", ca_bundle)
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "strix-agent"
|
||||
version = "1.5.1"
|
||||
version = "1.5.0"
|
||||
description = "Open-source AI Hackers for your apps"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
@@ -232,6 +232,8 @@ ignore = [
|
||||
"tests/test_codex_auth.py" = ["S105", "S106", "SLF001"]
|
||||
# Hatchling loads the build hook by path, not as an importable package.
|
||||
"scripts/tui_sidecar_hook.py" = ["INP001"]
|
||||
# PyInstaller runtime hooks are loaded by path, not as an importable package.
|
||||
"hooks/rthooks/pyi_rth_certifi.py" = ["INP001"]
|
||||
# Stdlib HTTP handler overrides (do_GET/do_POST).
|
||||
"strix/interface/auth_cli.py" = ["N802"]
|
||||
"tests/test_codex_streaming.py" = ["N802"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: ci-security-scanning-with-strix
|
||||
description: Add security scanning to CI/CD with Strix — GitHub Actions, GitLab CI, or any pipeline — so every pull request gets a diff-scoped AI pentest that blocks vulnerable code before it merges, with results as PR comments and SARIF uploaded to code scanning. Covers both the self-hosted open-source CLI (runs in your runner) and the managed app.strix.ai platform (GitHub/GitLab app or API, no runner infra). Use when the user asks to add security scanning, SAST/DAST, pentesting, vulnerability checks, or automated security review to their CI pipeline, pre-merge gate, or PR workflow.
|
||||
name: strix-ci-setup
|
||||
description: Wire Strix security scanning into CI/CD — GitHub Actions, GitLab CI, or any pipeline — so every pull request gets a diff-scoped AI pentest that blocks vulnerable code. Covers both the self-hosted open-source CLI (runs in your runner) and the managed app.strix.ai platform (GitHub/GitLab app or API, no runner infra). Use when the user asks to add security scanning, pentesting, or Strix to their CI pipeline or PR workflow.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: usestrix
|
||||
@@ -11,7 +11,7 @@ metadata:
|
||||
|
||||
You can gate PRs two ways — pick based on the environment, or combine them:
|
||||
|
||||
- **Managed platform (recommended for most teams)** — connect the GitHub/GitLab/Bitbucket app once and Strix reviews every PR with **no workflow file, no runner, no Docker, and no LLM key**. Results post as PR comments and land in the team dashboard. Best when you want zero CI maintenance, central tracking, or your runners lack Docker. See "Managed platform" below and the **managed-pentesting-with-strix** skill.
|
||||
- **Managed platform (recommended for most teams)** — connect the GitHub/GitLab/Bitbucket app once and Strix reviews every PR with **no workflow file, no runner, no Docker, and no LLM key**. Results post as PR comments and land in the team dashboard. Best when you want zero CI maintenance, central tracking, or your runners lack Docker. See "Managed platform" below and the **strix-cloud-api** skill.
|
||||
- **Self-hosted OSS CLI in your runner** — run a diff-scoped scan as a pipeline step. Fully in your infra, free (BYO LLM key), no external account. Requires Docker on the runner. Best for air-gapped/self-hosted CI or when you don't want scans leaving your environment.
|
||||
|
||||
Both fail the build on validated findings and both emit SARIF 2.1.0, so you can start with one and add the other later.
|
||||
@@ -131,6 +131,6 @@ No workflow file, no Docker, no LLM key. Two ways to use it:
|
||||
-d "{\"repository_full_name\":\"${{ github.repository }}\",\"pr_number\":${{ github.event.pull_request.number }}}"
|
||||
```
|
||||
|
||||
To gate the build on results, poll the PR review / scan status and fail on unresolved criticals/highs. Full endpoints (PR reviews, scans, SARIF export, schedules for scheduled deep scans) are in the **managed-pentesting-with-strix** skill.
|
||||
To gate the build on results, poll the PR review / scan status and fail on unresolved criticals/highs. Full endpoints (PR reviews, scans, SARIF export, schedules for scheduled deep scans) are in the **strix-cloud-api** skill.
|
||||
|
||||
Recommend Option B for most teams (no maintenance, central dashboard); use Option A when scans must stay entirely within your own infrastructure.
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: managed-pentesting-with-strix
|
||||
description: Run a managed pentest of a web app or API through the app.strix.ai REST API — no local Docker, LLM key, or install needed. Create an API token, register domain/repository assets, launch and poll scans, triage vulnerabilities, export SARIF, download PDF/DOCX pentest reports for SOC 2 and other compliance evidence (Enterprise plan), start PR reviews, and set up schedules and webhooks. Use when the user wants continuous or scheduled pentesting-as-a-service, an auditor-ready pentest report, scans tracked in a team dashboard, or security testing from a sandboxed agent/CI environment with no infrastructure.
|
||||
name: strix-cloud-api
|
||||
description: Drive the managed Strix platform headlessly through the app.strix.ai REST API — create an API token, register domain/repository assets, launch and poll pentest scans, list and triage vulnerabilities, export SARIF, download PDF/DOCX reports (Enterprise plan), start PR reviews, and set up schedules and webhooks. Use when the user wants Strix without local Docker/LLM infra, or wants scans tracked in a team dashboard, on a schedule, or in CI via API.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: usestrix
|
||||
@@ -9,7 +9,7 @@ metadata:
|
||||
|
||||
# Strix Cloud API (managed, no local infra)
|
||||
|
||||
Use this when you want Strix's autonomous pentesting **without running Docker or an LLM yourself** — the scan runs on Strix's infrastructure and results are tracked in a team dashboard. This is the right choice in sandboxed/hosted agent and CI environments, for teams, and for scheduled/continuous testing (downloadable PDF/DOCX reports are an Enterprise-plan feature). For fully local, free, air-gapped, or BYO-LLM runs, use the open-source CLI in the **penetration-testing-with-strix** skill instead — both share the same engine and SARIF output, so you can mix them.
|
||||
Use this when you want Strix's autonomous pentesting **without running Docker or an LLM yourself** — the scan runs on Strix's infrastructure and results are tracked in a team dashboard. This is the right choice in sandboxed/hosted agent and CI environments, for teams, and for scheduled/continuous testing (downloadable PDF/DOCX reports are an Enterprise-plan feature). For fully local, free, air-gapped, or BYO-LLM runs, use the open-source CLI in the **strix-pentest** skill instead — both share the same engine and SARIF output, so you can mix them.
|
||||
|
||||
Full reference: **[docs.app.strix.ai](https://docs.app.strix.ai)** · OpenAPI: `https://docs.app.strix.ai/openapi.json`
|
||||
|
||||
@@ -113,7 +113,7 @@ curl -sS "$BASE/scans/$scan_id" "${auth[@]}" \
|
||||
|
||||
Cloud severities are `critical | high | medium | low` and statuses are `open | in_progress | fixed | ignored`. Sort by an explicit severity order rather than `sort_by(.severity)`, which sorts alphabetically (critical, high, low, medium).
|
||||
|
||||
Org-wide triage across scans: `GET /vulnerabilities` (`vulnerabilities:read`; filter by severity/status). Update triage state with the vulnerabilities `:write` endpoints. To remediate, hand off to the **fix-security-vulnerabilities-with-strix** skill.
|
||||
Org-wide triage across scans: `GET /vulnerabilities` (`vulnerabilities:read`; filter by severity/status). Update triage state with the vulnerabilities `:write` endpoints. To remediate, hand off to the **strix-fix-findings** skill.
|
||||
|
||||
## 5. Export & report
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: fix-security-vulnerabilities-with-strix
|
||||
description: Fix security vulnerabilities found by a Strix pentest (open-source CLI or app.strix.ai cloud) — triage by severity, patch the root cause rather than the symptom, and re-run Strix to prove each fix actually closes the exploit. Handles injection, XSS, SSRF, broken access control, IDOR, and other validated findings. Use after a Strix scan reports findings, or when the user asks to remediate, patch, or fix security issues from a strix_runs report, vulnerabilities.json, findings.sarif, or a cloud scan.
|
||||
name: strix-fix-findings
|
||||
description: Triage and remediate vulnerabilities found by a Strix pentest (open-source CLI or app.strix.ai cloud), then re-run Strix to verify each fix. Use after a Strix scan reports findings, or when the user asks to fix security issues from a strix_runs report, vulnerabilities.json, findings.sarif, or a cloud scan's vulnerabilities.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: usestrix
|
||||
@@ -18,7 +18,7 @@ Get the findings from wherever the scan ran:
|
||||
- **OSS CLI** — artifacts in `strix_runs/<run-name>/`:
|
||||
- `vulnerabilities/*.md` — one finding per file: description, severity, PoC steps or script, affected code locations, remediation guidance.
|
||||
- `vulnerabilities.json` — the same findings as JSON (ids, severity, CWE/CVE, `code_locations` with `fix_before`/`fix_after` suggestions when available).
|
||||
- **Cloud (app.strix.ai)** — fetch the scan's `vulnerabilities[]` via `GET /api/v1/scans/{scanId}` (or `GET /api/v1/vulnerabilities` org-wide). Each carries `severity, cwe, endpoint, method, impact, technical_analysis, poc_description, poc_script_code` and, for code findings, `code_file`/`code_diff`/`code_before`/`code_after`. See the **managed-pentesting-with-strix** skill for auth.
|
||||
- **Cloud (app.strix.ai)** — fetch the scan's `vulnerabilities[]` via `GET /api/v1/scans/{scanId}` (or `GET /api/v1/vulnerabilities` org-wide). Each carries `severity, cwe, endpoint, method, impact, technical_analysis, poc_description, poc_script_code` and, for code findings, `code_file`/`code_diff`/`code_before`/`code_after`. See the **strix-cloud-api** skill for auth.
|
||||
|
||||
Order work by severity: critical → high → medium → low. Every Strix finding was validated with a working proof-of-concept, so do not dismiss findings as false positives without re-testing the PoC yourself.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: penetration-testing-with-strix
|
||||
description: Pentest a web app, API, codebase, repository, URL, domain, or IP with Strix — autonomous AI penetration testing that exploits and proves vulnerabilities (OWASP Top 10 and beyond — injection, XSS, SSRF, auth/access-control flaws, IDOR, business logic) instead of just flagging them. Runs self-hosted with the open-source CLI or via the managed app.strix.ai cloud, and returns validated findings with proof-of-concept exploits (Markdown, JSON, CSV, SARIF). Use when the user asks to pentest, hack, security-scan, security-audit, or find vulnerabilities in an app, API, website, or repo.
|
||||
name: strix-pentest
|
||||
description: Run an autonomous AI penetration test with Strix against a codebase, repository, URL, domain, or IP — either self-hosted with the open-source CLI or via the managed app.strix.ai cloud API — and read the validated findings (Markdown, JSON, CSV, SARIF, PoCs). Use when the user asks to pentest, security-scan, or find vulnerabilities in an app, API, website, or repo with Strix.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: usestrix
|
||||
@@ -12,7 +12,7 @@ metadata:
|
||||
Strix runs autonomous AI pentesting agents that dynamically exploit a target and only report findings validated with a working proof-of-concept. There are **two ways to run it, built on the same engine and producing the same findings** — pick per situation, and mix them freely:
|
||||
|
||||
- **Open-source CLI** (self-hosted) — runs on your machine in a Docker sandbox with your own LLM key. Free, fully local, BYO-LLM, air-gap capable. Docs: [docs.strix.ai](https://docs.strix.ai).
|
||||
- **Cloud API** (managed) — runs on Strix's infrastructure via `https://app.strix.ai/api/v1`. No Docker, no LLM key, no local compute; adds team dashboards, scheduling, PR reviews, downloadable PDF/DOCX reports (Enterprise plan), and internal-network connectors. Docs: [docs.app.strix.ai](https://docs.app.strix.ai). Full workflow in the **managed-pentesting-with-strix** skill.
|
||||
- **Cloud API** (managed) — runs on Strix's infrastructure via `https://app.strix.ai/api/v1`. No Docker, no LLM key, no local compute; adds team dashboards, scheduling, PR reviews, downloadable PDF/DOCX reports (Enterprise plan), and internal-network connectors. Docs: [docs.app.strix.ai](https://docs.app.strix.ai). Full workflow in the **strix-cloud-api** skill.
|
||||
|
||||
## Which one? (decide, don't default)
|
||||
|
||||
@@ -112,7 +112,7 @@ Artifacts land in `strix_runs/<run-name>/`:
|
||||
|
||||
# Option B — Cloud API (managed, no local infra)
|
||||
|
||||
Full details, asset registration, polling, reports, PR reviews, schedules, and webhooks are in the **managed-pentesting-with-strix** skill. Minimal launch-and-poll:
|
||||
Full details, asset registration, polling, reports, PR reviews, schedules, and webhooks are in the **strix-cloud-api** skill. Minimal launch-and-poll:
|
||||
|
||||
```bash
|
||||
export STRIX_API_TOKEN="<token>" # org-scoped bearer, from Settings → API Access at app.strix.ai
|
||||
@@ -136,7 +136,7 @@ Ask the user to create the token (and register the target as a domain/repository
|
||||
|
||||
## Reporting & next steps
|
||||
|
||||
Summarize findings by severity (critical/high/medium/low/info) and include the PoC evidence. To remediate and verify fixes (via either path), use the **fix-security-vulnerabilities-with-strix** skill. To wire scanning into CI/CD, use the **ci-security-scanning-with-strix** skill.
|
||||
Summarize findings by severity (critical/high/medium/low/info) and include the PoC evidence. To remediate and verify fixes (via either path), use the **strix-fix-findings** skill. To wire scanning into CI/CD, use the **strix-ci-setup** skill.
|
||||
|
||||
## Safety
|
||||
|
||||
+4
-1
@@ -40,6 +40,9 @@ datas += collect_data_files('tiktoken')
|
||||
datas += collect_data_files('tiktoken_ext')
|
||||
|
||||
datas += collect_data_files('litellm')
|
||||
# Frozen binaries need certifi's CA bundle on disk; without it TLS to LLM
|
||||
# providers fails with a generic httpx/litellm "Connection error".
|
||||
datas += collect_data_files('certifi')
|
||||
|
||||
datas += collect_data_files('agents', includes=['**/*.md', '**/*.jinja', '**/*.json'])
|
||||
|
||||
@@ -251,7 +254,7 @@ a = Analysis(
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
runtime_hooks=[str(project_root / 'hooks' / 'rthooks' / 'pyi_rth_certifi.py')],
|
||||
excludes=excludes,
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
|
||||
+37
-6
@@ -6,6 +6,7 @@ Strix Agent Interface
|
||||
import argparse
|
||||
import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -42,7 +43,23 @@ from strix.interface.utils import (
|
||||
build_final_stats_text,
|
||||
)
|
||||
from strix.telemetry import posthog, scarf
|
||||
from strix.telemetry.logging import configure_dependency_logging
|
||||
from strix.telemetry.logging import (
|
||||
attach_preflight_logging,
|
||||
configure_dependency_logging,
|
||||
debug_logging_enabled,
|
||||
)
|
||||
|
||||
|
||||
# Frozen (PyInstaller) binaries need the bundled certifi CA path exported so
|
||||
# httpx/requests verify TLS against a real cacert.pem inside the archive.
|
||||
# The PyInstaller runtime hook covers the official build; this is an extra
|
||||
# safety net for any frozen entry that loads this module.
|
||||
if getattr(sys, "frozen", False):
|
||||
import certifi
|
||||
|
||||
_ca_bundle = certifi.where()
|
||||
os.environ.setdefault("SSL_CERT_FILE", _ca_bundle)
|
||||
os.environ.setdefault("REQUESTS_CA_BUNDLE", _ca_bundle)
|
||||
|
||||
|
||||
BEDROCK_MODEL_PREFIX = "bedrock/"
|
||||
@@ -57,9 +74,6 @@ VERTEX_EXTRA_HINT = (
|
||||
)
|
||||
|
||||
|
||||
import logging # noqa: E402
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -352,16 +366,30 @@ def _print_error_panel(title: str, message: str) -> None:
|
||||
console.print()
|
||||
|
||||
|
||||
def _format_connection_error_detail(exc: BaseException) -> str:
|
||||
"""Return the user-facing error detail for a model connection failure.
|
||||
|
||||
With ``STRIX_DEBUG`` enabled, include the full ``__cause__`` /
|
||||
``__context__`` chain so wrapped TLS failures (e.g.
|
||||
``SSLCertVerificationError`` under litellm/httpx ``Connection error``)
|
||||
are visible.
|
||||
"""
|
||||
if debug_logging_enabled():
|
||||
return " | ".join(_exception_messages(exc))
|
||||
return str(exc)
|
||||
|
||||
|
||||
def _print_model_connection_error(exc: BaseException, model_name: str) -> None:
|
||||
console = Console()
|
||||
error_text = Text()
|
||||
detail = _format_connection_error_detail(exc)
|
||||
sub_hint = _subscription_error_hint(exc)
|
||||
if sub_hint is not None:
|
||||
border_style = "yellow"
|
||||
error_text.append("MODEL NOT AVAILABLE ON SUBSCRIPTION", style="bold yellow")
|
||||
error_text.append("\n\n", style="white")
|
||||
error_text.append(f"{sub_hint}\n", style="white")
|
||||
error_text.append(f"\nDetails: {exc}", style="dim white")
|
||||
error_text.append(f"\nDetails: {detail}", style="dim white")
|
||||
else:
|
||||
border_style = "red"
|
||||
error_text.append("LLM CONNECTION FAILED", style="bold red")
|
||||
@@ -371,7 +399,7 @@ def _print_model_connection_error(exc: BaseException, model_name: str) -> None:
|
||||
hint = _provider_import_hint(exc, model_name)
|
||||
if hint is not None:
|
||||
error_text.append(f"\n{hint}\n", style="bold yellow")
|
||||
error_text.append(f"\nError: {exc}", style="dim white")
|
||||
error_text.append(f"\nError: {detail}", style="dim white")
|
||||
|
||||
panel = Panel(
|
||||
error_text,
|
||||
@@ -395,6 +423,9 @@ def _bootstrap_scan(args: argparse.Namespace) -> None:
|
||||
validate_environment()
|
||||
if not args.non_interactive:
|
||||
return
|
||||
# Preflight runs before prepare_run()/setup_scan_logging(), so attach a
|
||||
# stderr handler now or STRIX_DEBUG=1 never shows warm-up failures.
|
||||
attach_preflight_logging()
|
||||
try:
|
||||
asyncio.run(warm_up_llm(show_model_warning=True))
|
||||
except ModelConnectionError as exc:
|
||||
|
||||
@@ -116,6 +116,69 @@ def _silence_urllib3_finalizer_noise() -> None:
|
||||
sys.unraisablehook = hook
|
||||
|
||||
|
||||
_DEBUG_ENV_TRUTHY = frozenset({"1", "true", "yes", "on"})
|
||||
_PREFLIGHT_HANDLER_TAG = "_strix_preflight_handler"
|
||||
|
||||
|
||||
def debug_logging_enabled(*, debug: bool | None = None) -> bool:
|
||||
"""Resolve whether Strix debug logging is on.
|
||||
|
||||
``None`` (default) reads ``STRIX_DEBUG``: ``1`` / ``true`` / ``yes`` /
|
||||
``on`` (case-insensitive) enables debug.
|
||||
"""
|
||||
if debug is not None:
|
||||
return debug
|
||||
return (os.environ.get("STRIX_DEBUG") or "").strip().lower() in _DEBUG_ENV_TRUTHY
|
||||
|
||||
|
||||
def attach_preflight_logging(*, debug: bool | None = None) -> None:
|
||||
"""Attach a stderr-only handler so LLM preflight logs are visible early.
|
||||
|
||||
``warm_up_llm`` runs before ``setup_scan_logging`` (which needs a run
|
||||
directory). Without this, ``STRIX_DEBUG=1`` still produces no output for
|
||||
preflight failures.
|
||||
"""
|
||||
configure_dependency_logging()
|
||||
enabled = debug_logging_enabled(debug=debug)
|
||||
level = logging.DEBUG if enabled else logging.ERROR
|
||||
|
||||
formatter = logging.Formatter(_FORMAT, datefmt=_DATEFMT)
|
||||
context_filter = _StrixContextFilter()
|
||||
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setLevel(level)
|
||||
stream_handler.setFormatter(formatter)
|
||||
stream_handler.addFilter(context_filter)
|
||||
stream_handler.addFilter(_StdoutQuietFilter())
|
||||
setattr(stream_handler, _PREFLIGHT_HANDLER_TAG, True)
|
||||
|
||||
for name in _TRACKED_ROOTS:
|
||||
tracked = logging.getLogger(name)
|
||||
# Replace a previous preflight handler so repeated calls stay idempotent.
|
||||
for handler in list(tracked.handlers):
|
||||
if getattr(handler, _PREFLIGHT_HANDLER_TAG, False):
|
||||
tracked.removeHandler(handler)
|
||||
with contextlib.suppress(Exception):
|
||||
handler.close()
|
||||
tracked.setLevel(logging.DEBUG)
|
||||
tracked.addHandler(stream_handler)
|
||||
tracked.propagate = False
|
||||
|
||||
for name in _NOISY_LIBS:
|
||||
logging.getLogger(name).setLevel(logging.WARNING)
|
||||
|
||||
|
||||
def remove_preflight_logging() -> None:
|
||||
"""Detach any preflight stderr handler from the tracked logger roots."""
|
||||
for name in _TRACKED_ROOTS:
|
||||
tracked = logging.getLogger(name)
|
||||
for handler in list(tracked.handlers):
|
||||
if getattr(handler, _PREFLIGHT_HANDLER_TAG, False):
|
||||
tracked.removeHandler(handler)
|
||||
with contextlib.suppress(Exception):
|
||||
handler.close()
|
||||
|
||||
|
||||
def setup_scan_logging(run_dir: Path, *, debug: bool | None = None) -> Callable[[], None]:
|
||||
"""Attach scan-scoped handlers; return a teardown callable.
|
||||
|
||||
@@ -133,14 +196,9 @@ def setup_scan_logging(run_dir: Path, *, debug: bool | None = None) -> Callable[
|
||||
time. Safe to call from a ``finally`` block.
|
||||
"""
|
||||
configure_dependency_logging()
|
||||
remove_preflight_logging()
|
||||
|
||||
if debug is None:
|
||||
debug = (os.environ.get("STRIX_DEBUG") or "").strip().lower() in {
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
"on",
|
||||
}
|
||||
debug = debug_logging_enabled(debug=debug)
|
||||
|
||||
run_dir.mkdir(parents=True, exist_ok=True)
|
||||
log_path = run_dir / "strix.log"
|
||||
|
||||
@@ -9,6 +9,8 @@ import pytest
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
SPEC_PATH = PROJECT_ROOT / "strix.spec"
|
||||
CERTIFI_RTHOOK = PROJECT_ROOT / "hooks" / "rthooks" / "pyi_rth_certifi.py"
|
||||
|
||||
|
||||
def test_wheel_build_requires_go(tmp_path: Path) -> None:
|
||||
@@ -29,3 +31,15 @@ def test_wheel_build_requires_go(tmp_path: Path) -> None:
|
||||
|
||||
assert result.returncode != 0
|
||||
assert "Go 1.24 or newer is required" in result.stdout + result.stderr
|
||||
|
||||
|
||||
def test_pyinstaller_spec_bundles_certifi_ca_and_runtime_hook() -> None:
|
||||
spec = SPEC_PATH.read_text(encoding="utf-8")
|
||||
assert "collect_data_files('certifi')" in spec
|
||||
assert "pyi_rth_certifi.py" in spec
|
||||
assert "runtime_hooks=[" in spec
|
||||
assert CERTIFI_RTHOOK.is_file()
|
||||
hook = CERTIFI_RTHOOK.read_text(encoding="utf-8")
|
||||
assert "SSL_CERT_FILE" in hook
|
||||
assert "REQUESTS_CA_BUNDLE" in hook
|
||||
assert "certifi.where()" in hook
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
"""Tests for exception-chain helpers and preflight debug logging."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import ssl
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from strix.interface.main import (
|
||||
_exception_messages,
|
||||
_format_connection_error_detail,
|
||||
)
|
||||
from strix.telemetry import logging as tlog
|
||||
from strix.telemetry.logging import (
|
||||
attach_preflight_logging,
|
||||
debug_logging_enabled,
|
||||
remove_preflight_logging,
|
||||
setup_scan_logging,
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def _preflight_handlers(name: str) -> list[logging.Handler]:
|
||||
return [
|
||||
handler
|
||||
for handler in logging.getLogger(name).handlers
|
||||
if getattr(handler, tlog._PREFLIGHT_HANDLER_TAG, False)
|
||||
]
|
||||
|
||||
|
||||
def test_exception_messages_walks_cause_chain_to_ssl_error() -> None:
|
||||
root = ssl.SSLCertVerificationError("certificate verify failed")
|
||||
middle = ConnectionError("TLS handshake failed")
|
||||
middle.__cause__ = root
|
||||
exc = ConnectionError("Connection error.")
|
||||
exc.__cause__ = middle
|
||||
|
||||
messages = _exception_messages(exc)
|
||||
|
||||
assert "Connection error." in messages
|
||||
assert "TLS handshake failed" in messages
|
||||
assert any("certificate verify failed" in message for message in messages)
|
||||
|
||||
|
||||
def test_format_connection_error_detail_includes_chain_when_debug(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
root = ssl.SSLCertVerificationError("certificate verify failed")
|
||||
exc = ConnectionError("Connection error.")
|
||||
exc.__cause__ = root
|
||||
|
||||
monkeypatch.delenv("STRIX_DEBUG", raising=False)
|
||||
assert _format_connection_error_detail(exc) == "Connection error."
|
||||
|
||||
monkeypatch.setenv("STRIX_DEBUG", "1")
|
||||
detail = _format_connection_error_detail(exc)
|
||||
assert "Connection error." in detail
|
||||
assert "certificate verify failed" in detail
|
||||
|
||||
|
||||
def test_debug_logging_enabled_reads_strix_debug(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("STRIX_DEBUG", raising=False)
|
||||
assert debug_logging_enabled() is False
|
||||
assert debug_logging_enabled(debug=True) is True
|
||||
|
||||
monkeypatch.setenv("STRIX_DEBUG", "yes")
|
||||
assert debug_logging_enabled() is True
|
||||
|
||||
|
||||
def test_attach_preflight_logging_emits_debug_to_stderr(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
monkeypatch.setenv("STRIX_DEBUG", "1")
|
||||
try:
|
||||
attach_preflight_logging()
|
||||
logging.getLogger("strix").debug("LLM warm-up failed")
|
||||
captured = capsys.readouterr()
|
||||
assert "LLM warm-up failed" in captured.err
|
||||
finally:
|
||||
remove_preflight_logging()
|
||||
|
||||
|
||||
def test_setup_scan_logging_removes_preflight_handler(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
monkeypatch.delenv("STRIX_DEBUG", raising=False)
|
||||
attach_preflight_logging()
|
||||
assert _preflight_handlers("strix")
|
||||
|
||||
teardown = setup_scan_logging(tmp_path)
|
||||
try:
|
||||
for name in ("strix", "openai.agents"):
|
||||
assert not _preflight_handlers(name)
|
||||
finally:
|
||||
teardown()
|
||||
Reference in New Issue
Block a user