SentrySentry
Architecture

Risk Levels and Verdicts

Score, levels, signal weights, policy and repeat-offender escalation

Risk Levels and Verdicts

The scorer (pipeline.rs) sums the weights of the Signals produced by rules, heuristics, trackers and forks into a risk_score (0–100) and maps it to a RiskLevel. The VerdictPolicy turns the level into the final verdict, which can then be overridden by policy and by repeat-offender escalation.

Score → level → verdict

ScoreLevelColor (TUI)Default verdict ([policy])
0–9Infograyallow
10–29Lowblueallow
30–49Mediumyellowrate_limit
50–74Highorangechallenge
75–100Criticalredblock
  • The level buckets are fixed (stable dashboards); the verdict per level is configurable in [policy] (info/low/medium/high/ critical), and ordered [[policy.override]] entries (DSL match → verdict) win on first match.
  • Short-circuit rules enter with a fixed score: Block = 100, Challenge = 50, RateLimit = 30, Quarantine = 40 (process).
  • The sum is capped at 100 and gains a repetition bonus in a sliding window (repetition_window_secs, 60 s) when the same IP repeats signals.

Default signal weights

Every weight is overridable in [scorer.weights] by the signal key (e.g. sql_injection = 80). Defaults verified in code:

SignalWeightSource
log4shell80heuristics.rs
rce / cmd injection70heuristics
sql_injection60heuristics
upload_polyglot60analysis.rs (F10)
lfi50heuristics
known_bad_ip50reputation.rs
upload_executable50analysis.rs (F10)
suspicious_login_success45behavior.rs
xss45heuristics
path_traversal40heuristics
credential_stuffing40behavior.rs
bad_crawler40heuristics
upload_type_mismatch30analysis.rs (F10)
upload_disallowed30analysis.rs (F12)
sensitive_path30heuristics
directory_brute_force30behavior.rs
tcp_scanner30heuristics (SYN fingerprint)
auth_brute_force35behavior.rs
scan_behavior35scan.rs
spoofed_bot35botverify.rs (F7.7)
random_scan25scan.rs
upload_flood25analysis.rs (F10)
anomalous_payload (ONNX)25onnx_model.rs ([ai] signal_weight)
protocol_violation25analysis.rs (F9; schemas override per policy)
vpn_proxy20reputation.rs
tls_sni_mismatch20analysis.rs (F8)
upload_oversize20analysis.rs (F12)
scan_attack_correlation20correlation.rs (F3.10)
tor_exit_node15reputation.rs
promiscuous_scanner10reputation.rs (F3.10)
suspicious_ua / empty UA10heuristics
method_not_allowed10pipeline.rs (route validator)
unknown_route8pipeline.rs
llm_maliciousscore × confidenceLLM fork (F3.5)
external_reputationscaled by abuseConfidenceScoreexternal lookup (F7.5)
posture_advisory0 (locked)posture.rs (F11)

The PostureAdvisory nuance (F11)

Posture advisories describe the protected site (missing security headers on the upstream response), never the visitor. The weight is the POSTURE_ADVISORY_WEIGHT = 0 constant and weight_for returns 0 before consulting [scorer.weights] — no configuration can turn an advisory into enforcement. The signal shows up in dashboards, metrics and sentry posture, but never changes score, verdict or webhooks.

Repeat-offender escalation (F2.12)

Every event whose final verdict is not Allow records one strike in the IP's OffenderTracker. Strikes expire after window_secs (default 7 days — outlives the 24 h edge-rule TTL). Escalation only raises the verdict:

  • challenge_at (default 3) strikes → at least Challenge.
  • block_at (default 5) strikes → Block.
  • persist = true mirrors strikes to the ip_state table (Postgres) and pre-warms the tracker on startup — a returning offender past the edge TTL is re-blocked on its first violating event.

Allow never records a strike (no ratchet against benign traffic).

Guards that override everything

  • Trusted IPs (F7.2): IPs in [real_ip] trusted_ips get an immediate Allow and are immune even to rescore_from — the "you can't lock yourself out" guard.
  • Shadow IPs: Block/Quarantine are downgraded to Challenge (cap_shadow) — fully analyzed and logged, never banned.
  • Blacklist: an IP on [real_ip] blacklist is blocked (score 100) before any detector.

Cross-IP scan→attack correlation (F3.10)

The "shot calling" pattern observed between nodes watching the same surface: a scan (404 sweep, masscan/zmap SYNs, …) from a "clean" IP followed by an exploit/brute-force from another IP in the same /24 (IPv4), /64 (IPv6) or ASN within the window emits ScanAttackCorrelation (weight 20) — an aggravator of the attack, never a standalone detector. SYNs captured by the tcp source feed the same window as HTTP sweeps.

[correlation]
enabled = true
window_secs = 900           # 15 min

Alongside comes the scanner taxonomy as reputation tiers: authorized (a contracted scanner — no signal; trust with reputation = "authorized" → allow) and promiscuous (publishes recon for anyone to query → PromiscuousScanner, weight 10 — a scanner that publishes recon for anyone is not benign).

On this page