code wiki / (root) / nx_safe_archive_ingest.nx

nx_safe_archive_ingest.nx

buildroot/runtime/nx_safe_archive_ingest.nx

18619 B350 linesdepth 8pulls 10 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_safe_archive_ingest.nx -- SAFE sovereign ingestion of an UNTRUSTED .zip archive into the permanent library. Composes the shipped zip + deflate primitives (rule 15 DRY) under a NEVER-POISON-THE-LIBRARY safety envelope -- the #26 NEVER-BRICK law applied to ingestion: an untrusted archive can NEVER, BY CONSTRUCTION: (1) write outside the dest dir -- Zip-Slip-proof: the stored path is NEVER used as a filesystem path; output is dest + "/" + a sanitized BASENAME (chars mapped to [A-Za-z0-9._-], ".." rejected). (2) exhaust memory/disk -- zip-bomb caps: per-entry uncompressed cap, total cap, compression-ratio cap, entry-count cap, archive cap. (3) be executed -- data-only: files written 0644 (no exec bit), content is sanitized to printable text; nothing is ever run/chmod+x. (4) smuggle an unknown codec -- method whitelist: 0 (stored) / 8 (deflate) only. (5) smuggle a binary/script as library -- content-type whitelist by extension; non-text SKIPPED (never banked); even "text" is stripped of control bytes (defuses terminal-escape injection). (6) link out via symlink -- entries with unix S_IFLNK mode are SKIPPED. (7) bomb via nested archive -- we do NOT recurse; a .zip entry is non-text -> SKIPPED. Zip64-sentinel sizes are SKIPPED (not yet handled = refuse, never guess). usage: nx_safe_archive_ingest <archive.zip> [dest-dir] (default dest = knowledge/library) exit 0 always on a well-formed run (per-entry verdicts on stdout); nonzero only on unreadable archive / not-a-zip. Rule 11: every cap is a named const (data-driven). license_tier: ORIGINAL

dependencies 4 imports · 0 importers

nx_syscalls.nx nx_zip_header.nx nx_deflate.nx nx_pdf_text.nx nx_safe_archive_ingest.nx

imports: nx_syscalls.nxnx_zip_header.nxnx_deflate.nxnx_pdf_text.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main sai_puts sys_write sai_strlen sys_mmap sys_read_file sys_openat_rd sys_lseek sys_mmap ↻ sys_read sys_close sai_putn sys_write ↻ sys_mmap ↻ zip_find_eocd zip_read_u32 zip_parse_eocd zip_read_u32 ↻ zip_read_u16 sys_mkdir zip_read_u32 ↻ zip_read_u16 ↻ sai_process_entry zip_read_u16 ↻ zip_read_u32 ↻ sai_puts ↻ sai_put_name sys_mmap ↻ sys_write ↻ sai_putn ↻ sys_mmap ↻ sai_safe_basename sai_safe_char sai_name_is_cruft sai_is_skip_ext sai_ext_eq sai_lc zip_parse_lfh zip_read_u32 ↻ zip_read_u16 ↻

structs

none

consts

27const SAI_MAGIC_2048: i64 = 2048
28const SAI_MAGIC_16777216: i64 = 16777216
31const SAI_MAX_ARCHIVE: i64 = 67108864 // 64 MiB total archive bytes
32const SAI_MAX_ENTRIES: i64 = 4096
33const SAI_MAX_ENTRY_USIZE: i64 = 33554432 // 32 MiB uncompressed per entry
34const SAI_MAX_TOTAL_USIZE: i64 = 268435456 // 256 MiB uncompressed total
35const SAI_MAX_RATIO: i64 = 200 // uncompressed/compressed ceiling (zip-bomb guard)
36const SAI_CDFH_SIG: i64 = 0x02014b50
38const SAI_BANKED: i64 = 1
39const SAI_SKIPPED: i64 = 2
40const SAI_REJECTED: i64 = 3

functions

42func sai_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
called by 2: sai_process_entrymain calls 1: sys_write
43func sai_putn(v: i64) -> i64
54func sai_put_name(buf: *u8, off: i64, len: i64) -> i64
called by 1: sai_process_entry calls 2: sys_mmapsys_write
60func sai_lc(c: i64) -> i64 { if c >= 0x41 { if c <= 0x5a { return c + 0x20 } } return c }
called by 1: sai_ext_eq
63func sai_safe_char(c: i64) -> i64
called by 1: sai_safe_basename
75func sai_safe_basename(name: *u8, off: i64, len: i64, out: *u8, cap: i64) -> i64
called by 1: sai_process_entry calls 1: sai_safe_char
105func sai_ext_eq(b: *u8, start: i64, end: i64, lit: *u8) -> i64
117func sai_name_is_cruft(b: *u8, blen: i64) -> i64
called by 1: sai_process_entry
125func sai_is_skip_ext(b: *u8, blen: i64) -> i64
called by 1: sai_process_entry calls 1: sai_ext_eq
168func sai_ext_is_pdf(b: *u8, blen: i64) -> i64
called by 1: sai_process_entry calls 1: sai_ext_eq
179func sai_is_text_content(c: *u8, len: i64) -> i64
called by 1: sai_process_entry
194func sai_scat(dst: *u8, o: i64, s: *u8, slen: i64) -> i64 { var i: i64 = 0; while i < slen { dst[o + i] = s[i]; i = i + 1 } return o + i }
called by 1: sai_bank_text
195func sai_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
called by 1: main
200func sai_bank_text(dest: *u8, destlen: i64, base: *u8, blen: i64, content: *u8, clen: i64) -> i64
229func sai_process_entry(buf: *u8, n: i64, off: i64, dest: *u8, destlen: i64, tot_p: *i64) -> i64
297func main(argc: i64, argv: *i64) -> i64