Fix security vulnerabilities and logic flaws

This commit is contained in:
Timo
2026-04-22 10:04:46 +02:00
parent bf48d6a75d
commit d750d96db5
4 changed files with 82 additions and 27 deletions
+31 -6
View File
@@ -11,11 +11,28 @@ let currentTabId = null;
let currentTabTitle = null; // New: for Smart Matching let currentTabTitle = null; // New: for Smart Matching
let logs = []; let logs = [];
let history = []; // New: for Action History let history = []; // New: for Action History
let storageInitialized = false;
let pendingLogs = [];
let pendingHistory = [];
// Restore state from session storage // Restore state from session storage
chrome.storage.session.get(['logs', 'history'], (data) => { chrome.storage.session.get(['logs', 'history'], (data) => {
if (data.logs) logs = data.logs; if (data.logs) logs = data.logs;
if (data.history) history = data.history; if (data.history) history = data.history;
storageInitialized = true;
if (pendingLogs.length > 0) {
logs.unshift(...pendingLogs);
if (logs.length > 50) logs = logs.slice(0, 50);
chrome.storage.session.set({ logs });
pendingLogs = [];
}
if (pendingHistory.length > 0) {
history.unshift(...pendingHistory);
if (history.length > 20) history = history.slice(0, 20);
chrome.storage.session.set({ history });
pendingHistory = [];
}
}); });
let reconnectTimer = null; let reconnectTimer = null;
@@ -56,9 +73,13 @@ function addLog(message, type = 'info') {
message, message,
type type
}; };
logs.unshift(log); if (!storageInitialized) {
if (logs.length > 50) logs.pop(); pendingLogs.unshift(log);
chrome.storage.session.set({ logs }); } else {
logs.unshift(log);
if (logs.length > 50) logs.pop();
chrome.storage.session.set({ logs });
}
chrome.runtime.sendMessage({ type: 'LOG_UPDATE', log }).catch(() => {}); chrome.runtime.sendMessage({ type: 'LOG_UPDATE', log }).catch(() => {});
} }
@@ -260,9 +281,13 @@ function addToHistory(action, senderId) {
senderId: senderId || 'You', senderId: senderId || 'You',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}; };
history.unshift(historyEntry); if (!storageInitialized) {
if (history.length > 20) history.pop(); pendingHistory.unshift(historyEntry);
chrome.storage.session.set({ history }); } else {
history.unshift(historyEntry);
if (history.length > 20) history.pop();
chrome.storage.session.set({ history });
}
chrome.runtime.sendMessage({ type: 'HISTORY_UPDATE', history }).catch(() => {}); chrome.runtime.sendMessage({ type: 'HISTORY_UPDATE', history }).catch(() => {});
} }
+27 -15
View File
@@ -7,7 +7,7 @@
if (window.koalaSyncInjected) return; if (window.koalaSyncInjected) return;
window.koalaSyncInjected = true; window.koalaSyncInjected = true;
let isProcessingCommand = false; let processingCommandUntil = 0;
// --- Helper: find the best video element on the page --- // --- Helper: find the best video element on the page ---
function findVideo() { function findVideo() {
@@ -20,7 +20,7 @@
const video = findVideo(); const video = findVideo();
if (!video) return; if (!video) return;
isProcessingCommand = true; processingCommandUntil = Date.now() + 1000;
try { try {
const host = window.location.hostname.toLowerCase(); const host = window.location.hostname.toLowerCase();
const isYouTube = host.includes('youtube.com'); const isYouTube = host.includes('youtube.com');
@@ -60,9 +60,6 @@
} }
} catch (e) { } catch (e) {
console.error('KoalaSync Media Action Error:', e); console.error('KoalaSync Media Action Error:', e);
} finally {
// Guarantee reset even on early returns in YouTube/Twitch blocks
setTimeout(() => { isProcessingCommand = false; }, 1000);
} }
} }
@@ -91,6 +88,12 @@
// Listen for commands from background.js // Listen for commands from background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'get_current_time') {
const video = findVideo();
sendResponse({ currentTime: video ? video.currentTime : undefined });
return true;
}
if (message.type === 'SERVER_COMMAND') { if (message.type === 'SERVER_COMMAND') {
const { action, payload } = message; const { action, payload } = message;
@@ -101,17 +104,17 @@
} else if (action === 'seek') { } else if (action === 'seek') {
tryMediaAction('seek', payload); tryMediaAction('seek', payload);
} else if (action === 'force_sync_prepare') { } else if (action === 'force_sync_prepare') {
isProcessingCommand = true; processingCommandUntil = Date.now() + 10000;
const video = findVideo(); const video = findVideo();
if (video) { if (video) {
video.pause(); video.pause();
video.currentTime = payload.targetTime; video.currentTime = payload.targetTime;
pollSeekReady(payload.targetTime).then(() => { pollSeekReady(payload.targetTime).then(() => {
chrome.runtime.sendMessage({ type: 'FORCE_SYNC_ACK' }); chrome.runtime.sendMessage({ type: 'FORCE_SYNC_ACK' });
setTimeout(() => { isProcessingCommand = false; }, 1000); processingCommandUntil = Date.now() + 1000;
}); });
} else { } else {
isProcessingCommand = false; processingCommandUntil = 0;
} }
} else if (action === 'force_sync_execute') { } else if (action === 'force_sync_execute') {
tryMediaAction('play'); tryMediaAction('play');
@@ -121,7 +124,7 @@
// Detect native events // Detect native events
function reportEvent(action) { function reportEvent(action) {
if (isProcessingCommand) return; if (Date.now() < processingCommandUntil) return;
const video = findVideo(); const video = findVideo();
if (!video) return; if (!video) return;
@@ -147,15 +150,24 @@
// SPA Navigation Handler (MutationObserver) // SPA Navigation Handler (MutationObserver)
let lastMutate = 0; let lastMutate = 0;
let observerTimeout = null;
function checkVideo() {
lastMutate = Date.now();
const video = findVideo();
if (video && !video.dataset.koalaAttached) {
console.log('KoalaSync: New video detected via navigation.');
setupListeners();
}
}
const observer = new MutationObserver(() => { const observer = new MutationObserver(() => {
const now = Date.now(); const now = Date.now();
if (now - lastMutate >= 1000) { if (now - lastMutate >= 1000) {
lastMutate = now; checkVideo();
const video = findVideo(); } else {
if (video && !video.dataset.koalaAttached) { if (observerTimeout) clearTimeout(observerTimeout);
console.log('KoalaSync: New video detected via navigation.'); observerTimeout = setTimeout(checkVideo, 1000 - (now - lastMutate));
setupListeners();
}
} }
}); });
observer.observe(document.body, { childList: true, subtree: true }); observer.observe(document.body, { childList: true, subtree: true });
+9 -6
View File
@@ -337,12 +337,15 @@ function showError(msg) {
elements.password.style.borderColor = 'var(--error)'; elements.password.style.borderColor = 'var(--error)';
// Shake effect // Shake effect
document.getElementById('tab-room').animate([ const activeTab = document.querySelector('.tab-content.active');
{ transform: 'translateX(0)' }, if (activeTab) {
{ transform: 'translateX(-5px)' }, activeTab.animate([
{ transform: 'translateX(5px)' }, { transform: 'translateX(0)' },
{ transform: 'translateX(0)' } { transform: 'translateX(-5px)' },
], { duration: 200, iterations: 2 }); { transform: 'translateX(5px)' },
{ transform: 'translateX(0)' }
], { duration: 200, iterations: 2 });
}
setTimeout(() => { setTimeout(() => {
if (elements.roomError) elements.roomError.style.display = 'none'; if (elements.roomError) elements.roomError.style.display = 'none';
+15
View File
@@ -84,6 +84,16 @@ setInterval(() => {
const eventCounts = new Map(); // socketId -> { count, resetTime } const eventCounts = new Map(); // socketId -> { count, resetTime }
// Clean up connection counts to prevent memory leak
setInterval(() => {
const now = Date.now();
for (const [ip, entry] of connectionCounts.entries()) {
if (now > entry.resetTime) {
connectionCounts.delete(ip);
}
}
}, 60000);
function checkConnectionRate(ip) { function checkConnectionRate(ip) {
const now = Date.now(); const now = Date.now();
const entry = connectionCounts.get(ip) || { count: 0, resetTime: now + 60000 }; const entry = connectionCounts.get(ip) || { count: 0, resetTime: now + 60000 };
@@ -150,6 +160,9 @@ io.on('connection', (socket) => {
// Cleanup old room if re-joining // Cleanup old room if re-joining
const oldMapping = socketToRoom.get(socket.id); const oldMapping = socketToRoom.get(socket.id);
if (oldMapping && oldMapping.roomId === roomId) {
return; // Already in this room, ignore to prevent spam
}
if (oldMapping && oldMapping.roomId !== roomId) { if (oldMapping && oldMapping.roomId !== roomId) {
socket.leave(oldMapping.roomId); socket.leave(oldMapping.roomId);
const oldRoom = rooms.get(oldMapping.roomId); const oldRoom = rooms.get(oldMapping.roomId);
@@ -232,6 +245,8 @@ io.on('connection', (socket) => {
return; return;
} }
if (!data || typeof data !== 'object') return; // Prevent null/invalid payload crash
const mapping = socketToRoom.get(socket.id); const mapping = socketToRoom.get(socket.id);
if (mapping) { if (mapping) {
const room = rooms.get(mapping.roomId); const room = rooms.get(mapping.roomId);