feat(dashboard): check uploaded character XML before storing it

The XML upload is the one place hand-written XML reaches the game, whose
load path trusts the XML because the server writes it itself. Instead of
making the game skip bad data at load, the upload is checked against
what the load path assumes and refused (400, with the problems) when the
game couldn't load it: required elements, attributes that must parse
(flags read with std::stoul/stoull), required item and mission fields,
known inventory types, mission states and character versions, items and
missions that exist in the CDClient, unique item IDs and slots, and the
acct attribute matching the owner.

Suspicious but loadable content is returned as warnings that need
confirm=true (409 otherwise): contraband (same matching as the world,
now shared in ContrabandRules.h), stacks above the stack size, coins,
level or u-score out of reach, a GM level above the account's.
Contraband marked flag-and-remove is removed only if the uploader asks;
once stored, findings are flagged (CONTRABAND) and audited. The XML
editor shows the findings and offers "Save anyway". Related: issue 1332.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 08:21:53 -05:00
parent 9957e7f363
commit 3e10eefef4
9 changed files with 743 additions and 59 deletions

View File

@@ -680,6 +680,14 @@ shows under the account's related data); that world saves nothing more for the c
connected to it is disconnected (the client's save failure message) so they load the newer data.
- **Edit** (GM 8+, `characters_edit`): coins, U-score, level, and adding or removing items.
- **Edit XML** (GM 8+, `characters_edit_xml`, needs `enable_char_xml_upload=1`): replace the whole character XML. The
upload is checked against everything the game assumes when it loads a character (required elements, numbers that
must parse, known inventory types and mission states, items and missions that exist, unique item IDs and slots, the
`acct` attribute matching the owner) and refused with the list of problems if the game couldn't load it. Suspicious
content (contraband, stacks above the item's stack size, coins, level or U-score out of reach, a GM level above the
account's) is shown as warnings and needs **Save anyway**; contraband marked *flag and remove* is taken out only if
you tick the box, and either way it is flagged (kind *Contraband*) and the warnings are audited
(`character_xml_warnings`).
- **History** (GM 3+, `characters_history`): earlier versions of the character. Each one shows what differs from
now, can be downloaded as XML, and (with `characters_edit`) restored. The `character_snapshots` task saves every
character that changed once a day; old snapshots are removed after `snapshot_days` (90), but each character always