Fix: Service Worker persistence, Ping-Pong failsafe, URL parsing, and server rate limit DoS

This commit is contained in:
Timo
2026-04-22 10:31:46 +02:00
parent 88cb313fc0
commit 0b28274766
4 changed files with 48 additions and 10 deletions
+14 -1
View File
@@ -280,6 +280,10 @@ function emit(event, data) {
socket.send(msg); socket.send(msg);
} else { } else {
eventQueue.push({ event, data }); eventQueue.push({ event, data });
if (eventQueue.length > 50) {
eventQueue.shift();
addLog('Event queue cap reached, dropping oldest event', 'warn');
}
} }
} }
@@ -433,7 +437,16 @@ async function routeToContent(action, payload) {
} }
// --- Keep-Alive Mechanism --- // --- Keep-Alive Mechanism ---
chrome.alarms.clearAll(); chrome.alarms.create('keepAlive', { periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'keepAlive') {
chrome.storage.session.get('keepAlive', () => {});
if (!socket || socket.readyState !== WebSocket.OPEN) {
connect();
}
}
});
setInterval(() => { setInterval(() => {
// Calling a chrome API keeps the SW alive in MV3 (Chrome 110+) // Calling a chrome API keeps the SW alive in MV3 (Chrome 110+)
chrome.storage.session.get('keepAlive', () => {}); chrome.storage.session.get('keepAlive', () => {});
+22 -8
View File
@@ -8,6 +8,17 @@
window.koalaSyncInjected = true; window.koalaSyncInjected = true;
let lastTargetState = null; let lastTargetState = null;
let targetStateTimeout = null;
function setTargetState(state) {
lastTargetState = state;
if (targetStateTimeout) clearTimeout(targetStateTimeout);
if (state !== null) {
targetStateTimeout = setTimeout(() => {
lastTargetState = null;
}, 1500);
}
}
// --- Helper: find the best video element on the page --- // --- Helper: find the best video element on the page ---
function findVideo() { function findVideo() {
@@ -30,7 +41,7 @@
if (ytButton) { if (ytButton) {
const isCurrentlyPlaying = !video.paused; const isCurrentlyPlaying = !video.paused;
if ((action === 'play' && !isCurrentlyPlaying) || (action === 'pause' && isCurrentlyPlaying)) { if ((action === 'play' && !isCurrentlyPlaying) || (action === 'pause' && isCurrentlyPlaying)) {
lastTargetState = action === 'play' ? 'playing' : 'paused'; setTargetState(action === 'play' ? 'playing' : 'paused');
ytButton.click(); ytButton.click();
} }
if (action === 'seek') video.currentTime = data.targetTime; if (action === 'seek') video.currentTime = data.targetTime;
@@ -43,7 +54,7 @@
if (twitchButton) { if (twitchButton) {
const isCurrentlyPlaying = !video.paused; const isCurrentlyPlaying = !video.paused;
if ((action === 'play' && !isCurrentlyPlaying) || (action === 'pause' && isCurrentlyPlaying)) { if ((action === 'play' && !isCurrentlyPlaying) || (action === 'pause' && isCurrentlyPlaying)) {
lastTargetState = action === 'play' ? 'playing' : 'paused'; setTargetState(action === 'play' ? 'playing' : 'paused');
twitchButton.click(); twitchButton.click();
} }
if (action === 'seek') video.currentTime = data.targetTime; if (action === 'seek') video.currentTime = data.targetTime;
@@ -53,10 +64,13 @@
// Fallback for native HTML5 // Fallback for native HTML5
if (action === 'play') { if (action === 'play') {
lastTargetState = 'playing'; setTargetState('playing');
video.play().catch(() => {}); video.play().catch((e) => {
console.warn('KoalaSync playback prevented:', e);
setTargetState(null);
});
} else if (action === 'pause') { } else if (action === 'pause') {
lastTargetState = 'paused'; setTargetState('paused');
video.pause(); video.pause();
} else if (action === 'seek') { } else if (action === 'seek') {
video.currentTime = data.targetTime; video.currentTime = data.targetTime;
@@ -110,7 +124,7 @@
if (!payload || payload.targetTime === undefined) return; if (!payload || payload.targetTime === undefined) return;
const video = findVideo(); const video = findVideo();
if (video) { if (video) {
lastTargetState = 'paused'; setTargetState('paused');
video.pause(); video.pause();
video.currentTime = payload.targetTime; video.currentTime = payload.targetTime;
pollSeekReady(payload.targetTime).then(() => { pollSeekReady(payload.targetTime).then(() => {
@@ -131,11 +145,11 @@
const eventState = action === 'play' ? 'playing' : (action === 'pause' ? 'paused' : null); const eventState = action === 'play' ? 'playing' : (action === 'pause' ? 'paused' : null);
if (eventState && lastTargetState === eventState) { if (eventState && lastTargetState === eventState) {
lastTargetState = null; // Consume the match setTargetState(null); // Consume the match
return; // Ignore event caused by our programmatic action return; // Ignore event caused by our programmatic action
} }
if (action !== 'seek') { if (action !== 'seek') {
lastTargetState = null; // Reset on mismatch setTargetState(null); // Reset on mismatch
} }
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
+7 -1
View File
@@ -268,7 +268,13 @@ function checkInviteLink() {
let serverUrl = ''; let serverUrl = '';
// Smart Link: Parse Server Config if present // Smart Link: Parse Server Config if present
if (parts.length >= 3 && (parts[parts.length - 2] === '0' || parts[parts.length - 2] === '1')) { const last = parts[parts.length - 1];
const secondToLast = parts[parts.length - 2];
const decodedLast = decodeURIComponent(last || '');
const isCustom = secondToLast === '1' && (decodedLast.startsWith('ws://') || decodedLast.startsWith('wss://'));
const isOfficial = secondToLast === '0' && last === '';
if (parts.length >= 3 && (isCustom || isOfficial)) {
serverUrl = decodeURIComponent(parts.pop()); serverUrl = decodeURIComponent(parts.pop());
useCustomServer = parts.pop() === '1'; useCustomServer = parts.pop() === '1';
} }
+5
View File
@@ -148,6 +148,11 @@ io.on('connection', (socket) => {
log('CONN', `New connection: ${socket.id} from ${clientIp}`); log('CONN', `New connection: ${socket.id} from ${clientIp}`);
socket.on(EVENTS.JOIN_ROOM, async (payload) => { socket.on(EVENTS.JOIN_ROOM, async (payload) => {
if (!checkEventRate(socket.id)) {
log('SECURITY', `Event rate limit exceeded for socket (JOIN): ${socket.id}`);
socket.disconnect(true);
return;
}
if (!payload || typeof payload.roomId !== 'string') return; if (!payload || typeof payload.roomId !== 'string') return;
const { roomId, password, peerId, protocolVersion } = payload; const { roomId, password, peerId, protocolVersion } = payload;
try { try {