Build authorization-gated tenancy evidence harness
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a0260c-4067-7052-9647-ad000d576e38
This commit is contained in:
parent
2c8e1d41ad
commit
beab2a04d1
32 changed files with 1816 additions and 11 deletions
73
src/whitehat_security/capacity.py
Normal file
73
src/whitehat_security/capacity.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .model import Outcome
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CapacitySample:
|
||||
consumer: str
|
||||
latency_ms: float
|
||||
error_rate: float
|
||||
throughput_per_second: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CapacityResult:
|
||||
outcome: Outcome
|
||||
governor_bound: bool
|
||||
aggressor_peak: float
|
||||
aggressor_ceiling: float
|
||||
neighbour_degradation: dict[str, dict[str, float]]
|
||||
reasons: list[str]
|
||||
|
||||
|
||||
def characterize(*, baseline: list[CapacitySample], loaded: list[CapacitySample],
|
||||
governor_bound: bool, aggressor_peak: float,
|
||||
aggressor_ceiling: float) -> CapacityResult:
|
||||
before = {sample.consumer: sample for sample in baseline}
|
||||
after = {sample.consumer: sample for sample in loaded}
|
||||
reasons: list[str] = []
|
||||
if not governor_bound:
|
||||
reasons.append("declared governor did not bind")
|
||||
if aggressor_peak > aggressor_ceiling:
|
||||
reasons.append("probe exceeded its authorized allowance")
|
||||
missing = sorted(before.keys() - after.keys())
|
||||
if missing:
|
||||
reasons.append("loaded samples missing consumers: " + ", ".join(missing))
|
||||
|
||||
degradation: dict[str, dict[str, float]] = {}
|
||||
for consumer in sorted(before.keys() & after.keys()):
|
||||
old, new = before[consumer], after[consumer]
|
||||
degradation[consumer] = {
|
||||
"latency_increase_percent": _change(old.latency_ms, new.latency_ms),
|
||||
"error_rate_increase_points": new.error_rate - old.error_rate,
|
||||
"throughput_decrease_percent": _decrease(old.throughput_per_second,
|
||||
new.throughput_per_second),
|
||||
}
|
||||
outcome: Outcome = "pass"
|
||||
if aggressor_peak > aggressor_ceiling:
|
||||
outcome = "aborted"
|
||||
elif reasons:
|
||||
outcome = "finding"
|
||||
return CapacityResult(
|
||||
outcome=outcome,
|
||||
governor_bound=governor_bound,
|
||||
aggressor_peak=aggressor_peak,
|
||||
aggressor_ceiling=aggressor_ceiling,
|
||||
neighbour_degradation=degradation,
|
||||
reasons=reasons,
|
||||
)
|
||||
|
||||
|
||||
def _change(before: float, after: float) -> float:
|
||||
if before == 0:
|
||||
return 0.0 if after == 0 else float("inf")
|
||||
return round((after - before) / before * 100, 3)
|
||||
|
||||
|
||||
def _decrease(before: float, after: float) -> float:
|
||||
if before == 0:
|
||||
return 0.0
|
||||
return round((before - after) / before * 100, 3)
|
||||
Loading…
Add table
Add a link
Reference in a new issue