From 06f13d140445557db4591efb9099baeb229b03bc Mon Sep 17 00:00:00 2001 From: Zimeng Xiong Date: Sat, 22 Nov 2025 20:38:40 -0800 Subject: [PATCH] fix XSS and Root execution of NPM in docker --- SECURITY_FIXES_SUMMARY.md | 202 ++++++++++++++++++++ backend/Dockerfile | 18 +- backend/docker-entrypoint.sh | 24 ++- backend/src/index.ts | 145 ++++++++++++-- backend/src/security.ts | 301 ++++++++++++++++++++++++++++++ backend/src/securityTest.ts | 210 +++++++++++++++++++++ frontend/src/utils/importUtils.ts | 5 +- 7 files changed, 887 insertions(+), 18 deletions(-) create mode 100644 SECURITY_FIXES_SUMMARY.md create mode 100644 backend/src/security.ts create mode 100644 backend/src/securityTest.ts diff --git a/SECURITY_FIXES_SUMMARY.md b/SECURITY_FIXES_SUMMARY.md new file mode 100644 index 0000000..12aea6b --- /dev/null +++ b/SECURITY_FIXES_SUMMARY.md @@ -0,0 +1,202 @@ +# Security Fixes Implementation Summary + +## Overview + +This document summarizes the comprehensive security fixes implemented to address two critical security vulnerabilities identified in ExcaliDash: + +1. **Stored XSS Vector (High Severity)** - Data sanitization negligence +2. **Root Execution Privilege (Critical Severity)** - Container escape risk + +## Security Issues Fixed + +### Issue 1: Stored XSS Vector (High Severity) ✅ FIXED + +**Problem**: Backend used lazy `z.object({}).passthrough()` validation for elements and appState, allowing arbitrary JSON storage without sanitization. + +**Attack Vectors**: + +- Malicious `.excalidraw` files containing ` + + + + + Normal text content +`; +const sanitizedHtml = sanitizeHtml(maliciousHtml); +console.log("✅ Original:", maliciousHtml.substring(0, 100) + "..."); +console.log("✅ Sanitized:", sanitizedHtml.substring(0, 100) + "..."); +console.log("✅ Script tags removed:", !sanitizedHtml.includes(" + + + + + +`; +const sanitizedSvg = sanitizeSvg(maliciousSvg); +console.log("✅ Original:", maliciousSvg.substring(0, 100) + "..."); +console.log("✅ Sanitized:", sanitizedSvg.substring(0, 100) + "..."); +console.log("✅ SVG scripts removed:", !sanitizedSvg.includes("", + "vbscript:msgbox('XSS')", + "https://example.com", + "/relative/path", + "./current/path", + "../parent/path", + "mailto:test@example.com", +]; + +maliciousUrls.forEach((url) => { + const sanitized = sanitizeUrl(url); + const isSafe = sanitized !== ""; + console.log(`✅ "${url}" -> "${sanitized}" (${isSafe ? "SAFE" : "BLOCKED"})`); +}); +console.log(""); + +// Test 4: Text Sanitization with Length Limits +console.log("Test 4: Text Sanitization with Length Limits"); +const longText = "A".repeat(2000); +const sanitizedLongText = sanitizeText(longText, 500); +console.log( + `✅ Long text truncated: ${longText.length} -> ${sanitizedLongText.length} chars` +); + +const maliciousText = "Normal text"; +const sanitizedText = sanitizeText(maliciousText); +console.log(`✅ Text sanitized: "${maliciousText}" -> "${sanitizedText}"`); +console.log( + "✅ Malicious content removed:", + !sanitizedText.includes("Malicious text", + }, + { + id: "test2", + type: "rectangle", + x: 10, + y: 10, + width: 100, + height: 100, + angle: 0, + version: 1, + versionNonce: 1, + link: "javascript:alert('XSS')", + }, + ], + appState: { + viewBackgroundColor: "", + }, + files: null, + preview: '', +}; + +console.log("Testing malicious drawing validation..."); +const isValidDrawing = validateImportedDrawing(maliciousDrawing); +console.log(`✅ Malicious drawing rejected: ${!isValidDrawing}`); + +try { + const sanitizedDrawing = sanitizeDrawingData(maliciousDrawing); + console.log("✅ Sanitization successful"); + console.log(`✅ Text sanitized: ${sanitizedDrawing.elements[0].text}`); + console.log( + `✅ Link sanitized: ${sanitizedDrawing.elements[1].link || "null"}` + ); + console.log( + `✅ SVG sanitized: ${!sanitizedDrawing.preview?.includes("