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:
2026-06-17 10:50:26 +09:30
parent 0e696e0175
commit 8a5424c5a4
70 changed files with 1229 additions and 430 deletions
@@ -17,7 +17,7 @@ import {
processAssessments,
type WeightingEntry,
} from "./utils.ts";
import { injectRubricCopyButtons } from "./rubricCopy.ts";
import { injectRubricCopyButtons, teardownRubricCopyButtons } from "./rubricCopy.ts";
interface weightingsStorage {
weightings: Record<string, WeightingEntry>;
@@ -41,6 +41,8 @@ class AssessmentsAveragePluginClass extends BasePlugin<typeof settings> {
const instance = new AssessmentsAveragePluginClass();
let overrideListenerController: AbortController | null = null;
let wrapperColourObserver: MutationObserver | null = null;
let wrapperColourObserverTimeout: ReturnType<typeof setTimeout> | null = null;
const assessmentsAveragePlugin: Plugin<typeof settings, weightingsStorage> = {
id: "assessments-average",
@@ -54,7 +56,9 @@ const assessmentsAveragePlugin: Plugin<typeof settings, weightingsStorage> = {
await initStorage(api);
clearStuck(api);
api.seqta.onMount(".assessmentsWrapper", async () => {
const { unregister: unregisterWrapperMount } = api.seqta.onMount(
".assessmentsWrapper",
async () => {
await waitForElm(
"#main > .assessmentsWrapper .assessments [class*='AssessmentItem__AssessmentItem___']",
true,
@@ -88,17 +92,43 @@ const assessmentsAveragePlugin: Plugin<typeof settings, weightingsStorage> = {
void parseAssessments(api);
const wrapper = document.querySelector(".assessmentsWrapper");
if (wrapper) {
const observer = new MutationObserver(() => {
wrapperColourObserver?.disconnect();
if (wrapperColourObserverTimeout) {
clearTimeout(wrapperColourObserverTimeout);
}
wrapperColourObserver = new MutationObserver(() => {
applySubjectColourToOverallResult();
});
observer.observe(wrapper, { childList: true, subtree: true });
setTimeout(() => observer.disconnect(), 10000);
wrapperColourObserver.observe(wrapper, { childList: true, subtree: true });
wrapperColourObserverTimeout = setTimeout(() => {
wrapperColourObserver?.disconnect();
wrapperColourObserver = null;
wrapperColourObserverTimeout = null;
}, 10000);
}
});
api.seqta.onMount("[class*='SelectedAssessment__']", () => {
},
);
const { unregister: unregisterSelectedMount } = api.seqta.onMount(
"[class*='SelectedAssessment__']",
() => {
injectWeightingsTab(api);
injectRubricCopyButtons();
});
},
);
return () => {
overrideListenerController?.abort();
overrideListenerController = null;
wrapperColourObserver?.disconnect();
wrapperColourObserver = null;
if (wrapperColourObserverTimeout) {
clearTimeout(wrapperColourObserverTimeout);
wrapperColourObserverTimeout = null;
}
teardownRubricCopyButtons();
unregisterWrapperMount();
unregisterSelectedMount();
};
},
};