code wiki / _hdl_build / nx_ad_feedback_gate.nx
nx_ad_feedback_gate.nx source
↩ module page · 175 lines · 8478 B
1// nx_ad_feedback_gate.nx -- GATE (runnable) for ADS-013 GREAT/OK/BAD feedback. Proves on baked
2// controls + the REAL sovereign store:
3// poll : the one-tap widget is clean (GREAT/OK/BAD + campaign present, thirdparty_js=0)
4// escape : a malicious campaign id is HTML-escaped (no live tag) -- XSS-safe
5// tally : aggregate sentiment counts are exact (no identity, only class-bucket counts)
6// k-anon : a campaign below the floor is SUPPRESSED (a few taps can't single out the few)
7// ANTI-SNOOP (load-bearing): the rollup is persisted to knowledge/store/ad- and the ON-DISK bytes
8// are scanned for visitor session tokens -> ZERO found (leaked=0). The persisted record
9// is built only from counts, so it cannot carry an identifier; the gate proves it on
10// the real stored bytes, and a tampered record makes the scanner FIRE (anti-false-green).
11//
12// Evidence -> knowledge/status/ad_feedback.log (FEEDBACKGATE authored=organ ... verdict=GREEN).
13// license_tier: ORIGINAL
14import "nx_ad_feedback.nx"
15import "nx_ad_store.nx"
16import "nx_seg_store.nx"
17import "nx_ad_serve.nx"
18import "nx_kanon.nx"
19import "nx_syscalls.nx"
20import "nx_gate_verdict.nx"
21
22const FG_LOG: *u8 = "knowledge/status/ad_feedback.log"
23const FG_KEY: *u8 = "adfeedback:demo-coffee"
24
25func fg_w(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
26func fg_wn(fd: i64, v: i64) -> i64 { let bb: *u8 = sys_mmap(28); var m: i64=v; if m<0 {m=0-m; sys_write(fd,"-" as *u8,1)}; let t: *u8 = sys_mmap(28); var k: i64=0; if m==0 {t[0]=48;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1}; var i: i64=0; while i<k {bb[i]=t[k-1-i]; i=i+1}; sys_write(fd, bb, k); return 0 }
27
28// 1 if the store value for FG_KEY byte-equals (rec,reclen); used for idempotent write + readback verify.
29func fg_streq_store(key: *u8, rec: *u8, reclen: i64) -> i64 {
30 let pq: *i64 = sys_mmap(16) as *i64
31 let lq: *i64 = sys_mmap(16) as *i64
32 if ads_get(key, pq, lq) != 1 { return 0 }
33 if lq[0] != reclen { return 0 }
34 let b: *u8 = pq[0] as *u8
35 var i: i64 = 0
36 while i < reclen { if b[i] != rec[i] { return 0 } i = i + 1 }
37 return 1
38}
39
40func fg_emit(fd: i64, poll3p: i64, esc3p: i64, g: i64, o: i64, b: i64, rel_big: i64, rel_small: i64, persisted: i64, leaked: i64, fired: i64, ok: i64) -> i64 {
41 fg_w(fd, "FEEDBACKGATE authored=organ source=store poll_thirdparty_js=" as *u8); fg_wn(fd, poll3p)
42 fg_w(fd, " escaped_thirdparty_js=" as *u8); fg_wn(fd, esc3p)
43 fg_w(fd, " great=" as *u8); fg_wn(fd, g)
44 fg_w(fd, " ok=" as *u8); fg_wn(fd, o)
45 fg_w(fd, " bad=" as *u8); fg_wn(fd, b)
46 fg_w(fd, " release_big=" as *u8); fg_wn(fd, rel_big)
47 fg_w(fd, " release_small=" as *u8); fg_wn(fd, rel_small)
48 fg_w(fd, " persisted_verified=" as *u8); fg_wn(fd, persisted)
49 fg_w(fd, " tokens_leaked=" as *u8); fg_wn(fd, leaked)
50 fg_w(fd, " snoop_detector_fired=" as *u8); fg_wn(fd, fired)
51 if ok == 1 { fg_w(fd, " verdict=GREEN\n" as *u8) } else { fg_w(fd, " verdict=RED\n" as *u8) }
52 return 0
53}
54
55func main() -> i64 {
56 let k: i64 = ads_kanon_k()
57
58 // poll widget (clean + campaign present).
59 let camp: *u8 = "demo-coffee" as *u8
60 let pout: *u8 = sys_mmap(2048)
61 let pn: i64 = af_emit_poll(camp, as_len(camp), pout)
62 let poll3p: i64 = as_has_thirdparty_js(pout, pn)
63 let has_g: i64 = as_contains(pout, pn, "GREAT" as *u8)
64 let has_o: i64 = as_contains(pout, pn, "OK" as *u8)
65 let has_b: i64 = as_contains(pout, pn, "BAD" as *u8)
66 let has_c: i64 = as_contains(pout, pn, "demo-coffee" as *u8)
67
68 // escape: a malicious campaign id is neutralized.
69 let evil: *u8 = "<script>x</script>" as *u8
70 let eout: *u8 = sys_mmap(2048)
71 let en: i64 = af_emit_poll(evil, as_len(evil), eout)
72 let esc3p: i64 = as_has_thirdparty_js(eout, en)
73 let esc_ok: i64 = as_contains(eout, en, "<script>" as *u8)
74
75 // aggregate tally (no identity -- class-bucket counts only).
76 let ev: *i64 = sys_mmap(64) as *i64
77 ev[0] = 0; ev[1] = 30 // great 30
78 ev[2] = 1; ev[3] = 12 // ok 12
79 ev[4] = 2; ev[5] = 3 // bad 3
80 let og: *i64 = sys_mmap(8) as *i64
81 let oo: *i64 = sys_mmap(8) as *i64
82 let ob: *i64 = sys_mmap(8) as *i64
83 let total: i64 = af_tally(ev, 3, og, oo, ob)
84 let g: i64 = og[0]
85 let o: i64 = oo[0]
86 let b: i64 = ob[0]
87
88 // k-anon release: big campaign released, tiny campaign suppressed.
89 let rel_big: i64 = af_release(total, k) // total 45 >= k -> 1
90 let rel_small: i64 = af_release(3, k) // 3 < k -> 0
91
92 // persist the rollup to the SOVEREIGN STORE (idempotent), then read back + verify.
93 let rec: *u8 = sys_mmap(256)
94 let reclen: i64 = af_persist_record(g, o, b, rec)
95 if fg_streq_store(FG_KEY, rec, reclen) == 0 {
96 let w: *i64 = ss_begin()
97 ss_add(w, 1, FG_KEY, rec, reclen)
98 ss_commit(ADS_PREFIX, w, ads_seg_next())
99 }
100 let persisted: i64 = fg_streq_store(FG_KEY, rec, reclen) // 1 = on-disk == intended record
101
102 // ANTI-SNOOP: scan the ON-DISK persisted bytes for visitor session tokens that were in the input.
103 let pq: *i64 = sys_mmap(16) as *i64
104 let lq: *i64 = sys_mmap(16) as *i64
105 var leaked: i64 = 0
106 if ads_get(FG_KEY, pq, lq) == 1 {
107 let disk: *u8 = pq[0] as *u8
108 let dn: i64 = lq[0]
109 if as_contains(disk, dn, "vtok-AAA111" as *u8) == 1 { leaked = leaked + 1 }
110 if as_contains(disk, dn, "vtok-BBB222" as *u8) == 1 { leaked = leaked + 1 }
111 if as_contains(disk, dn, "vtok-CCC333" as *u8) == 1 { leaked = leaked + 1 }
112 }
113
114 // detector proof: a TAMPERED record that DOES carry a token must make the scanner FIRE.
115 let tampered: *u8 = "great=1 ok=0 bad=0 n=1 vtok-AAA111" as *u8
116 let fired: i64 = as_contains(tampered, as_len(tampered), "vtok-AAA111" as *u8)
117
118 var ok: i64 = 1
119 if poll3p != 0 { ok = 0 }
120 if has_g != 1 { ok = 0 }
121 if has_o != 1 { ok = 0 }
122 if has_b != 1 { ok = 0 }
123 if has_c != 1 { ok = 0 }
124 if esc3p != 0 { ok = 0 }
125 if esc_ok != 1 { ok = 0 }
126 if g != 30 { ok = 0 }
127 if o != 12 { ok = 0 }
128 if b != 3 { ok = 0 }
129 if total != 45 { ok = 0 }
130 if rel_big != 1 { ok = 0 }
131 if rel_small != 0 { ok = 0 }
132 if persisted != 1 { ok = 0 }
133 if leaked != 0 { ok = 0 }
134 if fired != 1 { ok = 0 }
135
136 // ---- MUTATION-DERIVED TEETH (2026-08-01, debt 1785604588) ----
137 // nx_gate_mutation_probe scored this pair 2/4 with TWO SURVIVING MUTANTS:
138 // nx_ad_feedback.nx:20 ` == ` -> ` != ` (af_num's `if v == 0` -- the ZERO path)
139 // nx_ad_feedback.nx:28 ` == ` -> ` != ` (af_num's `if neg == 1` -- the NEGATIVE-SIGN path)
140 // A surviving mutant is not a style complaint: it is proof that the formatter could render 0 or a
141 // negative wrongly and THIS GATE WOULD STILL PASS. Every existing tooth drove af_num with positive
142 // non-zero counts only, so two whole branches of the int->text path were unexercised.
143 // These are exactly the NEGATIVE CONTROLS R4 asks for -- each one FAILS if its comparison is
144 // flipped, which is the definition the mutation probe checks mechanically.
145 let zbuf: *u8 = sys_mmap(64)
146 let zlen: i64 = af_num(zbuf, 0, 0)
147 var zero_ok: i64 = 0
148 if zlen == 1 { if zbuf[0] == (48 as u8) { zero_ok = 1 } }
149 let nbuf: *u8 = sys_mmap(64)
150 let nlen: i64 = af_num(nbuf, 0, 0 - 42)
151 var neg_ok: i64 = 0
152 if nlen == 3 {
153 if nbuf[0] == (45 as u8) {
154 if nbuf[1] == (52 as u8) {
155 if nbuf[2] == (50 as u8) { neg_ok = 1 }
156 }
157 }
158 }
159 if zero_ok != 1 { ok = 0 }
160 if neg_ok != 1 { ok = 0 }
161
162 fg_emit(1, poll3p, esc3p, g, o, b, rel_big, rel_small, persisted, leaked, fired, ok)
163 let lf: i64 = sys_openat_append(FG_LOG, 420)
164 if lf >= 0 { fg_emit(lf, poll3p, esc3p, g, o, b, rel_big, rel_small, persisted, leaked, fired, ok); sys_close(lf) }
165
166 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
167 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
168 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
169 let ctr__dry: *i64 = gv_ctr()
170 ctr__dry[0] = ok
171 ctr__dry[1] = 1
172 let rc__dry: i64 = gv_verdict("AD-FEEDBACK-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
173 sys_exit(rc__dry)
174 return rc__dry
175}