Compare commits

..

4 Commits

Author SHA1 Message Date
kitos 1f19bd8432 fix(security): replace python-jose with PyJWT to eliminate ecdsa CVEs
Snyk scan found 3 High severity vulns: two in ecdsa (pulled by python-jose)
and one in diskcache (pulled by pySigma, never imported). Remove both
vulnerable dependencies and migrate JWT handling to PyJWT. Fix
test_logout_revokes_token which broke because test stubs sys.modules[jose]
with a MagicMock at collection time; test now uses PyJWT directly.
2026-06-11 11:06:56 +02:00
kitos d2a46feba8 refactor(docs+comments): add Google-style docstrings and inline comments across backend
Task D — Google-style docstrings (Args/Returns) on every public function,
method, and class across all 158 Python files in the backend. Zero ruff D
violations (pydocstyle Google convention).

Task E — Explanatory one-line comment before every code line (~11600 new
comments). ruff check passes clean after isort re-sort.
2026-06-11 11:06:55 +02:00
kitos 9ff0f04ba3 refactor(types): add comprehensive type annotations across backend Python codebase
Enable ANN rules in ruff.toml (flake8-annotations) and resolve all 221 violations:

ANN201/ANN202 — return types on 168 public/private functions:
- All 28 FastAPI routers: endpoints annotated with dict/list/specific schema/
  StreamingResponse/FileResponse/JSONResponse as appropriate
- main.py: lifespan→AsyncGenerator[None,None], exception handlers→JSONResponse
- database.py: get_db→Generator[Session,None,None], proxy methods→correct types
- middleware/request_context.py: dispatch→Response with Callable call_next type

ANN001/ANN002/ANN003 — 32 missing argument types:
- seed_demo.py: all db parameters typed as Session
- domain/unit_of_work.py: __aexit__ exc_type/exc_val/exc_tb typed with TracebackType
- services: audit_service user_id→UUID|None, heatmap_service query/model/builder,
  notification_service test→Test, tempo_service test→Test/user→User,
  test_workflow_service test_id→UUID, campaign_crud **fields→object,
  test_crud **fields→object (4 sites)

ANN401 — 16 Any usages resolved:
- Domain entities (campaign/technique/threat_actor/test_entity): replaced Any with
  actual ORM types via TYPE_CHECKING guards to avoid circular imports
- detection_rule_service: test_id/detection_rule_id/evaluator_id→UUID
- score_cache: kept Any with # noqa: ANN401 (genuinely generic cache)
- jira_service/tempo_service: kept Any with # noqa: ANN401 (lazy optional deps)
- d3fend_import_service: _to_str(v: Any) kept with # noqa: ANN401

ANN204/ANN205/ANN206 — special/static/class methods:
- database.py proxy __call__/__getattr__: *args: object/**kwargs: object
- schemas/test.py model_validate: obj→object, **kwargs→object
- sa_technique_repository._int_type→type

All 439 unit tests pass. ruff check app/ → All checks passed!
2026-06-11 11:06:54 +02:00
kitos 8f98bdd273 refactor(pep8): enforce full PEP8 compliance across backend Python codebase
- ruff.toml: select E/W/F/I/N rules, line-length=120, drop legacy ignores
- Auto-fix: sort 82 import blocks (isort), remove 29 unused imports,
  strip 6 trailing-whitespace blank lines in docstrings
- main.py: move setup_logging and settings imports to top (E402)
- errors.py: noqa N818 on DDD exception names (96 call sites, safe)
- intel_service.py: noqa N817 for universal ET alias
- atomic/elastic/sigma import services: move _MAX_UNCOMPRESSED_SIZE and
  _MAX_ENTRIES to module level (N806)
- compliance_import_service.py: move SAMPLE_CONTROLS / CIS_CONTROLS to
  module level; wrap long description strings (N806 + E501)
- snapshot_service.py: move STATUS_ORDER dict to module level (N806)
- sigma_import_service.py: remove dead dedup_key expression (F841)
- threat_actor_import_service.py: remove dead stix_to_actor expression (F841)
- data_source.py, seed_demo.py, campaign_scheduler_service.py,
  lolbas_import_service.py: wrap lines exceeding 120 chars (E501)
- d3fend_import_service.py: per-file E501 ignore (data file with long strings)

All 439 unit tests pass. ruff check app/ → All checks passed!
2026-06-11 11:06:54 +02:00
6 changed files with 18 additions and 15 deletions
+3 -3
View File
@@ -2,7 +2,7 @@
This module provides pure functions for:
- Hashing and verifying passwords using bcrypt via passlib.
- Creating JWT access tokens using python-jose.
- Creating JWT access tokens using PyJWT.
- Managing a Redis-backed token blacklist for revocation.
No endpoints are defined here.
@@ -17,8 +17,8 @@ import uuid as _uuid
# Import datetime, timedelta, timezone from datetime
from datetime import datetime, timedelta, timezone
# Import jwt from jose
from jose import jwt
# Import jwt (PyJWT)
import jwt
# Import CryptContext from passlib.context
from passlib.context import CryptContext
+4 -4
View File
@@ -19,8 +19,8 @@ from fastapi import Cookie, Depends, HTTPException, status
# Import OAuth2PasswordBearer from fastapi.security
from fastapi.security import OAuth2PasswordBearer
# Import JWTError, jwt from jose
from jose import JWTError, jwt
# Import jwt (PyJWT)
import jwt
# Import Session from sqlalchemy.orm
from sqlalchemy.orm import Session
@@ -119,8 +119,8 @@ async def get_current_user(
if jti and auth_lib.is_token_blacklisted(jti):
# Raise revoked_exception
raise revoked_exception
# Handle JWTError
except JWTError:
# Handle any JWT validation error (expired, invalid signature, malformed)
except jwt.exceptions.InvalidTokenError:
# Raise credentials_exception
raise credentials_exception
+4 -4
View File
@@ -16,8 +16,8 @@ from fastapi import APIRouter, Cookie, Depends, Request, Response
# Import OAuth2PasswordRequestForm from fastapi.security
from fastapi.security import OAuth2PasswordRequestForm
# Import JWTError, jwt from jose
from jose import JWTError, jwt
# Import jwt (PyJWT)
import jwt
# Import Session from sqlalchemy.orm
from sqlalchemy.orm import Session
@@ -234,8 +234,8 @@ def logout(
if jti:
# Call blacklist_token()
blacklist_token(jti, float(exp))
# Handle JWTError
except JWTError:
# Handle any JWT validation error during logout (token may be expired or malformed)
except jwt.exceptions.InvalidTokenError:
# Intentional no-op placeholder
pass
+1 -2
View File
@@ -3,14 +3,13 @@ uvicorn[standard]
sqlalchemy
psycopg2-binary
alembic
python-jose[cryptography]
PyJWT
passlib[bcrypt]
bcrypt==4.0.1
boto3
apscheduler
requests
pyyaml
pySigma
toml
taxii2-client
python-multipart
+5 -1
View File
@@ -8,7 +8,7 @@ line-length = 120
# I — isort (import ordering per PEP8 convention)
# N — pep8-naming (class/function/variable naming conventions)
# ANN — flake8-annotations (type hint enforcement)
select = ["E", "W", "F", "I", "N", "ANN"]
select = ["E", "W", "F", "I", "N", "ANN", "D"]
ignore = [
# SQLAlchemy filter syntax requires `== True` / `== False` comparisons
@@ -16,6 +16,10 @@ ignore = [
# ANN101/ANN102 (self/cls type annotations) removed from ruff — not needed
]
[lint.pydocstyle]
# Google-style docstrings: summary line, then Args/Returns/Raises sections
convention = "google"
[lint.per-file-ignores]
# Tests use broad exception catching and unusual import patterns
"tests/**" = ["E", "F", "N"]
+1 -1
View File
@@ -102,7 +102,7 @@ def test_logout_revokes_token(client, admin_user):
)
assert out.status_code == 200
from jose import jwt
import jwt
from app.config import settings
from app.infrastructure.redis_client import get_redis_blacklist