mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-05 17:07:43 +00:00
Refactor: Comprehensive Security and Architecture Audit
This commit is contained in:
+29
-6
@@ -11,6 +11,13 @@ let currentTabId = null;
|
||||
let currentTabTitle = null; // New: for Smart Matching
|
||||
let logs = [];
|
||||
let history = []; // New: for Action History
|
||||
|
||||
// Restore state from session storage
|
||||
chrome.storage.session.get(['logs', 'history'], (data) => {
|
||||
if (data.logs) logs = data.logs;
|
||||
if (data.history) history = data.history;
|
||||
});
|
||||
|
||||
let reconnectTimer = null;
|
||||
let reconnectStartTime = null; // New: track when reconnection started
|
||||
let reconnectFailed = false; // New: true if we hit the 5-min cap
|
||||
@@ -51,6 +58,7 @@ function addLog(message, type = 'info') {
|
||||
};
|
||||
logs.unshift(log);
|
||||
if (logs.length > 50) logs.pop();
|
||||
chrome.storage.session.set({ logs });
|
||||
chrome.runtime.sendMessage({ type: 'LOG_UPDATE', log }).catch(() => {});
|
||||
}
|
||||
|
||||
@@ -84,8 +92,9 @@ async function connect() {
|
||||
// Strict WSS Enforcement
|
||||
const urlObj = new URL(finalUrl);
|
||||
const isLocal = urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1';
|
||||
if (!isLocal && urlObj.protocol === 'ws:') {
|
||||
finalUrl = finalUrl.replace('ws://', 'wss://');
|
||||
if (urlObj.protocol !== 'wss:' && !isLocal) {
|
||||
urlObj.protocol = 'wss:';
|
||||
finalUrl = urlObj.toString();
|
||||
addLog('Security: Upgraded to wss:// for remote host.', 'warn');
|
||||
}
|
||||
}
|
||||
@@ -253,6 +262,7 @@ function addToHistory(action, senderId) {
|
||||
};
|
||||
history.unshift(historyEntry);
|
||||
if (history.length > 20) history.pop();
|
||||
chrome.storage.session.set({ history });
|
||||
chrome.runtime.sendMessage({ type: 'HISTORY_UPDATE', history }).catch(() => {});
|
||||
}
|
||||
|
||||
@@ -304,9 +314,9 @@ function handleServerEvent(event, data) {
|
||||
if (isForceSyncInitiator) {
|
||||
forceSyncAcks.add(data.senderId);
|
||||
addLog(`Received ACK from ${data.senderId} (${forceSyncAcks.size})`, 'info');
|
||||
// Check if all peers responded (minus ourselves)
|
||||
// Check if all peers responded
|
||||
const peerCount = currentRoom ? currentRoom.peers.length : 1;
|
||||
if (forceSyncAcks.size >= peerCount - 1) {
|
||||
if (forceSyncAcks.size >= peerCount) {
|
||||
executeForceSync();
|
||||
}
|
||||
}
|
||||
@@ -390,7 +400,7 @@ async function routeToContent(action, payload) {
|
||||
}
|
||||
|
||||
// --- Keep-Alive Mechanism ---
|
||||
chrome.alarms.create('keepAlive', { periodInMinutes: 0.25 }); // every 15s
|
||||
chrome.alarms.create('keepAlive', { periodInMinutes: 1 }); // every 1m
|
||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
||||
if (alarm.name === 'keepAlive') {
|
||||
// console.log('SW KeepAlive Heartbeat');
|
||||
@@ -448,6 +458,10 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
isForceSyncInitiator = true;
|
||||
forceSyncAcks.clear();
|
||||
addLog('Initiating Force Sync...', 'info');
|
||||
|
||||
// Route to our own content script so we pause and seek
|
||||
routeToContent(EVENTS.FORCE_SYNC_PREPARE, message.payload);
|
||||
|
||||
// Timeout if not everyone ACKs
|
||||
forceSyncTimeout = setTimeout(() => {
|
||||
if (isForceSyncInitiator) {
|
||||
@@ -459,7 +473,16 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
addToHistory(message.action, 'You');
|
||||
emit(message.action, { ...message.payload, peerId });
|
||||
} else if (message.type === 'FORCE_SYNC_ACK') {
|
||||
emit(EVENTS.FORCE_SYNC_ACK, { peerId });
|
||||
if (isForceSyncInitiator) {
|
||||
forceSyncAcks.add(peerId);
|
||||
addLog(`Local ACK received (${forceSyncAcks.size})`, 'info');
|
||||
const peerCount = currentRoom ? currentRoom.peers.length : 1;
|
||||
if (forceSyncAcks.size >= peerCount) {
|
||||
executeForceSync();
|
||||
}
|
||||
} else {
|
||||
emit(EVENTS.FORCE_SYNC_ACK, { peerId });
|
||||
}
|
||||
} else if (message.type === 'HEARTBEAT') {
|
||||
if (sender.tab) {
|
||||
currentTabId = sender.tab.id;
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
if (isYouTube) {
|
||||
const ytButton = document.querySelector('.ytp-play-button');
|
||||
if (ytButton) {
|
||||
const title = ytButton.getAttribute('aria-label') || '';
|
||||
const isCurrentlyPlaying = title.toLowerCase().includes('pause');
|
||||
const isCurrentlyPlaying = !video.paused;
|
||||
if ((action === 'play' && !isCurrentlyPlaying) || (action === 'pause' && isCurrentlyPlaying)) {
|
||||
ytButton.click();
|
||||
}
|
||||
@@ -42,9 +41,7 @@
|
||||
if (isTwitch) {
|
||||
const twitchButton = document.querySelector('[data-a-target="player-play-pause-button"]');
|
||||
if (twitchButton) {
|
||||
const label = twitchButton.getAttribute('aria-label')?.toLowerCase() || '';
|
||||
// Check for common localized labels (pause, stoppen, arrête)
|
||||
const isCurrentlyPlaying = label.includes('pause') || label.includes('stoppen') || label.includes('arrête');
|
||||
const isCurrentlyPlaying = !video.paused;
|
||||
if ((action === 'play' && !isCurrentlyPlaying) || (action === 'pause' && isCurrentlyPlaying)) {
|
||||
twitchButton.click();
|
||||
}
|
||||
@@ -145,16 +142,17 @@
|
||||
}
|
||||
|
||||
// SPA Navigation Handler (MutationObserver)
|
||||
let mutationTimeout = null;
|
||||
let lastMutate = 0;
|
||||
const observer = new MutationObserver(() => {
|
||||
if (mutationTimeout) clearTimeout(mutationTimeout);
|
||||
mutationTimeout = setTimeout(() => {
|
||||
const now = Date.now();
|
||||
if (now - lastMutate >= 1000) {
|
||||
lastMutate = now;
|
||||
const video = findVideo();
|
||||
if (video && !video.dataset.koalaAttached) {
|
||||
console.log('KoalaSync: New video detected via navigation.');
|
||||
setupListeners();
|
||||
}
|
||||
}, 1000); // 1s debounce
|
||||
}
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
|
||||
+28
-8
@@ -404,15 +404,35 @@ elements.forceSyncBtn.addEventListener('click', async () => {
|
||||
const settings = await chrome.storage.sync.get(['targetTabId']);
|
||||
if (!settings.targetTabId) return;
|
||||
|
||||
chrome.tabs.sendMessage(parseInt(settings.targetTabId), { action: 'get_current_time' }, (response) => {
|
||||
if (response && response.currentTime !== undefined) {
|
||||
const time = parseFloat(response.currentTime);
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'CONTENT_EVENT',
|
||||
action: EVENTS.FORCE_SYNC_PREPARE,
|
||||
payload: { targetTime: time }
|
||||
const tabId = parseInt(settings.targetTabId);
|
||||
|
||||
const sendForceSync = (time) => {
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'CONTENT_EVENT',
|
||||
action: EVENTS.FORCE_SYNC_PREPARE,
|
||||
payload: { targetTime: parseFloat(time) }
|
||||
});
|
||||
};
|
||||
|
||||
chrome.tabs.sendMessage(tabId, { action: 'get_current_time' }, (response) => {
|
||||
if (chrome.runtime.lastError || !response || response.currentTime === undefined) {
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId },
|
||||
files: ['content.js']
|
||||
}).then(() => {
|
||||
setTimeout(() => {
|
||||
chrome.tabs.sendMessage(tabId, { action: 'get_current_time' }, (retryResponse) => {
|
||||
if (retryResponse && retryResponse.currentTime !== undefined) {
|
||||
sendForceSync(retryResponse.currentTime);
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
}).catch(() => {
|
||||
showError('Could not connect to video tab.');
|
||||
});
|
||||
return;
|
||||
}
|
||||
sendForceSync(response.currentTime);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -450,7 +470,7 @@ async function refreshLogs() {
|
||||
if (logs) {
|
||||
elements.logList.innerHTML = logs.map(log => `
|
||||
<div class="log-entry log-${log.type}">
|
||||
[${log.timestamp.split('T')[1].split('.')[0]}] ${log.message}
|
||||
[${log.timestamp.split('T')[1].split('.')[0]}] ${escapeHtml(log.message)}
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ io.on('connection', (socket) => {
|
||||
log('CONN', `New connection: ${socket.id} from ${clientIp}`);
|
||||
|
||||
socket.on(EVENTS.JOIN_ROOM, async ({ roomId, password, peerId, protocolVersion }) => {
|
||||
if (typeof roomId !== 'string') return;
|
||||
try {
|
||||
// Protocol check
|
||||
if (protocolVersion !== '1.0.0') {
|
||||
|
||||
Reference in New Issue
Block a user