diff --git a/banned.html b/banned.html index 658f5b44..e1d0e2d5 100644 --- a/banned.html +++ b/banned.html @@ -137,8 +137,13 @@

This account is banned

reasonEl.textContent = `Reason: ${reason}`; } - if (appeal && /^https:\/\//i.test(appeal)) { - linkEl.href = appeal; + if (appeal) { + // Only allow passing a Discord invite code, not a full URL, to avoid open-redirects. + // Example: ?appeal=rKgF9s32EV + const inviteCode = appeal.replace(/^https?:\/\/(www\.)?discord\.gg\//i, '').trim(); + if (/^[A-Za-z0-9-]{2,64}$/.test(inviteCode)) { + linkEl.href = `https://discord.gg/${inviteCode}`; + } } })(); diff --git a/index.html b/index.html index 95f2eba5..1da0c349 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@ - + diff --git a/learn/ui/js/services/trialMode.js b/learn/ui/js/services/trialMode.js index 61d0e938..39196fba 100644 --- a/learn/ui/js/services/trialMode.js +++ b/learn/ui/js/services/trialMode.js @@ -126,7 +126,15 @@ export function clearTrialSession() { } function generateSessionId() { - return `trial_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`; + const cryptoObj = globalThis.crypto || globalThis.msCrypto; + if (!cryptoObj || typeof cryptoObj.getRandomValues !== 'function') { + throw new Error('Secure randomness unavailable (crypto.getRandomValues)'); + } + + const bytes = new Uint8Array(16); + cryptoObj.getRandomValues(bytes); + const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + return `trial_${Date.now()}_${hex}`; } export function getTrialStats() {