Skip to content

fix(tools): report deeply nested JSON as invalid input and run the reserved-name archive test - #12266

Merged
oneandonlydean merged 5 commits into
boykopovar:mainfrom
MarcZX10:fix/tools-deeply-nested-json-and-web-test
Oct 28, 2026
Merged

oneandonlydean merged 5 commits into
boykopovar:mainfrom
MarcZX10:fix/tools-deeply-nested-json-and-web-test

Conversation

@MarcZX10

Copy link
Copy Markdown
Contributor

What

Three defects in repository tooling. One commit each; the two Python fixes have a test commit each:

  • tools/hw-oracle/compare_results.py: read_report passes the file to json.loads, and a report nested a few hundred thousand levels deep raises RecursionError, which main does not catch. The command ended in a traceback with exit code 1, the code the tool documents as "different outputs", so a corrupt file looked like a hardware difference. It now raises a ValueError naming the file, and main reports it with exit code 2 like every other invalid report.
  • tools/import_audit.py: read_registry and read_needed_registry catch OSError, UnicodeDecodeError and JSONDecodeError but not RecursionError. main returns 2 for an AuditError and 1 for missing imports, so a deeply nested registry ended in a traceback with exit code 1 and looked like a failed import audit. RecursionError is now reported as an AuditError (exit code 2).
  • tools/test_web.py launched archive-root, archive-windows and archive.test but not demo/web/tests/archive-reserved.test.mjs, which covers the superscript Windows device names added with 3397e87. No workflow, script or CTest entry referenced it, so a regression in validPath for those names went unnoticed. It runs next to the other archive tests now. I did not run it (it needs a web build and the zip library download).

Tested

python3 -m unittest test_compare_results in tools/hw-oracle (12 tests) and python3 -m unittest test_import_audit in tools/tests (157 tests, 1 skipped) pass. Each of the two new tests fails against main's version of its script. tools/check_conventions.py --base upstream/main and git diff --check pass.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

read_report passes the file to json.loads, and a report nested a few hundred
thousand levels deep raised RecursionError, which main does not catch. The
command ended in a traceback with exit code 1, the code the tool documents as
'different outputs', so a corrupt file looked like a hardware difference. The
error is now a ValueError naming the file, and main reports it with exit code 2
like every other invalid report.
…rt_audit

read_registry and read_needed_registry catch OSError, UnicodeDecodeError and
JSONDecodeError, but a registry nested a few hundred thousand levels deep raises
RecursionError. main returns 2 for an AuditError and 1 for missing imports, so
the traceback ended with exit code 1 and a corrupt registry looked like a failed
import audit. RecursionError is now caught with the other read errors and
reported as an AuditError, so the exit code is 2.
tools/test_web.py launched archive-root, archive-windows and archive.test but not
archive-reserved.test.mjs, which covers the superscript Windows device names
(COM1 with a superscript digit, LPT2 and so on) added with 3397e87. No
workflow, script or CTest entry referenced it, so a regression in validPath for
those names went unnoticed. It runs next to the other archive tests now.
…ser allows

The command has to exit with 2 and say the file is nested too deeply, without a
traceback, instead of exiting with the code that means different outputs.
… JSON parser allows

A registry and a needed-libraries file of 200000 opening brackets have to end in
an audit error with exit code 2 and the read-error message, not a traceback.
@fogydes

fogydes commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

verdict: OK

  1. [read-only] I found no concrete defect in the RecursionError handling or the web archive test registration; the catches use the existing invalid-input paths, and the new test calls the same archive-test harness as its neighbors.
  2. [author-reported] The Python unit tests and conventions check are reported as passing. The new web archive test is explicitly reported as not run; I did not run the suites locally.
  3. [read-only] The PR title differs from the latest commit subject: test(tools): audit imports from registries nested far deeper than the JSON parser allows.

Freshness envelope: head 70cd9c9; merge-base 0cd07dc; behind main: 0; git merge-tree --write-tree origin/main pull/12266: exit 0, clean. The conventions checker and git diff --check pass.

Cross-PR ledger: open non-draft PR #7454 also changes tools/import_audit.py and tools/tests/test_import_audit.py. Pairwise git merge-tree reports conflicts in both shared files, so the two current heads do not combine cleanly. Draft PR #6171 was excluded.

@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

A Windows run, since the description lists none. Head 70cd9c91c2 was merged onto main 0cd07dc4ab with no conflicts. Setup: Windows 11 Enterprise 10.0.22621, Python 3.15. Each passes in all 3 runs:

  • python -m unittest tools/tests/test_import_audit.py: 157 tests, OK (2 skipped). The skips are test_existing_json_report_permissions_are_preserved and test_new_json_report_permissions_follow_the_process_umask, POSIX-only tests that skip on Windows by design.
  • python tools/hw-oracle/test_compare_results.py (run as a script, as the hardware_oracle_compare CTest does): 11 tests, OK.

I didn't run tools/test_web.py, which needs a WebAssembly build (--build build-wasm).

AI-assisted: yes (Claude Code).

@github-actions

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 6171 tools/import_audit.py
tools/tests/test_import_audit.py
PR 7454 tools/import_audit.py
tools/tests/test_import_audit.py

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@MarcZX10 the two RecursionError fixes look right to me, and their tests cover both readers. One small thing in tools/test_web.py: demo/web/tests/archive.test.mjs already ends its imports with await import("./archive-reserved.test.mjs") (line 12), and test_web.py already runs archive.test.mjs, so the reserved-name test is not unreferenced: it runs once there today, and the new subprocess.run for it runs it a second time. Could you drop that line, or say what a separate run adds, and correct the sentence in the description that says nothing references it?

@oneandonlydean oneandonlydean self-assigned this Oct 28, 2026

@rooklz rooklz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of 70cd9c91c2, macOS arm64, run locally on this machine. The head merges cleanly onto main 3a40945e62.

JSON depth (does the fixture trigger on every supported Python?) json.loads("[" * n):

Python n=20000 n=200000
3.9.6 RecursionError RecursionError
3.13.1 RecursionError RecursionError
3.14.8 JSONDecodeError RecursionError ("Stack overflow")
3.15.1 JSONDecodeError RecursionError ("Stack overflow")

At 20000 the input is caught on 3.14+ already. 200000 hits the uncaught path on all four versions, so the fixture depth is the right choice.

Python fixes

  • With the head checked out, test_compare_results (12 tests) and test_import_audit (157 tests, 1 skipped) pass on 3.9.6, 3.13 and 3.14.
  • With only the two scripts set back to main (3.9.6), each module fails exactly one test: the new one.
  • Mutants, all killed: dropping RecursionError from read_registry only, dropping it from read_needed_registry only, and changing the nested too deeply message. The second half of the import_audit test does its job.
  • The three json.loads call sites in these two files are all covered now.

archive-reserved.test.mjs wiring (the body says this was not run, so I ran it). build_web.copy_sources copies demo/web as-is, so I put a scratch <build>/web copy together and fetched the pinned zip.js:

  • Head: node demo/web/tests/archive-reserved.test.mjs <build>/web exits 0 on Node 26.4.0.
  • When I set the scratch archive.mjs back to the regex from before 3397e87 (com[1-9]|lpt[1-9]), it fails with AssertionError: Missing expected exception., so the newly wired test does catch the regression it was written for.
  • The whole tools/test_web.py --build <scratch> run stops later, at archive.test.mjs, because it needs the real native/relinker_wasm.mjs. That is expected without a wasm build and is not part of this change.
  • Non-blocking: web-tests.yml triggers on tools/test_web.py but never runs it; it only runs archive-root plus node --test for 4 files. test_web.py runs only in release.yml, so the reserved-name test will run at release time, not on PRs. Adding a step to web-tests.yml the way archive-root is wired would catch it per PR. That could be a follow-up.

Gates (merged tree) check_conventions --base upstream/main passes. Relinker-only build 270/270, ctest 378/378. pr_overlap reports only #6171 and #7454 as not comparable (both conflicting) on the import_audit files; this diff adds lines to the except tuples and one test, so I don't see a topical overlap.

Approving.

AI-assisted: yes

@THEMANnt THEMANnt left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 70cd9c91c2afdb3348e644b83772346c7058acfc — approve. Deeply nested import registries are reported as invalid input instead of escaping as RecursionError. The Windows reserved-name archive case is enabled as part of this patch.

Windows/Python 3.11: head test_import_audit.py: 154 passed, 3 skipped (157 total); base test_import_audit.py: 153 passed, 3 skipped (156 total). The new nested-registry case fails against old production with the reproduced RecursionError. This control establishes the parsing fix; it does not imply that every platform-only archive case executed. Full command/output evidence is retained locally. Hosted CI and mergeability are rechecked before publication. No game or full product execution is claimed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants