nx_path_canonicalize.nx
buildroot/runtime/nx_path_canonicalize.nx
about
nx_path_canonicalize.nx -- structural CWE-22 path-traversal prevention.
Named by nx_bug_tape_intelligence.sh F1 rule as rank-5 next ship-
order. Closes the path-traversal class STRUCTURALLY: caller
provides a base directory + a user-supplied relative path; this
primitive returns either NXP_OK with the canonical path, or one
of five sealed-enum rejection verdicts. No "partial" path return,
no silent fixup, no late escape.
The CWE-22 attack shape: user submits `../../../etc/passwd` (or
URL-encoded equivalent), web framework joins it with base + opens
the resulting path, leaking outside the intended directory.
Substrate's structural prevention:
1. Reject ANY `..` segment outright (no "join + normalize")
2. Reject leading `/` (caller's base is the only absolute path)
3. Reject NUL bytes (truncation attacks)
4. Reject backslash on POSIX targets (no Windows-path confusion)
5. Reject empty path (no implicit index)
6. Reject paths > caller's max_len (resource bound)
Sealed-enum verdict:
NXP_OK validated path written to out_buf
NXP_TRAVERSAL contains `..` segment or leading `/`
NXP_NUL contains NUL byte
NXP_BACKSLASH contains backslash (POSIX target)
NXP_EMPTY zero-length input
NXP_TOO_LONG exceeds max_out
NXP_BAD_ARG null pointers / negative sizes
Per cardinal feedback-defensive-at-boundaries-trusting-internally:
canonicalize ONCE at the user-input boundary; trust the canonical
path internally.
Per cardinal user-owns-every-bit: caller provides base + max_len +
out_buf. Substrate never reads/writes filesystem; this is a pure
validator + byte-copier.
nx_capability_claims:
needs: [sealed_enum, byte_ops]
dependencies 0 imports · 4 importers
imports: none
imported by: nx_audit_server_routed.nxnx_nishipages_serve.nxnx_pages_static.nxnx_path_canonicalize_test.nx
structs
| none |
consts
| 52 | const NXP_OK: i64 = 0 |
| 53 | const NXP_TRAVERSAL: i64 = 1 |
| 54 | const NXP_NUL: i64 = 2 |
| 55 | const NXP_BACKSLASH: i64 = 3 |
| 56 | const NXP_EMPTY: i64 = 4 |
| 57 | const NXP_TOO_LONG: i64 = 5 |
| 58 | const NXP_BAD_ARG: i64 = 6 |
| 59 | const NXP_VERDICT_N: i64 = 7 |
functions
| 61 | func nxp_verdict_is_valid(v: i64) -> i64 called by 1: main |
| 67 | func nxp_verdict_name(v: i64) -> *u8 called by 1: main |
| 88 | func nxp_is_dotdot_segment(src: *u8, i: i64, n: i64) -> i64 called by 1: nx_path_canonicalize |
| 113 | func nx_path_canonicalize(src: *u8, src_n: i64, called by 5: nxar_serve_audit_cachednps_try_filenx_pages_serve_filecheck_vmain calls 1: nxp_is_dotdot_segment |
| 163 | func nx_path_join(base: *u8, base_n: i64, |