78af6d4755
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m29s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 11s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 8s
build / build (push) Successful in 19s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m30s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 11s
Testy / Kontrola składni wszystkich warstw (push) Successful in 7s
Zabezpieczenie DETEKCYJNE (nie prewencyjne): kilka unikalnych, wiarygodnie wyglądających rekordów-pułapek w bazach. Nie zmieniają interpretacji (odsiewamy je z wyników), ale jeśli pojawią się w cudzej kopii — są dowodem pochodzenia, a przy wariancie na kopię — wskazują ŹRÓDŁO wycieku. `canary.py`: pułapkę rozpoznajemy po MARKERZE (unikalny ciąg z ENV, nieobecny w realnych danych). `screen(rows, query_value)`: - ODSIEWA rekordy z markerem z wyników — i to na WYJŚCIU z warstwy danych (`/search`), więc nie dotrą wyżej ani do promptu LLM (LOG-30), niezależnie od dostawcy (Excel/SQL); - TRIPWIRE: gdy zapytanie celuje wprost w marker (enumeracja bazy, nie liczenie horoskopu) → log warning. Bez `CANARY_MARKERS` — przezroczyste, zero kosztu dla normalnego ruchu. Rejestr wariant→kopia (traitor tracing) i wstrzyknięcie do REALNYCH baz to krok właściciela (poza kodem — nie ruszamy kupionych plików automatycznie); instrukcja: docs/canary-registry.md. Mechanizm zbudowany i przetestowany na syntetycznych pułapkach. Testy: +7 (przezroczystość bez markerów, odsiewanie, tripwire, marker w dowolnym polu, endpoint odsiewa przed zwrotem). Pierwsze testy w usłudze `data`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
2.8 KiB
Python
71 lines
2.8 KiB
Python
"""Rekordy-pułapki (canary) — wykrywanie wycieku baz (DAN-26).
|
|
|
|
Zabezpieczenie DETEKCYJNE, nie prewencyjne. Kilka unikalnych, wiarygodnie
|
|
wyglądających rekordów wplecionych w bazy: nie zmieniają interpretacji (są
|
|
ODSIEWANE z wyników, więc nie trafiają ani do użytkownika, ani do promptu LLM —
|
|
wymóg LOG-30), ale jeśli kiedyś pojawią się w cudzej kopii, są dowodem pochodzenia.
|
|
|
|
Pułapkę rozpoznajemy po MARKERZE: unikalny ciąg, który nie występuje w realnych
|
|
danych (wpleciony np. w pole znaczące). Markery i WARIANT tego wdrożenia biorą się
|
|
z konfiguracji (ENV `CANARY_MARKERS`, `CANARY_VARIANT`) — każda kopia może dostać
|
|
swój zestaw. Który wariant trafił do którego wdrożenia trzyma rejestr po stronie
|
|
ops (osobno, poza kodem — patrz docs/canary-registry.md).
|
|
|
|
Dwa sygnały:
|
|
* ODSIEWANIE — pułapka w wynikach znika, zanim opuści warstwę danych (log info);
|
|
* TRIPWIRE — zapytanie CELUJE wprost w marker (ktoś enumeruje bazę, a nie liczy
|
|
realny horoskop) → log warning, bo to podejrzane zachowanie.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
|
|
log = logging.getLogger("astrololo.data.canary")
|
|
|
|
|
|
def _load_markers() -> list[str]:
|
|
return [m.strip() for m in os.getenv("CANARY_MARKERS", "").split(",") if m.strip()]
|
|
|
|
|
|
MARKERS = _load_markers()
|
|
VARIANT = os.getenv("CANARY_VARIANT", "")
|
|
|
|
|
|
def _row_is_canary(row: dict, markers: list[str]) -> bool:
|
|
"""Czy KTÓRAKOLWIEK tekstowa wartość wiersza zawiera marker pułapki."""
|
|
for v in row.values():
|
|
if isinstance(v, str):
|
|
for m in markers:
|
|
if m in v:
|
|
return True
|
|
return False
|
|
|
|
|
|
def screen(
|
|
rows: list[dict], query_value: str = "",
|
|
markers: list[str] | None = None, variant: str | None = None,
|
|
) -> tuple[list[dict], dict]:
|
|
"""Zwraca (widoczne_wiersze, raport).
|
|
|
|
Odsiewa pułapki z wyników (nie opuszczą warstwy danych). Gdy zapytanie celuje
|
|
wprost w marker — podnosi TRIPWIRE (możliwa enumeracja bazy). Bez skonfigurowanych
|
|
markerów: przezroczyste (`active=False`), zero kosztu dla normalnego ruchu.
|
|
"""
|
|
markers = MARKERS if markers is None else markers
|
|
variant = VARIANT if variant is None else variant
|
|
if not markers:
|
|
return rows, {"active": False, "removed": 0, "tripwire": False}
|
|
|
|
visible = [r for r in rows if not _row_is_canary(r, markers)]
|
|
removed = len(rows) - len(visible)
|
|
tripwire = any(m in (query_value or "") for m in markers)
|
|
|
|
if tripwire:
|
|
log.warning(
|
|
"CANARY TRIPWIRE: zapytanie celuje wprost w rekord-pułapkę (wariant %s) "
|
|
"— możliwa enumeracja bazy", variant or "?")
|
|
elif removed:
|
|
log.info("Odsiano %d rekord(ów)-pułapek z wyników (wariant %s)", removed, variant or "?")
|
|
return visible, {"active": True, "removed": removed, "tripwire": tripwire}
|