Skip to content

test(data): exercise corrupted GeoPackage headers - #1138

Merged
deniial00 merged 1 commit into
opengeos:mainfrom
choupeanut:fix/779-hostile-reader
Oct 29, 2026
Merged

deniial00 merged 1 commit into
opengeos:mainfrom
choupeanut:fix/779-hostile-reader

Conversation

@choupeanut

@choupeanut choupeanut commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Exercise the browser's in-memory GeoPackage reader with deterministic byte mutations in a generated SQLite file: the database header, page headers, and three GeoPackage geometry headers. Every mutated file is opened and, when opening succeeds, read; errors are accepted but a panic fails the test. This adds focused malformed-input coverage and does not implement a fuzzing harness or claim a newly discovered crash.

Testing

  • cargo test -p rustgis-data --lib geopackage_in_memory_handles_mutated_headers
  • uvx pre-commit run --all-files
  • cargo xtask ci (fmt, clippy, workspace tests and layers)
  • cargo build --locked --target wasm32-unknown-unknown -p rustgis-web with clang

Related issue

Part of #779.

Summary by CodeRabbit

  • Tests
    • Added coverage for GeoPackage files with altered SQLite headers, page headers, and geometry metadata bytes.

@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 8805a7ae-75ca-47e5-8f23-e3791a098a6c
Commits

Reviewing files that changed from the base of the PR and between 02c9386 and d0c8c55.

Files selected for processing: 1
  • crates/data/src/source/tests.rs

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Walkthrough

The pull request adds a test that mutates selected SQLite file-header, page-header, and GeoPackage geometry bytes. The test attempts to open each mutated file and reads its features if opening succeeds.

Changes: GeoPackage mutation tests

Layer / File(s) Summary
Mutated input test
crates/data/src/source/tests.rs
Adds coverage for mutations to SQLite file and page headers and three GeoPackage geometry blobs. The test attempts to open each mutated file and reads all features when opening succeeds.

Priority: Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Other

Possibly related issues

  • opengeos/RustGIS#779 requests malformed-input testing for readers, including GeoPackage. This change adds coverage for mutated GeoPackage input.

Possibly related PRs

  • opengeos/RustGIS#965 adds malformed-input panic-resistance coverage for a different data-reader format.

Suggested reviewers: giswqs

Merge Risk: Minimal · up to d0c8c

This test-only change is mergeable after normal checks. The previously reported mutation-coverage gaps remain follow-up improvements.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title "test(data): exercise corrupted GeoPackage headers" matches the main change. The PR adds a test that mutates GeoPackage and SQLite header bytes and checks that opening and reading the file d…
Description check Passed The description follows the repository template. It has a Summary that explains the change, the malformed-input coverage, and the scope limits. It has a Testing section that lists the test command, pr…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title "test(data): exercise corrupted GeoPackage headers" matches the main change. The PR adds a test that mutates GeoPackage and SQLite header bytes and checks that opening and reading the file does not panic. The title is short, specific, and uses a conventional commit prefix.

Full details: Description check

Explanation

The description follows the repository template. It has a Summary that explains the change, the malformed-input coverage, and the scope limits. It has a Testing section that lists the test command, pre-commit, cargo xtask ci, and the wasm build. It has a Related issue section that says "Part of #779." The UI evidence section is omitted, which is correct because the change has no visible UI impact. The description does not name a test file or give a result for the test run, but those details are not required by the template.

Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 1 functions across 1 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks each header byte,
Then nudges geometry into sight.
The pages shift; the tests proceed,
Open, read, and check the feed.
Soft paws hop through every case,
While features find their place.

Comment @coderabbitai help to get the list of available commands.

original.windows(4).enumerate().filter_map(|(i, bytes)| (bytes == b"GP\x00\x03").then_some(i)).take(3).collect();
assert_eq!(geometries.len(), 3);
for i in geometries {
offsets.extend(i..i + 8);

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.

Envelope is never mutated, despite the comment on line 387. The fixture writes each blob as an 8-byte header (GP, version, flags, srs_id) followed by a 32-byte XY envelope. i..i + 8 covers only the header, so the envelope that gpkg_mem reads via split_blob for pruning is untouched. Covering the envelope too is a one-line change (confidence: high).

Suggested change
offsets.extend(i..i + 8);
offsets.extend(i..i + 40);

Also update the comment on line 387 to say it covers the header and the XY envelope.

Comment on lines +384 to +386
for page in (4096..original.len()).step_by(4096) {
offsets.extend(page..(page + 12).min(original.len()));
}

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.

Page 1's B-tree header is skipped. The loop starts at 4096, so page 1 is never visited. Its B-tree header sits at byte 100, just past the 100-byte database header that line 383 covers, so bytes 100–111 are never mutated. The 4096 page size is also hard-coded; it is stored in the database header at bytes 16–17 (confidence: medium-high).

Suggested change
for page in (4096..original.len()).step_by(4096) {
offsets.extend(page..(page + 12).min(original.len()));
}
for page in (0..original.len()).step_by(4096) {
let start = if page == 0 { 100 } else { page };
offsets.extend(start..(start + 12).min(original.len()));
}

}
// Exercise flags and envelope metadata in actual GeoPackage geometry blobs.
let geometries: Vec<_> =
original.windows(4).enumerate().filter_map(|(i, bytes)| (bytes == b"GP\x00\x03").then_some(i)).take(3).collect();

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.

Nits, grouped:

  • 100 and 4096 are magic numbers. A named constant or a reference to the SQLite header layout would make the intent clearer.
  • filter_map(|(i, bytes)| (bytes == b"GP\x00\x03").then_some(i)) reads more simply as .filter(...).map(|(i, _)| i).
  • A mutation that makes the reader loop or over-allocate would hang the test rather than fail it. There is no timeout, so this is worth knowing about even though the test is correct as written.

@github-actions

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • The mutation loop never touches the GeoPackage envelope. offsets.extend(i..i + 8) covers only the 8-byte blob header, while the comment says it exercises "flags and envelope metadata". The reader parses that envelope in split_blob for pruning, so this is a coverage gap. Fix: i..i + 40. (Confidence: high.)
  • Page 1's B-tree header (bytes 100–111) is never mutated. The page loop starts at 4096, so page 1 is skipped, and the 4096 page size is hard-coded where the header stores it at bytes 16–17. (Confidence: medium-high.)

Security

  • None found. The test only mutates in-memory bytes and asserts on panics.

Performance

  • None significant. The clone-per-offset loop is fine for a fixture of this size.

Quality

  • Nits: 100 and 4096 are unnamed magic numbers; filter_map(...then_some) could be filter; a hang in the reader would stall the test rather than fail it, because there is no timeout. (Confidence: high, low severity.)

AGENTS.md

  • Compliant. The test uses unwrap only in test code, the module is native-only, and no dependencies or assets change.

Verified: Shared, gpkg_mem::GpkgMemSource::open_shared and Request::all() exist with matching signatures. The fixture (write_gpkg) writes GP\x00\x03 blobs with a 32-byte XY envelope, which matches the mutation offsets the test uses.

Inline comments are posted on crates/data/src/source/tests.rs at lines 392 (envelope), 384–386 (page loop) and 389 (nits).

Change size

Compared with the base branch: the whole PR, main at 90576c5 to PR head d0c8c55.

Measure Value
Files changed 1 (added 0, modified 1, removed 0, renamed 0)
Size before 88.7 KB
Size after 89.8 KB
Total size change +1.1 KB
Path Before After Change
crates/data/src/source/tests.rs 88.7 KB 89.8 KB +1.1 KB

@github-actions

Copy link
Copy Markdown
Contributor

📖 Docs preview

Item Value
Preview https://preview.rustgis.app/pr-1138/
Web app https://preview.rustgis.app/pr-1138/app/
Commit d0c8c55

GitHub Pages takes a minute or two to publish a new commit.

@deniial00
deniial00 merged commit 373efef into opengeos:main Oct 29, 2026
18 checks passed
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.

2 participants