Skip to content

Commit f7c0da7

Browse files
committed
Merge remote-tracking branch 'origin/master' into develop
2 parents 6dbfb84 + 54a4bc9 commit f7c0da7

15 files changed

Lines changed: 1696 additions & 114 deletions

File tree

config/slips.yaml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,13 @@ bruteforcing:
235235
anomaly_detection_https:
236236
# Number of initial hours used to train the baseline model assuming benign traffic.
237237
# If set to 0, detection starts immediately and baseline is learned online.
238-
training_hours: 0
238+
training_hours: 2
239+
# Training fit technique: welford or ewma.
240+
training_fit_method: welford
241+
# Training adaptation strength:
242+
# 1.0 = Welford benign fit (strongest, equal-weight fit),
243+
# <1.0 = EWMA-style training with this alpha (used when training_fit_method=ewma).
244+
training_alpha: 1
239245

240246
# Thresholds for anomaly detection in hourly and per-flow checks.
241247
hourly_zscore_threshold: 3.0
@@ -245,7 +251,7 @@ anomaly_detection_https:
245251
adaptation_score_threshold: 2.0
246252

247253
# Adaptation speed for baseline updates.
248-
baseline_alpha: 0.1
254+
baseline_alpha: 0.5
249255
drift_alpha: 0.05
250256
suspicious_alpha: 0.005
251257

@@ -259,10 +265,12 @@ anomaly_detection_https:
259265
# If true, drift/suspicious update is decided only after ADWIN signals drift.
260266
# If false, Slips uses the previous threshold-only drift logic.
261267
use_adwin_drift: true
262-
adwin_delta: 0.002
263-
adwin_clock: 32
264-
adwin_grace_period: 10
268+
adwin_delta: 0.01
269+
adwin_clock: 1
270+
adwin_grace_period: 5
265271
adwin_min_window_length: 5
272+
empirical_threshold_quantile: 0.995
273+
266274

267275
# JA3 statistical fallback gate used only when training_hours = 0.
268276
# If there is no benign training period, ja3_changes is not scored

docs/https_anomaly_detection.md

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ For the first configured benign hours, the module does **fit-only** (Welford onl
5757
- no detection decisions are emitted from hourly z-score rules before training ends,
5858
- baseline mean/variance are learned strongly from this period.
5959

60+
Training fit strength is configurable with `training_alpha`:
61+
62+
Training fit technique is selected by `training_fit_method`:
63+
64+
- `training_fit_method = welford` -> Welford benign fit.
65+
- `training_fit_method = ewma` -> EWMA-style training adaptation.
66+
67+
When `training_fit_method = ewma`, `training_alpha` controls strength:
68+
69+
- higher `training_alpha` = faster adaptation,
70+
- lower `training_alpha` = slower adaptation.
71+
6072
### No explicit training (`training_hours = 0`)
6173

6274
Detection starts immediately using online adaptation.
@@ -67,19 +79,53 @@ Special fallback only for `ja3_changes`:
6779

6880
## Scoring
6981

70-
Each modeled feature uses z-score:
82+
Each modeled feature uses robust scoring in three explicit steps:
83+
84+
1. Transform heavy-tail signals: `y = log(1 + x)` (`log1p`) for non-negative count/bytes features.
85+
2. Estimate robust center/scale on recent transformed values:
86+
- `m = median(y)`
87+
- `MAD = median(|y - m|)`
88+
- `sigma_robust = max(1.4826 * MAD, min_std_floor)`
89+
3. Score deviation:
90+
- `z_robust = |y_t - m| / sigma_robust`
7191

72-
- `z = |x - mean| / std_effective`
73-
- `std_effective` uses variance with a robust minimum floor to avoid unstable near-zero std.
92+
Why this is used:
93+
94+
- HTTPS counts and byte volumes are typically right-skewed and heavy-tailed,
95+
- mean/std-only scoring overreacts to bursts and underreacts after outliers,
96+
- `log1p + median/MAD` is more stable under non-Gaussian traffic.
7497

7598
Thresholds:
7699

77-
- `hourly_zscore_threshold` for hourly features
78-
- `flow_zscore_threshold` for flow bytes to known servers
100+
- empirical thresholds calibrated from benign training when `training_hours > 0`,
101+
- otherwise defaults (`hourly_zscore_threshold`, `flow_zscore_threshold`).
102+
103+
Calibration rule:
104+
105+
- per signal, collect robust z-scores on confirmed benign training data,
106+
- set threshold to high benign quantile (`empirical_threshold_quantile`, default 0.995),
107+
- fallback to defaults if training data is insufficient.
79108

80109
## Adaptation states
81110

82-
After each hour closes, the module chooses model update mode:
111+
After each hour closes, the module chooses model update mode.
112+
113+
Update event semantics:
114+
115+
- `training_fit`:
116+
initial benign baseline fit while `trained_hours < training_hours`;
117+
uses training fit method (Welford-style), not EWMA alpha.
118+
- `baseline_update`:
119+
normal post-training adaptation; uses EWMA with `baseline_alpha`.
120+
In ADWIN mode, this is used when ADWIN does not signal drift.
121+
- `drift_update`:
122+
post-training drift adaptation; uses EWMA with `drift_alpha`.
123+
In ADWIN mode, this is used only after ADWIN drift signal and small/drift-like classification.
124+
- `suspicious_update`:
125+
post-training conservative adaptation; uses EWMA with `suspicious_alpha`.
126+
In ADWIN mode, this is used only after ADWIN drift signal and suspicious classification.
127+
128+
When `use_adwin_drift=false`:
83129

84130
1. `training_fit`
85131
During benign training: Welford fit (no EWMA alpha).
@@ -92,14 +138,14 @@ After each hour closes, the module chooses model update mode:
92138

93139
For normal non-anomalous periods outside training, per-feature EWMA uses `baseline_alpha`.
94140

95-
### Optional ADWIN drift trigger
141+
### ADWIN drift trigger (`use_adwin_drift=true`)
96142

97-
If `use_adwin_drift=true` and `river` is installed, ADWIN is used as drift trigger in both paths:
143+
If `use_adwin_drift=true` and `river` is installed, ADWIN is the only drift trigger in both paths:
98144

99-
- **Hourly path**: ADWIN receives `hourly_adwin_score` (sum of hourly feature z-scores).
100-
- **Flow path**: ADWIN receives `flow_score` (sum of reason z-scores, novelty reasons mapped to a small fixed score).
145+
- **Hourly path**: ADWIN receives each raw hourly feature stream.
146+
- **Flow path**: ADWIN receives each raw per-flow signal stream.
101147
- ADWIN drift detected -> classify as `drift_update` or `suspicious_update` using existing thresholds.
102-
- No ADWIN drift -> use `baseline_update` (`baseline_alpha`).
148+
- No ADWIN drift -> use `baseline_update` (`baseline_alpha`), even if anomalies exist.
103149
- During benign training, ADWIN is still warmed with benign scores to reduce cold-start noise after training.
104150

105151
Why raw signals:
@@ -113,6 +159,13 @@ Performance note:
113159
- flow ADWIN cost scales with per-flow signal count,
114160
- both are constant-time scalar updates and usually lightweight.
115161

162+
Current tuned defaults for faster ADWIN reaction:
163+
164+
- `adwin_delta: 0.01`
165+
- `adwin_clock: 1`
166+
- `adwin_grace_period: 5`
167+
- `adwin_min_window_length: 5`
168+
116169
## New server vs JA3 behavior
117170

118171
- `new_servers` is modeled as an hourly statistical feature and adapted over time.
@@ -158,6 +211,8 @@ Section: `anomaly_detection_https` in `config/slips.yaml`.
158211
Main keys:
159212

160213
- `training_hours`
214+
- `training_fit_method`
215+
- `training_alpha`
161216
- `hourly_zscore_threshold`
162217
- `flow_zscore_threshold`
163218
- `adaptation_score_threshold`
@@ -172,13 +227,24 @@ Main keys:
172227
- `adwin_clock`
173228
- `adwin_grace_period`
174229
- `adwin_min_window_length`
230+
- `empirical_threshold_quantile`
175231
- `log_verbosity`
176232

177-
Default: `use_adwin_drift=true`.
233+
Defaults (from parser/config):
234+
235+
- `training_alpha: 1.0`
236+
- `training_fit_method: welford`
237+
- `use_adwin_drift: true`
238+
- `adwin_delta: 0.01`
239+
- `adwin_clock: 1`
240+
- `adwin_grace_period: 5`
241+
- `adwin_min_window_length: 5`
178242

179243
Reference:
180244

181245
- River ADWIN: https://riverml.xyz/latest/api/drift/ADWIN/
246+
- Data transformations for skew/heavy tails: https://otexts.com/fpp3/transformations.html
247+
- Robust scale (MAD): https://en.wikipedia.org/wiki/Median_absolute_deviation
182248

183249
## Operational logs
184250

install/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ pytest-asyncio
5050
vulture
5151
detect-secrets
5252
git+https://github.com/SECEF/python-idmefv2.git
53+
river

managers/redis_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def start_redis_cache_if_not_running(
125125
Check if we have redis-server running (this is the cache db it should
126126
always be running) adn start it if not running.
127127
"""
128-
start_redis_server = True
128+
start_redis_server = not utils.is_port_in_use(redis_port)
129129
# we dont care about the logger here we're just making sure the
130130
# server is up, this r isnt gonna be used later
131131
logger = ""

0 commit comments

Comments
 (0)