mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 17:21:05 +00:00
fix: harden extension security and plugin reliability
Address audit findings across background handlers, openers, plugins, and UI: URL allowlists, XSS reductions, popup lifecycle fixes, plugin dispose/cleanup, cloud sync hardening, global search mathjs sandbox, and settings storage fixes.
This commit is contained in:
@@ -16,6 +16,24 @@ import { updateAllColors } from "./colors/Manager";
|
||||
import { delay } from "@/seqta/utils/delay";
|
||||
|
||||
let cachedUserInfo: any = null;
|
||||
let userInfoCacheListenersAttached = false;
|
||||
|
||||
export function invalidateCachedUserInfo(): void {
|
||||
cachedUserInfo = null;
|
||||
}
|
||||
|
||||
function attachUserInfoCacheInvalidation(): void {
|
||||
if (userInfoCacheListenersAttached || typeof window === "undefined") return;
|
||||
userInfoCacheListenersAttached = true;
|
||||
|
||||
window.addEventListener("pageshow", (event) => {
|
||||
if (event.persisted) {
|
||||
invalidateCachedUserInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
attachUserInfoCacheInvalidation();
|
||||
|
||||
let LightDarkModeSnakeEggButton = 0;
|
||||
let sidebarAccessibilityObserver: MutationObserver | null = null;
|
||||
@@ -25,8 +43,10 @@ let sidebarAccessibilityListenersAttached = false;
|
||||
/** Marks menu rows that are off-screen in the drill stack (CSS blocks clicks). */
|
||||
const BSPLUS_SIDEBAR_OFFSCREEN = "bsplus-sidebar-offscreen";
|
||||
|
||||
export async function getUserInfo() {
|
||||
if (cachedUserInfo) return cachedUserInfo;
|
||||
export async function getUserInfo(options?: { validateSession?: boolean }) {
|
||||
if (cachedUserInfo && !options?.validateSession) {
|
||||
return cachedUserInfo;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${location.origin}/seqta/student/login`, {
|
||||
@@ -41,7 +61,26 @@ export async function getUserInfo() {
|
||||
}),
|
||||
});
|
||||
|
||||
cachedUserInfo = (await response.json()).payload;
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to get user info: HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = (await response.json()).payload;
|
||||
|
||||
if (
|
||||
cachedUserInfo &&
|
||||
options?.validateSession &&
|
||||
payload?.id != null &&
|
||||
cachedUserInfo.id != null &&
|
||||
payload.id !== cachedUserInfo.id
|
||||
) {
|
||||
console.warn(
|
||||
"[BetterSEQTA+] Session user changed; invalidating cached user info",
|
||||
);
|
||||
invalidateCachedUserInfo();
|
||||
}
|
||||
|
||||
cachedUserInfo = payload;
|
||||
return cachedUserInfo;
|
||||
} catch (error) {
|
||||
console.error("[BetterSEQTA+] Failed to get user info:", error);
|
||||
|
||||
@@ -21,6 +21,7 @@ export async function appendBackgroundToUI() {
|
||||
}
|
||||
|
||||
let lastLoadedId: string | null = null;
|
||||
let lastBlobUrl: string | null = null;
|
||||
|
||||
export async function loadBackground() {
|
||||
if (!isIndexedDBSupported()) {
|
||||
@@ -36,6 +37,10 @@ export async function loadBackground() {
|
||||
backgroundContainer.remove();
|
||||
}
|
||||
lastLoadedId = null;
|
||||
if (lastBlobUrl) {
|
||||
URL.revokeObjectURL(lastBlobUrl);
|
||||
lastBlobUrl = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,12 +78,19 @@ export async function loadBackground() {
|
||||
|
||||
mediaContainer.innerHTML = "";
|
||||
|
||||
if (lastBlobUrl) {
|
||||
URL.revokeObjectURL(lastBlobUrl);
|
||||
lastBlobUrl = null;
|
||||
}
|
||||
|
||||
const mediaElement =
|
||||
background.type === "video"
|
||||
? document.createElement("video")
|
||||
: document.createElement("img");
|
||||
|
||||
mediaElement.src = URL.createObjectURL(background.blob);
|
||||
const blobUrl = URL.createObjectURL(background.blob);
|
||||
lastBlobUrl = blobUrl;
|
||||
mediaElement.src = blobUrl;
|
||||
mediaElement.classList.add("background");
|
||||
|
||||
if (mediaElement instanceof HTMLVideoElement) {
|
||||
|
||||
Reference in New Issue
Block a user