Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[submodule "nscl"]
path = src/nscl
url = ../nscl.git
branch = container-tabs
15 changes: 14 additions & 1 deletion src/bg/LifeCycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ var LifeCycle = (() => {
let {url} = tab;
let {cypherText, key, iv} = await encrypt(JSON.stringify({
policy: ns.policy.dry(true),
contextStore: ns.contextStore.dry(true),
allSeen,
unrestrictedTabs: [...ns.unrestrictedTabs]
}));
Expand Down Expand Up @@ -208,14 +209,15 @@ var LifeCycle = (() => {
iv
}, key, cypherText
);
let {policy, allSeen, unrestrictedTabs} = JSON.parse(new TextDecoder().decode(encoded));
let {policy, contextStore, allSeen, unrestrictedTabs} = JSON.parse(new TextDecoder().decode(encoded));
if (!policy) {
throw new error("Ephemeral policy not found in survival tab %s!", tabId);
}
ns.unrestrictedTabs = new Set(unrestrictedTabs);
destroyIfNeeded();
if (ns.initializing) await ns.initializing;
ns.policy = new Policy(policy);
ns.contextStore = new ContextStore(contextStore);
await Promise.allSettled(
Object.entries(allSeen).map(
async ([tabId, seen]) => {
Expand Down Expand Up @@ -308,6 +310,17 @@ var LifeCycle = (() => {
if (changed) {
await ns.savePolicy();
}
if (ns.contextStore) {
changed = false;
for (let k of Object.keys(ns.contextStore.policies)){
for (let p of ns.contextStore.policies[k].getPresets(presetNames)) {
if (callback(p)) changed = true;
}
}
if (changed) {
await ns.saveContextStore();
}
}
};

const configureNewCap = async (cap, presetNames, capsFilter) => {
Expand Down
30 changes: 18 additions & 12 deletions src/bg/RequestGuard.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@

const wantsContext = checked.includes("ctx");

let { siteMatch, contextMatch, perms } = ns.policy.get(key, contextUrl);
let cookieStoreId = sender.tab && sender.tab.cookieStoreId;
let policy = ns.getPolicy(cookieStoreId);
let { siteMatch, contextMatch, perms } = policy.get(key, contextUrl);

if (!perms.capabilities.has(policyType) ||
!contextMatch && wantsContext && ctxKey) {
Expand All @@ -389,14 +391,15 @@
const isDefault = perms === ns.policy.DEFAULT;
perms = perms.clone();
if (isDefault) perms.temp = wantsTemp;
ns.policy.set(key, perms);
policy.set(key, perms);
if (ctxKey && wantsContext) {
perms.contextual.set(ctxKey, perms = perms.clone(/* noContext = */ true));
}
}
perms.temp = wantsTemp;
perms.capabilities.add(policyType);
await ns.savePolicy();
await ns.saveContextStore();
await RequestGuard.DNRPolicy?.update();
}
return {enable: key};
Expand Down Expand Up @@ -638,12 +641,13 @@

function intersectCapabilities(policyMatch, request) {
if (request.frameId !== 0 && ns.sync.cascadeRestrictions) {
const {tabUrl, frameAncestors} = request;
const {tabUrl, frameAncestors, cookieStoreId} = request;
const topUrl = tabUrl ||
frameAncestors && frameAncestors[frameAncestors?.length - 1]?.url ||
TabCache.get(request.tabId)?.url;
if (topUrl) {
return ns.policy.cascadeRestrictions(policyMatch, topUrl).capabilities;
const policy = ns.getPolicy(cookieStoreId);
return policy.cascadeRestrictions(policyMatch, topUrl).capabilities;
}
}
return policyMatch.perms.capabilities;
Expand Down Expand Up @@ -708,9 +712,10 @@

function checkLANRequest(request) {
if (!ns.isEnforced(request.tabId)) return ALLOW;
let {originUrl, url} = request;
let {originUrl, url, cookieStoreId} = request;
let policy = ns.getPolicy(cookieStoreId);
if (originUrl && !Sites.isInternal(originUrl) && url.startsWith("http") &&
!ns.policy.can(originUrl, "lan", ns.policyContext(request))) {
!policy.can(originUrl, "lan", ns.policyContext(request))) {
// we want to block any request whose origin resolves to at least one external WAN IP
// and whose destination resolves to at least one LAN IP
const {proxyInfo} = request; // see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo
Expand Down Expand Up @@ -745,9 +750,9 @@

normalizeRequest(request);

let {tabId, type, url, originUrl} = request;
let {tabId, type, cookieStoreId, url, originUrl} = request;

const {policy} = ns
const policy = ns.getPolicy(cookieStoreId);

let previous = recent.find(request);
if (previous) {
Expand Down Expand Up @@ -907,12 +912,12 @@
let promises = [];

pending.headersProcessed = true;
let {url, documentUrl, tabId, responseHeaders, type} = request;
let {url, documentUrl, tabId, cookieStoreId, responseHeaders, type} = request;
let isMainFrame = type === "main_frame";
try {
let capabilities;
if (ns.isEnforced(tabId)) {
const { policy } = ns;
const policy = ns.getPolicy(cookieStoreId);
const policyMatch = policy.get(url, ns.policyContext(request));
let { perms } = policyMatch;
if (isMainFrame) {
Expand Down Expand Up @@ -1008,13 +1013,14 @@
async function injectPolicyScript(details) {
await ns.initializing;
if (ns.local.debug?.disablePolicyInjection) return ''; // DEV_ONLY
const {url, tabId, frameId, type} = details;
const {url, tabId, frameId, cookieStoreId, type} = details;
const isTop = type == "main_frame";
const domPolicy = await ns.computeChildPolicy(
{ url },
{
tab: { id: tabId, url: isTop ? url : null },
frameId: isTop ? 0 : frameId,
cookieStoreId,
}
);
domPolicy.navigationURL = url;
Expand Down Expand Up @@ -1106,4 +1112,4 @@
}, filterDocs, ["blocking", "responseHeaders"])).install();
}
}
}
}
9 changes: 9 additions & 0 deletions src/bg/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ var Settings = {
async update(settings) {
let {
policy,
contextStore,
xssUserChoices,
tabId,
unrestrictedTab,
Expand Down Expand Up @@ -176,6 +177,7 @@ var Settings = {
// User is resetting options:
// pick either current Tor Browser Security Level or default NoScript policy
policy = ns.local.torBrowserPolicy || this.createDefaultDryPolicy();
contextStore = new ContextStore().dry();
reloadOptionsUI = true;
}

Expand All @@ -195,6 +197,12 @@ var Settings = {
await ns.savePolicy();
}

if (contextStore) {
let newContextStore = new ContextStore(contextStore);
ns.contextStore = newContextStore
await ns.saveContextStore();
}

if (typeof unrestrictedTab === "boolean") {
await ns.toggleTabRestrictions(tabId, !unrestrictedTab);
}
Expand Down Expand Up @@ -242,6 +250,7 @@ var Settings = {
knownCapabilities: Permissions.ALL,
},
policy: ns.policy.dry(),
contextStore: ns.contextStore.dry(),
local: ns.local,
sync: ns.sync,
xssUserChoices: XSS.getUserChoices(),
Expand Down
53 changes: 51 additions & 2 deletions src/bg/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@
}
}

if (!ns.contextStore) { // it could have been already retrieved by LifeCycle
const contextStoreData = (await Storage.get("sync", "contextStore")).contextStore;
if (contextStoreData) {
ns.contextStore = new ContextStore(contextStoreData);
await ns.contextStore.updateContainers(ns.policy);
} else {
log("No container data found. Initializing new policies.")
ns.contextStore = new ContextStore();
await ns.contextStore.updateContainers(ns.policy);
await ns.saveContextStore();
}
}

const {isTorBrowser} = ns.local;
Sites.onionSecure = isTorBrowser;

Expand Down Expand Up @@ -175,10 +188,12 @@
tabId = -1
}) {
const policy = ns.policy.dry(true);
const contextStore = ns.contextStore.dry(true);
const seen = tabId !== -1 ? await ns.collectSeen(tabId) : null;
const xssUserChoices = await XSS.getUserChoices();
await Messages.send("settings", {
policy,
contextStore,
seen,
xssUserChoices,
local: ns.local,
Expand Down Expand Up @@ -260,6 +275,7 @@
}

let _policy = null;
let _contextStore = null;

globalThis.ns = {
running: false,
Expand All @@ -268,6 +284,11 @@
RequestGuard.DNRPolicy?.update();
},
get policy() { return _policy; },
set contextStore(c) {
_contextStore = c;
RequestGuard.DNRPolicy?.update();
},
get contextStore() { return _contextStore; },
local: null,
sync: null,
initializing: null,
Expand Down Expand Up @@ -301,13 +322,28 @@
return !this.isEnforced(request.tabId) || this.policy.can(request.url, capability, this.policyContext(request));
},

getPolicy(cookieStoreId){
if (
ns.contextStore &&
ns.contextStore.enabled &&
ns.contextStore.policies.hasOwnProperty(cookieStoreId)
) {
let currentPolicy = ns.contextStore.policies[cookieStoreId];
debug("id", cookieStoreId, "has cookiestore", currentPolicy);
if (currentPolicy) return currentPolicy;
}
debug("default cookiestore", cookieStoreId);
return ns.policy;
},

async computeChildPolicy({url, contextUrl}, sender) {
await ns.initializing;
let {tab, origin, frameId, documentLifecycle} = sender;
let {tab, origin, frameId, cookieStoreId, documentLifecycle} = sender;
if (url == sender.url && url == "about:blank") {
url = origin;
}
let policy = ns.policy;
if (!cookieStoreId && tab) cookieStoreId = tab.cookieStoreId;
let policy = ns.getPolicy(cookieStoreId);
const {isTorBrowser} = ns.local;
if (!policy) {
console.log("Policy is null, initializing: %o, sending fallback.", ns.initializing);
Expand Down Expand Up @@ -414,6 +450,19 @@
return this.policy;
},

async saveContextStore() {
if (this.contextStore) {
await Promise.allSettled([
Storage.set("sync", {
policy: this.contextStore.dry()
}),
session.save(),
browser.webRequest.handlerBehaviorChanged()
]);
}
return this.contextStore;
},

openOptionsPage({tab, focus, hilite}) {
const url = new URL(browser.runtime.getManifest().options_ui.page);
if (tab !== undefined) {
Expand Down
4 changes: 3 additions & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"webRequestFilterResponse",
"webRequestFilterResponse.serviceWorkerScript",
"dns",
"<all_urls>"
"<all_urls>",
"contextualIdentities"
],
"host_permissions": [
"<all_urls>"
Expand All @@ -62,6 +63,7 @@
"/nscl/common/Sites.js",
"/nscl/common/Permissions.js",
"/nscl/common/Policy.js",
"/nscl/common/ContextStore.js",
"/nscl/common/locale.js",
"/nscl/common/Storage.js",
"/nscl/common/include.js",
Expand Down
2 changes: 1 addition & 1 deletion src/nscl
32 changes: 32 additions & 0 deletions src/ui/options.css
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,24 @@ fieldset:disabled {
flex: 2 2;
}

.per-site-buttons {
display: flex;
flex-flow: row wrap;
justify-content: flex-end;
width: 100%;
text-align: right;
margin: .5em 0 0 0;
}
#btn-clear-container {
margin-inline-start: .5em;
}
#copy-container {
margin-inline: .5em;
}
#copy-container-label {
margin-block: auto;
}

#policy {
display: block;
margin-top: .5em;
Expand All @@ -95,6 +113,12 @@ fieldset:disabled {
.hide, body:not(.debug) div.debug {
display: none;
}
#context-store {
display: block;
margin-top: .5em;
min-height: 20em;
width: 90%;
}

#debug-tools {
padding-left: 2.5em;
Expand All @@ -114,6 +138,14 @@ fieldset:disabled {
font-weight: bold;
}

#context-store-error {
background: red;
color: #ff8;
padding: 0;
margin: 0;
font-weight: bold;
}

input, button {
font-size: 1em;
}
Expand Down
Loading