Compare commits

..
Author SHA1 Message Date
Alex Schapiro f170619d45 fix(logging): detach preflight stderr handler when scan logging starts 2026-08-07 17:14:43 +00:00
not-knope 78d92ddd83 fix(packaging): bundle certifi CA bundle in PyInstaller binary
Standalone macOS builds failed LLM preflight with a generic Connection
error because cacert.pem was missing from the frozen bundle; also surface
STRIX_DEBUG logs and exception cause chains before scan logging starts.

Fixes #1008
2026-08-07 15:46:29 +02:00
15 changed files with 274 additions and 44 deletions
+4 -4
View File
@@ -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:**
+1 -1
View File
@@ -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.
---
+7 -7
View File
@@ -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
+20
View File
@@ -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
View File
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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:
+65 -7
View File
@@ -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"
+14
View File
@@ -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
+102
View File
@@ -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()
Generated
+1 -1
View File
@@ -2378,7 +2378,7 @@ wheels = [
[[package]]
name = "strix-agent"
version = "1.5.1"
version = "1.5.0"
source = { editable = "." }
dependencies = [
{ name = "caido-sdk-client" },