nx_html_render.nx
buildroot/runtime/nx_html_render.nx
about
nx_html_render.nx -- typed-context HTML render primitive.
Named by nx_bug_tape_intelligence.sh F1 rule (2026-05-16) as the
rank-2 next ship-order: CWE-79 Cross-Site Scripting. Phase-2
critical primitive for the audit-dashboard arc (per
docs/NISHI_AUDIT_DASHBOARD_ROADMAP.md).
Why "typed context" instead of "escape this string":
The XSS bug class isn't "you forgot to escape". It's "you
escaped for the wrong context." HTML body text needs `<` -> `<`;
attribute-value-double-quoted needs `"` -> `"`;
URL context needs %-encoding (RFC 3986); JavaScript string
context needs `\` + control-char escaping; <style> CSS context
needs yet another escape table. A primitive that takes a
single "value" parameter without specifying which context it's
being emitted into is a perpetual XSS-vector machine.
Substrate's structural prevention: sealed-enum NxHtmlContext +
every emit takes a context + the escape is dispatched per context
at the boundary, never inferred from string content.
Sealed enum NxHtmlContext (closes the XSS class):
NXH_CTX_TEXT <body> text, between tags
NXH_CTX_ATTR_DQ inside an attribute="..." (double-quoted)
NXH_CTX_ATTR_SQ inside an attribute='...' (single-quoted)
NXH_CTX_URL href / src / action attribute (URL value)
NXH_CTX_COMMENT inside <!-- ... -->
NXH_CTX_CDATA inside <![CDATA[...]]> (XHTML)
NXH_CTX_RAW EXPLICIT raw-html bypass; caller asserts
the bytes are already safe (e.g., a
pre-rendered nested fragment). Sealed-
enum status: AUDIT-WHITELIST.
Sealed enum NxHtmlNodeKind:
NXH_NODE_ELEMENT <tag attrs>children</tag>
NXH_NODE_TEXT text content (auto-escaped per ctx)
NXH_NODE_RAW caller-asserted-safe raw bytes (audited)
NXH_NODE_COMMENT <!-- ... --> (text auto-escaped)
dependencies 0 imports · 2 importers
imports: none
imported by: nx_html_render_test.nxnx_http_template.nx
structs
| none |
consts
| 62 | const NXH_CTX_TEXT: i64 = 0 |
| 63 | const NXH_CTX_ATTR_DQ: i64 = 1 |
| 64 | const NXH_CTX_ATTR_SQ: i64 = 2 |
| 65 | const NXH_CTX_URL: i64 = 3 |
| 66 | const NXH_CTX_COMMENT: i64 = 4 |
| 67 | const NXH_CTX_CDATA: i64 = 5 |
| 68 | const NXH_CTX_RAW: i64 = 6 |
| 69 | const NXH_CTX_N: i64 = 7 |
| 90 | const NXH_NODE_ELEMENT: i64 = 0 |
| 91 | const NXH_NODE_TEXT: i64 = 1 |
| 92 | const NXH_NODE_RAW: i64 = 2 |
| 93 | const NXH_NODE_COMMENT: i64 = 3 |
| 94 | const NXH_NODE_N: i64 = 4 |
| 104 | const NXH_OK: i64 = 0 |
| 105 | const NXH_OOM_BUFFER: i64 = 1 |
| 106 | const NXH_BAD_CTX: i64 = 2 |
| 107 | const NXH_BAD_INPUT: i64 = 3 |
| 108 | const NXH_BAD_URL: i64 = 4 |
| 109 | const NXH_VERDICT_N: i64 = 5 |
functions
| 71 | func nxh_ctx_is_valid(c: i64) -> i64 |
| 77 | func nxh_ctx_name(c: i64) -> *u8 |
| 96 | func nxh_node_is_valid(k: i64) -> i64 called by 1: main |
| 111 | func nxh_verdict_is_valid(v: i64) -> i64 called by 1: main |
| 121 | func nxh_put(out: *u8, off: *i64, cap: i64, b: i64) -> i64 |
| 129 | func nxh_put_lit(out: *u8, off: *i64, cap: i64, lit: *u8) -> i64 |
| 140 | func nxh_put_bytes(out: *u8, off: *i64, cap: i64, src: *u8, n: i64) -> i64 |
| 154 | func nxh_url_safe(b: i64) -> i64 called by 1: nx_html_escape |
| 173 | func nxh_hex_nibble(n: i64) -> i64 called by 1: nx_html_escape |
| 198 | func nx_html_escape(out: *u8, off: *i64, cap: i64, |
| 300 | func nxh_is_alnum(b: i64) -> i64 |
| 308 | func nxh_is_attr_name_char(b: i64) -> i64 |
| 316 | func nx_html_open_tag(out: *u8, off: *i64, cap: i64, |
| 334 | func nx_html_attr(out: *u8, off: *i64, cap: i64, |
| 359 | func nx_html_attr_url(out: *u8, off: *i64, cap: i64, |
| 377 | func nx_html_close_open_tag(out: *u8, off: *i64, cap: i64) -> i64 |
| 382 | func nx_html_self_close(out: *u8, off: *i64, cap: i64) -> i64 calls 1: nxh_put |
| 389 | func nx_html_end_tag(out: *u8, off: *i64, cap: i64, |
| 407 | func nx_html_text(out: *u8, off: *i64, cap: i64, |
| 415 | func nx_html_raw(out: *u8, off: *i64, cap: i64, calls 1: nx_html_escape |