Commit graph

3683 commits

Author SHA1 Message Date
Kelsi
aa9ef6f2ca fix(adt): scrub NaN/inf floats at write time
ADTWriter::writeFloat now coerces non-finite values to 0 before
emitting bits, so a stray NaN in a chunk position, MDDF rotation,
or MODF extent can't leak into the saved ADT and produce invisible
terrain or off-map placements after reload.
2026-05-06 06:49:51 -07:00
Kelsi
bc1839d9ab fix(zone): drop out-of-range tile coords on manifest load
WoW's tile grid is 64x64. A tile (200,200) entry would generate an
ADT filename the loader rejects and silently leave the zone with no
terrain. Drop bad entries instead.
2026-05-06 06:48:28 -07:00
Kelsi
4a98dd7a2c fix(editor): slugify mapName for module dir + conf keys
A zone named with spaces or punctuation (e.g. "My Zone!") used to
produce a module directory path "mod_wowee_My Zone!/" and conf
keys like "Wowee.My Zone!.Enabled" which AzerothCore's config
parser rejects. The slug is now stripped to [A-Za-z0-9_-], with
spaces/slashes mapped to underscores. SQL VALUES still use the
raw display text via SQLExporter::escape.
2026-05-06 06:47:43 -07:00
Kelsi
a8464fc367 fix(editor): escape user strings in server module map/zone/tele SQL
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.
2026-05-06 06:46:58 -07:00
Kelsi
cc1e1cb7fa fix(editor): cap stamp vertex count and skip NaN samples on load
A malformed stamp JSON could carry millions of entries (would OOM)
or NaN dx/dy/height (would propagate through brush blends and leave
permanent holes in the heightmap).
2026-05-06 06:45:41 -07:00
Kelsi
0a5583310c fix(editor): clamp project mapId and zone tile coords on load
A hand-edited project.json could carry a 0 or massively-OOR mapId
(would break DBC indexing) or tile coords outside 0..63 (would
produce garbage ADT filenames). Defaults restore safe values.
2026-05-06 06:45:09 -07:00
Kelsi
721efb2ecb fix(zone): clamp manifest audio volumes and reject NaN baseHeight on load
A user-edited zone.json could carry NaN/inf or out-of-range volumes
which would silently corrupt terrain elevation or produce silent /
clipping audio. Now baseHeight defaults back to 100 if non-finite,
and music/ambience volumes clamp to [0,1] with NaN fallbacks.
2026-05-06 06:44:30 -07:00
Kelsi
185b7b522d fix(wob): sanitize boundRadius + per-group boundMin/Max at save time
Same float-NaN scrub for WoB save matching the WHM/WOC/WOM saves.
Building boundRadius defaults to 1.0 if non-finite; per-group bounds
zero out non-finite components (would otherwise corrupt the cull
frustum).
2026-05-06 06:39:49 -07:00
Kelsi
d25654d11a fix(wom): sanitize boundRadius/min/max floats at save time
Mirrors the WHM and WOC save sanitize. boundRadius defaults to 1.0 if
non-finite (matches load-time default); boundMin/boundMax components
zero out non-finite values. Prevents an in-memory model with a NaN
spike (e.g. mid-edit) from being persisted into the WOM and requiring
load-time cleanup forever after.
2026-05-06 06:36:46 -07:00
Kelsi
663d34af0d fix(woc): sanitize triangle vertices + bounds at save time
In-memory collision can be polluted by addMesh on bad input (e.g. a
WMO with NaN vertex positions). Without this, the save would persist
that NaN into the WOC and the load-time guards would have to clean
it up forever. Now scrubs vertices and bounds at write time, matching
the WHM save sanitize.
2026-05-06 06:35:05 -07:00
Kelsi
71378c20ff fix(whm): exportOpen sanitizes heights + caps alphaSize at 64KB on save
Sanitize at write time too, not just on load. A mid-edit NaN spike
(e.g. brush operation that produced NaN before being committed) would
otherwise be persisted into the WHM and require the load-time guard
to clean it up forever after. AlphaSize is also capped at 64KB to
match the loader cap.
2026-05-06 06:32:24 -07:00
Kelsi
2df49c725f fix(editor): texture exporter validates BLP image before passing to stbi
Three new sanity checks before stbi_write_png:
  - dimensions <=0 or >8K rejected (matches PNG override loader cap)
  - data buffer must be >= width * height * 4 bytes (corrupt BLP could
    have mismatched dimensions vs data length, and stbi reads off the
    end of the buffer otherwise)
Skips with warning rather than crashing the exporter mid-zone.
2026-05-06 06:29:26 -07:00
Kelsi
d3a85776f8 fix(content-pack): packZone truncates path length + skips files >4GB
Two write-side guards mirroring the unpack-side ones:
  - Path length truncated to 1KB (matches unpack cap; long paths would
    silently wrap u16 and corrupt the pack)
  - Files >4GB on disk skipped with a zero-length entry rather than
    silently producing a truncated dataSize that overflows uint32
2026-05-06 06:27:04 -07:00
Kelsi
5019e21787 fix(wom): writeStr helper truncates length-prefixed strings to fit u16 length
Same fix as WoB save just got. Without truncation a model name or
texture path over 65535 chars would silently get a wrap-around length
and corrupt the file.
2026-05-06 06:25:33 -07:00
Kelsi
361ff712d7 fix(wob): writeStr helper truncates length-prefixed strings to fit u16 length
Without truncation, names over 65535 chars would silently get a wrap-
around length value and produce a corrupt file (the actual string would
be longer than the saved length, shifting everything after it). Single
shared writeStr lambda replaces five copy-pasted (uint16 length + bytes)
write blocks.
2026-05-06 06:23:35 -07:00
Kelsi
be64298218 fix(dbc): cap JSON DBC string sizes (4KB/string, 64MB total) + NaN floats
JSON DBC values are mostly small integers, but a malicious file could
stuff 100MB strings into every cell to OOM the stringBlock (which has
no per-string or total cap). Added 4KB per-string + 64MB total caps —
fields exceeding either are zeroed. Float fields also get NaN scrub
(same pattern as the earlier vertex/keyframe guards).
2026-05-06 06:19:01 -07:00
Kelsi
719951976d fix(wom+wob): reject path traversal in WOM texture paths + WOB material/group texPaths
Same defensive check as the WoB doodad path guard. Texture paths from
hostile WOM/WoB are passed to the asset manager; '..' or absolute paths
could probe outside the assets/ tree. Now cleared on detection — slot
survives but loads no texture (renderer falls back to white).

Single shared rejectTraversal lambda in WoB to avoid copy-paste.
2026-05-06 06:16:54 -07:00
Kelsi
c4463ba96e fix(wob): reject doodad paths with traversal/absolute components
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.
2026-05-06 06:14:22 -07:00
Kelsi
bbfc364119 fix(editor): texture exporter rejects path-traversal in source M2/WMO texture paths
Texture paths come from M2/WMO files which a malicious zone author
could craft to include '..' or absolute paths. Without this check,
exporting such a zone would write PNGs outside outputDir/textures/
and clobber sibling export files.
2026-05-06 06:12:11 -07:00
Kelsi
b5a9ce7816 fix(assets): cap PNG override texture dimensions at 8K to prevent OOM
stbi_load happily decodes any PNG up to 32K x 32K — at 4 bytes/pixel
that's 4GB which OOMs the editor before the override even returns.
WoW textures top out at 4K; 8K cap leaves headroom for HD upgrades
without enabling abuse. Also widens the wxh multiplication to size_t
to defeat int overflow on 8K x 8K images.
2026-05-06 06:09:13 -07:00
Kelsi
2d8c843704 fix(dbc): cap JSON DBC fieldCount/recordCount to prevent OOM on hostile file
Real DBCs cap at ~250 fields and a few million records (Spell.dbc is
the biggest at ~50K rows). A malicious JSON DBC declaring fieldCount=
1G or recordCount * recordSize > 256MB would OOM the recordData
allocation. Now rejects upfront — JSON DBCs are user-shareable so a
zone export downloaded from a forum should not be able to OOM the
client by including a bad data table.
2026-05-06 06:07:09 -07:00
Kelsi
5b6f59bbbd fix(adt): scrub NaN/inf in MDDF + MODF placement floats
ADT placement positions and rotations are loaded as raw floats from the
binary chunks. A corrupted MDDF/MODF entry could feed NaN into the
M2/WMO instance transform and crash render. Now position/rotation are
scrubbed for both MDDF doodads and MODF buildings; MODF also scrubs the
extentLower/extentUpper bounding box used for cull tests.
2026-05-06 06:05:17 -07:00
Kelsi
15bf77c616 fix(editor): NPC stat field bounds across the board
Across-the-board NPC stat sanity on JSON load:
  - level: 0 -> 1, >255 -> 255
  - health: 0 -> 1
  - maxDmg < minDmg -> bumped to minDmg
  - behavior enum: clamp to 0..3 range
  - wander/aggro/leash radius: NaN/inf -> default; wander capped at 1000
  - respawnTimeMs: <1s -> 1s, >24h -> 24h
2026-05-06 06:02:10 -07:00
Kelsi
f5fc23e003 fix(editor): quest level + reward sanity bounds + item slot cap
Rounds out the quest load guards:
  - requiredLevel: 0 -> 1 (no level-0 quests), >255 -> 80 (typo guard)
  - reward.xp: cap 1M
  - reward.gold: cap 10000
  - reward.silver/copper: cap 99 (server overflows otherwise)
  - reward.itemRewards: cap 6 entries (matches WoW quest_template
    RewardItemId[1..6] slot capacity)
2026-05-06 05:59:05 -07:00
Kelsi
c4c8d9e7ed fix(editor): quest objective load clamps type, count, and per-quest size
Three guards on quest objective loading from JSON:
  - type out of QuestObjectiveType range (0..5) -> defaults to 0 (Kill)
  - targetCount of 0 -> 1 (no-op objectives are nonsense)
  - targetCount > 1000 -> 1000 (typo guard, biggest legit WoW quest is ~100)
  - >10 objectives per quest -> dropped (matches SQL slot capacity, also
    bounds per-quest memory)
2026-05-06 05:56:11 -07:00
Kelsi
62b668e898 fix(content-pack): cap WCP per-entry path length + catch backslash/drive traversal
Path length cap (1KB) — uint16 can hold 64KB but no real zone path
should exceed 256 chars. Path traversal check extended to also catch:
  - Windows backslash absolute paths ('\' at start)
  - Windows drive-prefixed paths ('C:\...')
A WCP downloaded from a forum and unpacked on Windows would otherwise
have these vectors open.
2026-05-06 05:54:31 -07:00
Kelsi
c0c1be1c9e fix(wob): cap material/doodad path lengths + portal vertex count
Three more per-record sanity bounds that the per-group sweep didn't
cover:
  - material.texturePath length cap (1KB) — was unbounded
  - doodad.modelPath length cap (1KB) — was unbounded
  - portal.vertexCount cap (4096) — real portals are 4-12 verts;
    >4K is corrupt and would OOM the resize
2026-05-06 05:52:58 -07:00
Kelsi
93edf1bd55 fix(wob): per-group count sanity bounds + group name length cap
The WoB top-level header sanity bounds catch obviously-bad totals, but
each group's vc/ic/tc was still unbounded. A corrupted group could
declare 4G vertices and OOM the resize before the next group even
started. Now per-group: vc<=1M, ic<=4M, tc<=1K, name<=1KB.
2026-05-06 05:49:15 -07:00
Kelsi
cb7f11f2ea fix(wom): cap total keyframes at 10M to prevent OOM on hostile model
Per-bone-anim cap of 10K keyframes still let a malicious file allocate
up to 1024 anims × 512 bones × 10K keys = 5.24B keyframes — multi-GB
pre-OOM allocation. Now tracks total across the whole model and stops
allocating after 10M (real models stay well under 100K). When the cap
is hit we still seek past the remaining payload to keep file alignment
intact for whatever follows.
2026-05-06 05:46:55 -07:00
Kelsi
a7d62d1af9 fix(woc): reject corrupted triCount + clamp out-of-range tile coords
Header sanity bounds for WOC matching the WOM/WoB ones. A whole-tile
collision mesh maxes at ~32K terrain tris + a few thousand building
overlay tris; triCount > 2M is corrupted and would OOM. Tile coords
are 0..63 in WoW; clamp to 32 with warning when bogus.
2026-05-06 05:44:37 -07:00
Kelsi
90289ba48b fix(wob+wom): reject corrupted header counts before allocating
Adds upfront sanity bounds to both WoB and WOM load:
  WOM: vert<=1M, index<=4M, tex<=1K
  WOB: groups<=4K, portals<=8K, doodads<=64K
Real WoW models stay well under these limits (M2 vert is uint16 anyway).
Without these checks a corrupted header could trigger a multi-GB
allocation and OOM the process before we finish reading the body. Also
caps name length to 1KB on WoB load (already done on WOM).
2026-05-06 05:42:50 -07:00
Kelsi
c05d421c29 fix(content-pack): reject malicious WCP headers (oversize counts + path traversal)
Three security/robustness guards on unpackZone:
1. fileCount > 1M or infoSize > 16MB rejected upfront — would OOM on
   the next allocation.
2. Per-file dataSize > 256MB rejected — single malicious entry could
   exhaust memory mid-extraction.
3. Path traversal ('..' or absolute paths) rejected — would write
   outside destDir/<zoneName>/ and clobber system files.
WCPs are user-shareable archives, so a hostile pack downloaded from a
forum should not be able to OOM the editor or write to /etc.
2026-05-06 05:38:53 -07:00
Kelsi
53780de6da fix(wob): clamp out-of-range group indices + reject absurd texture path lengths
Mirrors the WOM index-clamp + texture-path-length guards. Out-of-range
indices into the WMO group's vertex buffer would crash the GPU draw.
Texture path length over 1KB indicates a corrupted/truncated WoB; clamp
to 0 to prevent allocating 65KB-string buffers per bad entry.
2026-05-06 05:36:20 -07:00
Kelsi
a2eaf3965a fix(wom): clamp out-of-range indices + reject absurd texture path lengths
Out-of-range indices were a silent vector overrun on the GPU side that
could crash the vertex shader on some drivers. Replace with 0 rather
than dropping so triangle counts stay aligned (a degenerate triangle is
harmless, an off-by-one indexing the wrong vertex is silent corruption).

Texture path length over 1KB is almost certainly a corrupted or
truncated file — was previously read into a 65KB-string allocation per
entry which could exhaust memory on a malicious file.
2026-05-06 05:34:41 -07:00
Kelsi
fd4354c17d fix(wom): fromM2 sanitizes vertex floats during conversion (matches WOB)
Same NaN scrub during fromM2 conversion that fromWMO got. Ensures a
corrupt source M2 (mangled MPQ block, partial extraction) doesn't
silently produce a NaN-laced WOM. Also feeds the cleaned positions
into boundMin/boundMax so the saved WOM bounds are clean too.
2026-05-06 05:32:47 -07:00
Kelsi
7acde76025 fix(wob): fromWMO sanitizes vertex floats during conversion
Sanitize at conversion time too, not just on WoB load. Avoids the
case where a corrupt source WMO (extracted from a partially-decoded
MPQ) silently poisons the WOB the editor exports — and the WOB
load-time guard from the previous commit only catches it on later
reload, not during the first conversion. Also catches the boundRadius
calculation which would otherwise inherit a NaN from one bad vertex.
2026-05-06 05:29:30 -07:00
Kelsi
2b5f69187e fix(woc): fromTerrain skips degenerate triangles before normalize
The walkability classifier did glm::normalize(cross(...)) without
guarding for zero-length cross. A flat-on-itself triangle (e.g. all
three vertices at the same height in a hole-edge case) produces NaN
normal, NaN walkability flag, and crashes the downstream nz check.
Now the cross-length is computed once and the triangle is skipped if
it's effectively zero.
2026-05-06 05:26:46 -07:00
Kelsi
70bbed4222 fix(woc): scrub NaN triangle vertices + skip degenerate triangles on load
NaN vertices in collision triangles produce NaNs in ray-triangle
intersection (used by movement collision queries), making the player
phase through walls or fall through the floor. Degenerate triangles
(zero-area, collinear) similarly produce NaN normals. Now both are
sanitized/skipped on load and the count is reported in the log.
2026-05-06 05:24:46 -07:00
Kelsi
a3fb267e0b fix(wom): sanitize per-keyframe translation/rotation/scale + movingSpeed NaN
Bone interpolation returns NaN for any NaN input. A single bad keyframe
in any animation would corrupt the entire skeleton during playback —
even bones that weren't being keyed in that animation got NaN final
matrices via parent-chain multiplication. Also catches movingSpeed which
leaks into the engine's displacement maths.
2026-05-06 05:22:32 -07:00
Kelsi
f0abd1794b fix(wom): sanitize bone pivot NaN + clamp out-of-range parentBone
Bones with NaN pivots produce broken skeleton matrices that ripple into
every child bone via the parent-chain multiplication. Out-of-range
parentBone indices would cause a use-after-free during bone-matrix
computation. Both now defensively clamped.
2026-05-06 05:19:24 -07:00
Kelsi
15648e21ec fix(wob): per-vertex NaN/inf scrub on load (matches WOM)
Same NaN guard as the just-applied WOM one. Sanitizes position/normal/
texCoord/color components after the bulk read. WMO renderer's matrix
math is sensitive — a single NaN position could desync the entire
group's draw state.
2026-05-06 05:16:16 -07:00
Kelsi
29ae850307 fix(wom): per-vertex NaN/inf scrub on load
Even after the bound-field guards, individual vertex floats (position,
normal, texCoord) could still poison the GPU. NaN positions would crash
the M2 vertex shader on some drivers (silent device-lost). Now each
component defaults to 0 (or 1 for normal Z) when non-finite — vertex
ends up at origin instead of corrupting the whole pipeline.
2026-05-06 05:14:35 -07:00
Kelsi
1467417b11 fix(wom): sanitize boundRadius / boundMin / boundMax against NaN/inf on load
WOM bound fields drive M2 culling and collision AABBs — non-finite
values would either cull the model out entirely or crash the cull math.
Now boundRadius defaults to 1.0 when invalid, and each boundMin/boundMax
component defaults to 0 when non-finite.
2026-05-06 05:12:53 -07:00
Kelsi
de983c2728 fix(whm): replace NaN/inf chunk base + per-vertex heights with 0.0 on load
A WHM with non-finite height values would produce non-finite vertex
positions in the terrain mesh, breaking collision queries, pathing,
and the GPU's matrix math. Both the chunk base (one float per chunk)
and the 145 per-vertex heights are now individually validated.
2026-05-06 05:10:04 -07:00
Kelsi
2b02ca6b58 fix(editor): patrol waypoint NaN guard + 10-minute wait-time cap
NaN positions in waypoints would teleport the creature to chaos coords
mid-patrol. Cap wait time at 600000ms (10 min) — prevents obvious typos
(e.g. 24h = 86400000) from producing a creature that effectively never
moves.
2026-05-06 05:05:39 -07:00
Kelsi
604d29d375 fix(editor): NPC + object position/rotation NaN guards on JSON load
Mirrors the recent scale guards. NaN/inf positions or rotations make
the M2 renderer's matrix math produce chaos-shaped instances or crash
during normal computation. Now both fields are reset to zero on load
when any component is non-finite.
2026-05-06 05:04:25 -07:00
Kelsi
d525318e9c fix(editor): normalise NPC orientation to [0,360) and guard scale against NaN
Orientation values from edited JSON could be negative or wrap multiple
revolutions; now normalised once at load. Scale was already clamped
on small-positive but didn't reject NaN/inf — now rejects both with the
same defensive check object_placer just got.
2026-05-06 05:02:24 -07:00
Kelsi
1979d921a7 fix(editor): clamp PlacedObject scale to 1.0 when JSON value is invalid
Same defensive check the WoB doodad load just got — guards against
corrupted/partial-write JSON where scale ends up 0/NaN/inf. Without
this an invisible (scale=0) or crashed (NaN matrix) placement could
silently bork a loaded zone.
2026-05-06 04:59:28 -07:00
Kelsi
199f18cdac fix(wob): clamp doodad scale to 1.0 when loaded value is 0/NaN/inf
Without this guard, a corrupted or partially-written WoB with scale=0
would render the doodad at zero size (invisible) and a NaN/inf would
crash the renderer's matrix math. Now defaults to 1.0 for any non-
finite or near-zero value.
2026-05-06 04:57:09 -07:00
Kelsi
70e48640d8 feat(editor): surface quest-chain validation issues to user via toast
validateChains warnings were only going to the log file. Most users
don't tail the log while editing, so a broken chain (quest pointing at
a deleted nextQuestId, circular dependency) would only be discovered
when testing in-game. Now also shows a 5s toast with the issue count.
2026-05-06 04:54:51 -07:00