From 7da5413fdda0ffd30f2677218567b54ba5419f01 Mon Sep 17 00:00:00 2001 From: Lucas 'Paperboy' Rose-Winters Date: Mon, 20 Oct 2025 08:10:41 +1100 Subject: [PATCH] fix(inventory): preserve empty storage locations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/utils/security.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/utils/security.js b/src/utils/security.js index a59712f..63ac2ab 100644 --- a/src/utils/security.js +++ b/src/utils/security.js @@ -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}"`); } }