summaryrefslogtreecommitdiff
path: root/modules/test/test-cryptogzip-guard.js
Commit message (Collapse)AuthorAgeFilesLines
* fix(newos4): one arg-coercion boundary + binary-safe gunzip + clean boot log ↵Hermes SysOp3 days1-3/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | (v4.6.159) 1. Tool arguments are coerced once, at the dispatch boundary (module-aiassist.js) - _coerceToolArgs(name, a) at :1584 reads the tool's own JSON schema and, for every declared string prop, converts number/boolean with String() and object/array with JSON.stringify (never "[object Object]"); declared integer/number props accept unambiguous numeric strings. null/undefined stay absent so the handlers' defaults and _pathArg still own "no value given". - Called at both dispatch boundaries: runTool (:2650) and runModuleTool (:3565 — reachable from view_module and from ship_module -> test_module/health_check, which have their own parse+switch). - A JSON body of "null" or "[1,2]" parses to a non-object; both sites normalise to {} before any handler reads a.field. - The 26 handlers keep their `a.x || ''` reads: `|| ''` catches only FALSY, which is how a truthy number or object reached .trim()/.split()/.replace() and filed one auto-report per tool (user_get "key.split is not a function", create_skill, patch_skill, delegate_task "task.substring is not a function", create_mindmap, web_fetch, list_skills, ship_module). 2. gunzip is binary-safe (cryptogzip.js — plan 2026-11-03 T3, the last open task) - _cmdGunzip used _vfsRead + _gunzipString (base64-only), so a real .gz fetched with wget reported "not valid base64 input (illegal character \u001f)" and could not be decompressed at all. It now reads bytes (_vfsReadBytes handles both the module's base64 output and raw Latin-1 bytes), checks the 1f 8b magic, and reports "not a gzip file" for non-gzip input without writing or deleting anything. The module's own gzip output still round-trips. 3. Boot log is clean (terminal.js) - Seven _safeRegister() calls for Editor/Shell/Alias/Finder/FileManager/Diff/BusyBox were guaranteed no-ops: those classes are declared in critical-ext-bundle.js, a <script> after module-loader.js, so they do not exist yet when new WebX11Terminal() runs; adding them to the registry map would still resolve to undefined. Removed with a comment — the ext bundle registers each one on load. - SimpleGUIModule IS a core class but was missing from _safeRegister's registry map, so the fallback could never restore it. Added. 4. New guard: admin/scripts/verify-plan-age.py + profile wrapper plan-age-guard.sh (cron newos4-plan-age-guard, no_agent, daily 07:10). Silent when healthy; reports plans marked <!-- autonomy: review --> with no execution stamp older than 72 h — the process gap that let four fix plans sit for six days before v4.6.158. Read-only by construction (148 real plans byte+mtime identical before/after a run), rc=2 when the directory cannot be scanned, --strict opt-in so a report is never recorded as a crashed guard. Verified: 37/37 node tests, 3 new test files and 631 new assertions (613 arg-coercion, 18 gunzip); test-cryptogzip-guard's C8 updated because it pinned the old misleading "not valid base64" message. Independent orchestrator checks: all 66 declared string props across 58 tool defs coerced under 4 hostile shapes; positive cases untouched (string content not double-encoded, numbers still numbers, null still absent, unknown tool identity preserved). Served bytes byte-identical for core-bundle.js, main.html and cryptogzip.js. Live Chromium on v4.6.159: write_file with object content wrote 27 chars of readable JSON (not "[object Object]"), read_file with offset:"0"/limit:"200" coerced to numbers, user_set with key 12345 -> "12345" and object value -> JSON, and the boot log now carries 0 SafeRegister warnings (52 messages, 0 JS errors) with SimpleGUIModule registered.
* fix(newos4): six crash classes from the feedback queue (v4.6.158)Hermes SysOp6 days1-0/+136
Each item is symptom -> cause -> fix, re-verified against the served bytes. 1. debug-ai panel threw "Cannot read properties of undefined (reading 'substring')" (RPT-20261108-231622). _updateDebugPanel and two sibling previews called JSON.stringify(entry.args).substring(); JSON.stringify(undefined) returns undefined, and _toolCallLog entries carry args: undefined for any tool call with no arguments. All three previews now go through _formatToolArgs(), which renders "null" and never throws. 2. test_module threw "Cannot convert undefined or null to object" (RPT-20261108-232127). Object.keys(instance.commands) assumed every module initialises a command map. The throw landed after loadModuleFromCode had already succeeded, so the rolledBack:true branches were skipped and an unverified module stayed loaded. Both sites now require instance.commands. 3. list_files threw "originalPath.replace is not a function" (RPT-20261109-172742, 3 occurrences in errors.json). A model can send a non-string path despite the schema. The path family (list_files/read_file/write_file/make_directory/ delete_file) now coerces through _pathArg() and answers the tool's own honest error instead of a TypeError. 4. wpm search failed for EVERY keyword (RPT-20261109-123907): packages.json entry `kanban` used `description` while the other 31 use `desc`, and search() called pkg.desc.toLowerCase() mid-loop. Catalog reads now go through _pkg() / _packageMap() (desc || description, values coerced to strings), and the kanban entry carries name/version/desc/commands. Re-running the original loop for pdf/git/zzz is clean. 5. wx/webxai/webx turned a chat-lock timeout into an unhandled rejection (RPT-20261107-150726): forwardToAI() called chat(args) in a synchronous function without returning or catching the promise, so the 10 s busy throw never reached dispatchCommand's catch. It now returns the promise with a .catch that prints "AI error: ..." through terminal.print — console.error is hooked by the debug supervisor and would re-file the same report. 6. CryptoGzip._base64ToBytes threw a raw DOMException on non-base64 input (RPT-20261102-203616). It now validates first and throws Error("not valid base64 input (<reason>)"). The duplicate shadowing definitions at the bottom of the class were deleted — the later copy is the one that carried the unguarded atob(). Also regenerated modules/core/core-bundle.js (generated, never hand-edited) and bumped the build v4.6.157 -> v4.6.158 (main.html x3 + module-loader.js). Verified: 35/35 node tests, including 6 new regression tests each proven red against the pre-fix source; an independent orchestrator harness 36/36; and newos4-maintain.py --fix all PASS after regenerating the stale changelog page. Served bytes are byte-identical for the bundle, main.html and both ext modules, and a live headless-Chromium run against v4.6.158 shows WpmModule.search('pdf') returning 1 result, the debug panel rendering a no-arg entry as "null", _base64ToBytes('grüße') raising a plain Error that names base64, and 0 JS errors on load. m.pull (RPT-20261102-211438) is closed as a usage error: no module in the tree defines pull(); the real interfaces are the webdav-pull and model-pull commands.