feat(inventory): add security hardening for prototype pollution and DoS

Created comprehensive security layer to protect against malicious input
and resource exhaustion attacks.

New security.js module:
- sanitizeLocationName(): Blocks __proto__, constructor, toString, etc.
- sanitizeItemName(): Enforces max length (500 chars)
- validateStoredInventory(): Validates entire stored object structure
- MAX_ITEMS_PER_SECTION: Limit of 500 items per section

Protected attack vectors:
1. Prototype pollution via location names
   - Blocked: "__proto__", "constructor", "prototype", etc.
   - Alert shown to user if attempted

2. DoS via extremely long names
   - Location names: max 200 chars (truncated with warning)
   - Item names: max 500 chars (truncated with warning)

3. DoS via massive item lists
   - Max 500 items per section (truncated with warning)

Integration:
- itemParser.js: Uses sanitizeItemName() and enforces max items
- inventoryActions.js: Validates all user input before saving
  - Manual location creation: blocked dangerous names
  - Manual item addition: length limits enforced

Security best practices (2025):
- No regex DoS vulnerabilities (character-by-character parsing)
- Explicit hasOwnProperty checks to avoid inherited properties
- Console warnings for all security events (auditing)
- Graceful degradation (truncate, don't crash)
- Defense in depth (validation at multiple layers)

This protects against both malicious actors and accidental abuse.
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-20 07:16:54 +11:00
parent 3a84e24c0a
commit 681c2f0e47
3 changed files with 196 additions and 5 deletions
+19 -2
View File
@@ -8,6 +8,7 @@ import { saveSettings, saveChatData, updateMessageSwipeData } from '../../core/p
import { buildInventorySummary } from '../generation/promptBuilder.js';
import { renderInventory } from '../rendering/inventory.js';
import { parseItems, serializeItems } from '../../utils/itemParser.js';
import { sanitizeLocationName, sanitizeItemName } from '../../utils/security.js';
// Type imports
/** @typedef {import('../../types/inventory.js').InventoryV2} InventoryV2 */
@@ -139,9 +140,17 @@ export function saveAddItem(field, location) {
}
const input = $(inputId);
const itemName = input.val().trim();
const rawItemName = input.val().trim();
if (!rawItemName) {
hideAddItemForm(field, location);
return;
}
// Security: Validate and sanitize item name
const itemName = sanitizeItemName(rawItemName);
if (!itemName) {
alert('Invalid item name.');
hideAddItemForm(field, location);
return;
}
@@ -246,9 +255,17 @@ export function hideAddLocationForm() {
export function saveAddLocation() {
const inventory = extensionSettings.userStats.inventory;
const input = $('#rpg-new-location-name');
const locationName = input.val().trim();
const rawLocationName = input.val().trim();
if (!rawLocationName) {
hideAddLocationForm();
return;
}
// Security: Validate and sanitize location name
const locationName = sanitizeLocationName(rawLocationName);
if (!locationName) {
alert('Invalid location name. Avoid special names like "__proto__" or "constructor".');
hideAddLocationForm();
return;
}