Skip to content

Commit 2620d45

Browse files
committed
[XSS] New UI to reveal and selectively remove permanent user choices.
1 parent 1908b4b commit 2620d45

8 files changed

Lines changed: 114 additions & 18 deletions

File tree

src/bg/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
sync: ns.sync,
146146
unrestrictedTab: ns.unrestrictedTabs.has(tabId),
147147
tabId,
148+
xssBlockedInTab: XSS.getBlockedInTab(tabId),
148149
});
149150
},
150151

src/ui/options.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ <h3 class="flextabs__tab"><button class="flextabs__toggle">__MSG_SectionAdvanced
109109
<span id="xss-opt">
110110
<input type="checkbox" id="opt-xss"><label for="opt-xss" id="lbl-xss">__MSG_OptFilterXGet__</label>
111111
<span id="xssFaq">(<a href="https://noscript.net/faq#xss" title="https://noscript.net/faq#xss">__MSG_XssFaq__</a>)</span>
112-
<button id="btn-delete-xss-choices" disabled>__MSG_XSS_clearUserChoices__</button>
113112
</span>
113+
<div id="xssChoices">
114+
</div>
114115
</div>
115116
<div id="clearclick-options" class="opt-group">
116117
<input type="checkbox" id="opt-clearclick"><label for="opt-clearclick" id="lbl-clearclick">ClearClick</label>

src/ui/options.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,6 @@
108108
url: a.href
109109
});
110110
}
111-
let button = document.querySelector("#btn-delete-xss-choices");
112-
let choices = UI.xssUserChoices;
113-
button.disabled = !choices || Object.keys(choices).length === 0;
114-
button.onclick = () => {
115-
UI.updateSettings({
116-
xssUserChoices: {}
117-
});
118-
button.disabled = true
119-
};
120-
121111
}
122112

123113
opt("clearclick");

src/ui/popup.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
><span class="tor">__MSG_OptOverrideTorBrowserPolicy__</span><span class="not-tor">__MSG_OptIncognitoPerm__</span></label>
4646
</span>
4747
</div>
48+
<div id="xssChoices">
49+
</div>
4850
<div id="content"></div>
4951
<div id="sites"></div>
5052
<div id="buttons">

src/ui/ui.css

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,4 +519,26 @@ legend {
519519
.hilite-end .url {
520520
transform: none;
521521
transition: 1s transform;
522-
}
522+
}
523+
524+
#xssChoices {
525+
padding: .5em;
526+
display: none;
527+
flex-direction: column;
528+
}
529+
530+
#xssChoices.populated {
531+
display: flex;
532+
}
533+
534+
#xssChoices option {
535+
background: white;
536+
}
537+
538+
#xssChoices option.block {
539+
color: #a00;
540+
}
541+
542+
#xssChoices option.allow {
543+
color: #080;
544+
}

src/ui/ui.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ var UI = (() => {
3939
UI.seen = m.seen;
4040
UI.unrestrictedTab = m.unrestrictedTab;
4141
UI.xssUserChoices = m.xssUserChoices;
42+
UI.xssBlockedInTab = m.xssBlockedInTab;
4243
UI.local = m.local;
4344
UI.sync = m.sync;
4445
UI.forceIncognito = UI.incognito && !UI.sync.overrideTorBrowserPolicy;
@@ -53,6 +54,7 @@ var UI = (() => {
5354
}
5455
resolve();
5556
if (UI.onSettings) UI.onSettings();
57+
if (UI.tabId === -1 || UI.xssBlockedInTab) UI.createXSSChoiceManager();
5658
await HighContrast.init();
5759
}
5860
});
@@ -128,6 +130,61 @@ var UI = (() => {
128130
}
129131
}
130132
return input;
133+
},
134+
135+
createXSSChoiceManager(parent = "#xssChoices") {
136+
let choicesUI = document.querySelector(parent);
137+
if (!choicesUI) return;
138+
choicesUI.classList.remove("populated");
139+
let choices = Object.entries(UI.xssUserChoices);
140+
let choiceKeys = UI.xssBlockedInTab;
141+
if (choiceKeys) {
142+
choices = choices.filter(([key,])=> choiceKeys.includes(key));
143+
}
144+
if (!choices || Object.keys(choices).length === 0) {
145+
return;
146+
}
147+
148+
choicesUI.classList.add("populated");
149+
150+
choices.sort((a, b) => {
151+
let x = a.join("|"), y = b.join("|");
152+
return x < y ? -1 : x > y ? 1 : 0;
153+
});
154+
let list = choicesUI.querySelector("select") || choicesUI.appendChild(document.createElement("select"));
155+
list.size = Math.min(choices.length, 6);
156+
list.multiple = true;
157+
for (let o of list.options) {
158+
list.remove(o);
159+
}
160+
for (let [originKey, choice] of choices) {
161+
let [source, destOrigin] = originKey.split(">");
162+
let opt = document.createElement("option");
163+
opt.className = choice;
164+
opt.value = originKey;
165+
let block = choice === "block";
166+
opt.defaultSelected = block;
167+
opt.text = _(`XSS_optAlways${block ? "Block" : "Allow"}`, [source || "[...]", destOrigin]);
168+
list.add(opt);
169+
}
170+
let button = choicesUI.querySelector("button");
171+
if (!button) {
172+
button = choicesUI.appendChild(document.createElement("button"));
173+
button.textContent = _("XSS_clearUserChoices");
174+
}
175+
button.onclick = () => {
176+
let xssUserChoices = UI.xssUserChoices;
177+
for (let o of list.selectedOptions) {
178+
delete xssUserChoices[o.value];
179+
list.remove(o);
180+
}
181+
if (list.options.length === 0) {
182+
choicesUI.classList.remove("populated");
183+
}
184+
UI.updateSettings({
185+
xssUserChoices
186+
});
187+
};
131188
}
132189
};
133190

src/xss/InjectionCheckWorker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ include("InjectionChecker.js");
1919

2020
let Handlers = {
2121
async check({xssReq, skip}) {
22-
let {destUrl, unparsedRequest: request, debugging} = xssReq;
22+
let {destUrl, request, debugging} = xssReq;
2323
let {
2424
skipParams,
2525
skipRx

src/xss/XSS.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,38 @@ var XSS = (() => {
66

77
let workersMap = new Map();
88
let promptsMap = new Map();
9+
let blockedTabs = new Map();
910

1011
let requestIdCount = 0;
1112

1213
async function getUserResponse(xssReq) {
13-
let {originKey} = xssReq;
14+
let {originKey, request} = xssReq;
15+
let {tabId, frameId} = request;
16+
let {browserAction} = browser;
17+
if (frameId === 0) {
18+
if (blockedTabs.has(tabId)) {
19+
blockedTabs.delete(tabId);
20+
if ("setBadgeText" in browserAction) {
21+
browserAction.setBadgeText({tabId, text: ""});
22+
}
23+
}
24+
}
1425
await promptsMap.get(originKey);
15-
// promptsMap.delete(originKey);
26+
1627
switch (await XSS.getUserChoice(originKey)) {
1728
case "allow":
1829
return ALLOW;
1930
case "block":
2031
log("Blocking request from %s to %s by previous XSS prompt user choice",
2132
xssReq.srcUrl, xssReq.destUrl);
33+
34+
if ("setBadgeText" in browserAction) {
35+
browserAction.setBadgeText({tabId, text: "XSS"});
36+
browserAction.setBadgeBackgroundColor({tabId, color: [0, 0, 128, 160]});
37+
}
38+
let keys = blockedTabs.get(tabId);
39+
if (!keys) blockedTabs.set(tabId, keys = new Set());
40+
keys.add(originKey);
2241
return ABORT;
2342
}
2443
return null;
@@ -215,7 +234,7 @@ var XSS = (() => {
215234

216235
let isGet = method === "GET";
217236
return {
218-
unparsedRequest: request,
237+
request,
219238
srcUrl,
220239
destUrl,
221240
srcObj,
@@ -247,14 +266,18 @@ var XSS = (() => {
247266
return this._userChoices[originKey];
248267
},
249268

269+
getBlockedInTab(tabId) {
270+
return blockedTabs.has(tabId) ? [...blockedTabs.get(tabId)] : null;
271+
},
272+
250273
async maybe(xssReq) { // return reason or null if everything seems fine
251274
if (await this.Exceptions.shouldIgnore(xssReq)) {
252275
return null;
253276
}
254277

255278
let skip = this.Exceptions.partial(xssReq);
256279
let worker = new Worker(browser.runtime.getURL("/xss/InjectionCheckWorker.js"));
257-
let {requestId} = xssReq.unparsedRequest;
280+
let {requestId} = xssReq.request;
258281
workersMap.set(requestId, worker)
259282
return await new Promise((resolve, reject) => {
260283
worker.onmessage = e => {
@@ -282,7 +305,7 @@ var XSS = (() => {
282305
let onNavError = details => {
283306
debug("Navigation error: %o", details);
284307
let {tabId, frameId, url} = details;
285-
let r = xssReq.unparsedRequest;
308+
let r = xssReq.request;
286309
if (tabId === r.tabId && frameId === r.frameId) {
287310
cleanup();
288311
reject(new Error("Timing: request interrupted while being filtered, no need to go on."));

0 commit comments

Comments
 (0)