Bezpieczeństwo i jakość (bandit, sekrety, obsługa błędów)
ZEUS to platforma bezpieczeństwa — nasz własny kod musi być wzorem. Ta lekcja zbiera house rules dotyczące bezpieczeństwa i jakości: statyczna analiza bandit, sekrety poza kodem i obsługa błędów, która nie ukrywa problemów.
bandit — skaner bezpieczeństwa kodu
bandit przeszukuje kod pod kątem znanych antywzorców: eval, subprocess
z shell=True, słabe hasze, wstrzyknięcia SQL.
bandit -r app/ -ll # -ll = tylko medium/high severity
# bandit: B602 subprocess with shell=True — podatność na injection
subprocess.run(f"nslookup {host}", shell=True) # ŹLE
# dobrze — bez powłoki, argumenty jako lista
subprocess.run(["nslookup", host], check=True)
Standard ProfessNet: bandit działa w CI obok ruff/mypy. Wszystkie findingi high/medium muszą być rozwiązane lub świadomie oznaczone
# nosecz komentarzem.
Sekrety nigdy w kodzie
Hasła, tokeny, connection stringi — wyłącznie ze zmiennych środowiskowych lub
sejfu (Azure Key Vault). W repo trzymamy tylko .env.example z pustymi wartościami.
# źle — sekret w kodzie, trafi do git history na zawsze
API_KEY = "sk_live_8f3a9c2e..."
# dobrze — z konfiguracji
from app.core.config import settings
client = Client(api_key=settings.api_key)
Standard ProfessNet:
.envjest w.gitignore. Przed commitem działadetect-secrets(pre-commit). Sekret w historii git = rotacja klucza, nie tylkogit rm.
Walidacja wejścia = bezpieczeństwo
Każde wejście zewnętrzne walidujemy Pydantikiem — to nie tylko UX, to obrona przed injection i nieprawidłowymi danymi.
class HostQuery(BaseModel):
forest: str = Field(pattern=r"^[a-z0-9.\-]+$", max_length=255)
Zapytania do bazy — zawsze parametryzowane
# źle — SQL injection
await db.execute(f"SELECT * FROM hosts WHERE forest = '{forest}'")
# dobrze — parametry bind
await db.execute(
text("SELECT * FROM hosts WHERE forest = :forest"),
{"forest": forest},
)
W praktyce używamy ORM SQLAlchemy, który parametryzuje za nas.
Obsługa błędów, która nie kłamie
Nie połykamy wyjątków. Łapiemy konkretny typ, logujemy z kontekstem i albo obsługujemy, albo propagujemy.
import logging
logger = logging.getLogger(__name__)
# źle — cisza, błąd znika
try:
await collect(forest)
except Exception:
pass
# dobrze — konkretny wyjątek, log, czytelna reakcja
try:
await collect(forest)
except ProbeTimeoutError as exc:
logger.warning("Timeout probe %s: %s", forest, exc)
raise HTTPException(status_code=504, detail="Probe nie odpowiedział")
Standard ProfessNet: nigdy
except Exception: pass. Logujemy bez sekretów w treści (żadnych tokenów ani haseł w logach).
| Reguła | Dlaczego |
|---|---|
| bandit w CI | wyłapuje znane podatności kodu |
| sekrety z env/Key Vault | brak wycieków do repo |
| Pydantic na wejściu | walidacja i obrona przed injection |
| ORM / parametry bind | brak SQL injection |
konkretny except + log | błędy są widoczne, nie znikają |
Bezpieczny kod to suma drobnych nawyków: skan, sekrety poza repo, walidacja, parametryzacja i uczciwa obsługa błędów. W produkcie bezpieczeństwa to nie opcja — to definicja ukończonego zadania.