From c4463ba96ec66255325a217e9d9bad8e76907c00 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 06:14:22 -0700 Subject: [PATCH] fix(wob): reject doodad paths with traversal/absolute components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doodad model paths from a WoB are passed to the asset manager via outModel.doodadNames. The asset manager only reads files, but '..' or absolute paths from a hostile WoB could probe for files outside the expected assets/ tree. Now clears the modelPath on traversal — the doodad slot survives but loads no model. --- src/pipeline/wowee_building.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pipeline/wowee_building.cpp b/src/pipeline/wowee_building.cpp index adb43575..7883e069 100644 --- a/src/pipeline/wowee_building.cpp +++ b/src/pipeline/wowee_building.cpp @@ -148,6 +148,16 @@ WoweeBuilding WoweeBuildingLoader::load(const std::string& basePath) { if (pl > 1024) pl = 0; dp.modelPath.resize(pl); f.read(dp.modelPath.data(), pl); + // Reject path-traversal in doodad model paths — these end up in + // outModel.doodadNames and are passed to the asset manager. While + // the manager only reads files, '..' paths in custom_zones could + // probe for files outside the assets/ tree. + if (dp.modelPath.find("..") != std::string::npos || + (!dp.modelPath.empty() && (dp.modelPath[0] == '/' || dp.modelPath[0] == '\\')) || + (dp.modelPath.size() >= 2 && dp.modelPath[1] == ':')) { + LOG_WARNING("WOB doodad path rejected (traversal): ", dp.modelPath); + dp.modelPath.clear(); + } f.read(reinterpret_cast(&dp.position), 12); f.read(reinterpret_cast(&dp.rotation), 12); f.read(reinterpret_cast(&dp.scale), 4);