trisquel-icecat/icecat/extensions/gnu/jsr@javascriptrestrictor/http_shield_firefox.js

202 lines
6 KiB
JavaScript

/** \file
* \brief This file contains Firefox-specific functions for Network Boundary Shield.
*
* \author Copyright (C) 2020 Pavel Pohner
* \author Copyright (C) 2020-2021 Martin Bednář
*
* \license SPDX-License-Identifier: GPL-3.0-or-later
*/
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
/** \file
*
* \brief This file contains Firefox specific functions for Network Boundary Shield.
*
* \ingroup NBS
*
* This file contains webRequest API listeners. These listeners handle HTTP requests in the "before send headers" phase
* and handle messages (on message event).
*
* NBS for Firefox uses the DNS web extension API to resolve domain names. As the domain names are
* cached and needs to be resolved without NBS, the performance impact should be negligible. The DNS
* API is not used for proxied requests to prevent DNS leaks of resolutions that would be initiated
* by the DNS proxy. See https://pagure.io/JShelter/webextension/issue/41 for more details.
*/
/**
* The event listener, hooked up to webRequest onBeforeSendHeaders event.
* Receives detail of HTTP request in requestDetail.
* Catches the request, analyzes its origin and target URLs and blocks it/permits it based
* on their IP adresses. Requests coming from public IP ranges targeting the private IPs are
* blocked by default. Others are permitted by default.
*
* \param requestDetail Details of HTTP request.
*/
async function beforeSendHeadersListener(requestDetail)
{
//If either of information is undefined, permit it
//originUrl happens to be undefined for the first request of the page loading the document itself
if (requestDetail.originUrl === undefined || requestDetail.url === undefined || requestDetail.originUrl === "null" || requestDetail.url === "null")
{
return {cancel:false};
}
var sourceDomain = getEffectiveDomain(requestDetail.originUrl);
var fullSourceDomain = new URL(requestDetail.originUrl).hostname;
var targetDomain = getEffectiveDomain(requestDetail.url);
var fullTargetDomain = new URL(requestDetail.url).hostname;
var targetIP;
var sourceIP;
var isSourcePrivate = false;
var isDestinationPrivate = false;
var destinationResolution = "";
var sourceResolution = "";
var blockNotifications = false;
const {proxyInfo} = requestDetail; // see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo
const dnsAllowed = !(proxyInfo && (proxyInfo.type && proxyInfo.type.startsWith("http") || proxyInfo.proxyDNS));
//Host found among user's trusted hosts, allow it right away
if (isNbsWhitelisted(sourceDomain))
{
return {cancel:false};
}
//Checking type of SOURCE URL
if (isIPV4(sourceDomain)) //SOURCE is IPV4 adddr
{
//Checking privacy of IPv4
if (isIPV4Private(sourceDomain))
{
//Source is IPv4 private
isSourcePrivate = true;
}
}
else if(isIPV6(sourceDomain)) //SOURCE is IPV6
{
//Checking privacy of IPv6
if (isIPV6Private(sourceDomain))
{
//Source is IPv6 private
isSourcePrivate = true;
}
}
else if (dnsAllowed) //SOURCE is hostname
{
//Resolving DNS query for source domain
sourceResolution = browser.dns.resolve(fullSourceDomain).then((val) =>
{
//Assigning source IPs
sourceIP = val;
//More IPs could have been found, for each of them
for (let ip of sourceIP.addresses)
{
//Check whether it's IPv4
if (isIPV4(ip))
{
if (isIPV4Private(ip))
{
//Source is IPv4 private
isSourcePrivate = true;
}
}
else if (isIPV6(ip))
{
if (isIPV6Private(ip))
{
//Source is IPv6 private
isSourcePrivate = true;
}
}
}
});
}
//Analyzing targetDomain
//Check IPv4/IPv6 and privacy
if (isIPV4(targetDomain))
{
if (isIPV4Private(targetDomain))
{
isDestinationPrivate = true;
if (targetDomain === "0.0.0.0") {
blockNotifications = true;
}
}
}
else if(isIPV6(targetDomain))
{
if (isIPV6Private(targetDomain))
{
isDestinationPrivate = true;
if (ip === "[::]") {
blockNotifications = true;
}
}
}
else if (dnsAllowed) //Target is hostname
{
//Resolving DNS query for destination domain
destinationResolution = browser.dns.resolve(fullTargetDomain).then((val) =>
{
//Assigning source IPs
targetIP = val;
//More IPs could have been found, for each of them
for (let ip of targetIP.addresses)
{
//Check whether it's IPv4
if (isIPV4(ip))
{
if (isIPV4Private(ip))
{
//Destination is IPv4 private
isDestinationPrivate = true;
if (ip === "0.0.0.0") {
blockNotifications = true;
}
}
}
else if (isIPV6(ip))
{
if (isIPV6Private(ip))
{
//Destination is IPv6 private
isDestinationPrivate = true;
if (ip === "[::]") {
blockNotifications = true;
}
}
}
}
});
}
//Wait till all DNS resolutions are done, because its neccessary for upcoming actions
await Promise.all([sourceResolution, destinationResolution]);
//Blocking direction Public -> Private
if (!isSourcePrivate && isDestinationPrivate)
{
if (!blockNotifications) {
notifyBlockedRequest(sourceDomain, targetDomain, requestDetail.tabId);
}
return {cancel: nbsSettings.blocking ? true : false}
}
else //Permitting others
{
return {cancel: false};
}
}