Updated client-side DOM manipulation functions to unescape char-codes before injecting them via innerText instead of raw-dogging it into innerHTML

This commit is contained in:
rainbow napkin 2025-04-12 07:21:36 -04:00
parent e46513cc1a
commit 4ed4b572f2
10 changed files with 25 additions and 26 deletions

View file

@ -216,7 +216,7 @@ class adminUserBanList{
userNode.title = "Nuked" userNode.title = "Nuked"
}else{ }else{
var userNode = document.createElement('p'); var userNode = document.createElement('p');
userNode.innerHTML = user.user; userNode.textContent = utils.unescapeEntities(user.user);
} }
return userNode; return userNode;
} }
@ -251,7 +251,7 @@ class adminUserBanList{
//Create a node //Create a node
const userNode = document.createElement('p'); const userNode = document.createElement('p');
//Fill it wit the ip //Fill it wit the ip
userNode.innerHTML = user; userNode.textContent = utils.unescapeEntities(user);
//Append it //Append it
deadUsers.appendChild(userNode); deadUsers.appendChild(userNode);
} }
@ -269,7 +269,7 @@ class adminUserBanList{
//Create a node //Create a node
const ipNode = document.createElement('p'); const ipNode = document.createElement('p');
//Fill it wit the ip //Fill it wit the ip
ipNode.innerHTML = ip; ipNode.textContent = utils.unescapeEntities(ip);
//Append it //Append it
ipList.appendChild(ipNode); ipList.appendChild(ipNode);
} }
@ -279,7 +279,7 @@ class adminUserBanList{
//Create a node //Create a node
const ipNode = document.createElement('p'); const ipNode = document.createElement('p');
//List it as a hashed ip with the hash as alt text //List it as a hashed ip with the hash as alt text
ipNode.innerHTML = '[Hashed IP]'; ipNode.textContent = utils.unescapeEntities('[Hashed IP]');
ipNode.title = hash; ipNode.title = hash;
//Append the node //Append the node
ipList.appendChild(ipNode); ipList.appendChild(ipNode);
@ -372,7 +372,7 @@ class adminTokeCommandList{
//Create toke command label //Create toke command label
const tokeLabel = document.createElement('p'); const tokeLabel = document.createElement('p');
tokeLabel.innerHTML = `!${toke}`; tokeLabel.textContent = utils.unescapeEntities(`!${toke}`);
tokeLabel.classList.add('toke-command-list'); tokeLabel.classList.add('toke-command-list');
//Create toke command delete icon //Create toke command delete icon
@ -481,7 +481,7 @@ class adminEmoteList{
//Set title class //Set title class
emoteTitle.classList.add('emote-list-title'); emoteTitle.classList.add('emote-list-title');
//Set emote title //Set emote title
emoteTitle.innerHTML = `[${emote.name}]`; emoteTitle.textContent = utils.unescapeEntities(`[${emote.name}]`);
//Create delete icon //Create delete icon
const deleteIcon = document.createElement('i'); const deleteIcon = document.createElement('i');

View file

@ -109,7 +109,7 @@ class chatBox{
//Create high-level label //Create high-level label
var highLevel = document.createElement('p'); var highLevel = document.createElement('p');
highLevel.classList.add("chat-panel-buffer","chat-entry-high-level","high-level"); highLevel.classList.add("chat-panel-buffer","chat-entry-high-level","high-level");
highLevel.innerHTML = `${data.highLevel}`; highLevel.textContent = utils.unescapeEntities(`${data.highLevel}`);
chatEntry.appendChild(highLevel); chatEntry.appendChild(highLevel);
//Create username label //Create username label
@ -125,8 +125,8 @@ class chatBox{
//Create color span //Create color span
var colorSpan = document.createElement('span'); var colorSpan = document.createElement('span');
colorSpan.classList.add("chat-entry-flair-span", flair); colorSpan.classList.add("chat-entry-flair-span", flair);
colorSpan.innerHTML = `${data.user}`; colorSpan.textContent = utils.unescapeEntities(`${data.user}`);
userLabel.innerHTML = `${colorSpan.outerHTML}: `; userLabel.textContent = utils.unescapeEntities(`${colorSpan.outerHTML}: `);
chatEntry.appendChild(userLabel); chatEntry.appendChild(userLabel);
@ -277,7 +277,7 @@ class chatBox{
var flairOption = document.createElement('option'); var flairOption = document.createElement('option');
//Set the name and innerHTML //Set the name and innerHTML
flairOption.value = flair.name; flairOption.value = flair.name;
flairOption.innerHTML = flair.displayName; flairOption.textContent = utils.unescapeEntities(flair.displayName);
//Append it to the select //Append it to the select
this.flairSelect.appendChild(flairOption); this.flairSelect.appendChild(flairOption);

View file

@ -73,9 +73,7 @@ class chatPostprocessor{
//Create an empty array to hold the body //Create an empty array to hold the body
this.messageArray = []; this.messageArray = [];
//First unescape char codes to keep from splitting on them //Escape any sanatized char codes as we use .textContent for double-safety, and to prevent splitting of char codes
//This also means all text should be added to element via textContent and *NOT* innerHTML
//I'd rather not do this, but pre-processing everything while preserving codes is a fucking nightmare
//Split string by word-boundries on words and non-word boundries around whitespace, with negative lookaheads to exclude file seperators so we don't split link placeholders //Split string by word-boundries on words and non-word boundries around whitespace, with negative lookaheads to exclude file seperators so we don't split link placeholders
//Also split by any invisble whitespace as a crutch to handle mushed links/emotes //Also split by any invisble whitespace as a crutch to handle mushed links/emotes
//If we can one day figure out how to split non-repeating special chars instead of special chars with whitespace, that would be perf, unfortunately my brain hasn't rotted enough to understand regex like that just yet. //If we can one day figure out how to split non-repeating special chars instead of special chars with whitespace, that would be perf, unfortunately my brain hasn't rotted enough to understand regex like that just yet.

View file

@ -220,7 +220,7 @@ class emotePanel extends panelObj{
//Set title class //Set title class
emoteTitle.classList.add('emote-list-title'); emoteTitle.classList.add('emote-list-title');
//Set emote title //Set emote title
emoteTitle.innerHTML = `[${emote.name}]`; emoteTitle.textContent = utils.unescapeEntities(`[${emote.name}]`);
//if we're rendering personal emotes //if we're rendering personal emotes
if(personal){ if(personal){

View file

@ -833,6 +833,7 @@ class queuePanel extends panelObj{
timetip.moveToMouse(event); timetip.moveToMouse(event);
//Inject timetip label //Inject timetip label
//Normally wouldn't do innerHTML but these values are calculated serverside and it saves us making a <br> element
timetip.tooltip.innerHTML = [ timetip.tooltip.innerHTML = [
`Start Time: ${utils.ux.timeStringFromDate(start, true)}`, `Start Time: ${utils.ux.timeStringFromDate(start, true)}`,
`End Time: ${utils.ux.timeStringFromDate(new Date(start.getTime() + (target.dataset['duration'] * 1000)), true)}` `End Time: ${utils.ux.timeStringFromDate(new Date(start.getTime() + (target.dataset['duration'] * 1000)), true)}`

View file

@ -100,7 +100,7 @@ class userList{
//Create high-level label //Create high-level label
var highLevel = document.createElement('p'); var highLevel = document.createElement('p');
highLevel.classList.add("user-list-high-level","high-level"); highLevel.classList.add("user-list-high-level","high-level");
highLevel.innerHTML = `${user.highLevel}`; highLevel.textContent = `${user.highLevel}`;
//Create nameplate //Create nameplate
var userEntry = document.createElement('p'); var userEntry = document.createElement('p');

View file

@ -114,7 +114,7 @@ class rankList{
//Create an option for the given rank //Create an option for the given rank
const rankOption = document.createElement('option'); const rankOption = document.createElement('option');
rankOption.value = rank; rankOption.value = rank;
rankOption.innerHTML = rank; rankOption.textContent = utils.unescapeEntities(rank);
rankOption.selected = user.rank == rank; rankOption.selected = user.rank == rank;
rankContent.appendChild(rankOption); rankContent.appendChild(rankOption);
}); });
@ -343,7 +343,7 @@ class tokeCommandList{
//Create toke command label //Create toke command label
const tokeLabel = document.createElement('p'); const tokeLabel = document.createElement('p');
tokeLabel.innerHTML = `!${toke}`; tokeLabel.textContent = utils.unescapeEntities(`!${toke}`);
tokeLabel.classList.add('toke-command-list'); tokeLabel.classList.add('toke-command-list');
//Create toke command delete icon //Create toke command delete icon
@ -453,7 +453,7 @@ class emoteList{
//Set title class //Set title class
emoteTitle.classList.add('emote-list-title'); emoteTitle.classList.add('emote-list-title');
//Set emote title //Set emote title
emoteTitle.innerHTML = `[${emote.name}]`; emoteTitle.textContent = utils.unescapeEntities(`[${emote.name}]`);
//Create delete icon //Create delete icon
const deleteIcon = document.createElement('i'); const deleteIcon = document.createElement('i');
@ -504,7 +504,7 @@ class deleteAccountPopup{
this.label = document.querySelector("#delete-channel-popup-content"); this.label = document.querySelector("#delete-channel-popup-content");
//Fill channel label //Fill channel label
this.label.innerHTML = this.label.innerHTML.replaceAll("[CHANNEL]", this.channel); this.label.textContent = this.label.textContent.replaceAll("[CHANNEL]", utils.unescapeEntities(this.channel));
this.setupInput(); this.setupInput();
} }

View file

@ -8,7 +8,7 @@ class banUserPopup{
asyncConstruction(){ asyncConstruction(){
this.title = document.querySelector(".popup-title"); this.title = document.querySelector(".popup-title");
//Setup title text real quick-like :P //Setup title text real quick-like :P
this.title.innerHTML = `Ban ${this.target}`; this.title.textContent = utils.unescapeEntities(`Ban ${this.target}`);
this.ipBan = document.querySelector("#ban-popup-ip"); this.ipBan = document.querySelector("#ban-popup-ip");
this.permBan = document.querySelector("#ban-popup-perm"); this.permBan = document.querySelector("#ban-popup-perm");
@ -28,10 +28,10 @@ class banUserPopup{
permaBanLabel(event){ permaBanLabel(event){
if(event.target.checked){ if(event.target.checked){
this.expirationPrefix.innerHTML = "Account Deletion In: " this.expirationPrefix.textContent = "Account Deletion In: "
this.expiration.value = 30; this.expiration.value = 30;
}else{ }else{
this.expirationPrefix.innerHTML = "Ban Expires In: " this.expirationPrefix.textContent = "Ban Expires In: "
this.expiration.value = 14; this.expiration.value = 14;
} }
} }
@ -63,7 +63,7 @@ class chanBanUserPopup{
asyncConstruction(){ asyncConstruction(){
this.title = document.querySelector(".popup-title"); this.title = document.querySelector(".popup-title");
//Setup title text real quick-like :P //Setup title text real quick-like :P
this.title.innerHTML = `Ban ${this.target}`; this.title.textContent = utils.unescapeEntities(`Ban ${this.target}`);
this.permBan = document.querySelector("#ban-popup-perm"); this.permBan = document.querySelector("#ban-popup-perm");
this.expiration = document.querySelector("#ban-popup-expiration"); this.expiration = document.querySelector("#ban-popup-expiration");

View file

@ -102,7 +102,7 @@ class profileUpdateTextPrompt extends profileUpdatePrompt{
const update = await utils.ajax.updateProfile(updateObj); const update = await utils.ajax.updateProfile(updateObj);
//Fill content from update, make sure to add line breaks for the bio //Fill content from update, make sure to add line breaks for the bio
this.contentNode.innerHTML = update[this.field].replaceAll('\n','<br>'); this.contentNode.textContent = utils.unescapeEntities(update[this.field].replaceAll('\n','<br>'));
} }
} }
} }

View file

@ -330,7 +330,7 @@ class canopyUXUtils{
//Create menu title //Create menu title
const menuTitle = document.createElement('h2'); const menuTitle = document.createElement('h2');
menuTitle.innerHTML = this.title; menuTitle.textContent = utils.unescapeEntities(this.title);
//Append the title to the tooltip //Append the title to the tooltip
this.tooltip.append(menuTitle); this.tooltip.append(menuTitle);
@ -338,7 +338,7 @@ class canopyUXUtils{
for(let choice of this.menuMap){ for(let choice of this.menuMap){
//Create button //Create button
const button = document.createElement('button'); const button = document.createElement('button');
button.innerHTML = choice[0]; button.textContent = utils.unescapeEntities(choice[0]);
//Add event listeners //Add event listeners
button.addEventListener('click', choice[1]); button.addEventListener('click', choice[1]);