Skip to content

Commit bda83f4

Browse files
committed
Add support for container tabs
1 parent 2f7bcd1 commit bda83f4

File tree

14 files changed

+373
-68
lines changed

14 files changed

+373
-68
lines changed

.gitmodules

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[submodule "nscl"]
22
path = src/nscl
33
url = ../nscl.git
4+
branch = container-tabs

src/bg/LifeCycle.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ var LifeCycle = (() => {
104104
let {url} = tab;
105105
let {cypherText, key, iv} = await encrypt(JSON.stringify({
106106
policy: ns.policy.dry(true),
107+
contextStore: ns.contextStore.dry(true),
107108
allSeen,
108109
unrestrictedTabs: [...ns.unrestrictedTabs]
109110
}));
@@ -188,14 +189,15 @@ var LifeCycle = (() => {
188189
iv
189190
}, key, cypherText
190191
);
191-
let {policy, allSeen, unrestrictedTabs} = JSON.parse(new TextDecoder().decode(encoded));
192+
let {policy, contextStore, allSeen, unrestrictedTabs} = JSON.parse(new TextDecoder().decode(encoded));
192193
if (!policy) {
193194
throw new error("Ephemeral policy not found in survival tab %s!", tabId);
194195
}
195196
ns.unrestrictedTabs = new Set(unrestrictedTabs);
196197
destroyIfNeeded();
197198
if (ns.initializing) await ns.initializing;
198199
ns.policy = new Policy(policy);
200+
ns.contextStore = new ContextStore(contextStore);
199201
await Promise.all(
200202
Object.entries(allSeen).map(
201203
async ([tabId, seen]) => {
@@ -274,6 +276,17 @@ var LifeCycle = (() => {
274276
if (changed) {
275277
await ns.savePolicy();
276278
}
279+
if (ns.contextStore) {
280+
changed = false;
281+
for (let k of Object.keys(ns.contextStore.policies)){
282+
for (let p of ns.contextStore.policies[k].getPresets(presetNames)) {
283+
if (callback(p)) changed = true;
284+
}
285+
}
286+
if (changed) {
287+
await ns.saveContextStore();
288+
}
289+
}
277290
};
278291

279292
let configureNewCap = async (cap, presetNames, capsFilter) => {

src/bg/RequestGuard.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,10 @@ var RequestGuard = (() => {
275275
}
276276
let key = [siteKey, origin][ret.option || 0];
277277
if (!key) return;
278+
let cookieStoreId = sender.tab && sender.tab.cookieStoreId;
279+
let policy = ns.getPolicy(cookieStoreId);
278280
let contextUrl = sender.tab.url || documentUrl;
279-
let {siteMatch, contextMatch, perms} = ns.policy.get(key, contextUrl);
281+
let {siteMatch, contextMatch, perms} = policy.get(key, contextUrl);
280282
let {capabilities} = perms;
281283
if (!capabilities.has(policyType)) {
282284
let temp = sender.tab.incognito; // we don't want to store in PBM
@@ -289,8 +291,9 @@ var RequestGuard = (() => {
289291
perms = new Permissions(new Set(capabilities), false, contextualSites);
290292
}
291293
*/
292-
ns.policy.set(key, perms);
294+
policy.set(key, perms);
293295
await ns.savePolicy();
296+
await ns.saveContextStore();
294297
}
295298
return {enable: key};
296299
},
@@ -365,7 +368,7 @@ var RequestGuard = (() => {
365368
};
366369

367370
function intersectCapabilities(perms, request) {
368-
let {frameId, frameAncestors, tabId} = request;
371+
let {frameId, frameAncestors, tabId, cookieStoreId} = request;
369372
if (frameId !== 0 && ns.sync.cascadeRestrictions) {
370373
let topUrl = frameAncestors && frameAncestors.length
371374
&& frameAncestors[frameAncestors.length - 1].url;
@@ -374,7 +377,8 @@ var RequestGuard = (() => {
374377
if (tab) topUrl = tab.url;
375378
}
376379
if (topUrl) {
377-
return ns.policy.cascadeRestrictions(perms, topUrl).capabilities;
380+
let policy = ns.getPolicy(cookieStoreId);
381+
return policy.cascadeRestrictions(perms, topUrl).capabilities;
378382
}
379383
}
380384
return perms.capabilities;
@@ -436,9 +440,10 @@ var RequestGuard = (() => {
436440

437441
function checkLANRequest(request) {
438442
if (!ns.isEnforced(request.tabId)) return ALLOW;
439-
let {originUrl, url} = request;
443+
let {originUrl, url, cookieStoreId} = request;
444+
let policy = ns.getPolicy(cookieStoreId);
440445
if (originUrl && !Sites.isInternal(originUrl) && url.startsWith("http") &&
441-
!ns.policy.can(originUrl, "lan", ns.policyContext(request))) {
446+
!policy.can(originUrl, "lan", ns.policyContext(request))) {
442447
// we want to block any request whose origin resolves to at least one external WAN IP
443448
// and whose destination resolves to at least one LAN IP
444449
let {proxyInfo} = request; // see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo
@@ -472,7 +477,6 @@ var RequestGuard = (() => {
472477
normalizeRequest(request);
473478
initPendingRequest(request);
474479

475-
let {policy} = ns
476480
let {tabId, type, url, originUrl} = request;
477481

478482
if (type in policyTypesMap) {
@@ -495,7 +499,9 @@ var RequestGuard = (() => {
495499
}
496500
return ALLOW;
497501
}
502+
let {cookieStoreId} = request;
498503
let isFetch = "fetch" === policyType;
504+
let policy = ns.getPolicy(cookieStoreId);
499505

500506
if ((isFetch || "frame" === policyType) &&
501507
(((isFetch && !originUrl
@@ -597,12 +603,12 @@ var RequestGuard = (() => {
597603
let headersModified = false;
598604

599605
pending.headersProcessed = true;
600-
let {url, documentUrl, tabId, responseHeaders, type} = request;
606+
let {url, documentUrl, tabId, cookieStoreId, responseHeaders, type} = request;
601607
let isMainFrame = type === "main_frame";
602608
try {
603609
let capabilities;
604610
if (ns.isEnforced(tabId)) {
605-
let policy = ns.policy;
611+
let policy = ns.getPolicy(cookieStoreId);
606612
let {perms} = policy.get(url, ns.policyContext(request));
607613
if (isMainFrame) {
608614
if (policy.autoAllowTop && perms === policy.DEFAULT) {
@@ -723,8 +729,8 @@ var RequestGuard = (() => {
723729
}
724730

725731
function injectPolicyScript(details) {
726-
let {url, tabId, frameId} = details;
727-
let policy = ns.computeChildPolicy({url}, {tab: {id: tabId}, frameId});
732+
let {url, tabId, frameId, cookieStoreId} = details;
733+
let policy = ns.computeChildPolicy({url}, {tab: {id: tabId}, frameId, cookieStoreId});
728734
policy.navigationURL = url;
729735
let debugStatement = ns.local.debug ? `
730736
let mark = Date.now() + ":" + Math.random();

src/bg/Settings.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ var Settings = {
9898
async update(settings) {
9999
let {
100100
policy,
101+
contextStore,
101102
xssUserChoices,
102103
tabId,
103104
unrestrictedTab,
@@ -146,6 +147,7 @@ var Settings = {
146147
if (settings.sync === null) {
147148
// user is resetting options
148149
policy = this.createDefaultDryPolicy();
150+
contextStore = new ContextStore().dry();
149151

150152
// overriden defaults when user manually resets options
151153

@@ -170,6 +172,12 @@ var Settings = {
170172
await ns.savePolicy();
171173
}
172174

175+
if (contextStore) {
176+
let newContextStore = new ContextStore(contextStore);
177+
ns.contextStore = newContextStore
178+
await ns.saveContextStore();
179+
}
180+
173181
if (typeof unrestrictedTab === "boolean") {
174182
ns.unrestrictedTabs[unrestrictedTab ? "add" : "delete"](tabId);
175183
}
@@ -213,6 +221,7 @@ var Settings = {
213221
export() {
214222
return JSON.stringify({
215223
policy: ns.policy.dry(),
224+
contextStore: ns.contextStore.dry(),
216225
local: ns.local,
217226
sync: ns.sync,
218227
xssUserChoices: XSS.getUserChoices(),

src/bg/main.js

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@
6767
}
6868
}
6969

70+
if (!ns.contextStore) { // it could have been already retrieved by LifeCycle
71+
let contextStoreData = (await Storage.get("sync", "contextStore")).contextStore;
72+
if (contextStoreData) {
73+
ns.contextStore = new ContextStore(contextStoreData);
74+
await ns.contextStore.updateContainers(ns.policy);
75+
} else {
76+
log("No container data found. Initializing new policies.")
77+
ns.contextStore = new ContextStore();
78+
await ns.contextStore.updateContainers(ns.policy);
79+
await ns.saveContextStore();
80+
}
81+
}
82+
7083
let {isTorBrowser} = ns.local;
7184
Sites.onionSecure = isTorBrowser;
7285

@@ -164,10 +177,12 @@
164177
tabId = -1
165178
}) {
166179
let policy = ns.policy.dry(true);
180+
let contextStore = ns.contextStore.dry(true);
167181
let seen = tabId !== -1 ? await ns.collectSeen(tabId) : null;
168182
let xssUserChoices = await XSS.getUserChoices();
169183
await Messages.send("settings", {
170184
policy,
185+
contextStore,
171186
seen,
172187
xssUserChoices,
173188
local: ns.local,
@@ -225,6 +240,7 @@
225240
var ns = {
226241
running: false,
227242
policy: null,
243+
contextStore: null,
228244
local: null,
229245
sync: null,
230246
initializing: null,
@@ -246,21 +262,22 @@
246262
return !this.isEnforced(request.tabId) || this.policy.can(request.url, capability, this.policyContext(request));
247263
},
248264

249-
computeChildPolicy({url, contextUrl}, sender) {
250-
let {tab, frameId} = sender;
251-
let policy = ns.policy;
252-
let {isTorBrowser} = ns.local;
253-
if (!policy) {
254-
console.log("Policy is null, initializing: %o, sending fallback.", ns.initializing);
255-
return {
256-
permissions: new Permissions(Permissions.DEFAULT).dry(),
257-
unrestricted: false,
258-
cascaded: false,
259-
fallback: true,
260-
isTorBrowser,
261-
};
265+
getPolicy(cookieStoreId){
266+
if (
267+
ns.contextStore &&
268+
ns.contextStore.enabled &&
269+
ns.contextStore.policies.hasOwnProperty(cookieStoreId)
270+
) {
271+
let currentPolicy = ns.contextStore.policies[cookieStoreId];
272+
debug("id", cookieStoreId, "has cookiestore", currentPolicy);
273+
if (currentPolicy) return currentPolicy;
262274
}
275+
debug("default cookiestore", cookieStoreId);
276+
return ns.policy;
277+
},
263278

279+
computeChildPolicy({url, contextUrl}, sender) {
280+
let {tab, frameId, cookieStoreId} = sender;
264281
let tabId = tab ? tab.id : -1;
265282
let topUrl;
266283
if (frameId === 0) {
@@ -272,6 +289,20 @@
272289
if (!topUrl) topUrl = url;
273290
if (!contextUrl) contextUrl = topUrl;
274291

292+
if (!cookieStoreId && tab) cookieStoreId = tab.cookieStoreId;
293+
let policy = ns.getPolicy(cookieStoreId);
294+
let {isTorBrowser} = ns.local;
295+
if (!policy) {
296+
console.log("Policy is null, initializing: %o, sending fallback.", ns.initializing);
297+
return {
298+
permissions: new Permissions(Permissions.DEFAULT).dry(),
299+
unrestricted: false,
300+
cascaded: false,
301+
fallback: true,
302+
isTorBrowser,
303+
};
304+
}
305+
275306
if (Sites.isInternal(url) || !ns.isEnforced(tabId)) {
276307
policy = null;
277308
}
@@ -337,7 +368,7 @@
337368
await Storage.set("sync", {
338369
policy: this.policy.dry()
339370
});
340-
await browser.webRequest.handlerBehaviorChanged()
371+
await browser.webRequest.handlerBehaviorChanged();
341372
}
342373
return this.policy;
343374
},
@@ -354,6 +385,16 @@
354385
browser.tabs.create({url: url.toString() });
355386
},
356387

388+
async saveContextStore() {
389+
if (this.contextStore) {
390+
await Storage.set("sync", {
391+
contextStore: this.contextStore.dry()
392+
});
393+
await browser.webRequest.handlerBehaviorChanged();
394+
}
395+
return this.contextStore;
396+
},
397+
357398
async save(obj) {
358399
if (obj && obj.storage) {
359400
let toBeSaved = {

src/manifest.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"webRequest",
3030
"webRequestBlocking",
3131
"dns",
32-
"<all_urls>"
32+
"<all_urls>",
33+
"contextualIdentities"
3334
],
3435

3536
"background": {
@@ -51,6 +52,7 @@
5152
"/nscl/common/Sites.js",
5253
"/nscl/common/Permissions.js",
5354
"/nscl/common/Policy.js",
55+
"/nscl/common/ContextStore.js",
5456
"/nscl/common/locale.js",
5557
"/nscl/common/Storage.js",
5658
"/nscl/common/include.js",

src/ui/options.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,24 @@ fieldset:disabled {
8282
flex: 2 2;
8383
}
8484

85+
.per-site-buttons {
86+
display: flex;
87+
flex-flow: row wrap;
88+
justify-content: flex-end;
89+
width: 100%;
90+
text-align: right;
91+
margin: .5em 0 0 0;
92+
}
93+
#btn-clear-container {
94+
margin-inline-start: .5em;
95+
}
96+
#copy-container {
97+
margin-inline: .5em;
98+
}
99+
#copy-container-label {
100+
margin-block: auto;
101+
}
102+
85103
#policy {
86104
display: block;
87105
margin-top: .5em;
@@ -91,6 +109,12 @@ fieldset:disabled {
91109
.hide, body:not(.debug) div.debug {
92110
display: none;
93111
}
112+
#context-store {
113+
display: block;
114+
margin-top: .5em;
115+
min-height: 20em;
116+
width: 90%;
117+
}
94118

95119
#debug-tools {
96120
padding-left: 2.5em;
@@ -110,6 +134,14 @@ fieldset:disabled {
110134
font-weight: bold;
111135
}
112136

137+
#context-store-error {
138+
background: red;
139+
color: #ff8;
140+
padding: 0;
141+
margin: 0;
142+
font-weight: bold;
143+
}
144+
113145
input, button {
114146
font-size: 1em;
115147
}

0 commit comments

Comments
 (0)