fix(inventory): preserve empty storage locations

Fixes bug where empty storage locations (with "None" as items) were
being removed during validation, preventing users from adding items
to newly created locations.

Problem:
- User creates location "Spatial Pouch" with no items (stores as "None")
- On next load, validateStoredInventory() is called
- Previous logic: if cleanedValue === "None", remove location
- Result: Location deleted before user can add items to it
- Console: "Location 'Spatial Pouch' had no valid items, removing"

Root Cause:
Previous granular validation (commit dc603b8) was too aggressive:
```javascript
if (cleanedValue !== 'None') {
    cleaned[sanitizedKey] = cleanedValue;
} else {
    // Remove location 
}
```

Solution:
"None" is a VALID state - it means location exists but is empty.
Always keep locations, only warn if items were actually corrupted.

```javascript
// Always keep the location (even if empty/"None")
cleaned[sanitizedKey] = cleanedValue;

// Warn only if we cleaned corrupted items (not just "None")
if (value !== cleanedValue && value.toLowerCase() !== 'none') {
    console.warn(`Cleaned corrupted items from "${sanitizedKey}"`);
}
```

Behavior Changes:

Before:
- Location with "None" → Removed 
- Location with "__proto__, Sword" → Removed (cleaned to "Sword") 

After:
- Location with "None" → Kept as "None" ✓
- Location with "__proto__, Sword" → Kept as "Sword" (warns about cleaning) ✓

Impact:
✓ Empty locations persist across loads
✓ Users can now add items to new locations
✓ Corrupted items still cleaned (just location kept)
✓ Better logging (warns when actual corruption cleaned)

Fixes: Cannot add items to newly created storage locations
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-20 08:10:41 +11:00
parent f09c42ec6e
commit 7da5413fdd
+13 -7
View File
@@ -102,6 +102,7 @@ export function sanitizeItemName(name) {
* Validates and cleans a stored inventory object.
* Ensures all keys are safe property names and all values are strings.
* Cleans items within each location (removes corrupted/dangerous items).
* Preserves empty locations (with "None") so users can add items later.
* Prevents prototype pollution attacks via object keys.
*
* @param {Object} stored - Raw stored inventory object
@@ -112,13 +113,16 @@ export function sanitizeItemName(name) {
* // → { "Home": "Sword, Shield" }
*
* validateStoredInventory({ "Home": "Sword, __proto__, Shield" })
* // → { "Home": "Sword, Shield" } (dangerous item removed)
* // → { "Home": "Sword, Shield" } (dangerous item removed, logged)
*
* validateStoredInventory({ "Home": "None" })
* // → { "Home": "None" } (empty location preserved)
*
* validateStoredInventory({ "__proto__": "malicious" })
* // → {} (dangerous key removed, logged)
*
* validateStoredInventory({ "BadLocation": "__proto__, constructor" })
* // → {} (location removed because all items were invalid, logged)
* // → { "BadLocation": "None" } (all items removed, location kept empty)
*
* validateStoredInventory(null)
* // → {} (invalid input, returns empty object)
@@ -155,11 +159,13 @@ export function validateStoredInventory(stored) {
// Clean items within this location (removes corrupted/dangerous items)
const cleanedValue = cleanItemString(value);
// Only add location if it has valid items remaining
if (cleanedValue && cleanedValue !== 'None' && cleanedValue.toLowerCase() !== 'none') {
cleaned[sanitizedKey] = cleanedValue;
} else {
console.warn(`[RPG Companion] Location "${sanitizedKey}" had no valid items after cleaning, removing location`);
// Always keep the location (even if empty/"None")
// "None" is a valid state - it means the location exists but has no items yet
cleaned[sanitizedKey] = cleanedValue;
// Warn if we had to clean corrupted items (but only if original wasn't just "None")
if (value !== cleanedValue && value.toLowerCase() !== 'none') {
console.warn(`[RPG Companion] Cleaned corrupted items from location "${sanitizedKey}": "${value}" → "${cleanedValue}"`);
}
}