pyproject.toml
Rama main · réplica de lectura del repositorio original.
[tool.pytest.ini_options]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
testpaths = ["tests", "management/tests"]
# management/tests/ has __init__.py but management/ itself doesn't (implicit
# namespace package) — pytest's default rootdir walk-up stops at management/
# instead of reaching the repo root, breaking `from management.api import ...`
# under bare `pytest` (phase-808). pythonpath sidesteps that without touching
# application packaging.
pythonpath = ["."]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
# --tb=short: concise tracebacks
# --timeout=60: per-test hard limit (requires pytest-timeout)
# -q: quiet mode by default; run-local-tests.sh adds -v when writing to log
addopts = "--tb=short --timeout=60"
markers = [
"live_services: requires live Go/Python proxy and Redis; excluded from make test (use make test-live)",
"integration: requires live external IdP (Okta, Entra ID, Keycloak); set OKTA_METADATA_URL or ENTRA_OIDC_DISCOVERY_URL",
"unit: marks a unit test",
"chaos: chaos/resilience test",
"adversarial: adversarial/fuzz test",
]
# Warning policy:
# - Third-party library internals: ignore (too noisy, nothing we can fix)
# - asyncio cleanup noise from mocks: ignore specific patterns
# - Everything else: default behaviour (shown but not fatal)
# - Our own code: any unexpected RuntimeWarning will surface naturally
filterwarnings = [
# pytest-asyncio internal deprecation notices
"ignore::DeprecationWarning:pytest_asyncio",
"ignore::DeprecationWarning:_pytest",
# Third-party library internals — nothing actionable for us
"ignore::DeprecationWarning:pkg_resources",
"ignore::DeprecationWarning:google",
"ignore::DeprecationWarning:aiohttp",
"ignore::DeprecationWarning:redis",
# urllib3 / requests noise in test environments
"ignore::urllib3.exceptions.InsecureRequestWarning",
"ignore::urllib3.exceptions.SecurityWarning",
# asyncio teardown with mocked tasks — not a real resource leak
"ignore:.*Enable tracemalloc.*:pytest.PytestUnraisableExceptionWarning",
"ignore:coroutine .* was never awaited:RuntimeWarning",
# aiohttp connector cleanup in unit tests (no real connections, just mocks)
"ignore:.*Unclosed client session.*:ResourceWarning",
"ignore:.*Unclosed connector.*:ResourceWarning",
# ssl.OP_NO_TLS* deprecated in Python 3.10+ (security/validation.py — third-party pattern)
"ignore:ssl.OP_NO_SSL.*:DeprecationWarning",
"ignore:ssl.OP_NO_TLS.*:DeprecationWarning",
# pycares async DNS callbacks arrive after asyncio loop closes during test teardown.
# This is a known interaction between pycares, Python 3.10 asyncio, and pytest-asyncio.
# Nothing actionable in our code — pycares teardown is outside our control.
# Approved Exception #001: see docs/security/EXCEPTIONS.md
"ignore::pytest.PytestUnraisableExceptionWarning",
# Google API Python 3.10 end-of-support notice — not actionable until we upgrade
"ignore::FutureWarning:google.api_core",
# Strict warning enforcement on first-party management and src modules
"error::DeprecationWarning:management.*",
"error::DeprecationWarning:src.*",
# Upstream python3-saml (onelogin.saml2) datetime & port deprecations (Python 3.14 upstream debt)
"ignore:datetime.datetime.utcnow\\(\\) is deprecated:DeprecationWarning:onelogin.saml2.*",
"ignore:datetime.datetime.utcfromtimestamp\\(\\) is deprecated:DeprecationWarning:onelogin.saml2.*",
"ignore:The server_port key in request data is deprecated:DeprecationWarning:onelogin.saml2.*",
# Upstream Authlib / Jose deprecation warnings (scheduled for Authlib 2.0.0)
"ignore::authlib.deprecate.AuthlibDeprecationWarning",
# Upstream Starlette / FastAPI testclient httpx warning
"ignore::starlette.exceptions.StarletteDeprecationWarning",
# HTTPX per-request cookie deprecation in integration test clients
"ignore:Setting per-request cookies=.* is being deprecated:DeprecationWarning:httpx.*",
# Intentional short HMAC key in test_protected_route_wrong_secret negative test
"ignore:The HMAC key is 12 bytes long:UserWarning:jwt.api_jwt",
]
# ── mypy configuration (consolidated from mypy.ini) ──────────────────────────
[tool.mypy]
exclude = ["^archive/", "^vendor/", "^node_modules/"]
# Phase 16f baseline — --ignore-missing-imports --no-strict-optional
# Goal: zero errors in new code; suppress known legacy patterns.
# Phase 16g will tighten to --strict once Redis typing is resolved.
python_version = "3.15"
ignore_missing_imports = true
no_strict_optional = true
warn_return_any = false
warn_unused_ignores = false
# Suppress "Library stubs not installed" (import-untyped) raised by mypy ≥1.19
# when third-party stub packages (e.g. types-PyYAML) are absent in CI Docker.
disable_error_code = "import-untyped"
[[tool.mypy.overrides]]
module = [
"proxy",
"src.security.validation",
"src.analytics.detection",
"src.analytics.stream_consumer",
"src.security.rdap_enrichment",
"src.security.tcp_analyzer",
"src.security.abuseipdb",
"src.security.beaconing_detector",
"src.security.sni_analyzer",
"src.security.tls_enforcer",
"src.security.dns_enrichment",
"src.security.pipeline",
"src.security.asn_classifier",
"src.security.gdpr_storage",
"src.cache.bloom",
"src.analytics.monitoring",
"src.analytics.shadow_scoring",
"src.analytics.baseline_monitor",
"src.pubsub",
"src.analytics.security_hardening",
"src.analytics.ml_detector",
]
ignore_errors = true
# ── flake8 configuration (consolidated from .flake8) ─────────────────────────
# Note: flake8 does not natively read pyproject.toml; requires Flake8-pyproject
# or use ruff instead. Kept here for reference alongside ruff config.
[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "W503", "E501", "E402", "F841"]
[tool.ruff]
exclude = ["archive", "vendor", "node_modules", "deploy/secrets"]
# Phase 16f baseline — check src/ and proxy.py; tests are advisory only.
target-version = "py313"
line-length = 120
[tool.ruff.lint]
# Enable: pyflakes (F), pycodestyle errors (E), isort (I)
# Skip: W (warnings — cosmetic), C (complexity), ANN (annotations — covered by mypy)
select = ["E", "F", "I"]
ignore = [
"E501", # line-too-long — handled by black
"E402", # module-level import not at top — some files have conditional imports
"F401", # unused import — too noisy in tests; addressed separately in src/
]
[tool.ruff.lint.per-file-ignores]
# Tests: allow unused imports (often imported for re-export or side effects)
"tests/**" = ["F401", "F811", "F841", "E711", "E712", "E721", "E741"]
# Admin CLI: allow broad exception handling
"scripts/ja4proxy_admin.py" = ["E722"]
# ── pylint configuration ─────────────────────────────────────────────────────
# `make lint-pylint` runs errors-only over src/ and management/. Two classes of
# false positive were drowning out real findings; both are silenced here rather
# than at each call site so the reason is recorded once.
[tool.pylint.main]
# `management/` is a PEP 420 namespace package (no __init__.py) while
# `management/api/` has one, so pylint sometimes anchors on `api` as the top
# level and then reports `from ..tasks.dial_revert` as an import beyond it —
# intermittently, depending on walk order. At runtime the module is always
# imported as `management.api.main` and the import is fine. Pinning the source
# root makes pylint resolve packages the same way the application does.
source-roots = ["."]
[tool.pylint.typecheck]
# weasyprint is an OPTIONAL dependency — it is deliberately absent from
# requirements.txt because it needs system libraries (pango, cairo, fontconfig)
# that the lint and CI images do not carry. Every import of it in this repo sits
# inside a try/except that degrades to HTML output, so `import-error` here
# reports a supported configuration as a failure. Removing this entry is only
# correct if weasyprint becomes a hard dependency.
ignored-modules = ["weasyprint"]