nx_antibot_gate.nx source
↩ module page · 107 lines · 7553 B
1// nx_antibot_gate.nx -- GATE for nx_antibot (the /compare/webscraping R4 contract abt_classify). Network-free;
2// specimens assembled at runtime from the marker rows' own vocabulary. The accept rule pre-declared on the board:
3// vendor challenge pages are named by vendor; a long article that MENTIONS captcha / Cloudflare / Access Denied reads
4// CLEAN (the false-positive control, because a wall verdict costs the index a page); 429 is a rate limit; 403 with a
5// body is a status block; an empty 200 is its own class, never a wall.
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_antibot.nx"
9import "nx_gate_verdict.nx"
10
11const AG_CAP: i64 = 65536
12
13func ag_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
14func ag_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p }
15func ag_vendor_is(v: *u8) -> i64 {
16 let vl: i64 = ag_len(v)
17 if vl != abt_last_vendor_len_g { return 0 }
18 var i: i64 = 0
19 while i < vl { if abt_last_vendor_g[i] != v[i] { return 0 } i = i + 1 }
20 return 1
21}
22func ag_cls(status: i64, s: *u8) -> i64 { return abt_classify(status, s, ag_len(s)) }
23
24func main() -> i64 {
25 gv_head("=== nx_antibot gate (bot-wall classification from marker rows, network-free) ===" as *u8)
26 let ctr: *i64 = gv_ctr()
27 let nr: i64 = abt_reload()
28 gv_puts(" markers=" as *u8); gv_num(nr); gv_puts(" malformed=" as *u8); gv_num(abt_nrows_bad_g)
29 gv_puts(" small_page_bytes=" as *u8); gv_num(abt_small_bytes_g); gv_puts(" shell_max_text=" as *u8); gv_num(abt_shell_max_text_g)
30 gv_puts(" conf_src=" as *u8); if abt_conf_src_g == 1 { gv_puts("file" as *u8) } else { gv_puts("defaults" as *u8) } gv_puts("\n" as *u8)
31 gv_subjects("marker-rows" as *u8, nr, ctr)
32 var t0: i64 = 0
33 if abt_nrows_bad_g == 0 { t0 = 1 }
34 gv_check("T0 every marker row parses (a malformed row is a missing wall, announced)" as *u8, t0, ctr)
35
36 // ---- vendor specimens (tier 1, any size) ----
37 let cf: *u8 = "<html><head><title>Just a moment...</title></head><body><form id=\"challenge-form\" action=\"/x?__cf_chl_f_tk=abc\"><script src=\"/cdn-cgi/challenge-platform/h/b/orchestrate/jsch/v1\"></script></form></body></html>" as *u8
38 let c1: i64 = ag_cls(503, cf)
39 var t1: i64 = 0
40 if c1 == ABT_VENDOR_WALL { if ag_vendor_is("cloudflare" as *u8) == 1 { t1 = 1 } }
41 gv_check("T1 a Cloudflare challenge page -> VENDOR-WALL named cloudflare" as *u8, t1, ctr)
42 let ak: *u8 = "<html><body><h1>Pardon Our Interruption</h1><p>As you were browsing something about your browser made us think you were a bot.</p></body></html>" as *u8
43 var t2: i64 = 0
44 if ag_cls(200, ak) == ABT_VENDOR_WALL { if ag_vendor_is("akamai" as *u8) == 1 { t2 = 1 } }
45 gv_check("T2 an Akamai interruption page -> VENDOR-WALL named akamai, even on a 200" as *u8, t2, ctr)
46 let px: *u8 = "<html><head><script>window._pxAppId='PX1234';</script></head><body></body></html>" as *u8
47 var t3: i64 = 0
48 if ag_cls(403, px) == ABT_VENDOR_WALL { if ag_vendor_is("perimeterx" as *u8) == 1 { t3 = 1 } }
49 gv_check("T3 a PerimeterX block -> VENDOR-WALL named perimeterx" as *u8, t3, ctr)
50 let dd: *u8 = "<html><body><script src=\"https://ct.captcha-delivery.com/c.js\"></script></body></html>" as *u8
51 var t4: i64 = 0
52 if ag_cls(403, dd) == ABT_VENDOR_WALL { if ag_vendor_is("datadome" as *u8) == 1 { t4 = 1 } }
53 gv_check("T4 a DataDome captcha page -> VENDOR-WALL named datadome" as *u8, t4, ctr)
54 let an: *u8 = "<html><head><title>Making sure you're not a bot!</title></head><body><img src=\"/.within.website/x/cmd/anubis/static/img/happy.webp\"></body></html>" as *u8
55 var t5: i64 = 0
56 if ag_cls(200, an) == ABT_VENDOR_WALL { if ag_vendor_is("anubis" as *u8) == 1 { t5 = 1 } }
57 gv_check("T5 an Anubis proof-of-work interstitial -> VENDOR-WALL named anubis" as *u8, t5, ctr)
58
59 // ---- status rules ----
60 var t6: i64 = 0
61 if ag_cls(429, "<html><body>slow down</body></html>" as *u8) == ABT_RATE_LIMIT { t6 = 1 }
62 gv_check("T6 HTTP 429 -> RATE-LIMIT regardless of body" as *u8, t6, ctr)
63 var t7: i64 = 0
64 if ag_cls(403, "<html><body><p>Forbidden by policy on this host.</p></body></html>" as *u8) == ABT_STATUS_BLOCK { t7 = 1 }
65 gv_check("T7 HTTP 403 with an HTML body and no marker -> STATUS-BLOCK (vendor unknown, not invented)" as *u8, t7, ctr)
66 var t7b: i64 = 0
67 if ag_cls(403, "" as *u8) != ABT_STATUS_BLOCK { t7b = 1 }
68 gv_check("T7b HTTP 403 with NO body is not a status block (nothing to classify)" as *u8, t7b, ctr)
69
70 // ---- generic tier only on SHORT or error bodies ----
71 let shortden: *u8 = "<html><body><h1>Access Denied</h1><p>You don't have permission to access this resource.</p></body></html>" as *u8
72 var t8: i64 = 0
73 if ag_cls(200, shortden) == ABT_GENERIC_WALL { t8 = 1 }
74 gv_check("T8 a short Access Denied page on a 200 -> GENERIC-WALL" as *u8, t8, ctr)
75
76 // ---- FALSE-POSITIVE CONTROL: a long article that talks ABOUT walls must read CLEAN ----
77 let art: *u8 = sys_mmap(AG_CAP)
78 var o: i64 = 0
79 o = ag_cat(art, o, "<html><head><title>How bot walls work</title></head><body><article>" as *u8)
80 var para: i64 = 0
81 while para < 40 {
82 o = ag_cat(art, o, "<p>Sites deploy a captcha or a CAPTCHA when they suspect automation; Cloudflare, Akamai and DataDome all sell this, and a visitor may see Access Denied, verify you are human, or bot detection notices while the page is still an ordinary article of prose about the mechanism, not a wall itself. " as *u8)
83 o = ag_cat(art, o, "This paragraph exists so the body is long enough that a generic phrase cannot be mistaken for a challenge; a real challenge page is short and structural.</p>\n" as *u8)
84 para = para + 1
85 }
86 o = ag_cat(art, o, "</article></body></html>" as *u8)
87 let cart: i64 = abt_classify(200, art, o)
88 gv_puts(" prose control bytes=" as *u8); gv_num(o); gv_puts(" code=" as *u8); gv_num(cart); gv_puts("\n" as *u8)
89 var t9: i64 = 0
90 if cart == ABT_CLEAN { if o > abt_small_bytes_g { t9 = 1 } }
91 gv_check("T9 FALSE-POSITIVE CONTROL: a long article mentioning captcha, Cloudflare, Access Denied reads CLEAN" as *u8, t9, ctr)
92 gv_bite("T10 BITE: the classifier fires on the short Access Denied page and stays silent on the long article" as *u8, t8, 1 - t9, ctr)
93
94 // ---- empty shell is its own class ----
95 let shell: *u8 = "<html><head><script src=\"/app.js\"></script></head><body><div id=\"root\"></div></body></html>" as *u8
96 var t11: i64 = 0
97 if ag_cls(200, shell) == ABT_EMPTY_SHELL { t11 = 1 }
98 gv_check("T11 a 200 with no rendered text -> EMPTY-SHELL (a class of its own, never reported as a wall)" as *u8, t11, ctr)
99 var t12: i64 = 0
100 if cart == ABT_CLEAN { if ag_cls(200, "<html><body><p>Hello world content that is short but real.</p></body></html>" as *u8) == ABT_EMPTY_SHELL { t12 = 1 } }
101 gv_check("T12 a short real page under shell_max_text also reads EMPTY-SHELL, and the crawler's thin bar is the same number" as *u8, t12, ctr)
102 var t13: i64 = 0
103 if ag_len(abt_code_name(ABT_VENDOR_WALL)) > 0 { if ag_len(abt_code_name(ABT_CLEAN)) > 0 { t13 = 1 } }
104 gv_check("T13 every code has an outcome-row name" as *u8, t13, ctr)
105
106 return gv_verdict("ANTIBOT-GATE" as *u8, ctr, "walls classified from specimen rows and named by vendor; long prose about walls reads CLEAN; 429 rate-limit, 403 status-block, empty 200 its own class" as *u8)
107}