From 9f2e8e197993d44952550633e550415d4681613c Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 09:26:08 -0700 Subject: [PATCH] test(wob): add 2 string-length reject tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Locks in the recent silent-corruption fix: - Overlong building name (5000 > 1024) → load returns invalid instead of silently zeroing the length and reading 5000 stale bytes as the next group's name+counts. - Overlong group name (9999 > 1024) → same. Catches regression of the silent-desync defect that affected 7 length fields across the WoB and WOM loaders. --- tests/test_open_formats.cpp | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/test_open_formats.cpp b/tests/test_open_formats.cpp index 8fb3e14e..e69ec9f9 100644 --- a/tests/test_open_formats.cpp +++ b/tests/test_open_formats.cpp @@ -807,3 +807,56 @@ TEST_CASE("WoweeCollision save caps tri count and clamps tile coords", "[woc][ha REQUIRE(reloaded.triangles.size() == 5); std::filesystem::remove(path); } + +TEST_CASE("WOB rejects load on overlong building name", "[wob][hardening]") { + ensureTestDir(); + std::string base = TEST_DIR + "/bad_name"; + std::string path = base + ".wob"; + { + std::ofstream f(path, std::ios::binary); + uint32_t magic = 0x31424F57; // WOB1 + uint32_t gc = 1, pc = 0, dc = 0; + float boundRadius = 1.0f; + f.write(reinterpret_cast(&magic), 4); + f.write(reinterpret_cast(&gc), 4); + f.write(reinterpret_cast(&pc), 4); + f.write(reinterpret_cast(&dc), 4); + f.write(reinterpret_cast(&boundRadius), 4); + // Overlong building name length (5000 > 1024 cap) + uint16_t nameLen = 5000; + f.write(reinterpret_cast(&nameLen), 2); + } + auto bld = WoweeBuildingLoader::load(base); + // Without the reject, the loader would silently set nameLen=0 and the + // 5000 stale bytes after would be misread as the next group's name+ + // counts. With the fix the load returns an empty WoweeBuilding. + REQUIRE_FALSE(bld.isValid()); + std::filesystem::remove(path); +} + +TEST_CASE("WOB rejects load on overlong group name", "[wob][hardening]") { + ensureTestDir(); + std::string base = TEST_DIR + "/bad_grp_name"; + std::string path = base + ".wob"; + { + std::ofstream f(path, std::ios::binary); + uint32_t magic = 0x31424F57; // WOB1 + uint32_t gc = 1, pc = 0, dc = 0; + float boundRadius = 1.0f; + f.write(reinterpret_cast(&magic), 4); + f.write(reinterpret_cast(&gc), 4); + f.write(reinterpret_cast(&pc), 4); + f.write(reinterpret_cast(&dc), 4); + f.write(reinterpret_cast(&boundRadius), 4); + // Valid building name + uint16_t nameLen = 4; + f.write(reinterpret_cast(&nameLen), 2); + f.write("Test", 4); + // Overlong group name length + uint16_t gnLen = 9999; + f.write(reinterpret_cast(&gnLen), 2); + } + auto bld = WoweeBuildingLoader::load(base); + REQUIRE_FALSE(bld.isValid()); + std::filesystem::remove(path); +}