From a4e8a472f995ffd8c65ce4515ae27940bc17657d Mon Sep 17 00:00:00 2001 From: dyzulk <66510723+dyzulk@users.noreply.github.com> Date: Mon, 12 Jan 2026 09:10:11 +0700 Subject: [PATCH] Security: Implement strict QR validation (URLs only, whitelisted domains, and username check) --- js/qr-scanner.js | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/js/qr-scanner.js b/js/qr-scanner.js index c633609..31cd8a7 100644 --- a/js/qr-scanner.js +++ b/js/qr-scanner.js @@ -24,9 +24,10 @@ function safeResume() { function handleDecodedText(decodedText) { console.log(`Scan result: ${decodedText}`); - let username = decodedText; + let username = ""; let password = ""; let isUnauthorized = false; + let blockReason = ""; // Track why it was blocked scannedUrl = ""; // Check if result is a URL @@ -36,35 +37,46 @@ function handleDecodedText(decodedText) { const hostname = url.hostname; const currentHostname = window.location.hostname; - // SECURITY CHECK: + // SECURITY CHECK 1: Domain Whitelist const isAllowed = (hostname === currentHostname) || (brandConfig.allowedDomains && brandConfig.allowedDomains.some(domain => hostname === domain || hostname.endsWith('.' + domain) )); if (isAllowed) { - scannedUrl = decodedText; // Store for redirection + // SECURITY CHECK 2: MikroTik Standards (Must have at least username) const searchParams = url.search || (decodedText.includes('?') ? '?' + decodedText.split('?')[1] : ''); const params = new URLSearchParams(searchParams); if (params.has('username')) { + scannedUrl = decodedText; // Approved for redirection username = params.get('username'); - } - if (params.has('password')) { - password = params.get('password'); + if (params.has('password')) { + password = params.get('password'); + } + } else { + isUnauthorized = true; + blockReason = "Invalid MikroTik Login URL"; } } else { // ILLEGAL DOMAIN: Strict blocking console.warn(`Blocked unauthorized domain: ${hostname}`); isUnauthorized = true; - username = hostname; // Show the blocked domain name + blockReason = `Unauthorized Domain: ${hostname}`; } + } else { + // NOT A URL: Block plain text / WiFi SSIDs per requirement + isUnauthorized = true; + blockReason = "Invalid Content (Only URL allowed)"; + console.warn("Blocked non-URL content:", decodedText); } } catch (e) { console.error("Error parsing QR URL:", e); + isUnauthorized = true; + blockReason = "QR Parse Error"; } // Fill inputs (only if authorized) - if (!isUnauthorized) { + if (!isUnauthorized && username) { const voucherInput = document.getElementById('voucher-input'); const passField = document.getElementById('voucher-pass'); if (voucherInput) voucherInput.value = username; @@ -78,7 +90,7 @@ function handleDecodedText(decodedText) { if (overlay && confirmUser) { if (isUnauthorized) { - confirmUser.innerHTML = `Blocked: ${username}`; + confirmUser.innerHTML = `Blocked: ${blockReason}`; if (connectBtn) connectBtn.style.display = 'none'; } else { confirmUser.innerText = username;