Skip to content

ADR 0001: Domain constraints as declarative feature-space schemas

  • Status: accepted
  • Date: 2026-09-26

Context

Textbook adversarial evaluations of network IDSs perturb every feature freely, producing flows with negative packet counts, server replies the attacker cannot forge, or a "mean packet size" inconsistent with bytes / packets. Those numbers overstate the attacker. The alternative, problem-space attacks (rewrite pcaps, replay, re-run CICFlowMeter), is faithful but slow and ties the study to one feature extractor.

Decision

Model the attacker in feature space with a per-dataset Schema (feint/schema.py) that declares one role per feature:

role meaning CIC-IDS2017 UNSW-NB15
up attacker may only increase (delay, add packets, pad) duration, fwd_pkts, fwd_bytes, fwd_pkt_len_max dur, spkts, sbytes
free attacker sets any value in a range init_win_fwd in [0, 65535] sttl in [1, 255], swin in [0, 255]
integer stays integral fwd_pkts spkts
derived recomputed from base features, never set mean_iat, fwd_bpp, pkt_ratio, bytes_per_s smean, dmean, sload, dload, rate, sinpkt
fixed everything else (server side, protocol-bound, context) bwd_*, syn_count, dst_port, init_win_bwd dpkts, dbytes, dttl, dwin, trans_depth, ct_, proto_

Cross-feature relations add, for CIC, fwd_bytes <= fwd_pkts * MTU and mean <= fwd_pkt_len_max <= min(fwd_bytes, MTU); for UNSW, every added packet carries at least 40 header bytes and at most an MTU. project() maps any perturbed matrix onto the realisable set, is_valid() checks it, every constrained attack projects after every step, and the tests assert 100 % validity.

Derived features are recomputed on load for clean data too, so clean and adversarial flows obey the same equations. For UNSW-NB15 the recomputed values do not match all published columns: only rate agrees; sload differs on 99.9 % of rows (ratio about spkts/(spkts-1), a different convention), dload on 53 %, sinpkt on 33 %, smean / dmean on about a third (integer rounding, at most 1.3 %).

Consequences

  • Robust features (Schema.robust_features()) fall out automatically: features that neither the attacker nor any derivation from attacker-controlled features can move. They drive the robust-feature model and the poisoning sanitiser.
  • The model is an approximation. It ignores, for example, that adding packets also lengthens duration, that ct_* context counters depend on the attacker's connection rate, and that some attacks (DoS) need their volume, so padding may defeat the attack's purpose. These are listed as limitations, and favour the attacker. In the other direction the schema fixes features a client can influence indirectly (ct_*, trans_depth, server-response bwd_* / dbytes, syn_count), which favours the defender; the net direction is not known.
  • New datasets need a new schema; attack and hardening code is schema-agnostic.