Skip to content

Add parser tests, fuzzing, request-gate tests and CodeQL - #13

Merged
switch-nz merged 6 commits into
switch-nz:mainfrom
deadp:test-hardening
Sep 17, 2026
Merged

switch-nz merged 6 commits into
switch-nz:mainfrom
deadp:test-hardening

Conversation

@deadp

@deadp deadp commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Follows #12: parser tests, mutation fuzzing, tests for the request gate, and CodeQL/Dependabot. No changes under engine/.

What's here

  • Parser tests for FAT12/16, FAT32, exFAT, NTFS, ext2/3/4 with jbd2, the registry with transaction-log replay, and EWF/raw. Images are built in code by tests/imagebuild_*.py, so no evidence or binary fixtures are committed. The builders double as fuzzing seeds.
  • Request-gate tests (tests/test_gate.py) covering the Host, Origin and Sec-Fetch-Site checks for loopback, wildcard and LAN binds. No holes found.
  • Fuzzing (tests/fuzz.py and .github/workflows/fuzz.yml). The fuzzer mutates the seeds and drives the same entry points the server uses. Cases run in a child worker with a memory cap and a per-case timeout, so a hang or out-of-memory kill is recorded and the run continues. Every finding's input is uploaded as an artifact and can be replayed with --replay. It runs for 3 minutes on PRs that touch the engine, without blocking them, and for 45 minutes weekly or on demand.
  • CodeQL (Python and JS, security-extended) and Dependabot for GitHub Actions only, since the project has no pip dependencies by design.

Unit tests run through the existing discover step: 289 tests, 17 of them expected failures, in under a second.

Engine bugs these found

Each test bug is an @unittest.expectedFailure with a comment naming the fault, so the test starts passing once it's fixed. None are fixed here.

From the tests

  • ext4: inline files over 60 bytes read the wrong bytes (ext4.py:296); inline directories miss the parent prefix (:311); sparse extents lose their holes (:257-271); a self-referencing extent index can hang reads (:214-222); reading a deleted file with an unreadable journal raises AttributeError (:140-151)
  • registry: a file under about 48 bytes starting with regf raises struct.error (registry.py:48-54); carve_deleted crashes on a truncated hive (:268-276)
  • EWF: any three-letter extension next to the image counts as a segment, so x.txt beside x.E01 breaks opening (ewf.py:56-60); a truncated zlib chunk is accepted without a finding (:307-318); split raw sets (.001) aren't supported although the README says they are (:538)
  • FAT: deleted long filenames come out scrambled (fat.py:180,190); slack for deleted files is computed at the wrong offset (:252-258)
  • exFAT: a contiguous stream's run list isn't bounded by the cluster count (exfat.py:86); contiguous directories are read only to their first cluster (:132-134); timestamps ignore the UTC offset but are labelled Z (:14)
  • NTFS: invalid MFT records are cached and returned on the second lookup (ntfs.py:418,428-430); the record count ignores volume size when $MFT is unreadable (:401-403)

From a 3-minute fuzz run on this branch (about 82,500 cases)

  • EWF memory exhaustion: section and chunk sizes from the file are passed straight to read() (ewf.py:195, 211, 291, 306, 317), so one corrupt descriptor can exhaust the examiner's memory. I'll send a fix as a separate PR.
  • exFAT: MemoryError at exfat.py:86 (the unbounded run list above)
  • jbd2: AttributeError: 'Journal' object has no attribute 'block_size' (jbd2.py:176)
  • NTFS: AttributeError on MftRecord.sequence (ntfs.py:699, same cause as the caching bug), plus one 10-second hang still to triage

Checked

🤖 Generated with Claude Code

deadp and others added 6 commits September 16, 2026 21:52
Locks in the Host, Origin and Sec-Fetch-Site checks that keep other
origins and DNS-rebinding pages from driving the local interface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Synthetic images are built in code (tests/imagebuild_*.py) so no evidence
or binary fixture is committed. Ten engine bugs found along the way are
recorded as expected failures, each naming the fault: inline data and
inline directories, sparse extents, extent-tree blow-up, the deleted-file
path without a journal, short and truncated hives, over-broad EWF segment
discovery, truncated zlib chunks, and split raw sets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Volumes are built in code (FAT32 sparsely, as it needs ~34 MB) and opened
the way the server opens them. Seven engine bugs are recorded as expected
failures: scrambled deleted long names and wrong slack for deleted files
on FAT; an unbounded contiguous run list, truncated contiguous directories
and unconverted UTC offsets on exFAT; invalid MFT records served from the
cache, and a record count that ignores volume size when $MFT is unreadable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tests/fuzz.py mutates the synthetic images and drives the app's own entry
points in a child worker, so hangs and memory blow-ups are caught and
their inputs kept for replay. It runs in its own workflow: briefly and
non-blocking on PRs that touch the engine, and at length weekly or on
demand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
EwfImage._fh is a method, not a handle, so the harness's cleanup raised
on every EWF case and swamped the real findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@switch-nz
switch-nz merged commit 8c35035 into switch-nz:main Sep 17, 2026
25 of 26 checks passed
switch-nz added a commit to deadp/strata that referenced this pull request Sep 17, 2026
Known issues pointed only at switch-nz#15, which covers crashes and hangs. The
bugs that matter more to an examiner reading release notes are the ones
that return wrong or missing results with no finding, tracked in switch-nz#19:
exFAT timestamps labelled UTC without applying the offset, sparse ext4
files at the wrong offsets, misread ext4 inline data, scrambled deleted
FAT names and misplaced FAT slack, a truncated EWF chunk returned short,
and split raw sets loading as a partial disk. Each is still an expected
failure on main, so each is listed individually rather than behind a
link.

0.1.0 is not tagged yet, so what has merged since this changelog was
written ships in it: the EWF read bounds (switch-nz#14), the exFAT contiguous
run clamp (switch-nz#17) and the parser tests, fuzzing and CodeQL (switch-nz#13) are
added. Raw/dd is described as single-file and split raw sets move to
Not implemented, matching the README correction in switch-nz#20.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@deadp
deadp deleted the test-hardening branch September 17, 2026 03:45
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.

3 participants