From a8464fc36787f56f5c76f1e4566b7e97bf045807 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 06:46:58 -0700 Subject: [PATCH] fix(editor): escape user strings in server module map/zone/tele SQL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The map_dbc, area_table_dbc, and game_tele INSERTs previously embedded mapName/displayName/manifest.mapName as raw strings — a zone called "King's Land" or anything containing a single quote would emit malformed SQL that AzerothCore would reject. Promotes the existing escapeSql helper to a public SQLExporter::escape and uses it in all three INSERTs. --- tools/editor/server_module_gen.cpp | 18 ++++++++++++------ tools/editor/sql_exporter.cpp | 5 ++++- tools/editor/sql_exporter.hpp | 6 ++++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/tools/editor/server_module_gen.cpp b/tools/editor/server_module_gen.cpp index 2c7cc2bc..bf3f8115 100644 --- a/tools/editor/server_module_gen.cpp +++ b/tools/editor/server_module_gen.cpp @@ -36,17 +36,22 @@ bool ServerModuleGenerator::generate(const ZoneManifest& manifest, f << "-- Generated: " << timeBuf << "\n"; f << "-- Map ID: " << cfg.mapId << "\n\n"; + // Escape user-provided strings — a zone name like "King's Land" + // would otherwise produce invalid SQL. + const std::string mapName = SQLExporter::escape(cfg.mapName); + const std::string displayName = SQLExporter::escape(cfg.displayName); + f << "-- Register custom map\n"; f << "INSERT INTO `map_dbc` (`ID`, `MapName`, `MapType`, `MapDescription`) VALUES (" - << cfg.mapId << ", '" << cfg.mapName << "', 0, '" - << cfg.displayName << "') ON DUPLICATE KEY UPDATE `MapName`='" - << cfg.mapName << "';\n\n"; + << cfg.mapId << ", '" << mapName << "', 0, '" + << displayName << "') ON DUPLICATE KEY UPDATE `MapName`='" + << mapName << "';\n\n"; f << "-- Register zone area\n"; f << "INSERT INTO `area_table_dbc` (`ID`, `MapID`, `AreaName`, `ExploreFlag`) VALUES (" << cfg.zoneId << ", " << cfg.mapId << ", '" - << cfg.displayName << "', 1) ON DUPLICATE KEY UPDATE `AreaName`='" - << cfg.displayName << "';\n"; + << displayName << "', 1) ON DUPLICATE KEY UPDATE `AreaName`='" + << displayName << "';\n"; } // 2. Generate creature + quest SQL @@ -63,9 +68,10 @@ bool ServerModuleGenerator::generate(const ZoneManifest& manifest, float tileSize = 533.33333f; float x = (32.0f - manifest.tiles[0].second) * tileSize; float y = (32.0f - manifest.tiles[0].first) * tileSize; + const std::string teleName = SQLExporter::escape(manifest.mapName); f << "INSERT INTO `game_tele` (`name`, `position_x`, `position_y`, " << "`position_z`, `orientation`, `map`) VALUES ('" - << manifest.mapName << "', " << x << ", " << y << ", " + << teleName << "', " << x << ", " << y << ", " << manifest.baseHeight + 10.0f << ", 0, " << cfg.mapId << ") ON DUPLICATE KEY UPDATE `position_x`=" << x << ";\n"; f << "\n-- Usage: .tele " << manifest.mapName << "\n"; diff --git a/tools/editor/sql_exporter.cpp b/tools/editor/sql_exporter.cpp index ba898532..c314cb3b 100644 --- a/tools/editor/sql_exporter.cpp +++ b/tools/editor/sql_exporter.cpp @@ -10,8 +10,9 @@ namespace wowee { namespace editor { -static std::string escapeSql(const std::string& s) { +std::string SQLExporter::escape(const std::string& s) { std::string out; + out.reserve(s.size()); for (char c : s) { if (c == '\'') out += "''"; else if (c == '\\') out += "\\\\"; @@ -20,6 +21,8 @@ static std::string escapeSql(const std::string& s) { return out; } +static std::string escapeSql(const std::string& s) { return SQLExporter::escape(s); } + bool SQLExporter::exportCreatures(const std::vector& spawns, const std::string& path, uint32_t mapId, diff --git a/tools/editor/sql_exporter.hpp b/tools/editor/sql_exporter.hpp index 5e589a10..3976064e 100644 --- a/tools/editor/sql_exporter.hpp +++ b/tools/editor/sql_exporter.hpp @@ -33,6 +33,12 @@ public: const std::string& path, uint32_t mapId = 9000, uint32_t startEntry = 100000); + + // Escape a string for safe inclusion inside single-quoted SQL literal. + // Doubles single quotes and escapes backslashes — matches MySQL/MariaDB + // string literal rules used by AzerothCore/TrinityCore. Use whenever you + // emit user-provided text into SQL outside of this class. + static std::string escape(const std::string& s); }; } // namespace editor