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;