mirror of
https://github.com/usestrix/strix.git
synced 2026-10-16 04:28:23 +00:00
Add identity token sources for Anthropic Workload Identity Federation beyond a token file: - ANTHROPIC_IDENTITY_TOKEN_COMMAND: a command run on every exchange whose stdout is the JWT (run without a shell; errors never include stdout). Saved to the Strix config like the other ANTHROPIC_* settings. - `strix auth login entra [--manual] [--tenant ID] [--app ID]`: PKCE sign-in as a Microsoft Entra ID user, using the audience app as its own public client so the stored refresh token only reaches that audience. Tokens are refreshed automatically; defaults come from STRIX_ENTRA_TENANT_ID / STRIX_ENTRA_APP_ID. Source order: token file, command, literal token, Entra sign-in, GitHub Actions OIDC. `strix auth status` and `logout` now cover both ChatGPT and Entra, and `strix auth test [--config PATH]` runs one exchange with the source a scan would use, reporting the source, claims and result without printing any token. Docs: new "Anthropic via Microsoft Entra ID" guide (Entra and Console setup, Entra users on any host, Azure VM managed identities, everyday use, security, troubleshooting), plus the WIF settings in the configuration reference and `strix auth` in the CLI reference.
425 lines
14 KiB
TOML
425 lines
14 KiB
TOML
[project]
|
|
name = "strix-agent"
|
|
version = "1.7.0"
|
|
description = "Open-source AI Hackers for your apps"
|
|
readme = "README.md"
|
|
license = "Apache-2.0"
|
|
requires-python = ">=3.12"
|
|
authors = [
|
|
{ name = "Strix", email = "hi@usestrix.com" },
|
|
]
|
|
keywords = [
|
|
"cybersecurity",
|
|
"security",
|
|
"vulnerability",
|
|
"scanner",
|
|
"pentest",
|
|
"agent",
|
|
"ai",
|
|
"cli",
|
|
]
|
|
classifiers = [
|
|
"Development Status :: 3 - Alpha",
|
|
"Intended Audience :: Information Technology",
|
|
"Intended Audience :: System Administrators",
|
|
"Topic :: Security",
|
|
"License :: OSI Approved :: Apache Software License",
|
|
"Environment :: Console",
|
|
"Programming Language :: Python",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3 :: Only",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Programming Language :: Python :: 3.14",
|
|
]
|
|
dependencies = [
|
|
"openai-agents[litellm]>=0.19.0,<0.20",
|
|
"openai>=2.45.0,<3",
|
|
"litellm>=1.101.0",
|
|
"google-auth>=2.0.0",
|
|
"boto3>=1.28.0",
|
|
"pydantic>=2.11.3",
|
|
"pydantic-settings>=2.13.0",
|
|
"rich",
|
|
"docker>=7.1.0",
|
|
"requests>=2.32.0",
|
|
"cvss>=3.2",
|
|
"caido-sdk-client>=0.2.0",
|
|
"markdown-it-py>=3.0.0",
|
|
"reportlab>=4.0",
|
|
"pypdf>=5.0",
|
|
# Cap <49: 49.x drops the universal2 macOS wheel (arm64-only), which breaks
|
|
# the Intel macOS (macos-x86_64) release build's `uv sync --frozen`.
|
|
"cryptography>=48.0.1,<49",
|
|
"pyyaml>=6.0",
|
|
]
|
|
|
|
[project.scripts]
|
|
strix = "strix.interface.main:main"
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"mypy>=1.16.0",
|
|
"ruff>=0.11.13",
|
|
"pyright>=1.1.401",
|
|
"bandit>=1.8.3",
|
|
"pre-commit>=4.2.0",
|
|
"pyinstaller>=6.17.0; python_version >= '3.12' and python_version < '3.15'",
|
|
"pytest>=8.3",
|
|
"pytest-asyncio>=0.24",
|
|
"types-requests>=2.32",
|
|
]
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "auto"
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["strix"]
|
|
# The prebuilt viewer bundle under strix/interface/viewer/static/ ships automatically
|
|
# (hatchling includes non-.py files under the package). The Vite SOURCE lives
|
|
# under the package dir too (strix/interface/viewer/frontend/) but must never ship in the wheel.
|
|
exclude = [
|
|
"strix/interface/viewer/frontend",
|
|
"strix/interface/viewer/frontend/**",
|
|
# Go TUI SOURCE lives under the package dir but must never ship in the wheel;
|
|
# the compiled sidecar is force-included as strix/bin/strix-tui instead.
|
|
"strix/interface/tui/cmd/**",
|
|
"strix/interface/tui/internal/**",
|
|
"strix/interface/tui/go.mod",
|
|
"strix/interface/tui/go.sum",
|
|
]
|
|
|
|
[tool.hatch.build.targets.wheel.hooks.custom]
|
|
path = "scripts/tui_sidecar_hook.py"
|
|
|
|
# ============================================================================
|
|
# Type Checking Configuration
|
|
# ============================================================================
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
strict = true
|
|
strict_optional = true
|
|
warn_redundant_casts = true
|
|
warn_unused_ignores = true
|
|
warn_return_any = true
|
|
warn_unreachable = true
|
|
disallow_untyped_defs = true
|
|
disallow_any_generics = true
|
|
disallow_subclassing_any = true
|
|
disallow_untyped_calls = true
|
|
disallow_incomplete_defs = true
|
|
check_untyped_defs = true
|
|
disallow_untyped_decorators = true
|
|
no_implicit_optional = true
|
|
warn_unused_configs = true
|
|
show_error_codes = true
|
|
show_column_numbers = true
|
|
pretty = true
|
|
|
|
# Allow some flexibility for third-party libraries
|
|
[[tool.mypy.overrides]]
|
|
module = [
|
|
"litellm.*",
|
|
"rich.*",
|
|
"jinja2.*",
|
|
"cvss.*",
|
|
"docker.*",
|
|
"caido_sdk_client.*",
|
|
"pydantic_settings.*",
|
|
"reportlab.*",
|
|
"pypdf.*",
|
|
"yaml.*",
|
|
"pygments.*",
|
|
]
|
|
ignore_missing_imports = true
|
|
disable_error_code = ["import-untyped"]
|
|
|
|
[[tool.mypy.overrides]]
|
|
module = ["tests.*"]
|
|
disallow_untyped_decorators = false
|
|
|
|
# ============================================================================
|
|
# Ruff Configuration (Fast Python Linter & Formatter)
|
|
# ============================================================================
|
|
|
|
[tool.ruff]
|
|
target-version = "py312"
|
|
line-length = 100
|
|
extend-exclude = [
|
|
".git",
|
|
".mypy_cache",
|
|
".ruff_cache",
|
|
"__pycache__",
|
|
"build",
|
|
"dist",
|
|
"migrations",
|
|
]
|
|
|
|
[tool.ruff.lint]
|
|
# Enable comprehensive rule sets
|
|
select = [
|
|
"E", # pycodestyle errors
|
|
"W", # pycodestyle warnings
|
|
"F", # Pyflakes
|
|
"I", # isort
|
|
"N", # pep8-naming
|
|
"UP", # pyupgrade
|
|
"YTT", # flake8-2020
|
|
"S", # flake8-bandit
|
|
"BLE", # flake8-blind-except
|
|
"FBT", # flake8-boolean-trap
|
|
"B", # flake8-bugbear
|
|
"A", # flake8-builtins
|
|
"COM", # flake8-commas
|
|
"C4", # flake8-comprehensions
|
|
"DTZ", # flake8-datetimez
|
|
"T10", # flake8-debugger
|
|
"EM", # flake8-errmsg
|
|
"FA", # flake8-future-annotations
|
|
"ISC", # flake8-implicit-str-concat
|
|
"ICN", # flake8-import-conventions
|
|
"G", # flake8-logging-format
|
|
"INP", # flake8-no-pep420
|
|
"PIE", # flake8-pie
|
|
"T20", # flake8-print
|
|
"PYI", # flake8-pyi
|
|
"Q", # flake8-quotes
|
|
"RSE", # flake8-raise
|
|
"RET", # flake8-return
|
|
"SLF", # flake8-self
|
|
"SIM", # flake8-simplify
|
|
"TID", # flake8-tidy-imports
|
|
"TCH", # flake8-type-checking
|
|
"ARG", # flake8-unused-arguments
|
|
"PTH", # flake8-use-pathlib
|
|
"ERA", # eradicate
|
|
"PD", # pandas-vet
|
|
"PGH", # pygrep-hooks
|
|
"PL", # Pylint
|
|
"TRY", # tryceratops
|
|
"FLY", # flynt
|
|
"PERF", # Perflint
|
|
"RUF", # Ruff-specific rules
|
|
]
|
|
|
|
ignore = [
|
|
"S101", # Use of assert
|
|
"S104", # Possible binding to all interfaces
|
|
"S301", # Use of pickle
|
|
"COM812", # Missing trailing comma (handled by formatter)
|
|
"ISC001", # Single line implicit string concatenation (handled by formatter)
|
|
"PLR0913", # Too many arguments to function call
|
|
"TRY003", # Avoid specifying long messages outside the exception class
|
|
"EM101", # Exception must not use a string literal
|
|
"EM102", # Exception must not use an f-string literal
|
|
"FBT001", # Boolean positional arg in function definition
|
|
"FBT002", # Boolean default positional argument in function definition
|
|
"G004", # Logging statement uses f-string
|
|
"PLR2004", # Magic value used in comparison
|
|
"SLF001", # Private member accessed
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
# Test doubles use fixture tokens/passwords and match a callee signature whose
|
|
# args they intentionally ignore.
|
|
"tests/test_viewer_auth.py" = ["S105", "S106", "ARG001"]
|
|
"tests/test_cloud_cli.py" = ["S105", "ARG001"]
|
|
"tests/test_codex_auth.py" = ["S105", "S106", "SLF001"]
|
|
"tests/test_entra_user.py" = ["S105", "S106", "SLF001"]
|
|
# Hatchling loads the build hook by path, not as an importable package.
|
|
"scripts/tui_sidecar_hook.py" = ["INP001"]
|
|
# Stdlib HTTP handler overrides (do_GET/do_POST).
|
|
"strix/interface/auth_cli.py" = ["N802"]
|
|
"tests/test_codex_streaming.py" = ["N802"]
|
|
"tests/test_disable_streaming.py" = ["N802"]
|
|
"tests/test_tool_call_ids.py" = ["N802"]
|
|
"tests/test_tool_call_limits.py" = ["N802", "SLF001"]
|
|
"tests/test_stream_idle_timeout.py" = ["N802", "SLF001"]
|
|
"tests/test_unknown_tool_recovery.py" = ["N802"]
|
|
"tests/test_report_pdf.py" = ["S105", "S106"]
|
|
# Fake MCP server matches the SDK's MCPServer signature; its args are unused.
|
|
"tests/test_mcp_client.py" = ["S105", "S106", "ARG002"]
|
|
# MCP connection request in a test carries a dummy bearer token.
|
|
"tests/test_runner_root_prompt.py" = ["S106"]
|
|
# Stdlib HTTP handler overrides (do_GET/do_POST) and lazy imports that avoid a
|
|
# circular dependency with strix.telemetry / strix.interface.viewer.report_pdf.
|
|
"strix/interface/viewer/server.py" = ["N802", "PLC0415"]
|
|
"strix/interface/cloud/payment_proxy.py" = ["N802"]
|
|
# Lazy telemetry import to avoid importing PostHog before the viewer starts.
|
|
"strix/interface/viewer/cli.py" = ["PLC0415"]
|
|
# Lazy imports inside functions to avoid circular dependency with
|
|
# strix.telemetry / strix.report.dedupe / cvss.
|
|
"strix/tools/notes/tools.py" = ["PLC0415", "TC002"]
|
|
"strix/tools/finish/tool.py" = ["PLC0415", "TC002"]
|
|
"strix/tools/reporting/tool.py" = ["PLC0415", "TC002"]
|
|
# Lazy imports of strix.tools.mcp.client avoid a circular import (client imports
|
|
# the session module at module load).
|
|
"strix/tools/mcp/session.py" = ["PLC0415"]
|
|
# call_mcp is a chain of guard clauses that each return an error string.
|
|
"strix/tools/mcp/agent_tools.py" = ["PLR0911"]
|
|
"strix/tools/**/*.py" = [
|
|
"ARG001", # Unused function argument (tools may have unused args for interface consistency)
|
|
]
|
|
# Custom Docker subclass duplicates parent body; some imports are for annotations.
|
|
# Backend factories import their backend's deps lazily so deployments
|
|
# that pick a different backend don't need every backend's libs installed.
|
|
"strix/runtime/backends.py" = ["PLC0415"]
|
|
"strix/runtime/docker_client.py" = [
|
|
"TC002", # Manifest, Container imported for annotations
|
|
"TC003", # uuid imported for annotation
|
|
]
|
|
# SDK function-tool wrappers: the SDK calls get_type_hints() at registration
|
|
# time to derive the JSON schema, which evaluates annotations at runtime —
|
|
# so RunContextWrapper / Tool / TResponseInputItem must be imported eagerly,
|
|
# not under TYPE_CHECKING.
|
|
"strix/tools/todo/tools.py" = ["TC002"]
|
|
"strix/tools/thinking/tool.py" = ["TC002"]
|
|
"strix/tools/web_search/tool.py" = ["TC002"]
|
|
"strix/tools/proxy/tools.py" = ["TC002", "PLR0911"]
|
|
# The generated Caido GraphQL schema is slow to import, so the SDK is imported
|
|
# on first proxy call instead of at module scope (keeps it off the launch path).
|
|
"strix/tools/proxy/caido_api.py" = ["PLC0415"]
|
|
"strix/runtime/caido_bootstrap.py" = ["PLC0415"]
|
|
"strix/tools/agents_graph/tools.py" = ["TC002"]
|
|
"strix/agents/factory.py" = ["TC002"]
|
|
# Entry point: ``Path`` is used at runtime by the typing of the
|
|
# session_manager call; importing under TYPE_CHECKING would defer
|
|
# resolution past where mypy needs it.
|
|
"strix/core/runner.py" = ["TC003", "PLR0912", "PLR0915", "PLC0415"]
|
|
# ReportState carries scan artifact/report fields and
|
|
# a runtime ``Callable`` annotation on ``vulnerability_found_callback``.
|
|
"strix/report/state.py" = ["TC003", "PLR0912", "PLR0915", "E501", "PERF401", "PLC0415"]
|
|
"strix/report/usage.py" = ["PLC0415"]
|
|
# LiteLLM and the Docker SDK are imported on first use, not at module scope:
|
|
# both cost seconds to import and neither is needed until a model call is made
|
|
# (or, for Docker, unless the Docker runtime backend is in use).
|
|
"strix/core/execution.py" = ["PLC0415"]
|
|
"strix/report/pricing.py" = ["PLC0415"]
|
|
"strix/llm/compaction.py" = ["PLC0415"]
|
|
"strix/llm/context_budget.py" = ["PLC0415"]
|
|
# Lazy import of strix.config.models avoids a circular dependency between the
|
|
# report pipeline and the config layer.
|
|
"strix/report/dedupe.py" = ["PLC0415"]
|
|
"strix/telemetry/logging.py" = ["PLC0415"]
|
|
"strix/config/models.py" = ["PLC0415"]
|
|
# LiteLLM is imported lazily: the request log is wired at startup on every
|
|
# route, including the native OpenAI ones that never load LiteLLM.
|
|
"strix/llm/request_log.py" = ["PLC0415"]
|
|
# Heavy inference deps (httpx, openai) imported lazily so auth-status checks
|
|
# don't pull them in.
|
|
"strix/config/codex.py" = ["PLC0415"]
|
|
# fcntl (cross-process refresh lock) is POSIX-only, so it is imported where used.
|
|
"strix/config/entra_user.py" = ["PLC0415"]
|
|
# Interface utility branches per scope-mode / target-type combination;
|
|
# splitting would obscure the decision tree without simplifying it.
|
|
"strix/interface/utils.py" = ["PLR0912", "BLE001", "PLC0415"]
|
|
# CLI / TUI / main keep extensive lazy imports + broad exception
|
|
# swallows for resilience around terminal-rendering errors.
|
|
"strix/interface/cli.py" = ["BLE001", "PLC0415"]
|
|
"strix/interface/scan_setup.py" = ["PLC0415"]
|
|
"strix/interface/main.py" = ["BLE001", "PLC0415", "PLR0912", "PLR0915"]
|
|
"strix/interface/cli_args.py" = ["BLE001", "PLC0415", "PLR0912", "PLR0915"]
|
|
"strix/interface/environment.py" = ["BLE001", "PLC0415", "PLR0912", "PLR0915"]
|
|
# The Go TUI runtime and backend controller import interface modules lazily so
|
|
# the sidecar entry point stays fast and avoids circular imports.
|
|
"strix/interface/interactive.py" = ["PLC0415"]
|
|
"strix/interface/tui/runtime.py" = ["PLC0415"]
|
|
"strix/interface/tui/backend/controller.py" = ["PLC0415"]
|
|
|
|
[tool.ruff.lint.isort]
|
|
force-single-line = false
|
|
lines-after-imports = 2
|
|
known-first-party = ["strix"]
|
|
known-third-party = ["pydantic"]
|
|
|
|
[tool.ruff.lint.pylint]
|
|
max-args = 8
|
|
|
|
[tool.ruff.format]
|
|
quote-style = "double"
|
|
indent-style = "space"
|
|
skip-magic-trailing-comma = false
|
|
line-ending = "auto"
|
|
|
|
# ============================================================================
|
|
# PyRight Configuration (Alternative type checker)
|
|
# ============================================================================
|
|
|
|
[tool.pyright]
|
|
include = ["strix"]
|
|
exclude = ["**/__pycache__", "build", "dist"]
|
|
pythonVersion = "3.12"
|
|
pythonPlatform = "Linux"
|
|
|
|
typeCheckingMode = "strict"
|
|
reportMissingImports = true
|
|
reportMissingTypeStubs = false
|
|
reportGeneralTypeIssues = true
|
|
reportPropertyTypeMismatch = true
|
|
reportFunctionMemberAccess = true
|
|
reportMissingParameterType = true
|
|
reportMissingTypeArgument = true
|
|
reportIncompatibleMethodOverride = true
|
|
reportIncompatibleVariableOverride = true
|
|
reportInconsistentConstructor = true
|
|
reportOverlappingOverload = true
|
|
reportConstantRedefinition = true
|
|
reportImportCycles = true
|
|
reportUnusedImport = true
|
|
reportUnusedClass = true
|
|
reportUnusedFunction = true
|
|
reportUnusedVariable = true
|
|
reportDuplicateImport = true
|
|
|
|
# ============================================================================
|
|
# Black Configuration (Code Formatter)
|
|
# ============================================================================
|
|
|
|
[tool.black]
|
|
line-length = 100
|
|
target-version = ['py312']
|
|
include = '\\.pyi?$'
|
|
extend-exclude = '''
|
|
/(
|
|
# directories
|
|
\.eggs
|
|
| \.git
|
|
| \.hg
|
|
| \.mypy_cache
|
|
| \.tox
|
|
| \.venv
|
|
| build
|
|
| dist
|
|
)/
|
|
'''
|
|
|
|
# ============================================================================
|
|
# isort Configuration (Import Sorting)
|
|
# ============================================================================
|
|
|
|
[tool.isort]
|
|
profile = "black"
|
|
line_length = 100
|
|
multi_line_output = 3
|
|
include_trailing_comma = true
|
|
force_grid_wrap = 0
|
|
use_parentheses = true
|
|
ensure_newline_before_comments = true
|
|
known_first_party = ["strix"]
|
|
known_third_party = ["pydantic", "litellm"]
|
|
|
|
# ============================================================================
|
|
# Bandit Configuration (Security Linting)
|
|
# ============================================================================
|
|
|
|
[tool.bandit]
|
|
# Tests are covered by ruff's flake8-bandit rules (see per-file-ignores above),
|
|
# which is where fixture tokens and loopback URL opens are already waived.
|
|
exclude_dirs = ["docs", "build", "dist", "tests"]
|
|
skips = ["B101", "B601", "B404", "B603", "B607"] # Skip assert, shell injection, subprocess import and partial path checks
|
|
severity = "medium"
|