mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-17 17:07:07 +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:
@@ -29,6 +29,9 @@ async function fetchJSON(url: string, body: any) {
|
||||
headers: { "Content-Type": "application/json; charset=utf-8" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status} for ${url}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -164,7 +167,7 @@ async function getLearnAssessmentsData(studentId: number) {
|
||||
}
|
||||
|
||||
export async function getAssessmentsData() {
|
||||
if (settingsState.mockNotices) {
|
||||
if (settingsState.hideSensitiveContent) {
|
||||
return getMockAssessmentsData();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ async function fetchJSON(url: string, body: unknown) {
|
||||
headers: { "Content-Type": "application/json; charset=utf-8" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status} for ${url}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Plugin } from "../../core/types";
|
||||
import { waitForElm } from "@/seqta/utils/waitForElm";
|
||||
import { getAssessmentsData } from "./api";
|
||||
import { renderErrorState, renderGrid, renderSkeletonLoader } from "./ui";
|
||||
import { renderErrorState, renderGrid, renderSkeletonLoader, teardownOverviewUi } from "./ui";
|
||||
import styles from "./styles.css?inline";
|
||||
import { delay } from "@/seqta/utils/delay";
|
||||
import { isSeqtaEngageExperience } from "@/seqta/utils/isSeqtaEngage";
|
||||
@@ -66,6 +66,8 @@ const assessmentsOverviewPlugin: Plugin<{}> = {
|
||||
gridItem.appendChild(label);
|
||||
menu.insertBefore(gridItem, menu.firstChild);
|
||||
|
||||
let loadRequestId = 0;
|
||||
|
||||
const menuObserver = new MutationObserver(() => {
|
||||
ensureOverviewMenuPosition(menu, gridItem);
|
||||
});
|
||||
@@ -81,7 +83,18 @@ const assessmentsOverviewPlugin: Plugin<{}> = {
|
||||
};
|
||||
gridItem.addEventListener("click", clickHandler);
|
||||
|
||||
const popstateHandler = () => {
|
||||
if (isOverviewRoute()) {
|
||||
void loadGridView();
|
||||
} else {
|
||||
loadRequestId += 1;
|
||||
teardownOverviewUi();
|
||||
}
|
||||
};
|
||||
window.addEventListener("popstate", popstateHandler);
|
||||
|
||||
async function loadGridView() {
|
||||
const requestId = ++loadRequestId;
|
||||
await delay(1);
|
||||
|
||||
if (isSeqtaEngageExperience()) {
|
||||
@@ -98,7 +111,7 @@ const assessmentsOverviewPlugin: Plugin<{}> = {
|
||||
}
|
||||
|
||||
const main = document.getElementById("main");
|
||||
if (!main) return;
|
||||
if (!main || requestId !== loadRequestId) return;
|
||||
|
||||
document
|
||||
.querySelectorAll('[data-key="assessments"] .item')
|
||||
@@ -110,17 +123,22 @@ const assessmentsOverviewPlugin: Plugin<{}> = {
|
||||
.querySelector('[data-key="assessments"]')
|
||||
?.classList.add("active");
|
||||
|
||||
main.innerHTML = '<div id="grid-view-container" class="bsplus-overview-host"></div>';
|
||||
main.innerHTML =
|
||||
'<div id="grid-view-container" class="bsplus-overview-host"></div>';
|
||||
const container = document.getElementById(
|
||||
"grid-view-container",
|
||||
) as HTMLElement;
|
||||
|
||||
if (requestId !== loadRequestId) return;
|
||||
|
||||
renderSkeletonLoader(container);
|
||||
|
||||
try {
|
||||
const data = await getAssessmentsData();
|
||||
if (requestId !== loadRequestId) return;
|
||||
renderGrid(container, data);
|
||||
} catch (err) {
|
||||
if (requestId !== loadRequestId) return;
|
||||
console.error("Failed to load assessments:", err);
|
||||
renderErrorState(
|
||||
container,
|
||||
@@ -130,8 +148,11 @@ const assessmentsOverviewPlugin: Plugin<{}> = {
|
||||
}
|
||||
|
||||
return () => {
|
||||
loadRequestId += 1;
|
||||
window.removeEventListener("popstate", popstateHandler);
|
||||
menuObserver.disconnect();
|
||||
gridItem.removeEventListener("click", clickHandler);
|
||||
teardownOverviewUi();
|
||||
gridItem.remove();
|
||||
};
|
||||
},
|
||||
|
||||
@@ -62,7 +62,7 @@ export function activeSubjectsFromEngageChild(child: {
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const term of child.terms ?? []) {
|
||||
if (term.active !== 1) continue;
|
||||
if (!isActiveTermFlag(term.active)) continue;
|
||||
for (const raw of term.subjects ?? []) {
|
||||
const subject = normalizeOverviewSubject(raw);
|
||||
if (!subject) continue;
|
||||
@@ -151,7 +151,14 @@ export function determineStatus(item: any): string {
|
||||
}
|
||||
|
||||
const completedKey = "betterseqta-completed-assessments";
|
||||
const completed = JSON.parse(localStorage.getItem(completedKey) || "[]");
|
||||
let completed: unknown[] = [];
|
||||
try {
|
||||
const raw = localStorage.getItem(completedKey);
|
||||
const parsed = raw ? JSON.parse(raw) : [];
|
||||
completed = Array.isArray(parsed) ? parsed : [];
|
||||
} catch {
|
||||
completed = [];
|
||||
}
|
||||
if (completed.includes(item.id)) {
|
||||
return "MARKS_RELEASED";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user