Fix code review findings for Story 3.3: Preset Import & Export
Security & Quality Improvements:
- Fix XSS vulnerabilities in PresetImportDialog, PresetExportDialog, and templates
- Add resource leak protection in downloadExportFile() with try/finally
- Fix encapsulation violation by using public API instead of _presetsCache
- Add rollback mechanism for partial failures in replace mode
- Add preset name validation (length, characters, empty check)
- Add duplicate name detection within import files
- Add file size validation (5MB limit) and type validation
- Fix event listener leaks with proper cleanup in _onRender/_onClose
- Add constructor parameter validation for all dialogs
Acceptance Criteria Compliance:
- Fix AC-2: Export filename now uses world name (via parent.name)
- Fix AC-6: Error message matches spec exactly ('Import failed: invalid JSON format')
- Fix AC-8: Merge/Replace messages match spec format
Code Quality:
- Add shared HTML escaping utilities (src/utils/html.js)
- Consolidate duplicate localization strings (removed 28 duplicates from SCRYING_POOL)
- Use SCENE_PRESET_VERSION constant instead of hardcoded 1
- Handle null options in importPresets()
- Graceful handling of skipValidation with invalid data
Test Results: 679 passed, 3 failed (pre-existing in DirectorsBoard)
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* HTML escaping utilities for safe DOM manipulation.
|
||||
*
|
||||
* Import rule: may only import from src/contracts/.
|
||||
* Constructors are side-effect free.
|
||||
*
|
||||
* @module utils/html
|
||||
*/
|
||||
|
||||
/**
|
||||
* Escapes HTML special characters to prevent XSS injection.
|
||||
*
|
||||
* @param {string} str - String to escape.
|
||||
* @returns {string} HTML-escaped string safe for innerHTML.
|
||||
*/
|
||||
export function escapeHtml(str) {
|
||||
if (str === null || str === undefined) return '';
|
||||
return String(str).replace(/[&<>"']/g, (c) => ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
}[c]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely sets innerHTML with escaped content.
|
||||
*
|
||||
* @param {HTMLElement} element - Target DOM element.
|
||||
* @param {string} html - HTML content to set (will be escaped).
|
||||
* @returns {void}
|
||||
*/
|
||||
export function safeInnerHTML(element, html) {
|
||||
if (!element) return;
|
||||
element.innerHTML = escapeHtml(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text node with escaped content.
|
||||
*
|
||||
* @param {string} text - Text content.
|
||||
* @returns {Text} Text node.
|
||||
*/
|
||||
export function safeText(text) {
|
||||
return document.createTextNode(escapeHtml(text));
|
||||
}
|
||||
Reference in New Issue
Block a user