From b162b3f4d806ca577fe104ef6f9cfefe44f10846 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 4 Jun 2026 20:54:13 +0200 Subject: [PATCH] fix(items): guard oversized manifest NPE in FurnidataReader + document JSON5 trailing-comma limit --- .../eu/habbo/habbohotel/items/FurnidataReader.java | 12 ++++++++++-- .../habbo/habbohotel/items/FurnidataReaderTest.java | 11 +++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataReader.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataReader.java index d3b753d8..1af3dfc7 100644 --- a/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataReader.java +++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/items/FurnidataReader.java @@ -85,7 +85,9 @@ public class FurnidataReader { Path m = dir.resolve(name); if (!Files.exists(m)) continue; try { - JsonObject obj = JsonParser.parseString(readJson5Capped(m)).getAsJsonObject(); + String raw = readJson5Capped(m); + if (raw == null) continue; + JsonObject obj = JsonParser.parseString(raw).getAsJsonObject(); if (obj.has(key) && obj.get(key).isJsonArray()) { List list = new ArrayList<>(); for (JsonElement el : obj.getAsJsonArray(key)) list.add(el.getAsString()); @@ -133,7 +135,13 @@ public class FurnidataReader { return candidate.toAbsolutePath().normalize().startsWith(baseNorm); } - /** Strip // and block comments and trailing commas so Gson can parse JSON5. */ + /** + * Strip // and block comments and trailing commas so Gson can parse JSON5. + * Known limitation: the trailing-comma pass is a regex over the whole output, + * so a string value literally containing ",[whitespace]}" or ",[whitespace]]" + * would be altered. Real Habbo furnidata names/descriptions do not contain + * that pattern; values are additionally sanitized downstream before use. + */ static String stripJson5(String content) { if (content == null || content.isEmpty()) return content; StringBuilder out = new StringBuilder(content.length()); diff --git a/Emulator/src/test/java/com/eu/habbo/habbohotel/items/FurnidataReaderTest.java b/Emulator/src/test/java/com/eu/habbo/habbohotel/items/FurnidataReaderTest.java index a15e2c19..acb1b3d2 100644 --- a/Emulator/src/test/java/com/eu/habbo/habbohotel/items/FurnidataReaderTest.java +++ b/Emulator/src/test/java/com/eu/habbo/habbohotel/items/FurnidataReaderTest.java @@ -55,6 +55,17 @@ class FurnidataReaderTest { }); } + @Test + void oversizedManifestIsSkippedNeverThrows(@TempDir Path dir) throws Exception { + Path base = dir.resolve("furnidata"); + Path core = base.resolve("core"); + Files.createDirectories(core); + // A root manifest larger than the cap we pass in. + Files.writeString(base.resolve("manifest.json"), "{ \"tiers\": [ \"core\" ] } // padding ".repeat(50)); + List entries = new FurnidataReader(base, 8 /* bytes */).read(); + assertTrue(entries.isEmpty()); + } + @Test void splitDirRejectsTraversalFiles(@TempDir Path dir) throws Exception { Path secret = dir.resolve("secret.json");