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}