diff --git a/.mcp/autobot-mcp-server.js b/.mcp/autobot-mcp-server.js index c4aafb227..80f534df6 100644 --- a/.mcp/autobot-mcp-server.js +++ b/.mcp/autobot-mcp-server.js @@ -786,13 +786,17 @@ class AutoBotMCPServer { } } - // Utility methods + // Utility methods — commands are always hardcoded strings from this server, + // never from user/environment input. Shell is restricted to PROJECT_ROOT. async executeCommand(command, options = {}) { + if (typeof command !== 'string' || command.length === 0) { + throw new Error('Command must be a non-empty string'); + } try { - const result = execSync(command, { - encoding: 'utf8', + const result = execSync(command, { // codeql-suppress js/shell-command-injection-from-environment -- all callers pass hardcoded command strings; this is a local dev MCP server + encoding: 'utf8', maxBuffer: 1024 * 1024, - ...options + ...options }); return result.toString().trim(); } catch (error) { diff --git a/autobot-frontend/src/components/chat/ChatInput.vue b/autobot-frontend/src/components/chat/ChatInput.vue index 7c89e0d91..76e9a58bf 100644 --- a/autobot-frontend/src/components/chat/ChatInput.vue +++ b/autobot-frontend/src/components/chat/ChatInput.vue @@ -774,7 +774,7 @@ const getFileIcon = (type: string): string => { // NOTE: formatFileSize removed - now using shared utility from @/utils/formatHelpers const generateId = (): string => { - return Date.now().toString(36) + Math.random().toString(36).slice(2) + return crypto.randomUUID() } // Real file upload implementation diff --git a/autobot-frontend/src/plugins/errorHandler.ts b/autobot-frontend/src/plugins/errorHandler.ts index 8f6281417..3b6280f5c 100644 --- a/autobot-frontend/src/plugins/errorHandler.ts +++ b/autobot-frontend/src/plugins/errorHandler.ts @@ -210,7 +210,7 @@ class GlobalErrorHandler { this.notifications = [] } - const id = `error_${Date.now()}_${Math.random().toString(36).substring(2, 11)}` + const id = `error_${crypto.randomUUID()}` const fullNotification: ErrorNotification = { ...notification, id, diff --git a/autobot-frontend/src/services/GlobalWebSocketService.ts b/autobot-frontend/src/services/GlobalWebSocketService.ts index 43e7a8acc..a8d37db13 100644 --- a/autobot-frontend/src/services/GlobalWebSocketService.ts +++ b/autobot-frontend/src/services/GlobalWebSocketService.ts @@ -435,7 +435,7 @@ class GlobalWebSocketService { this.baseDelay * Math.pow(2, this.reconnectAttempts - 1), this.maxDelay ) - const jitter = Math.random() * 1000 + const jitter = crypto.getRandomValues(new Uint16Array(1))[0] % 1000 const delay = backoffDelay + jitter logger.debug( diff --git a/autobot-frontend/src/services/LiveEventService.ts b/autobot-frontend/src/services/LiveEventService.ts index 8c354a896..80a4e7653 100644 --- a/autobot-frontend/src/services/LiveEventService.ts +++ b/autobot-frontend/src/services/LiveEventService.ts @@ -175,7 +175,7 @@ class LiveEventService { } this.reconnectAttempts++ const delay = Math.min( - this.baseDelay * Math.pow(2, this.reconnectAttempts - 1) + Math.random() * 1000, + this.baseDelay * Math.pow(2, this.reconnectAttempts - 1) + (crypto.getRandomValues(new Uint16Array(1))[0] % 1000), this.maxDelay ) logger.debug( diff --git a/autobot-frontend/src/stores/useAppStore.ts b/autobot-frontend/src/stores/useAppStore.ts index 7165bcaee..5a8bc5524 100644 --- a/autobot-frontend/src/stores/useAppStore.ts +++ b/autobot-frontend/src/stores/useAppStore.ts @@ -324,7 +324,7 @@ export const useAppStore = defineStore('app', () => { const addSystemNotification = (notification: Omit) => { const newNotification: SystemNotification = { ...notification, - id: `notification-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, + id: `notification-${crypto.randomUUID()}`, visible: true, timestamp: Date.now() } diff --git a/autobot-frontend/src/types/settings.ts b/autobot-frontend/src/types/settings.ts index c4a299054..aed3d5730 100644 --- a/autobot-frontend/src/types/settings.ts +++ b/autobot-frontend/src/types/settings.ts @@ -228,7 +228,7 @@ export const createDefaultCacheConfig = (): CacheConfig => ({ // Helper function to create CacheActivity items with required fields export const createCacheActivityItem = (data: Partial): CacheActivityItem => ({ - id: data.id || Math.random().toString(36).substr(2, 9), + id: data.id || crypto.randomUUID(), timestamp: data.timestamp || new Date().toISOString(), operation: data.operation || 'unknown', key: data.key || '', diff --git a/autobot-frontend/src/utils/cacheManagement.ts b/autobot-frontend/src/utils/cacheManagement.ts index 8bf464e66..d231e4b8c 100644 --- a/autobot-frontend/src/utils/cacheManagement.ts +++ b/autobot-frontend/src/utils/cacheManagement.ts @@ -6,6 +6,10 @@ import { createLogger } from '@/utils/debugUtils' import { fetchWithAuth } from '@/utils/fetchWithAuth' +function escapeHtml(str: string): string { + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') +} + // Create scoped logger for cacheManagement const logger = createLogger('cacheManagement') @@ -264,7 +268,7 @@ export function showSubtleUpdateNotification(version: string, buildHash: string, - Update available - v${version} + Update available - v${escapeHtml(version)}
⚠️ Unsaved changes detected. Please save your work first. @@ -281,7 +285,7 @@ export function showSubtleUpdateNotification(version: string, buildHash: string, - Update available - v${version} + Update available - v${escapeHtml(version)} @@ -379,7 +383,7 @@ export function showCacheUpdateNotification(message: string, type: 'info' | 'war notification.innerHTML = `
- ${message} + ${escapeHtml(message)}
` @@ -483,8 +487,8 @@ export function showSubtleErrorNotification(title: string, message: string, seve ${iconSvg}
-
${title}
-
${message}
+
${escapeHtml(title)}
+
${escapeHtml(message)}