Sicherheit: CSP aktiviert, JavaScript ausgelagert, XSS-Schutz und README aktualisiert

This commit is contained in:
Markus Busche
2025-07-03 10:23:45 +02:00
parent 7dcff0dc85
commit 7cdad0cf2d
4 changed files with 309 additions and 291 deletions

View File

@@ -3,9 +3,11 @@
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none';">
<title>Just a QR Code</title>
<link rel="icon" href="data:image/svg+xml;utf8,&lt;svg xmlns=&#39;http://www.w3.org/2000/svg&#39; viewBox=&#39;0 0 32 32&#39;&gt;&lt;rect width=&#39;32&#39; height=&#39;32&#39; fill=&#39;%23ffffff&#39; rx=&#39;3&#39; ry=&#39;3&#39;/&gt;&lt;rect x=&#39;8&#39; y=&#39;8&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;12&#39; y=&#39;8&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;16&#39; y=&#39;8&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;8&#39; y=&#39;12&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;8&#39; y=&#39;16&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;20&#39; y=&#39;8&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;16&#39; y=&#39;12&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;20&#39; y=&#39;16&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;8&#39; y=&#39;20&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;12&#39; y=&#39;20&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;16&#39; y=&#39;20&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;20&#39; y=&#39;20&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;rect x=&#39;20&#39; y=&#39;12&#39; width=&#39;4&#39; height=&#39;4&#39; fill=&#39;%23000000&#39;/&gt;&lt;/svg&gt;">
<script src="./assets/qrious.min.js.Download"></script>
<script src="./assets/qrious.min.js"></script>
<script src="./main.js"></script>
<!--
Licensed under the Creative Commons Attribution-NonCommercial (BY-NC) 4.0 license.
Feel free to use this for anything noncommercial.
@@ -395,293 +397,4 @@
<p class="credit">Made with ❤️ and 🍪 by Markus. Self hosted on <a href="https://unraid.net" target="_blank">Unraid</a> for <a href="https://medisoftware.de" target="_blank">medisoftware</a>. Credits: <a href="https://qr.alster.space/" target="_blank">alsternerd</a></p>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const textInput = document.getElementById('text');
const clearTextBtn = document.getElementById('clear-text');
const wifiSsidInput = document.getElementById('wifi-ssid');
const wifiPasswordInput = document.getElementById('wifi-password');
const sizeSelect = document.getElementById('size');
const errorCorrectionSelect = document.getElementById('errorCorrection');
const foregroundColor = document.getElementById('foreground');
const backgroundColor = document.getElementById('background');
const downloadBtn = document.getElementById('download');
const qrcodeCanvas = document.getElementById('qrcode');
const qrcodeImg = document.getElementById('qrcode-img');
const errorMessage = document.getElementById('error-message');
const infoButton = document.getElementById('info-button');
const infoPanel = document.getElementById('info-panel');
const infoClose = document.getElementById('info-close');
const titleElement = document.getElementById('title');
// Title Easter egg
let titleClickCount = 0;
let titleTimeoutId = null;
titleElement.addEventListener('click', function() {
titleClickCount++;
clearTimeout(titleTimeoutId);
if (titleClickCount === 1) {
this.textContent = "Just a [bleep]ing QR Code";
titleTimeoutId = setTimeout(() => {
this.textContent = "Just a QR Code";
titleClickCount = 0;
}, 500);
}
});
// Info panel toggle
infoButton.addEventListener('click', function() {
infoPanel.classList.add('open');
});
infoClose.addEventListener('click', function() {
infoPanel.classList.remove('open');
});
// Close info panel when clicking outside
document.addEventListener('click', function(event) {
if (infoPanel.classList.contains('open') &&
!infoPanel.contains(event.target) &&
event.target !== infoButton) {
infoPanel.classList.remove('open');
}
});
// Close info panel with Escape key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && infoPanel.classList.contains('open')) {
infoPanel.classList.remove('open');
}
});
// Set random compliment as default text and select it
textInput.value = 'https://medisoftware.de';
textInput.select();
// Show/hide clear button based on text input content
function toggleClearButton() {
if (textInput.value.length > 0) {
clearTextBtn.style.display = 'block';
} else {
clearTextBtn.style.display = 'none';
}
}
// Initialize clear button visibility
toggleClearButton();
// Clear text when clear button is clicked
clearTextBtn.addEventListener('click', function() {
textInput.value = '';
wifiSsidInput.value = '';
wifiPasswordInput.value = '';
toggleClearButton();
textInput.focus();
generateQRCode();
});
// Update clear button visibility when text changes
textInput.addEventListener('input', function() {
toggleClearButton();
// Only generate QR code if no wifi data is being entered
const wifiSsid = wifiSsidInput.value.trim();
const wifiPassword = wifiPasswordInput.value.trim();
if (!wifiSsid && !wifiPassword) {
generateQRCode();
}
});
let qr = null;
// Generate QR code when settings change
sizeSelect.addEventListener('change', generateQRCode);
errorCorrectionSelect.addEventListener('change', generateQRCode);
foregroundColor.addEventListener('input', generateQRCode);
backgroundColor.addEventListener('input', generateQRCode);
// Generate QR code when wifi settings change
wifiSsidInput.addEventListener('input', function() {
updateTextDisplay();
generateQRCode();
});
wifiPasswordInput.addEventListener('input', function() {
updateTextDisplay();
generateQRCode();
});
// Download QR code as image
downloadBtn.addEventListener('click', downloadQRCode);
// Or check for URL parameters if present
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('text')) {
textInput.value = urlParams.get('text');
}
if (urlParams.has('ssid')) {
wifiSsidInput.value = urlParams.get('ssid');
}
if (urlParams.has('password')) {
wifiPasswordInput.value = urlParams.get('password');
}
if (urlParams.has('size')) {
const sizeValue = urlParams.get('size');
if (['128', '256', '512', '1024'].includes(sizeValue)) {
sizeSelect.value = sizeValue;
}
}
if (urlParams.has('errorCorrection')) {
const ecValue = urlParams.get('errorCorrection').toUpperCase();
if (['L', 'M', 'Q', 'H'].includes(ecValue)) {
errorCorrectionSelect.value = ecValue;
}
}
if (urlParams.has('foreground')) {
const fgValue = urlParams.get('foreground');
if (/^#[0-9A-F]{6}$/i.test(fgValue)) {
foregroundColor.value = fgValue;
}
}
if (urlParams.has('background')) {
const bgValue = urlParams.get('background');
if (/^#[0-9A-F]{6}$/i.test(bgValue)) {
backgroundColor.value = bgValue;
}
}
generateQRCode();
function generateQRCode() {
const text = textInput.value.trim();
const wifiSsid = wifiSsidInput.value.trim();
const wifiPassword = wifiPasswordInput.value.trim();
const size = parseInt(sizeSelect.value);
const errorCorrection = errorCorrectionSelect.value.toLowerCase();
const fgColor = foregroundColor.value;
const bgColor = backgroundColor.value;
// Update the gabe link color to match foreground color
const gabeLink = document.querySelector('.credit a');
if (gabeLink) {
gabeLink.style.color = fgColor;
}
// Clear previous error
errorMessage.textContent = '';
downloadBtn.style.display = 'none';
// Determine what to encode
let qrText = text;
// If wifi credentials are provided, generate wifi QR code
if (wifiSsid && wifiPassword) {
// Wifi QR code format: WIFI:S:<SSID>;T:WPA;P:<password>;;
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
} else if (wifiSsid && !wifiPassword) {
// SSID only (open network)
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
} else if (!wifiSsid && wifiPassword) {
// Password without SSID is invalid
errorMessage.textContent = 'Bitte geben Sie eine SSID ein, wenn Sie ein Passwort angeben';
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
// Validate input
if (qrText === '') {
errorMessage.textContent = 'Bitte geben Sie Text/URL oder Wifi-Daten ein';
// Hide both canvas and image
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
return;
}
// Create new QR code on canvas
try {
if (!qr) {
qr = new QRious({
element: qrcodeCanvas,
size: size,
value: qrText,
foreground: fgColor,
background: bgColor,
level: errorCorrection
});
} else {
qr.set({
size: size,
value: qrText,
foreground: fgColor,
background: bgColor,
level: errorCorrection
});
}
// Convert canvas to image
const dataURL = qrcodeCanvas.toDataURL('image/png');
qrcodeImg.src = dataURL;
// Show image (hide canvas) and download button
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'block';
downloadBtn.style.display = 'inline-block';
} catch (err) {
errorMessage.textContent = 'Error generating QR code: ' + err.message;
qrcodeCanvas.style.display = 'none';
qrcodeImg.style.display = 'none';
}
}
// Helper function to escape special characters in wifi strings
function escapeSpecialChars(str) {
return str.replace(/[\\;:,]/g, '\\$&');
}
function downloadQRCode() {
// Use the image source for download
const link = document.createElement('a');
link.download = 'qrcode.png';
link.href = qrcodeImg.src;
// Simulate click to trigger download
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Show success feedback
const originalText = downloadBtn.textContent;
downloadBtn.textContent = 'Downloaded!';
// Reset button after a short delay
setTimeout(() => {
downloadBtn.textContent = originalText;
}, 2000);
}
// Function to update the text display with current QR code content
function updateTextDisplay() {
const wifiSsid = wifiSsidInput.value.trim();
const wifiPassword = wifiPasswordInput.value.trim();
if (wifiSsid && wifiPassword) {
// Show wifi QR code format
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
} else if (wifiSsid && !wifiPassword) {
// Show open network format
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
} else {
// If no wifi data, keep original text input value
// Don't change it here to avoid overwriting user input
}
// Update clear button visibility
toggleClearButton();
}
});
</script>
</body></html>