code wiki / _hdl_build / nx_ad_ivt.nx
nx_ad_ivt.nx source
↩ module page · 72 lines · 3383 B
1// nx_ad_ivt.nx -- LIB (PURE classify + store loader): STRENGTHENED aggregate INVALID-TRAFFIC (IVT)
2// classifier for the "filter bot/useless traffic for clients" face. Generalises nx_ad_botfilter's 3
3// signals to a wider GIVT signal vector (the categories the cited MRC Invalid-Traffic Detection
4// Guidelines + IAB name) -- additive and NON-BREAKING (nx_ad_botfilter stays as-is for conversion /
5// meter; this is the richer classifier the census grades against). [bar: srch_adfraud.raw]
6//
7// PRIVACY BY CONSTRUCTION (feedback-no-server-side-visitor-identity): inputs are AGGREGATE CLASS
8// CODES only -- NO visitor id / IP / fingerprint. We deliberately stay GIVT-tier (general invalid
9// traffic: data-center, known/declared bots, non-browser, no-JS, missing-referrer, impossible
10// cadence). We do NOT do SIVT-tier detection because the incumbent techniques for it require per-
11// user fingerprinting / device graphs -- the surveillance we REFUSE. "Systems, not people."
12//
13// Bucket = [c0, c1, ..., c(W-1), n] (W class-fields + a count). Field meanings (data-driven rules
14// in the store decide which codes are "bad", rule 11):
15// 0 ua_class 1=known-bot UA 2=non-browser UA
16// 1 src_class 1=data-center ASN 2=known proxy / VPN exit
17// 2 js_class 1=raw / no-JS-exec
18// 3 ref_class 1=missing referrer
19// 4 cadence_class 1=impossible request cadence (too fast to be human)
20// 5 declared_class 1=self-declared bot (robots/UA self-identifies)
21// license_tier: ORIGINAL
22import "nx_ad_botfilter.nx"
23import "nx_ad_store.nx"
24import "nx_syscalls.nx"
25
26// 1 if this W-wide class vector matches ANY rule (invalid traffic), else 0. Rules are flat
27// [field0,bad0, field1,bad1, ...]; a field may appear in several rules (e.g. ua bad in {1,2}).
28func iv_invalid(fields: *i64, W: i64, rules: *i64, nrules: i64) -> i64 {
29 var r: i64 = 0
30 while r < nrules {
31 let f: i64 = rules[r * 2]
32 let badv: i64 = rules[r * 2 + 1]
33 if f >= 0 { if f < W { if fields[f] == badv { return 1 } } }
34 r = r + 1
35 }
36 return 0
37}
38
39// tally valid vs invalid COUNTS over m buckets (each = W class-fields + count). Writes valid-n to
40// *ov, invalid-n to *oi; returns total. The meter counts only valid. Aggregate-only.
41func iv_tally(ev: *i64, m: i64, W: i64, rules: *i64, nrules: i64, ov: *i64, oi: *i64) -> i64 {
42 var v: i64 = 0
43 var iv: i64 = 0
44 var b: i64 = 0
45 let stride: i64 = W + 1
46 while b < m {
47 let base: i64 = b * stride
48 let cnt: i64 = ev[base + W]
49 var hit: i64 = 0
50 var r: i64 = 0
51 while r < nrules {
52 let f: i64 = rules[r * 2]
53 let badv: i64 = rules[r * 2 + 1]
54 if f >= 0 { if f < W { if ev[base + f] == badv { hit = 1 } } }
55 r = r + 1
56 }
57 if hit == 1 { iv = iv + cnt } else { v = v + cnt }
58 b = b + 1
59 }
60 ov[0] = v
61 oi[0] = iv
62 return v + iv
63}
64
65// load the richer rule set from the sovereign store (key e.g. adcfg:ivtrules) into flat field/bad
66// pairs; returns the pair count, or 0 if absent (fail-closed). Reuses bf_parse_rules (DRY rule 15).
67func iv_load_rules(key: *u8, rules: *i64, maxr: i64) -> i64 {
68 let pq: *i64 = sys_mmap(16) as *i64
69 let lq: *i64 = sys_mmap(16) as *i64
70 if ads_get(key, pq, lq) != 1 { return 0 }
71 return bf_parse_rules(pq[0] as *u8, lq[0], rules, maxr)
72}