nx_cap_invoke_gate.nx source
↩ module page · 89 lines · 5121 B
1// nx_cap_invoke_gate.nx -- proves R2: MCP tools/call over R0 is CAPABILITY-SCOPED. Drives ta_handle_pfx in-process
2// with POST /mcp tools/call requests carrying (or lacking) an X-Nishi-Cap token. The beyond-MCP property: invocation
3// requires an unforgeable capability that GRANTS THE SPECIFIC TOOL -- there is NO ambient authority, so confused-deputy
4// is structurally impossible. license_tier: ORIGINAL
5import "nx_tools_api.nx" // ta_handle_pfx + TA_CAP_SECRET + capt_issue/CAPT_OK (transitive) + ta_slen/ta_indexof
6import "nx_gate.nx"
7
8// build "POST /mcp ... tools/call {name:<tool>}" into out; include X-Nishi-Cap iff caplen>0. Returns length.
9func civ_req(cap: *u8, caplen: i64, tool: *u8, out: *u8) -> i64 {
10 var o: i64 = ta_cat(out, 0, "POST /mcp HTTP/1.1\r\nHost: x\r\n" as *u8)
11 if caplen > 0 {
12 o = ta_cat(out, o, "X-Nishi-Cap: " as *u8)
13 o = ta_catb(out, o, cap, caplen)
14 o = ta_cat(out, o, "\r\n" as *u8)
15 }
16 o = ta_cat(out, o, "Connection: close\r\n\r\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"" as *u8)
17 o = ta_cat(out, o, tool)
18 o = ta_cat(out, o, "\"}}" as *u8)
19 return o
20}
21func civ_has(out: *u8, n: i64, needle: *u8) -> i64 { if ta_indexof(out, n, needle) >= 0 { return 1 } return 0 }
22func civ_mark(cond: i64, pass: *i64, tot: *i64, label: *u8) -> i64 {
23 tot[0] = tot[0] + 1
24 if cond == 1 { pass[0] = pass[0] + 1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
25 gw(label); gw("\n" as *u8)
26 return 0
27}
28
29func main() -> i64 {
30 gw("=== nx_cap_invoke_gate: capability-scoped tools/call (R2 beyond MCP -- no ambient authority) ===\n" as *u8)
31 let TP: *u8 = "knowledge/toolreg-test-inv-" as *u8
32 let secret: *u8 = TA_CAP_SECRET
33 let slen: i64 = ta_slen(secret)
34 let pbox: *i64 = sys_mmap(16) as *i64; pbox[0] = 0
35 let tbox: *i64 = sys_mmap(16) as *i64; tbox[0] = 0
36 let out: *u8 = sys_mmap(1048576)
37 let reqb: *u8 = sys_mmap(4096)
38
39 // a capability granting ONLY nx_http_probe
40 let capb: *u8 = sys_mmap(1024)
41 let capn: i64 = capt_issue(secret, slen, "nx_http_probe" as *u8, 13, 9999999999, 1, capb, 1024)
42
43 // T1: WITH a valid cap for nx_http_probe -> AUTHORIZED (granted == CAPT_OK). Since the R2 exec rung,
44 // execution is a SEPARATE fail-closed layer (nx_tool_exec_allow); the deterministic gate-side terminal
45 // for an authorized-but-not-GREEN-runnable tool is the "capability-authorized ..." tool result -- that
46 // string is ONLY reachable when the capability verified, which is exactly this gate's concern.
47 let r1: i64 = civ_req(capb, capn, "nx_http_probe" as *u8, reqb)
48 let n1: i64 = ta_handle_pfx(TP, reqb, r1, out)
49 var t1: i64 = 0
50 if civ_has(out, n1, "capability-authorized" as *u8) == 1 { if civ_has(out, n1, "-32001" as *u8) == 0 { t1 = 1 } }
51 civ_mark(t1, pbox, tbox, "T1 valid capability for the tool -> AUTHORIZED (exec layer separate)" as *u8)
52
53 // T2: same probe-only cap, call a DIFFERENT tool -> DENIED (least authority: the cap doesn't grant it)
54 let r2: i64 = civ_req(capb, capn, "nx_mgmt_client" as *u8, reqb)
55 let n2: i64 = ta_handle_pfx(TP, reqb, r2, out)
56 var t2: i64 = 0
57 if civ_has(out, n2, "-32001" as *u8) == 1 { t2 = 1 }
58 civ_mark(t2, pbox, tbox, "T2 cap does NOT grant the requested tool -> DENIED (least authority)" as *u8)
59
60 // T3: NO capability at all -> DENIED (no ambient authority -- the beyond-MCP point)
61 let r3: i64 = civ_req(0 as *u8, 0, "nx_http_probe" as *u8, reqb)
62 let n3: i64 = ta_handle_pfx(TP, reqb, r3, out)
63 var t3: i64 = 0
64 if civ_has(out, n3, "-32001" as *u8) == 1 { t3 = 1 }
65 civ_mark(t3, pbox, tbox, "T3 NO capability -> DENIED (no ambient authority)" as *u8)
66
67 // T4: FORGED capability (minted with the wrong secret) -> DENIED (unforgeable)
68 let wk: *u8 = "WRONG-SECRET-NOT-THE-SERVERS-KEY-99999" as *u8
69 let fb: *u8 = sys_mmap(1024)
70 let fn: i64 = capt_issue(wk, ta_slen(wk), "nx_http_probe" as *u8, 13, 9999999999, 1, fb, 1024)
71 let r4: i64 = civ_req(fb, fn, "nx_http_probe" as *u8, reqb)
72 let n4: i64 = ta_handle_pfx(TP, reqb, r4, out)
73 var t4: i64 = 0
74 if civ_has(out, n4, "-32001" as *u8) == 1 { t4 = 1 }
75 civ_mark(t4, pbox, tbox, "T4 forged capability (wrong secret) -> DENIED (unforgeable)" as *u8)
76
77 // T5: EXPIRED capability -> DENIED (time-bound)
78 let eb: *u8 = sys_mmap(1024)
79 let en: i64 = capt_issue(secret, slen, "nx_http_probe" as *u8, 13, 1, 2, eb, 1024)
80 let r5: i64 = civ_req(eb, en, "nx_http_probe" as *u8, reqb)
81 let n5: i64 = ta_handle_pfx(TP, reqb, r5, out)
82 var t5: i64 = 0
83 if civ_has(out, n5, "-32001" as *u8) == 1 { t5 = 1 }
84 civ_mark(t5, pbox, tbox, "T5 expired capability -> DENIED (time-bound)" as *u8)
85
86 gw("\n=== nx_cap_invoke_gate " as *u8); gn(pbox[0]); gw("/" as *u8); gn(tbox[0]); gw(" ===\n" as *u8)
87 if pbox[0] == tbox[0] { gw("CAP-INVOKE GREEN -- tools/call is capability-scoped: authority-in-token, unforgeable, least-authority, time-bound (beyond MCP)\n" as *u8); sys_exit(0); return 0 }
88 gw("CAP-INVOKE RED\n" as *u8); sys_exit(1); return 1
89}