Quoted:So a ServiceWorker script that has access to the host browser's LocalStorage API on the ARFCOM domain... ever think the site stores a JWT there? Can you paste the source code (sans any personal credentials, API keys access tokens, blah blah) somewhere? I'm (morbidly) curious as to what the code looks like  View Quote
Meh fuck it, here's the javascript:
background.js
const STORAGE_KEY = "blockedUsers"; const HIDE_MENU_ID = "ar15-user-hider-hide-user"; const UNBLOCK_MENU_ID = "ar15-user-hider-unblock-user"; const AR15_URL_PATTERNS = ["*://ar15.com/forums/*", "*://*.ar15.com/forums/*"]; const MAX_USERNAME_LENGTH = 80; const MAX_REASON_LENGTH = 500;
const contextUsersByTab = new Map(); let migrationPromise = null;
function normalizeUsername(username) { return String(username || "").trim().toLocaleLowerCase(); }
function cleanUsername(username) { return String(username || "") .replace(/\s+/g, " ") .replace(/^@/, "") .trim() .slice(0, MAX_USERNAME_LENGTH); }
function cleanReason(reason) { return String(reason || "").trim().slice(0, MAX_REASON_LENGTH); }
function isAr15ForumsUrl(url) { try { const parsed = new URL(url); const isAr15 = parsed.hostname === "ar15.com" || parsed.hostname.endsWith(".ar15.com"); return ["http:", "https:"].includes(parsed.protocol) && isAr15 && parsed.pathname.startsWith("/forums/"); } catch { return false; } }
function getContentSafeBlockedUsers(blockedUsers) { return blockedUsers .filter((user) => user?.normalized) .map((user) => ({ username: cleanUsername(user.username), normalized: normalizeUsername(user.normalized) })); }
function getSenderUrl(sender) { return sender.tab?.url || sender.url || ""; }
async function getBlockedUsers() { await migrateBlockedUsersToLocal(); const result = await chrome.storage.local.get({ [STORAGE_KEY]: [] }); return Array.isArray(result[STORAGE_KEY]) ? result[STORAGE_KEY] : []; }
async function setBlockedUsers(blockedUsers) { await migrateBlockedUsersToLocal(); await chrome.storage.local.set({ [STORAGE_KEY]: blockedUsers }); }
async function migrateBlockedUsersToLocal() { if (!migrationPromise) { migrationPromise = (async () => { const [localResult, syncResult] = await Promise.all([ chrome.storage.local.get({ [STORAGE_KEY]: [] }), chrome.storage.sync.get({ [STORAGE_KEY]: [] }) ]); const localUsers = Array.isArray(localResult[STORAGE_KEY]) ? localResult[STORAGE_KEY] : []; const syncUsers = Array.isArray(syncResult[STORAGE_KEY]) ? syncResult[STORAGE_KEY] : [];
if (syncUsers.length > 0 && localUsers.length === 0) { await chrome.storage.local.set({ [STORAGE_KEY]: syncUsers }); }
if (syncUsers.length > 0) { await chrome.storage.sync.remove(STORAGE_KEY); } })(); }
return migrationPromise; }
async function upsertBlockedUser(username, reason) { const usernameToSave = cleanUsername(username); const normalized = normalizeUsername(usernameToSave);
if (!usernameToSave || !normalized) { return null; }
const blockedUsers = await getBlockedUsers(); const existingIndex = blockedUsers.findIndex((user) => user.normalized === normalized); const existing = existingIndex >= 0 ? blockedUsers[existingIndex] : null; const record = { username: existing?.username || usernameToSave, normalized, reason: cleanReason(reason), addedAt: existing?.addedAt || Date.now(), updatedAt: Date.now() };
if (existingIndex >= 0) { blockedUsers[existingIndex] = record; } else { blockedUsers.push(record); }
blockedUsers.sort((a, b) => a.username.localeCompare(b.username)); await setBlockedUsers(blockedUsers); await notifyAr15Tabs(); return record; }
async function removeBlockedUser(normalized) { const cleanNormalized = normalizeUsername(normalized); const blockedUsers = await getBlockedUsers(); const nextBlockedUsers = blockedUsers.filter((user) => user.normalized !== cleanNormalized);
if (nextBlockedUsers.length !== blockedUsers.length) { await setBlockedUsers(nextBlockedUsers); await notifyAr15Tabs(); } }
async function notifyAr15Tabs() { const tabs = await chrome.tabs.query({ url: AR15_URL_PATTERNS }); await Promise.allSettled( tabs.map((tab) => { if (!tab.id) { return null; }
return chrome.tabs.sendMessage(tab.id, { type: "BLOCKED_USERS_CHANGED" }); }) ); }
function updateContextMenus(tabId, username, isBlocked) { const title = username ? `Hide posts by ${username}` : "Hide AR15.com user"; const unblockTitle = username ? `Unblock posts by ${username}` : "Unblock AR15.com user";
chrome.contextMenus.update(HIDE_MENU_ID, { title, visible: Boolean(username && !isBlocked) }, () => { chrome.runtime.lastError; });
chrome.contextMenus.update(UNBLOCK_MENU_ID, { title: unblockTitle, visible: Boolean(username && isBlocked) }, () => { chrome.runtime.lastError; });
if (username) { contextUsersByTab.set(tabId, username); } else { contextUsersByTab.delete(tabId); } }
function createContextMenus() { chrome.contextMenus.removeAll(() => { chrome.contextMenus.create({ id: HIDE_MENU_ID, title: "Hide AR15.com user", contexts: ["all"], documentUrlPatterns: AR15_URL_PATTERNS, visible: false });
chrome.contextMenus.create({ id: UNBLOCK_MENU_ID, title: "Unblock AR15.com user", contexts: ["all"], documentUrlPatterns: AR15_URL_PATTERNS, visible: false }); }); }
chrome.runtime.onInstalled.addListener(() => { createContextMenus(); });
chrome.runtime.onStartup.addListener(() => { createContextMenus(); });
chrome.contextMenus.onClicked.addListener((info, tab) => { if (!tab?.id || ![HIDE_MENU_ID, UNBLOCK_MENU_ID].includes(info.menuItemId)) { return; }
const username = contextUsersByTab.get(tab.id);
if (!username) { chrome.tabs.sendMessage(tab.id, { type: "SHOW_NOTICE", message: "Right-click directly on a username to hide that user." }); return; }
if (info.menuItemId === UNBLOCK_MENU_ID) { removeBlockedUser(username).then(() => { chrome.tabs.sendMessage(tab.id, { type: "SHOW_NOTICE", message: "User unblocked." }); }); return; }
upsertBlockedUser(username, "").then((record) => { chrome.tabs.sendMessage(tab.id, { type: "SHOW_NOTICE", message: record ? "User hidden. Edit notes from the extension popup." : "Could not identify the username." }); }); });
chrome.tabs.onRemoved.addListener((tabId) => { contextUsersByTab.delete(tabId); });
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (!message || typeof message.type !== "string") { return false; }
if (message.type === "CONTEXT_USER_CHANGED") { if (sender.tab?.id && isAr15ForumsUrl(getSenderUrl(sender))) { updateContextMenus(sender.tab.id, message.username, message.isBlocked); } return false; }
if (message.type === "GET_BLOCKED_USERS_FOR_CONTENT") { if (!sender.tab?.id || !isAr15ForumsUrl(getSenderUrl(sender))) { sendResponse({ ok: false, blockedUsers: [] }); return false; }
getBlockedUsers() .then((blockedUsers) => sendResponse({ ok: true, blockedUsers: getContentSafeBlockedUsers(blockedUsers) })) .catch((error) => sendResponse({ ok: false, error: error.message })); return true; }
if (message.type === "BLOCKED_USERS_CHANGED") { if (sender.tab?.id) { sendResponse({ ok: false }); return false; }
notifyAr15Tabs() .then(() => sendResponse({ ok: true })) .catch((error) => sendResponse({ ok: false, error: error.message })); return true; }
return false; });
chrome.storage.onChanged.addListener((changes, areaName) => { if (areaName === "local" && changes[STORAGE_KEY]) { notifyAr15Tabs(); } });
content.js
(() => { const PLACEHOLDER_CLASS = "ar15-user-hider-placeholder"; const HIDDEN_CLASS = "ar15-user-hider-hidden-post"; const REVEALED_ATTR = "data-ar15-user-hider-revealed"; const AUTHOR_ATTR = "data-ar15-user-hider-author";
const authorSelectors = [ "[itemprop='author'] [itemprop='name']", "[itemprop='author'] a[itemprop='url']", "[itemprop='author'] a[href*='/member/user.html']", "[data-author]", "[data-username]", ".author a", ".author", ".username", ".user-name", ".userName", ".memberName", ".poster-name", ".post-author", ".postAuthor", "a[href*='/member/']", "a[href*='/member/user.html']", "a[href*='/members/']", "a[href*='/profile/']", "a[href*='/profiles/']", "a[href*='?user=']" ];
const postSelectors = [ "[itemtype='https://schema.org/DiscussionForumPosting'] > div", "[data-post-id]", "[id^='post-']", "[id^='post_']", "[id^='post']", "article", ".post", ".forum-post", ".thread-post", ".message", ".reply", ".comment" ];
const authorSelectorList = authorSelectors.join(","); const postSelectorList = postSelectors.join(","); let blockedUsers = []; let blockedUsersByName = new Map(); let observer = null; let applyTimer = null;
function normalizeUsername(username) { return String(username || "").trim().toLocaleLowerCase(); }
function cleanUsername(username) { return String(username || "") .replace(/\s+/g, " ") .replace(/^@/, "") .trim(); }
function isLikelyUsername(username) { const clean = cleanUsername(username); return clean.length > 0 && clean.length user?.normalized) .map((user) => [user.normalized, user]) ); }
function getNodeUsername(node) { if (!node || node.nodeType !== Node.ELEMENT_NODE) { return ""; }
const explicitUsername = node.getAttribute("data-username") || node.getAttribute("data-author"); if (isLikelyUsername(explicitUsername)) { return cleanUsername(explicitUsername); }
const text = cleanUsername(node.textContent); return isLikelyUsername(text) ? text : ""; }
function hasClasses(element, classNames) { return classNames.every((className) => element.classList.contains(className)); }
function isAr15ModernPostContainer(element) { if (!element || element.nodeType !== Node.ELEMENT_NODE) { return false; }
return hasClasses(element, [ "tw-rounded-sm", "tw-p-2", "tw-text-neutral-700", "tw-flex", "tw-flex-col", "tw-mb-6" ]); }
function getPostFromAuthorElement(authorElement) { const authorBlock = authorElement?.closest?.("[itemprop='author']"); if (authorBlock?.parentElement && isAr15ModernPostContainer(authorBlock.parentElement)) { return authorBlock.parentElement; }
const schemaPost = authorElement?.closest?.("[itemtype='https://schema.org/DiscussionForumPosting']"); const directPost = schemaPost?.querySelector?.(":scope > div"); if (directPost && isAr15ModernPostContainer(directPost)) { return directPost; }
const closestPost = authorElement?.closest?.(postSelectorList); if (closestPost) { return closestPost; }
let current = authorElement?.parentElement; while (current && current !== document.body) { if (isAr15ModernPostContainer(current)) { return current; } current = current.parentElement; }
return null; }
function getAuthorElementFromPointTarget(target) { if (!target || target.nodeType !== Node.ELEMENT_NODE) { return null; }
const directMatch = target.closest(authorSelectorList); if (directMatch && getNodeUsername(directMatch)) { return directMatch; }
const nearbyLink = target.closest("a"); if (nearbyLink && getNodeUsername(nearbyLink)) { return nearbyLink; }
return null; }
function getPostAuthor(post) { if (!post || post.nodeType !== Node.ELEMENT_NODE) { return ""; }
const cachedAuthor = post.getAttribute(AUTHOR_ATTR); if (cachedAuthor) { return cachedAuthor; }
const explicitAuthor = post.getAttribute("data-author") || post.getAttribute("data-username"); if (isLikelyUsername(explicitAuthor)) { const author = cleanUsername(explicitAuthor); post.setAttribute(AUTHOR_ATTR, author); return author; }
const schemaAuthorNode = post.querySelector("[itemprop='author'] [itemprop='name']"); const authorNode = schemaAuthorNode || post.querySelector(authorSelectorList); const author = getNodeUsername(authorNode);
if (author) { post.setAttribute(AUTHOR_ATTR, author); }
return author; }
function getPostCandidates() { const candidates = new Set();
document.querySelectorAll("[itemprop='author']").forEach((authorBlock) => { const post = authorBlock.parentElement; if (isAr15ModernPostContainer(post) && getPostAuthor(post)) { candidates.add(post); } });
document.querySelectorAll(postSelectorList).forEach((post) => { if (getPostAuthor(post)) { candidates.add(post); } });
document.querySelectorAll(authorSelectorList).forEach((authorNode) => { const post = getPostFromAuthorElement(authorNode); if (post && getPostAuthor(post)) { candidates.add(post); } });
return [...candidates].filter((post) => document.body.contains(post)); }
function getPlaceholderForPost(post) { const previous = post.previousElementSibling; if (previous?.classList.contains(PLACEHOLDER_CLASS)) { return previous; }
return null; }
function revealPost(post) { const placeholder = getPlaceholderForPost(post); if (placeholder) { placeholder.remove(); }
post.classList.remove(HIDDEN_CLASS); post.removeAttribute("aria-hidden"); post.setAttribute(REVEALED_ATTR, "true"); }
function hidePostAgain(post) { post.removeAttribute(REVEALED_ATTR); applyBlockingToPost(post); }
function createButton(label, action) { const button = document.createElement("button"); button.type = "button"; button.textContent = label; button.addEventListener("click", action); return button; }
function createPlaceholder(post) { const placeholder = document.createElement("section"); placeholder.className = PLACEHOLDER_CLASS; placeholder.setAttribute("aria-label", "Post hidden by AR15 User Hider");
const title = document.createElement("div"); title.className = "ar15-user-hider-placeholder-title"; title.textContent = "Post hidden";
const details = document.createElement("div"); details.className = "ar15-user-hider-placeholder-details"; details.textContent = "Manage hidden users from the extension popup.";
const actions = document.createElement("div"); actions.className = "ar15-user-hider-placeholder-actions";
actions.append( createButton("Show post", () => { revealPost(post); addTemporaryRevealControls(post); }) );
placeholder.append(title, details, actions); return placeholder; }
function applyBlockingToPost(post) { const author = getPostAuthor(post); const userRecord = blockedUsersByName.get(normalizeUsername(author)); const existingPlaceholder = getPlaceholderForPost(post); const revealControls = post.querySelector(":scope > .ar15-user-hider-reveal-controls");
if (!userRecord) { if (existingPlaceholder) { existingPlaceholder.remove(); } if (revealControls) { revealControls.remove(); } post.classList.remove(HIDDEN_CLASS); post.removeAttribute("aria-hidden"); post.removeAttribute(REVEALED_ATTR); return; }
if (post.getAttribute(REVEALED_ATTR) === "true") { if (existingPlaceholder) { existingPlaceholder.remove(); } addTemporaryRevealControls(post); return; }
if (!existingPlaceholder) { post.before(createPlaceholder(post)); }
if (revealControls) { revealControls.remove(); } post.classList.add(HIDDEN_CLASS); post.setAttribute("aria-hidden", "true"); }
function addTemporaryRevealControls(post) { let controls = post.querySelector(":scope > .ar15-user-hider-reveal-controls");
if (!controls) { controls = document.createElement("div"); controls.className = "ar15-user-hider-reveal-controls";
const label = document.createElement("span"); label.textContent = "Temporarily showing hidden post.";
controls.append( label, createButton("Hide again", () => hidePostAgain(post)) );
post.prepend(controls); } }
function applyBlocking() { getPostCandidates().forEach(applyBlockingToPost); }
function scheduleApplyBlocking() { clearTimeout(applyTimer); applyTimer = setTimeout(() => { applyBlocking(); }, 150); }
function showNotice(message) { const notice = document.createElement("div"); notice.className = "ar15-user-hider-notice"; notice.textContent = message; document.documentElement.append(notice);
setTimeout(() => { notice.remove(); }, 3500); }
function handleContextMenu(event) { const authorElement = getAuthorElementFromPointTarget(event.target); const username = authorElement ? getNodeUsername(authorElement) : ""; const isBlocked = blockedUsersByName.has(normalizeUsername(username));
chrome.runtime.sendMessage({ type: "CONTEXT_USER_CHANGED", username, isBlocked }); }
function startObserver() { if (observer) { observer.disconnect(); }
observer = new MutationObserver((mutations) => { if ( mutations.some((mutation) => [...mutation.addedNodes].some((node) => node.nodeType === Node.ELEMENT_NODE) ) ) { scheduleApplyBlocking(); } });
observer.observe(document.body, { childList: true, subtree: true }); }
chrome.runtime.onMessage.addListener((message) => { if (!message || typeof message.type !== "string") { return false; }
if (message.type === "BLOCKED_USERS_CHANGED") { loadBlockedUsers().then(applyBlocking); return false; }
if (message.type === "SHOW_NOTICE") { showNotice(message.message || "AR15 User Hider"); return false; }
return false; });
document.addEventListener("contextmenu", handleContextMenu, true);
loadBlockedUsers() .then(() => { applyBlocking(); startObserver(); }) .catch(() => { showNotice("AR15 User Hider could not load blocked users."); }); })();
popup.js
const STORAGE_KEY = "blockedUsers";
const blockedCount = document.querySelector("#blocked-count"); const blockedList = document.querySelector("#blocked-list"); const emptyState = document.querySelector("#empty-state"); const addUserForm = document.querySelector("#add-user-form"); const usernameInput = document.querySelector("#username-input"); const reasonInput = document.querySelector("#reason-input"); const MAX_USERNAME_LENGTH = 80; const MAX_REASON_LENGTH = 500; let migrationPromise = null;
function normalizeUsername(username) { return String(username || "").trim().toLocaleLowerCase(); }
function cleanUsername(username) { return String(username || "") .replace(/\s+/g, " ") .replace(/^@/, "") .trim() .slice(0, MAX_USERNAME_LENGTH); }
function cleanReason(reason) { return String(reason || "").trim().slice(0, MAX_REASON_LENGTH); }
async function getBlockedUsers() { await migrateBlockedUsersToLocal(); const result = await chrome.storage.local.get({ [STORAGE_KEY]: [] }); return Array.isArray(result[STORAGE_KEY]) ? result[STORAGE_KEY] : []; }
async function setBlockedUsers(blockedUsers) { await migrateBlockedUsersToLocal(); await chrome.storage.local.set({ [STORAGE_KEY]: blockedUsers }); chrome.runtime.sendMessage({ type: "BLOCKED_USERS_CHANGED" }); }
async function migrateBlockedUsersToLocal() { if (!migrationPromise) { migrationPromise = (async () => { const [localResult, syncResult] = await Promise.all([ chrome.storage.local.get({ [STORAGE_KEY]: [] }), chrome.storage.sync.get({ [STORAGE_KEY]: [] }) ]); const localUsers = Array.isArray(localResult[STORAGE_KEY]) ? localResult[STORAGE_KEY] : []; const syncUsers = Array.isArray(syncResult[STORAGE_KEY]) ? syncResult[STORAGE_KEY] : [];
if (syncUsers.length > 0 && localUsers.length === 0) { await chrome.storage.local.set({ [STORAGE_KEY]: syncUsers }); }
if (syncUsers.length > 0) { await chrome.storage.sync.remove(STORAGE_KEY); } })(); }
return migrationPromise; }
async function upsertBlockedUser(username, reason) { const clean = cleanUsername(username); const normalized = normalizeUsername(clean);
if (!clean || !normalized) { return; }
const blockedUsers = await getBlockedUsers(); const existingIndex = blockedUsers.findIndex((user) => user.normalized === normalized); const existing = existingIndex >= 0 ? blockedUsers[existingIndex] : null; const record = { username: existing?.username || clean, normalized, reason: cleanReason(reason), addedAt: existing?.addedAt || Date.now(), updatedAt: Date.now() };
if (existingIndex >= 0) { blockedUsers[existingIndex] = record; } else { blockedUsers.push(record); }
blockedUsers.sort((a, b) => a.username.localeCompare(b.username)); await setBlockedUsers(blockedUsers); }
async function removeBlockedUser(normalized) { const blockedUsers = await getBlockedUsers(); await setBlockedUsers(blockedUsers.filter((user) => user.normalized !== normalized)); }
function createButton(label, className, action) { const button = document.createElement("button"); button.type = "button"; button.textContent = label;
if (className) { button.className = className; }
button.addEventListener("click", action); return button; }
function renderBlockedUser(user) { const article = document.createElement("article"); article.className = "blocked-user";
const header = document.createElement("div"); header.className = "blocked-user-header";
const name = document.createElement("div"); name.className = "blocked-user-name"; name.textContent = user.username;
const reason = document.createElement("p"); reason.className = "blocked-user-reason"; reason.textContent = user.reason || "No reason specified.";
const actions = document.createElement("div"); actions.className = "blocked-user-actions";
const editButton = createButton("Edit reason", "", () => renderEditPanel(article, user)); const unblockButton = createButton("Unblock", "danger-button", async () => { await removeBlockedUser(user.normalized); await render(); });
actions.append(editButton, unblockButton); header.append(name, actions); article.append(header, reason);
return article; }
function renderEditPanel(article, user) { article.textContent = "";
const label = document.createElement("label"); label.textContent = `Reason for ${user.username}`;
const textarea = document.createElement("textarea"); textarea.rows = 4; textarea.maxLength = MAX_REASON_LENGTH; textarea.value = user.reason || "";
const actions = document.createElement("div"); actions.className = "blocked-user-actions";
const cancelButton = createButton("Cancel", "", render); const saveButton = createButton("Save", "", async () => { await upsertBlockedUser(user.username, textarea.value); await render(); });
label.append(textarea); actions.append(cancelButton, saveButton); article.append(label, actions); textarea.focus(); }
async function render() { const blockedUsers = await getBlockedUsers(); blockedCount.textContent = String(blockedUsers.length); blockedList.textContent = ""; emptyState.hidden = blockedUsers.length > 0;
blockedUsers.forEach((user) => { blockedList.append(renderBlockedUser(user)); }); }
addUserForm.addEventListener("submit", async (event) => { event.preventDefault();
await upsertBlockedUser(usernameInput.value, reasonInput.value); usernameInput.value = ""; reasonInput.value = ""; usernameInput.focus(); await render(); });
chrome.storage.onChanged.addListener((changes, areaName) => { if (areaName === "local" && changes[STORAGE_KEY]) { render(); } });
render();
Doesn't work as good as before hardening against the initial pass of vulnerabilities, but still does the job. 
@AbsoluteLemur
|