[XSS] New UI to reveal and selectively remove permanent user choices.

This commit is contained in:
hackademix 2021-01-25 13:17:49 +01:00
parent 1908b4b258
commit 2620d456b9
8 changed files with 114 additions and 18 deletions

View File

@ -145,6 +145,7 @@
sync: ns.sync,
unrestrictedTab: ns.unrestrictedTabs.has(tabId),
tabId,
xssBlockedInTab: XSS.getBlockedInTab(tabId),
});
},

View File

@ -109,8 +109,9 @@
<span id="xss-opt">
<input type="checkbox" id="opt-xss"><label for="opt-xss" id="lbl-xss">__MSG_OptFilterXGet__</label>
<span id="xssFaq">(<a href="https://noscript.net/faq#xss" title="https://noscript.net/faq#xss">__MSG_XssFaq__</a>)</span>
<button id="btn-delete-xss-choices" disabled>__MSG_XSS_clearUserChoices__</button>
</span>
<div id="xssChoices">
</div>
</div>
<div id="clearclick-options" class="opt-group">
<input type="checkbox" id="opt-clearclick"><label for="opt-clearclick" id="lbl-clearclick">ClearClick</label>

View File

@ -108,16 +108,6 @@
url: a.href
});
}
let button = document.querySelector("#btn-delete-xss-choices");
let choices = UI.xssUserChoices;
button.disabled = !choices || Object.keys(choices).length === 0;
button.onclick = () => {
UI.updateSettings({
xssUserChoices: {}
});
button.disabled = true
};
}
opt("clearclick");

View File

@ -45,6 +45,8 @@
><span class="tor">__MSG_OptOverrideTorBrowserPolicy__</span><span class="not-tor">__MSG_OptIncognitoPerm__</span></label>
</span>
</div>
<div id="xssChoices">
</div>
<div id="content"></div>
<div id="sites"></div>
<div id="buttons">

View File

@ -519,4 +519,26 @@ legend {
.hilite-end .url {
transform: none;
transition: 1s transform;
}
}
#xssChoices {
padding: .5em;
display: none;
flex-direction: column;
}
#xssChoices.populated {
display: flex;
}
#xssChoices option {
background: white;
}
#xssChoices option.block {
color: #a00;
}
#xssChoices option.allow {
color: #080;
}

View File

@ -39,6 +39,7 @@ var UI = (() => {
UI.seen = m.seen;
UI.unrestrictedTab = m.unrestrictedTab;
UI.xssUserChoices = m.xssUserChoices;
UI.xssBlockedInTab = m.xssBlockedInTab;
UI.local = m.local;
UI.sync = m.sync;
UI.forceIncognito = UI.incognito && !UI.sync.overrideTorBrowserPolicy;
@ -53,6 +54,7 @@ var UI = (() => {
}
resolve();
if (UI.onSettings) UI.onSettings();
if (UI.tabId === -1 || UI.xssBlockedInTab) UI.createXSSChoiceManager();
await HighContrast.init();
}
});
@ -128,6 +130,61 @@ var UI = (() => {
}
}
return input;
},
createXSSChoiceManager(parent = "#xssChoices") {
let choicesUI = document.querySelector(parent);
if (!choicesUI) return;
choicesUI.classList.remove("populated");
let choices = Object.entries(UI.xssUserChoices);
let choiceKeys = UI.xssBlockedInTab;
if (choiceKeys) {
choices = choices.filter(([key,])=> choiceKeys.includes(key));
}
if (!choices || Object.keys(choices).length === 0) {
return;
}
choicesUI.classList.add("populated");
choices.sort((a, b) => {
let x = a.join("|"), y = b.join("|");
return x < y ? -1 : x > y ? 1 : 0;
});
let list = choicesUI.querySelector("select") || choicesUI.appendChild(document.createElement("select"));
list.size = Math.min(choices.length, 6);
list.multiple = true;
for (let o of list.options) {
list.remove(o);
}
for (let [originKey, choice] of choices) {
let [source, destOrigin] = originKey.split(">");
let opt = document.createElement("option");
opt.className = choice;
opt.value = originKey;
let block = choice === "block";
opt.defaultSelected = block;
opt.text = _(`XSS_optAlways${block ? "Block" : "Allow"}`, [source || "[...]", destOrigin]);
list.add(opt);
}
let button = choicesUI.querySelector("button");
if (!button) {
button = choicesUI.appendChild(document.createElement("button"));
button.textContent = _("XSS_clearUserChoices");
}
button.onclick = () => {
let xssUserChoices = UI.xssUserChoices;
for (let o of list.selectedOptions) {
delete xssUserChoices[o.value];
list.remove(o);
}
if (list.options.length === 0) {
choicesUI.classList.remove("populated");
}
UI.updateSettings({
xssUserChoices
});
};
}
};

View File

@ -19,7 +19,7 @@ include("InjectionChecker.js");
let Handlers = {
async check({xssReq, skip}) {
let {destUrl, unparsedRequest: request, debugging} = xssReq;
let {destUrl, request, debugging} = xssReq;
let {
skipParams,
skipRx

View File

@ -6,19 +6,38 @@ var XSS = (() => {
let workersMap = new Map();
let promptsMap = new Map();
let blockedTabs = new Map();
let requestIdCount = 0;
async function getUserResponse(xssReq) {
let {originKey} = xssReq;
let {originKey, request} = xssReq;
let {tabId, frameId} = request;
let {browserAction} = browser;
if (frameId === 0) {
if (blockedTabs.has(tabId)) {
blockedTabs.delete(tabId);
if ("setBadgeText" in browserAction) {
browserAction.setBadgeText({tabId, text: ""});
}
}
}
await promptsMap.get(originKey);
// promptsMap.delete(originKey);
switch (await XSS.getUserChoice(originKey)) {
case "allow":
return ALLOW;
case "block":
log("Blocking request from %s to %s by previous XSS prompt user choice",
xssReq.srcUrl, xssReq.destUrl);
if ("setBadgeText" in browserAction) {
browserAction.setBadgeText({tabId, text: "XSS"});
browserAction.setBadgeBackgroundColor({tabId, color: [0, 0, 128, 160]});
}
let keys = blockedTabs.get(tabId);
if (!keys) blockedTabs.set(tabId, keys = new Set());
keys.add(originKey);
return ABORT;
}
return null;
@ -215,7 +234,7 @@ var XSS = (() => {
let isGet = method === "GET";
return {
unparsedRequest: request,
request,
srcUrl,
destUrl,
srcObj,
@ -247,6 +266,10 @@ var XSS = (() => {
return this._userChoices[originKey];
},
getBlockedInTab(tabId) {
return blockedTabs.has(tabId) ? [...blockedTabs.get(tabId)] : null;
},
async maybe(xssReq) { // return reason or null if everything seems fine
if (await this.Exceptions.shouldIgnore(xssReq)) {
return null;
@ -254,7 +277,7 @@ var XSS = (() => {
let skip = this.Exceptions.partial(xssReq);
let worker = new Worker(browser.runtime.getURL("/xss/InjectionCheckWorker.js"));
let {requestId} = xssReq.unparsedRequest;
let {requestId} = xssReq.request;
workersMap.set(requestId, worker)
return await new Promise((resolve, reject) => {
worker.onmessage = e => {
@ -282,7 +305,7 @@ var XSS = (() => {
let onNavError = details => {
debug("Navigation error: %o", details);
let {tabId, frameId, url} = details;
let r = xssReq.unparsedRequest;
let r = xssReq.request;
if (tabId === r.tabId && frameId === r.frameId) {
cleanup();
reject(new Error("Timing: request interrupted while being filtered, no need to go on."));