[XSS] New UI to reveal and selectively remove permanent user choices.
This commit is contained in:
parent
1908b4b258
commit
2620d456b9
|
@ -145,6 +145,7 @@
|
|||
sync: ns.sync,
|
||||
unrestrictedTab: ns.unrestrictedTabs.has(tabId),
|
||||
tabId,
|
||||
xssBlockedInTab: XSS.getBlockedInTab(tabId),
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
57
src/ui/ui.js
57
src/ui/ui.js
|
@ -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
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."));
|
||||
|
|
Loading…
Reference in New Issue