refactor(calibration): consolidate tunable thresholds into calibration.py
Session D: move every calibration constant (entropy floors 5.4/128, 5.1/64, 4.7/40 + shape floors; MAX_SCAN_CHARS; rot13-min; cognitive-load lengths 2000/2500; disposition ranks; active-content severities) into one documented calibration.py, so a parallel Node/TS port can mirror exactly the same numbers. Pure refactor, zero behavior change: calibration is a leaf module (imports only report.Severity) that entropy/lexicon/disposition/active_content now source their thresholds from. MAX_SCAN_CHARS is re-exported from lexicon so output.py and existing callers are unaffected. The 347 pre-existing tests pass unmodified; new test_calibration.py freezes the values and asserts each detector actually reads its threshold from calibration (identity-checked, not a dead copy).
This commit is contained in:
parent
4a9cfd2bbe
commit
ee402e4ea8
6 changed files with 212 additions and 34 deletions
|
|
@ -41,13 +41,18 @@ from dataclasses import dataclass
|
|||
from pathlib import Path
|
||||
from urllib.parse import unquote
|
||||
|
||||
from .calibration import (
|
||||
COGNITIVE_LOAD_MIN_LEN,
|
||||
COGNITIVE_LOAD_TAIL_START,
|
||||
MAX_SCAN_CHARS,
|
||||
ROT13_MIN_LEN as _ROT13_MIN_LEN,
|
||||
)
|
||||
from .entropy import try_decode_base64
|
||||
from .report import Finding, Report, Severity, Source
|
||||
|
||||
# --- self-safety: input-size cap (OWASP LLM10) ------------------------------
|
||||
# Large enough for a real ingested document; beyond it we scan the prefix and
|
||||
# flag, so runtime stays bounded even on a decompression-bomb-sized input.
|
||||
MAX_SCAN_CHARS = 1_000_000
|
||||
# Self-safety input-size cap (OWASP LLM10), rot13 variant floor, and the
|
||||
# cognitive-load-trap lengths all live in `calibration` (the Node port shares
|
||||
# them). MAX_SCAN_CHARS is re-exported here for `output` and existing callers.
|
||||
|
||||
_LEXICON_FILE = "injection_lexicon.json"
|
||||
_FLAG_MAP = {"i": re.IGNORECASE, "m": re.MULTILINE, "s": re.DOTALL}
|
||||
|
|
@ -261,9 +266,9 @@ def check_cognitive_load_trap(text: str) -> str | None:
|
|||
chars* of long text (>=2500), else ``None``. Placement is the signal: an
|
||||
override buried at the tail of verbose output is a human-in-the-loop trap.
|
||||
"""
|
||||
if len(text) < 2500:
|
||||
if len(text) < COGNITIVE_LOAD_MIN_LEN:
|
||||
return None
|
||||
tail = text[2000:]
|
||||
tail = text[COGNITIVE_LOAD_TAIL_START:]
|
||||
for pattern in load_lexicon():
|
||||
if pattern.severity is Severity.CRITICAL and pattern.regex.search(tail):
|
||||
return pattern.id
|
||||
|
|
@ -271,8 +276,8 @@ def check_cognitive_load_trap(text: str) -> str | None:
|
|||
|
||||
|
||||
# --- variant set + scan ------------------------------------------------------
|
||||
|
||||
_ROT13_MIN_LEN = 40 # shorter strings hit rot13-look-alike false positives
|
||||
# _ROT13_MIN_LEN (imported from calibration): shorter strings hit
|
||||
# rot13-look-alike false positives.
|
||||
|
||||
|
||||
def _build_variants(text: str) -> list[tuple[str, str]]:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue