From 3f139b34430faa1a4432924433b9d7a5ebdfd3d1 Mon Sep 17 00:00:00 2001 From: Luna B Date: Thu, 30 Jan 2025 17:28:42 +0100 Subject: [PATCH 1/4] fix broken links from using onclick instead of click see #32 --- assets/js/ts_fas_acih.js | 60 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/assets/js/ts_fas_acih.js b/assets/js/ts_fas_acih.js index 580ea3b..e8e585e 100644 --- a/assets/js/ts_fas_acih.js +++ b/assets/js/ts_fas_acih.js @@ -1703,7 +1703,7 @@ if (pathName === "/home" || pathName === "/home.html" || pathName === "/u" || pa text.innerHTML = sanitizeAndLinkify(noteContent.text) text.classList.add("noteText"); if (noteContent.replyingTo === undefined) { - text.addEventListener("onclick", () => window.location.href = `/note/${noteContent.id}`); + text.addEventListener("click", () => window.location.href = `/note/${noteContent.id}`); } text.querySelectorAll('a').forEach(link => { link.addEventListener('click', (event) => { @@ -1775,7 +1775,7 @@ if (pathName === "/home" || pathName === "/home.html" || pathName === "/u" || pa if (noteContent.quoting) { const container = document.createElement("div"); container.classList.add("quoteContainer"); - container.addEventListener("onclick", () => window.location.replace(`/note/${noteContent.quoting}`)); + container.addEventListener("click", () => window.location.replace(`/note/${noteContent.quoting}`)); noteDiv.appendChild(container); firebase.database().ref(`notes/${noteContent.quoting}`).once("value", (snapshot) => { @@ -1954,7 +1954,7 @@ if (pathName === "/home" || pathName === "/home.html" || pathName === "/u" || pa replyBtn.innerHTML = ` 0`; } if (pathName !== "/note" && !pathName.startsWith("/note/")) { - replyBtn.addEventListener("onclick", () => window.location.href = `/note/${noteContent.id}`); + replyBtn.addEventListener("click", () => window.location.href = `/note/${noteContent.id}`); } else { replyBtn.setAttribute("onclick", "replyToNote(this);"); } @@ -1964,14 +1964,14 @@ if (pathName === "/home" || pathName === "/home.html" || pathName === "/u" || pa const quoteRenote = document.createElement("p"); quoteRenote.classList.add("quoteRenoteBtn"); quoteRenote.innerHTML = ``; - quoteRenote.addEventListener("onclick", () => quoteRenote(`${noteContent.id}`)); + quoteRenote.addEventListener("click", () => quoteRenote(`${noteContent.id}`)); buttonRow.appendChild(quoteRenote); // Add favorite button const favorite = document.createElement("p"); favorite.classList.add("quoteRenoteBtn"); // eh. just reuse a class tbh favorite.innerHTML = ``; // apply the id to the favorites button or it will not change colors - favorite.addEventListener("onclick", () => favorite(`${noteContent.id}`)); + favorite.addEventListener("click", () => favorite(`${noteContent.id}`)); firebase.auth().onAuthStateChanged((user) => { if (user) { firebase.database().ref(`users/${user.uid}/favorites/${noteContent.id}`).once("value", (snapshot) => { @@ -2613,7 +2613,7 @@ if (pathName === "/u.html" || pathName === "/u" || pathName.startsWith("/u/")) { firebase.auth().onAuthStateChanged((user) => { if (user.uid === profileExists.user) { document.getElementById("followButton").textContent = "Edit Profile"; - document.getElementById("followButton").addEventListener("onclick", () => window.location.href = "/settings"); + document.getElementById("followButton").addEventListener("click", () => window.location.href = "/settings"); } else { const currentUserUid = user.uid; const profileUserUid = profileExists.user; @@ -2972,7 +2972,7 @@ if (pathName === "/note.html" || pathName === "/note" || pathName.startsWith("/u document.getElementById("melissa").style.display = "block"; document.getElementById("noteNotFound").style.display = "none"; - document.getElementById("quoteRenoteButton").addEventListener("onclick", () => quoteRenote(`${noteData.id}`)); + document.getElementById("quoteRenoteButton").addEventListener("click", () => quoteRenote(`${noteData.id}`)); if (noteData.quoting) { firebase.database().ref(`notes/${noteData.quoting}`).once("value", (snapshot) => { const quoteData = snapshot.val(); @@ -3003,7 +3003,7 @@ if (pathName === "/note.html" || pathName === "/note" || pathName.startsWith("/u document.getElementById("noteQuoteText").innerHTML = `@${quoteUser.username} is suspended, and notes by this user cannot be viewed.`; } - document.getElementById("quotingNote_note").addEventListener("onclick", () => window.location.href = `/note/${quoteData.id}`); + document.getElementById("quotingNote_note").addEventListener("click", () => window.location.href = `/note/${quoteData.id}`); }) } else { document.getElementById("noteQuotePfp").src = `/assets/imgs/defaultPfp.png`; @@ -3025,7 +3025,7 @@ if (pathName === "/note.html" || pathName === "/note" || pathName.startsWith("/u // check for favorites // also make the favorite button do smth - document.getElementById("favoriteButton").addEventListener("onclick", () => favoriteNoteView(`${noteData.id}`)); + document.getElementById("favoriteButton").addEventListener("click", () => favoriteNoteView(`${noteData.id}`)); firebase.auth().onAuthStateChanged((user) => { if (user) { @@ -3047,9 +3047,9 @@ if (pathName === "/note.html" || pathName === "/note" || pathName.startsWith("/u } document.getElementById(`melissa`).style.display = "block"; document.getElementById(`userImage-profile`).src = `https://firebasestorage.googleapis.com/v0/b/${firebaseConfig.storageBucket}/o/images%2Fpfp%2F${noteData.whoSentIt}%2F${profileData.pfp}?alt=media`; - document.getElementById(`userImage-profile`).addEventListener("onclick", () => window.location.href = `/u/${profileData.username}`); + document.getElementById(`userImage-profile`).addEventListener("click", () => window.location.href = `/u/${profileData.username}`); document.getElementById(`display-profile`).innerHTML = escapeAndEmoji(profileData.display); - document.getElementById(`display-profile`).addEventListener("onclick", () => window.location.href = `/u/${profileData.username}`); + document.getElementById(`display-profile`).addEventListener("click", () => window.location.href = `/u/${profileData.username}`); const verifiedBadge = document.createElement("span"); if (profileData.isVerified === true) { verifiedBadge.innerHTML = ``; @@ -3064,12 +3064,12 @@ if (pathName === "/note.html" || pathName === "/note" || pathName.startsWith("/u } document.getElementById("display-profile").appendChild(verifiedBadge); document.getElementById(`username-profile`).textContent = `@${profileData.username}`; - document.getElementById(`username-profile`).addEventListener("onclick", () => window.location.href = `/u/${profileData.username}`); + document.getElementById(`username-profile`).addEventListener("click", () => window.location.href = `/u/${profileData.username}`); if (profileData.pronouns === undefined || profileData.pronouns === null || profileData.pronouns === "") { document.getElementById(`pronouns-profile`).remove(); } else { document.getElementById(`pronouns-profile`).textContent = profileData.pronouns; - document.getElementById(`pronouns-profile`).addEventListener("onclick", () => window.location.href = `/u/${profileData.username}`); + document.getElementById(`pronouns-profile`).addEventListener("click", () => window.location.href = `/u/${profileData.username}`); } document.getElementById("noteContent").innerHTML = sanitizeAndLinkify(noteData.text); @@ -4967,10 +4967,10 @@ if (pathName === "/notifications" || pathName === "/notifications.html") { const user = snapshot.exists() ? snapshot.val() : fakeUserData; newNotificationDiv.innerHTML = ` @${user.username} followed you!`; - newNotificationDiv.addEventListener("onclick", () => window.location.href = `/u/${user.username}`); + newNotificationDiv.addEventListener("click", () => window.location.href = `/u/${user.username}`); }) } else if (notification.type === "Reply") { - newNotificationDiv.addEventListener("onclick", () => window.location.href = `/note/${notification.postId}`); + newNotificationDiv.addEventListener("click", () => window.location.href = `/note/${notification.postId}`); firebase.database().ref(`users/${notification.who}`).on("value", (snapshot) => { const user = snapshot.exists() ? snapshot.val() : fakeUserData; @@ -4978,7 +4978,7 @@ if (pathName === "/notifications" || pathName === "/notifications.html") { newNotificationDiv.innerHTML = ` @${user.username} replied to your note!`; }) } else if (notification.type === "Love") { - newNotificationDiv.addEventListener("onclick", () => window.location.href = `/note/${notification.postId}`); + newNotificationDiv.addEventListener("click", () => window.location.href = `/note/${notification.postId}`); firebase.database().ref(`users/${notification.who}`).on("value", (snapshot) => { const user = snapshot.exists() ? snapshot.val() : fakeUserData; @@ -4986,7 +4986,7 @@ if (pathName === "/notifications" || pathName === "/notifications.html") { newNotificationDiv.innerHTML = ` @${user.username} loved your note!`; }) } else if (notification.type === "Renote") { - newNotificationDiv.addEventListener("onclick", () => window.location.href = `/note/${notification.postId}`); + newNotificationDiv.addEventListener("click", () => window.location.href = `/note/${notification.postId}`); firebase.database().ref(`users/${notification.who}`).on("value", (snapshot) => { const user = snapshot.exists() ? snapshot.val() : fakeUserData; @@ -4994,7 +4994,7 @@ if (pathName === "/notifications" || pathName === "/notifications.html") { newNotificationDiv.innerHTML = ` @${user.username} renoted your note!`; }) } else if (notification.type === "Mention") { - newNotificationDiv.addEventListener("onclick", () => window.location.href = `/note/${notification.postId}`); + newNotificationDiv.addEventListener("click", () => window.location.href = `/note/${notification.postId}`); firebase.database().ref(`users/${notification.who}`).on("value", (snapshot) => { const user = snapshot.exists() ? snapshot.val() : fakeUserData; @@ -5678,7 +5678,7 @@ if (pathName === "/download") { document.getElementById("downloadButton").style.display = "none"; document.getElementById("systemRequirement").textContent = "Unavailable for Windows Phone."; } else if (/win/i.test(userAgent)) { - document.getElementById("downloadButton").addEventListener("onclick", () => window.location.href='/assets/releases/windows/ts_installer.msi'); + document.getElementById("downloadButton").addEventListener("click", () => window.location.href='/assets/releases/windows/ts_installer.msi'); document.getElementById("downloadButton").textContent = "Get TransSocial for Windows"; document.getElementById("systemRequirement").textContent = "Requires Windows 8 or later."; // macOS @@ -5691,7 +5691,7 @@ if (pathName === "/download") { document.getElementById("systemRequirement").textContent = "Unavailable for iOS at this time."; // Android } else if (/android/i.test(userAgent)) { - document.getElementById("downloadButton").addEventListener("onclick", () => window.location.href = '/assets/releases/android/app-release.apk'); + document.getElementById("downloadButton").addEventListener("click", () => window.location.href = '/assets/releases/android/app-release.apk'); document.getElementById("downloadButton").textContent = "Get TransSocial for Windows"; document.getElementById("systemRequirement").textContent = "Requires Android 6 or later."; // Linux @@ -6765,7 +6765,7 @@ if (pathName === "/userstudio" || pathName.startsWith("/userstudio/")) { themeDiv.appendChild(title); themeDiv.appendChild(desc); themeDiv.appendChild(creator); - themeDiv.addEventListener("onclick", () => window.location.replace(`/userstudio/${themeKey}`)); + themeDiv.addEventListener("click", () => window.location.replace(`/userstudio/${themeKey}`)); // append the theme to the entire page if (themeData.canAppearOnStore !== "false") { @@ -6806,7 +6806,7 @@ if (pathName === "/userstudio" || pathName.startsWith("/userstudio/")) { }); // allow the user to get the theme - document.getElementById("installTheme").addEventListener("onclick", () => installTheme(`${snapshot.key}`)); + document.getElementById("installTheme").addEventListener("click", () => installTheme(`${snapshot.key}`)); } }); } @@ -7168,7 +7168,7 @@ if (pathName === "/search") { // show them const container = document.createElement("div"); container.classList.add("note"); // yep - container.addEventListener("onclick", () => window.location.href = `/u/${userData.username}`); + container.addEventListener("click", () => window.location.href = `/u/${userData.username}`); const pfp = document.createElement("img"); pfp.classList.add("notePfp"); @@ -7222,7 +7222,7 @@ if (pathName === "/search") { // create note div const noteDiv = document.createElement("div"); noteDiv.classList.add("note"); - noteDiv.addEventListener("onclick", () => window.location.replace(`/note/${noteContent.id}`)); + noteDiv.addEventListener("click", () => window.location.replace(`/note/${noteContent.id}`)); // check nsfw/sensitive/political status if (noteContent.isNsfw === true) { @@ -7426,7 +7426,7 @@ if (pathName === "/search") { text.innerHTML = sanitizeAndLinkify(noteContent.text) text.classList.add("noteText"); if (noteContent.replyingTo === undefined) { - text.addEventListener("onclick", () => window.location.href = `/note/${noteContent.id}` ); + text.addEventListener("click", () => window.location.href = `/note/${noteContent.id}` ); } text.querySelectorAll('a').forEach(link => { link.addEventListener('click', (event) => { @@ -7498,7 +7498,7 @@ if (pathName === "/search") { if (noteContent.quoting) { const container = document.createElement("div"); container.classList.add("quoteContainer"); - container.addEventListener("onclick", () => window.location.replace(`/note/${noteContent.quoting}`)); + container.addEventListener("click", () => window.location.replace(`/note/${noteContent.quoting}`)); noteDiv.appendChild(container); firebase.database().ref(`notes/${noteContent.quoting}`).once("value", (snapshot) => { @@ -7673,7 +7673,7 @@ if (pathName === "/search") { replyBtn.innerHTML = ` 0`; } if (pathName !== "/note" && !pathName.startsWith("/note/")) { - replyBtn.addEventListener("onclick", () => window.location.href=`/note/${noteContent.id}`); + replyBtn.addEventListener("click", () => window.location.href=`/note/${noteContent.id}`); } else { replyBtn.setAttribute("onclick", "replyToNote(this);"); } @@ -7683,14 +7683,14 @@ if (pathName === "/search") { const quoteRenote = document.createElement("p"); quoteRenote.classList.add("quoteRenoteBtn"); quoteRenote.innerHTML = ``; - quoteRenote.addEventListener("onclick", () => quoteRenote(`${noteContent.id}`)); + quoteRenote.addEventListener("click", () => quoteRenote(`${noteContent.id}`)); buttonRow.appendChild(quoteRenote); // Add favorite button const favorite = document.createElement("p"); favorite.classList.add("quoteRenoteBtn"); // eh. just reuse a class tbh favorite.innerHTML = ``; // apply the id to the favorites button or it will not change colors - favorite.addEventListener("onclick", () => favorite(`${noteContent.id}`)); + favorite.addEventListener("click", () => favorite(`${noteContent.id}`)); firebase.auth().onAuthStateChanged((user) => { if (user) { firebase.database().ref(`users/${user.uid}/favorites/${noteContent.id}`).once("value", (snapshot) => { @@ -7982,7 +7982,7 @@ async function displayTracks(query) { const addToNote = document.createElement("button"); addToNote.textContent = `Add ${track.name} by ${track.artists[0].name}`; addToNote.className = "addToNote"; - addToNote.addEventListener("onclick", () => setNoteMusic(`${track.id}`));// + addToNote.addEventListener("click", () => setNoteMusic(`${track.id}`));// resultsContainer.appendChild(embed); resultsContainer.appendChild(addToNote); From 778a985406d58cdae7a52900d751010e3c1b2977 Mon Sep 17 00:00:00 2001 From: Luna B Date: Thu, 30 Jan 2025 18:01:43 +0100 Subject: [PATCH 2/4] rewrite objectToTable to not use html written by hand in format strings --- assets/js/ts_fas_acih.js | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/assets/js/ts_fas_acih.js b/assets/js/ts_fas_acih.js index e8e585e..b7edfac 100644 --- a/assets/js/ts_fas_acih.js +++ b/assets/js/ts_fas_acih.js @@ -7857,8 +7857,8 @@ function seeData_reauth() { if (user) { firebase.database().ref(`users/${user.uid}`).once("value", (snapshot) => { const data = snapshot.val(); - const tableHTML = objectToTable(data); - document.getElementById("dataDisplay").innerHTML = tableHTML; + const table = objectToTable(data); + document.getElementById("dataDisplay").appendChild(table); }); } }); @@ -7873,20 +7873,44 @@ function seeData_reauth() { } function objectToTable(data, level = 0) { - let html = ''; + let table = document.createElement("table"); for (const [key, value] of Object.entries(data)) { - html += ``; + let row = document.createElement("tr"); + + let cell = document.createElement("td"); + cell.style = "padding: 8px; border: 1px solid #ddd;"; + + let name = document.createElement("strong"); + name.textContent = key; + cell.appendChild(name); + + row.appendChild(cell); + + let data = document.createElement("td"); + data.style = "padding: 8px; border: 1px solid #ddd;"; if (typeof value === 'object' && value !== null) { - html += ``; + if (level <= 0) { + let button = document.createElement("button"); + button.addEventListener("click", () => toggleVisibility(button)); + button.textContent = "Show"; + data.appendChild(button); + } + + let div = document.createElement("div"); + div.style.display = level > 0 ? "block" : "none"; + div.appendChild(objectToTable(value, level + 1)); + data.appendChild(div); } else { - html += ``; + data.textContent = value; } + + row.appendChild(data); + table.appendChild(row); } - html += '
${key}:${level > 0 ? '' : ''}
${objectToTable(value, level + 1)}
${value}
'; - return html; + return table; } function toggleVisibility(button) { From 34a0fc54cee16455e39ae9fcbdc27396494c8d29 Mon Sep 17 00:00:00 2001 From: Luna B Date: Thu, 30 Jan 2025 20:19:21 +0100 Subject: [PATCH 3/4] remove more places where users could insert arbitrary html --- assets/js/ts_fas_acih.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/assets/js/ts_fas_acih.js b/assets/js/ts_fas_acih.js index b7edfac..77d8228 100644 --- a/assets/js/ts_fas_acih.js +++ b/assets/js/ts_fas_acih.js @@ -395,7 +395,7 @@ firebase.auth().onAuthStateChanged((user) => { unreadNotifications = snapshot.val(); if (unreadNotifications !== null && unreadNotifications !== 0) { document.getElementById("notificationsCount").classList.add("show"); - document.getElementById("notificationsCount").innerHTML = `${unreadNotifications}`; + document.getElementById("notificationsCount").textContent = `${unreadNotifications}`; } else { if (document.getElementById("notificationsCount")) { document.getElementById("notificationsCount").classList.remove("show"); @@ -474,8 +474,6 @@ if (pathName === "/auth/register" || pathName === "/auth/register.html") { } else { window.location.replace(`/auth/policies?return_to=${urlParam}`); } - } else { - // no need to do anything } }) } @@ -1103,7 +1101,7 @@ function profileCard() { .then(function (snapshot) { const username = snapshot.val(); - userUsername.innerHTML = `@${username}`; + userUsername.textContent = `@${username}`; }) // Display user pronouns @@ -1113,7 +1111,7 @@ function profileCard() { .then(function (snapshot) { const pronouns = snapshot.val(); - userPronouns.innerHTML = pronouns; + userPronouns.textContent = pronouns; if (pronouns === '') { userDisplay.style.marginTop = "8px"; @@ -3000,7 +2998,7 @@ if (pathName === "/note.html" || pathName === "/note" || pathName.startsWith("/u document.getElementById("noteQuoteDisplay").textContent = "User Unavailable"; document.getElementById("noteQuoteUsername").textContent = `User Unavailable`; document.getElementById("noteQuoteText").innerHTML = sanitizeAndLinkify(quoteData.text); - document.getElementById("noteQuoteText").innerHTML = `@${quoteUser.username} is suspended, and notes by this user cannot be viewed.`; + document.getElementById("noteQuoteText").textContent = `@${quoteUser.username} is suspended, and notes by this user cannot be viewed.`; } document.getElementById("quotingNote_note").addEventListener("click", () => window.location.href = `/note/${quoteData.id}`); From 9bd8fad87c6c86de2d4eea88ec72e63fba4e3d99 Mon Sep 17 00:00:00 2001 From: Luna B Date: Thu, 30 Jan 2025 20:29:01 +0100 Subject: [PATCH 4/4] update updates and bump version --- assets/js/ts_fas_acih.js | 4 ++-- updates.html | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/assets/js/ts_fas_acih.js b/assets/js/ts_fas_acih.js index 77d8228..82545c0 100644 --- a/assets/js/ts_fas_acih.js +++ b/assets/js/ts_fas_acih.js @@ -29,8 +29,8 @@ const pathName = pageURL.pathname; let isOnDesktopApp = null; // TransSocial Version -let transsocialVersion = "v2025.1.29"; -let transsocialUpdate = "v2025129-1"; +let transsocialVersion = "v2025.1.30"; +let transsocialUpdate = "v2025130-1"; let transsocialReleaseVersion = "pre-alpha"; const notices = document.getElementsByClassName("version-notice"); diff --git a/updates.html b/updates.html index ecfc5e8..f482d11 100644 --- a/updates.html +++ b/updates.html @@ -152,6 +152,13 @@

Trying to find older updates? You may be interested in the InDev tab, since TransSocial is no longer in InDev!

+
+

v2025.1.30_pre-alpha

+

Released: January 30, 2025

+
  • fix a lot more major security issues where users could easily insert arbitrary html
  • +
  • fix not being able to click on notes
  • +
    +

    v2025.1.29_pre-alpha

    Released: January 29, 2025