Security hardening and codebase cleanup

Security fixes:
- Replace Caesar cipher with electron.safeStorage for account credentials
- Validate shell.openExternal URLs (allow only http/https protocols)
- Remove rejectUnauthorized:false from all HTTPS calls
- Add redirect domain validation to auto-updater
- Fix XSS in matchmaker popup (innerHTML → textContent/createTextNode)
- Add IPC config key whitelist to prevent arbitrary store access
- Credentials never sent to renderer; decrypted on-demand via IPC

Optimizations and cleanup:
- Simplify onBeforeRequest from double-registration to single handler
- Lazy-init matchmaker popup DOM (defer until first use)
- Invalidate game config cache immediately on write, not on flush
- Remove unused STANDARD_ASSET_RE and KeybindDef exports
- Deduplicate Keybind type (import from config.ts)
- Replace custom hasOwn wrapper with Object.hasOwn

Bug fix:
- Stop Krunker's global keydown handler from eating keystrokes in
  alt manager input fields (stopPropagation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 08:54:52 -08:00
parent 96e0cbfc07
commit 819caea65a
7 changed files with 218 additions and 108 deletions
+62 -35
View File
@@ -39,39 +39,57 @@ function secondsToTimestring(num: number): string {
return `${minutes}m ${seconds}s`;
}
// ── Popup DOM (created once, reused) ──
// ── Popup DOM (lazy-initialized on first use) ──
const POPUP_ID = 'matchmakerPopupContainer';
const popupElement = document.createElement('div');
popupElement.id = POPUP_ID;
const popupTitle = document.createElement('div');
popupTitle.id = 'matchmakerPopupTitle';
popupElement.appendChild(popupTitle);
interface PopupDOM {
element: HTMLDivElement;
title: HTMLDivElement;
description: HTMLDivElement;
confirmBtn: HTMLDivElement;
cancelBtn: HTMLDivElement;
}
const popupDescription = document.createElement('div');
popupDescription.id = 'matchmakerPopupDescription';
popupElement.appendChild(popupDescription);
let _popup: PopupDOM | null = null;
const popupOptions = document.createElement('div');
popupOptions.id = 'matchmakerPopupOptions';
function getPopup(): PopupDOM {
if (_popup) return _popup;
const popupConfirmBtn = document.createElement('div');
popupConfirmBtn.id = 'matchmakerConfirmButton';
popupConfirmBtn.className = 'matchmakerPopupButton bigShadowT';
popupConfirmBtn.textContent = 'Join';
popupConfirmBtn.setAttribute('onmouseenter', 'playTick()');
popupConfirmBtn.addEventListener('click', () => decideMatchmakerDecision(true));
const element = document.createElement('div');
element.id = POPUP_ID;
const popupCancelBtn = document.createElement('div');
popupCancelBtn.id = 'matchmakerCancelButton';
popupCancelBtn.className = 'matchmakerPopupButton bigShadowT';
popupCancelBtn.textContent = 'Cancel';
popupCancelBtn.setAttribute('onmouseenter', 'playTick()');
popupCancelBtn.addEventListener('click', () => decideMatchmakerDecision(false));
const title = document.createElement('div');
title.id = 'matchmakerPopupTitle';
element.appendChild(title);
popupOptions.appendChild(popupConfirmBtn);
popupOptions.appendChild(popupCancelBtn);
popupElement.appendChild(popupOptions);
const description = document.createElement('div');
description.id = 'matchmakerPopupDescription';
element.appendChild(description);
const options = document.createElement('div');
options.id = 'matchmakerPopupOptions';
const confirmBtn = document.createElement('div');
confirmBtn.id = 'matchmakerConfirmButton';
confirmBtn.className = 'matchmakerPopupButton bigShadowT';
confirmBtn.textContent = 'Join';
confirmBtn.addEventListener('mouseenter', () => { (window as any).playTick?.(); });
confirmBtn.addEventListener('click', () => decideMatchmakerDecision(true));
const cancelBtn = document.createElement('div');
cancelBtn.id = 'matchmakerCancelButton';
cancelBtn.className = 'matchmakerPopupButton bigShadowT';
cancelBtn.textContent = 'Cancel';
cancelBtn.addEventListener('mouseenter', () => { (window as any).playTick?.(); });
cancelBtn.addEventListener('click', () => decideMatchmakerDecision(false));
options.appendChild(confirmBtn);
options.appendChild(cancelBtn);
element.appendChild(options);
_popup = { element, title, description, confirmBtn, cancelBtn };
return _popup;
}
// ── State ──
let currentMatch = '';
@@ -86,7 +104,8 @@ function decideMatchmakerDecision(accept: boolean): void {
if (accept && currentMatch !== 'none') {
window.location.href = `https://krunker.io/?game=${currentMatch}`;
} else {
if (popupElement.parentNode) popupElement.remove();
const popup = getPopup();
if (popup.element.parentNode) popup.element.remove();
if (currentMatch === 'none' && openServerBrowser && typeof w.openServerWindow === 'function') {
w.openServerWindow(0);
}
@@ -112,24 +131,32 @@ function handleMatchmakerBind(event: KeyboardEvent): void {
}
function createFetchedGamePopup(game: MatchmakerGame): void {
const popup = getPopup();
const mapIdx = MAP_ICON_INDICES.indexOf(game.map);
popupElement.style.backgroundImage = `url(https://assets.krunker.io/img/maps/map_${mapIdx >= 0 ? mapIdx : 0}.png)`;
popup.element.style.backgroundImage = `url(https://assets.krunker.io/img/maps/map_${mapIdx >= 0 ? mapIdx : 0}.png)`;
currentMatch = game.gameID;
if (game.gameID === 'none') {
popupTitle.innerText = 'No Games Found...';
popupDescription.innerHTML = 'Check the server browser to see other lobbies.';
popupConfirmBtn.style.display = 'none';
popup.title.textContent = 'No Games Found...';
popup.description.textContent = 'Check the server browser to see other lobbies.';
popup.confirmBtn.style.display = 'none';
} else {
popupTitle.innerText = 'Game Found!';
popup.title.textContent = 'Game Found!';
const regionName = MATCHMAKER_REGION_NAMES[game.region] ?? 'Unknown Region';
popupDescription.innerHTML = `${game.gamemode} on ${game.map} (${regionName})<br/>${game.playerCount}/${game.playerLimit} Players, ${secondsToTimestring(game.remainingTime)} Left`;
popupConfirmBtn.style.display = 'block';
popup.description.textContent = '';
popup.description.appendChild(document.createTextNode(
`${game.gamemode} on ${game.map} (${regionName})`
));
popup.description.appendChild(document.createElement('br'));
popup.description.appendChild(document.createTextNode(
`${game.playerCount}/${game.playerLimit} Players, ${secondsToTimestring(game.remainingTime)} Left`
));
popup.confirmBtn.style.display = 'block';
}
document.addEventListener('keydown', handleMatchmakerBind, true);
const uiBase = document.getElementById('uiBase');
if (uiBase) uiBase.appendChild(popupElement);
if (uiBase) uiBase.appendChild(popup.element);
}
export async function fetchGame(mmConfig: MatchmakerConfig, _con?: SavedConsole): Promise<void> {