Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • Synzvato/decentraleyes
  • gkrishnaks/decentraleyes
  • ExE-Boss/decentraleyes
  • whtsky/decentraleyes
  • grtgarrett/decentraleyes
  • An_dz/decentraleyes
  • Alaska/decentraleyes
  • finn/decentraleyes
  • klippy/decentraleyes
9 results
Show changes
Showing
with 1809 additions and 8 deletions
{
"extensionDescription": {
"message": "Para protektahan ka laban sa pagsubaybay sa pamamagitan ng \"libre\", sentralisadong, paghahatid ng nilalaman.",
"description": "Extension description."
},
"disableProtectionTitle": {
"message": "Huwag paganahin ang proteksyon para sa sayt na ito",
"description": "Disable protection title."
},
"enableProtectionTitle": {
"message": "Paganahin ang proteksyon",
"description": "Enable protection title."
},
"amountInjectedTitle": {
"message": "Laban para sa mga lokal na iniksiyong mga mapagkukunan",
"description": "Amount injected title."
},
"amountInjectedDescription": {
"message": "Ang halaga ng mga lokal na Nilalaman sa Network ng Paghahatid ng pinagmulan sa pag-install mula noong pag-install.",
"description": "Amount injected description."
},
"optionsTitle": {
"message": "Ang mga pagpipilian",
"description": "Options title."
},
"showIconBadgeTitle": {
"message": "Ang naka-displey na ineksyon ay mabibilang sa imahe",
"description": "Show icon badge title."
},
"showIconBadgeDescription": {
"message": "Ipakita ang bilang ng mga injected na pinagmulan sa ekstensyon nang imahe.",
"description": "Show icon badge description."
},
"blockMissingTitle": {
"message": "Harangan ang mga kahilingan para sa mga nawawalang mapagkukunan",
"description": "Block requests for missing resources title."
},
"blockMissingDescription": {
"message": "Kanselahin ang kahilingan na intercepted kung ang mga kinakailangang mapagkukunan ay hindi available sa lokal.",
"description": "Block requests for missing resources description."
},
"disablePrefetchTitle": {
"message": "Huwag paganahin ang prefetching link",
"description": "Disable prefetch title."
},
"disablePrefetchDescription": {
"message": "Pigilan ang mga kahilingan na hindi pinayagan mula sa pagkalat sa paghahatid ng mga network.",
"description": "Disable prefetch description."
},
"stripMetadataTitle": {
"message": "Iliston ang metadata mula sa mga pinayagan na kahilingan",
"description": "Strip metadata title."
},
"stripMetadataDescription": {
"message": "Burahin ang sensitibong datus mula sa mga pinahihintulutang kahilingan ng CDN para sa pinahusay na pagiging pribado nito.",
"description": "Strip metadata description."
},
"whitelistedDomainsTitle": {
"message": "Ibukod ang mga domain mula sa mga iinspeksyon",
"description": "Whitelisted domains title."
},
"whitelistedDomainsDescription": {
"message": "Ipasok ang mga domain upang i-whitelist sila. Paghiwalayin ang maramihang mga entry na may tuldok kuwit (;).",
"description": "Whitelisted domains description."
},
"advancedLabel": {
"message": "Naka-usad",
"description": "Advanced label."
}
}
{
"extensionDescription": {
"message": "Sizi içerik dağıtımıyla yapılan merkezi ve \"ücretsiz\" takipten korur.",
"description": "Extension description."
},
"disableProtectionTitle": {
"message": "Bu sitede korumayı devre dışı bırak",
"description": "Disable protection title."
},
"enableProtectionTitle": {
"message": "Korumayı etkinleştir",
"description": "Enable protection title."
},
"amountInjectedTitle": {
"message": "Yerel olarak eklenen kaynaklar için sayaç",
"description": "Amount injected title."
},
"amountInjectedDescription": {
"message": "Kurulumdan bu yana yerel İçerik Dağıtım Ağı kaynak eklemelerinin miktarı.",
"description": "Amount injected description."
},
"optionsTitle": {
"message": "Seçenekler",
"description": "Options title."
},
"showIconBadgeTitle": {
"message": "Simgede enjeksiyon sayaçlarını göster",
"description": "Show icon badge title."
},
"showIconBadgeDescription": {
"message": "Enjekte edilen kaynakların sayısını uzantı simgesinde gösterin.",
"description": "Show icon badge description."
},
"blockMissingTitle": {
"message": "Eksik kaynaklar için gelen istekleri engelle",
"description": "Block requests for missing resources title."
},
"blockMissingDescription": {
"message": "İstenilen kaynak yerel olarak mevcut değilse, yakalanan isteği iptal et.",
"description": "Block requests for missing resources description."
},
"disablePrefetchTitle": {
"message": "Bağlantı önalımını devre dışı bırak",
"description": "Disable prefetch title."
},
"disablePrefetchDescription": {
"message": "İzin verilmeyen isteklerin dağıtım ağlarına sızmasını engelleyin.",
"description": "Disable prefetch description."
},
"stripMetadataTitle": {
"message": "Meta verilerini izin verilen isteklerden ayır",
"description": "Strip metadata title."
},
"stripMetadataDescription": {
"message": "İleri düzey gizlilik için, izin verilen CDN isteklerinden hassas verileri sil.",
"description": "Strip metadata description."
},
"whitelistedDomainsTitle": {
"message": "Alanları denetimlerden hariç tut",
"description": "Whitelisted domains title."
},
"whitelistedDomainsDescription": {
"message": "Beyaz listeye eklemek için alan adlarını girin. Birden çok girdiyi noktalı virgül (;) ile ayırın.",
"description": "Whitelisted domains description."
},
"advancedLabel": {
"message": "Gelişmiş",
"description": "Advanced label."
}
}
{
"extensionDescription": {
"message": "Захищає вас від стеження \"безкоштовними\", централізованими мережами розповсюдження ресурсів.",
"description": "Extension description."
},
"disableProtectionTitle": {
"message": "Вимкнути захист на цій сторінці",
"description": "Disable protection title."
},
"enableProtectionTitle": {
"message": "Увімкнути захист",
"description": "Enable protection title."
},
"amountInjectedTitle": {
"message": "Лічильник ресурсів замінених локальними",
"description": "Amount injected title."
},
"amountInjectedDescription": {
"message": "Кількість ресурсів наданих локальною Мережею Розповсюдження Контенту (CDN) з початку встановлення.",
"description": "Amount injected description."
},
"optionsTitle": {
"message": "Параметри",
"description": "Options title."
},
"showIconBadgeTitle": {
"message": "Показувати кількість ін'єкцій на іконці",
"description": "Show icon badge title."
},
"showIconBadgeDescription": {
"message": "Показувати кількість локально наданих ресурсів на іконці розширення.",
"description": "Show icon badge description."
},
"blockMissingTitle": {
"message": "Блокувати запити для відсутніх ресурсів",
"description": "Block requests for missing resources title."
},
"blockMissingDescription": {
"message": "Скасувати перехоплений запит, якщо необхідний ресурс відсутній локально.",
"description": "Block requests for missing resources description."
},
"disablePrefetchTitle": {
"message": "Відключити попереднє завантаженя посилань",
"description": "Disable prefetch title."
},
"disablePrefetchDescription": {
"message": "Запобігти витік заборонених запитів в мережі розповсюдження.",
"description": "Disable prefetch description."
},
"stripMetadataTitle": {
"message": "Видалити метадані з дозволених запитів",
"description": "Strip metadata title."
},
"stripMetadataDescription": {
"message": "Стерти чутливі дані з запитів до дозволених CDN для кращої приватності.",
"description": "Strip metadata description."
},
"whitelistedDomainsTitle": {
"message": "Вимкнути перевірку для доменів",
"description": "Whitelisted domains title."
},
"whitelistedDomainsDescription": {
"message": "Введіть домени для включення в білий список. У якості розділювача використовуйте крапку з комою (;).",
"description": "Whitelisted domains description."
},
"advancedLabel": {
"message": "Розширені",
"description": "Advanced label."
}
}
{
"extensionDescription": {
"message": "Bảo vệ bạn khỏi bị theo dõi thông qua việc phân phối nội dung \"miễn phí\", tập trung.",
"description": "Extension description."
},
"disableProtectionTitle": {
"message": "Tắt bảo vệ cho trang này",
"description": "Disable protection title."
},
"enableProtectionTitle": {
"message": "Bật bảo vệ",
"description": "Enable protection title."
},
"amountInjectedTitle": {
"message": "Đếm các tài nguyên cục bộ được chèn vào",
"description": "Amount injected title."
},
"amountInjectedDescription": {
"message": "Số lượng tài nguyên cục bộ đã chèn của mạng phân phối nội dung (CDN) kể từ khi cài đặt.",
"description": "Amount injected description."
},
"optionsTitle": {
"message": "Tùy chỉnh",
"description": "Options title."
},
"showIconBadgeTitle": {
"message": "Hển thị số lượng chèn trên biểu tượng",
"description": "Show icon badge title."
},
"showIconBadgeDescription": {
"message": "Hiển thị số tài nguyên đã chèn trên biểu tượng của tiện ích.",
"description": "Show icon badge description."
},
"blockMissingTitle": {
"message": "Chặn yêu cầu cho tài nguyên bị thiếu",
"description": "Block requests for missing resources title."
},
"blockMissingDescription": {
"message": "Hủy yêu cầu bị chặn nếu tài nguyên cần tới không có sẵn.",
"description": "Block requests for missing resources description."
},
"disablePrefetchTitle": {
"message": "Vô hiệu tìm nạp trước liên kết",
"description": "Disable prefetch title."
},
"disablePrefetchDescription": {
"message": "Ngăn chặn các yêu cầu không được phép rò rỉ ra ngoài mạng lưới phân phối.",
"description": "Disable prefetch description."
},
"stripMetadataTitle": {
"message": "Loại bỏ siêu dữ liệu (metadata) khỏi các yêu cầu được phép",
"description": "Strip metadata title."
},
"stripMetadataDescription": {
"message": "Xóa dữ liệu nhạy cảm khỏi các yêu cầu CDN được phép để cải thiện quyền riêng tư.",
"description": "Strip metadata description."
},
"whitelistedDomainsTitle": {
"message": "Loại trừ tên miền khỏi việc giám sát",
"description": "Whitelisted domains title."
},
"whitelistedDomainsDescription": {
"message": "Nhập các miền để đưa vào danh sách trắng. Phân tách các mục nhập bằng dấu chấm phẩy (;).",
"description": "Whitelisted domains description."
},
"advancedLabel": {
"message": "Nâng cao",
"description": "Advanced label."
}
}
{
"extensionDescription": {
"message": "保护你免受中心化的内容分发网络(CDN)的跟踪。",
"description": "Extension description."
},
"disableProtectionTitle": {
"message": "在此网站上禁用保护",
"description": "Disable protection title."
},
"enableProtectionTitle": {
"message": "启用保护",
"description": "Enable protection title."
},
"amountInjectedTitle": {
"message": "本地注入资源的计数器",
"description": "Amount injected title."
},
"amountInjectedDescription": {
"message": "自安装以来,从本地“内容分发网络”资源注入数量。",
"description": "Amount injected description."
},
"optionsTitle": {
"message": "选项",
"description": "Options title."
},
"showIconBadgeTitle": {
"message": "在图标上显示注入数量",
"description": "Show icon badge title."
},
"showIconBadgeDescription": {
"message": "在扩展图标上显示注入资源的数量。",
"description": "Show icon badge description."
},
"blockMissingTitle": {
"message": "阻止缺失资源的请求",
"description": "Block requests for missing resources title."
},
"blockMissingDescription": {
"message": "如果请求的资源在本地不可用,则取消被拦截的请求。",
"description": "Block requests for missing resources description."
},
"disablePrefetchTitle": {
"message": "禁用链接预读取",
"description": "Disable prefetch title."
},
"disablePrefetchDescription": {
"message": "避免本应禁止的请求发到网络。",
"description": "Disable prefetch description."
},
"stripMetadataTitle": {
"message": "清除被允许的请求中的元数据",
"description": "Strip metadata title."
},
"stripMetadataDescription": {
"message": "从被允许的 CDN 请求中擦除敏感数据以保护隐私。",
"description": "Strip metadata description."
},
"whitelistedDomainsTitle": {
"message": "排除要检查的域名",
"description": "Whitelisted domains title."
},
"whitelistedDomainsDescription": {
"message": "输入要添加到白名单的域名。用分号 (;) 分隔多项。",
"description": "Whitelisted domains description."
},
"advancedLabel": {
"message": "高级",
"description": "Advanced label."
}
}
{
"extensionDescription": {
"message": "保護您不受「免費」、中心化的內容傳遞網路(CDN)的追蹤。",
"description": "Extension description."
},
"disableProtectionTitle": {
"message": "為此網站停用保護",
"description": "Disable protection title."
},
"enableProtectionTitle": {
"message": "啟用保護",
"description": "Enable protection title."
},
"amountInjectedTitle": {
"message": "由本機提供的資源計數器",
"description": "Amount injected title."
},
"amountInjectedDescription": {
"message": "安裝以來,由本機檔案所取代的 CDN 資源數量。",
"description": "Amount injected description."
},
"optionsTitle": {
"message": "選項",
"description": "Options title."
},
"showIconBadgeTitle": {
"message": "在圖示上顯示被取代的資源數量",
"description": "Show icon badge title."
},
"showIconBadgeDescription": {
"message": "在擴充套件圖示上,顯示被取代的資源數量。",
"description": "Show icon badge description."
},
"blockMissingTitle": {
"message": "封鎖缺少資源時的連線請求",
"description": "Block requests for missing resources title."
},
"blockMissingDescription": {
"message": "若本機沒有所請求的資源,就取消該請求。",
"description": "Block requests for missing resources description."
},
"disablePrefetchTitle": {
"message": "停用連結預讀功能",
"description": "Disable prefetch title."
},
"disablePrefetchDescription": {
"message": "防止發出不被允許的請求到 CDN 網路。",
"description": "Disable prefetch description."
},
"stripMetadataTitle": {
"message": "移除放行請求當中的後設資料",
"description": "Strip metadata title."
},
"stripMetadataDescription": {
"message": "清除放行的 CDN 請求當中的敏感資料,以保護隱私權。",
"description": "Strip metadata description."
},
"whitelistedDomainsTitle": {
"message": "要排除檢查的網域",
"description": "Whitelisted domains title."
},
"whitelistedDomainsDescription": {
"message": "輸入網域即可將其加入白名單。使用分號(;)分隔多個網域。",
"description": "Whitelisted domains description."
},
"advancedLabel": {
"message": "進階",
"description": "Advanced label."
}
}
report.txt
# Decentraleyes Audit
This resource auditor allows any user and extension reviewer to verify the integrity of the bundled resources. It provides a quick and reliable way to compare all bundled libraries to their original sources.
## Prerequisites
* Node.js 20.0.0 *(or higher)*.
## Usage Instructions
1. Open up a terminal, or a command prompt, and `cd` into its directory.
2. Run the audit script by executing *one* of the following commands:
```bash
node run -r # Validate recently updated resources.
node run # Validate all bundled resources.
```
***Note:** Append ` > report.txt` to write the output to a file.*
import globals from 'globals';
import js from '@eslint/js';
import {baseConfiguration} from '../eslint.config.mjs';
export default [
js.configs.recommended,
baseConfiguration,
{
'languageOptions': {
'globals': {
...globals.node
}
}
}
];
{
"name": "decentraleyes-audit",
"version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "decentraleyes-audit",
"version": "2.0.0",
"license": "MPL-2.0",
"engines": {
"node": ">= 20"
}
}
}
}
{ {
"name": "decentraleyes-audit", "name": "decentraleyes-audit",
"version": "1.3.0", "version": "2.0.0",
"author": "Thomas Rientjes", "author": "Thomas Rientjes",
"license": "MPL-2.0", "license": "MPL-2.0",
"description": "Library audit tool for Decentraleyes.", "description": "Resource auditor for Decentraleyes.",
"repository": "https://addons.mozilla.org/firefox/addon/decentraleyes", "repository": "https://git.synz.io/Synzvato/decentraleyes.git",
"keywords": [ "keywords": [
"decentraleyes", "decentraleyes",
"audit" "auditor"
], ],
"main": "run.js", "main": "run.js",
"dependencies": { "type": "module",
"source-map-url": "~0.4.0" "engines": {
"node": ">= 20"
} }
} }
/**
* Resource Auditor
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2014-07-24 (originally "audit")
* @license MPL 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import crypto from 'crypto';
import {promises as fileSystem} from 'fs';
/**
* Constants
*/
const mappingUrlBlockExpression = /\/\*(?:\s*\r?\n(?:\/\/)?)?[#@] sourceMappingURL=([^\s'"]*)\s*\*\/\s*/;
const mappingUrlLineExpression = /\/\/[#@] sourceMappingURL=([^\s'"]*)\s*/;
const localResourceLocation = '../resources';
const localResourceLocationLength = localResourceLocation.length;
const recentlyUpdatedResources = [
'backbone.js/1.4.0',
'jquery/3.6.0',
'jquery/3.6.1',
'jquery/3.6.4',
'jquery/3.7.0',
'jquery/3.7.1',
'moment.js/2.24.0',
'moment.js/2.29.1',
'moment.js/2.29.4',
'underscore.js/1.13.4'
];
/**
* Variables
*/
let verifyAllResources = true;
/**
* Functions
*/
const resourceHasSourceMappingUrl = (resourceContents) => {
return mappingUrlBlockExpression.test(resourceContents) || mappingUrlLineExpression.test(resourceContents);
};
const removeSourceMappingUrlsFromResource = (resourceContents) => {
resourceContents = resourceContents.replace(mappingUrlBlockExpression, '');
resourceContents = resourceContents.replace(mappingUrlLineExpression, '');
return resourceContents;
};
const filterLocalResourcePaths = (localResourcePaths) => {
return localResourcePaths.filter((path) => {
for (const recentlyUpdatedResource of recentlyUpdatedResources) {
if (path.includes(recentlyUpdatedResource)) {
return true;
}
}
return false;
});
};
const determineLocalResourcePaths = async (folderPath) => {
let resourceNames, localResourcePaths;
resourceNames = await fileSystem.readdir(folderPath);
localResourcePaths = [];
for (const resourceName of resourceNames) {
let resourcePath, resourceNodeStatus;
resourcePath = `${folderPath}/${resourceName}`;
resourceNodeStatus = await fileSystem.stat(resourcePath);
if (resourceNodeStatus && resourceNodeStatus.isDirectory()) {
localResourcePaths = [...localResourcePaths, ...(await determineLocalResourcePaths(resourcePath))];
} else {
localResourcePaths.push(resourcePath);
}
}
if (verifyAllResources === false) {
localResourcePaths = filterLocalResourcePaths(localResourcePaths);
}
return localResourcePaths;
};
const getLocalResourceContents = async (fileLocation) => {
try {
return await fileSystem.readFile(fileLocation, 'utf8');
} catch {
return null;
}
};
const getRemoteResource = async (remoteResourceRoute) => {
const remoteResourceUrls = [
`https://ajax.googleapis.com/ajax/libs/${remoteResourceRoute}`,
`https://cdnjs.cloudflare.com/ajax/libs/${remoteResourceRoute}`
];
for (const remoteResourceUrl of remoteResourceUrls) {
const response = await fetch(remoteResourceUrl);
if (response.status === 200) {
return {
'contents': await response.text(),
'url': remoteResourceUrl
};
}
}
throw new Error(`Resource "${remoteResourceRoute}" could not be fetched.`);
};
const hashFileContents = (fileContents) => {
const hash = crypto.createHash('sha3-512');
hash.update(fileContents);
return hash.digest('hex');
};
const compareResources = (localResourceContents, remoteResourceContents, url) => {
let hasSourceMappingUrl, sourceMappingNotice, localFileHash, remoteFileHash, fileHashesMatch;
hasSourceMappingUrl = resourceHasSourceMappingUrl(remoteResourceContents);
sourceMappingNotice = '[ ] REMOTE RESOURCE HAD SOURCE MAPPING URL';
if (hasSourceMappingUrl) {
remoteResourceContents = removeSourceMappingUrlsFromResource(remoteResourceContents);
sourceMappingNotice = '[X] REMOTE RESOURCE HAD SOURCE MAPPING URL';
}
localFileHash = hashFileContents(localResourceContents);
remoteFileHash = hashFileContents(remoteResourceContents);
console.log(`RESOURCE HASH (SHA3-512): ${localFileHash}`);
console.log(`RESOURCE HASH (SHA3-512): ${remoteFileHash}`);
fileHashesMatch = (localFileHash === remoteFileHash);
if (! fileHashesMatch) {
console.log(url);
console.log(remoteResourceContents);
throw new Error('Error: Decentraleyes resource hash mismatch.');
}
console.log();
console.log('[X] LOCAL AND REMOTE RESOURCE HASHES MATCH');
console.log(sourceMappingNotice);
};
const verifyResourceContents = async (resourcePath) => {
let resourceRoute, localResourceContents, remoteResource;
resourceRoute = resourcePath.substring(localResourceLocationLength + 1);
resourceRoute = resourceRoute.substring(0, resourceRoute.length - 1);
localResourceContents = await getLocalResourceContents(resourcePath);
remoteResource = await getRemoteResource(resourceRoute);
console.log(remoteResource.url);
console.log();
console.log(resourceRoute.toUpperCase());
console.log();
// Compare resource content hashes.
compareResources(localResourceContents, remoteResource.contents, remoteResource.url);
console.log();
console.log('------------------------------------------');
console.log();
};
/**
* Initializations
*/
if (process.argv[2] && process.argv[2] === '-r') {
verifyAllResources = false;
}
/**
* Main
*/
(async () => {
let localResourcePaths, amountOfResources, promises;
localResourcePaths = await determineLocalResourcePaths(localResourceLocation);
amountOfResources = localResourcePaths.length;
promises = localResourcePaths.map((localResourcePath) => {
const randomExecutionDelay = Math.floor(Math.random() * (amountOfResources * 750));
return new Promise((resolve) => {
setTimeout(async () => {
await verifyResourceContents(localResourcePath);
resolve();
}, randomExecutionDelay);
});
});
await Promise.all(promises).then(() => {
console.log(' *** FILE INTEGRITY CHECKS COMPLETED');
console.log(` *** ${amountOfResources}/${amountOfResources} RESOURCES WERE ANALYZED`);
console.log();
});
})();
locale decentraleyes de locale/de/
locale decentraleyes en-GB locale/en-GB/
locale decentraleyes en-US locale/en-US/
locale decentraleyes es-ES locale/es-ES/
locale decentraleyes fr locale/fr/
locale decentraleyes ja locale/ja/
locale decentraleyes nl locale/nl/
locale decentraleyes zh-CN locale/zh-CN/
/**
* Request Analyzer
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2016-04-11
* @license MPL 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import Constants from '../../data/constants.js';
import Helpers from '../../utilities/helpers.js';
import ResourceAliases from '../../data/resource/aliases.js';
import ResourceMappings from '../../data/resource/mappings.js';
/**
* Private Functions
*/
const _matchBasePath = (hostMappings, channelPath) => {
for (const basePath of Object.keys(hostMappings)) {
if (channelPath.startsWith(basePath)) {
return basePath;
}
}
return null;
};
const _findLocalTarget = (resourceMappings, basePath, channel) => {
let resourcePath, versionNumber, resourcePattern;
resourcePath = channel.path.replace(basePath, '');
versionNumber = resourcePath.match(Constants.Resource.VERSION_EXPRESSION);
resourcePattern = resourcePath.replace(versionNumber, Constants.Resource.VERSION_PLACEHOLDER);
for (const resourceMold of Object.keys(resourceMappings)) {
if (resourcePattern.startsWith(resourceMold)) {
let targetPath, hostAliases, version;
targetPath = resourceMappings[resourceMold].path;
targetPath = targetPath.replace(Constants.Resource.VERSION_PLACEHOLDER, versionNumber);
hostAliases = ResourceAliases[channel.host];
if (hostAliases && hostAliases[targetPath]) {
const alias = hostAliases[targetPath];
targetPath = alias.path;
version = alias.version;
} else {
version = versionNumber?.[0] ?? targetPath.match(Constants.Resource.VERSION_EXPRESSION);
}
// Prepare and return a local target.
return {'source': channel.host, 'version': version, 'path': targetPath};
}
}
return null;
};
/**
* Public Functions
*/
const determineLocalTarget = (requestDetails) => {
let destinationUrl, destination, hostMappings, basePath, resourceMappings;
destinationUrl = new URL(requestDetails.url);
destination = {'host': destinationUrl.host, 'path': destinationUrl.pathname};
// Use the appropriate mappings for the targeted host.
hostMappings = ResourceMappings[destination.host];
// Resource mapping files are never locally available.
if (Constants.Resource.MAPPING_EXPRESSION.test(destination.path)) {
return null;
}
basePath = _matchBasePath(hostMappings, destination.path);
resourceMappings = hostMappings[basePath];
if (! resourceMappings) {
return null;
}
// Return either a local request target or null.
return _findLocalTarget(resourceMappings, basePath, destination);
};
const isValidCandidate = async (requestDetails, tabDetails) => {
let initiatorDomain, isWhitelisted;
initiatorDomain = Helpers.extractDomainFromUrl(tabDetails.url);
if (initiatorDomain === null) {
initiatorDomain = Constants.Address.EXAMPLE;
}
isWhitelisted = await Helpers.domainIsWhitelisted(initiatorDomain);
if (isWhitelisted) {
return false;
}
// Only requests of type GET can be valid candidates.
return requestDetails.method === Constants.WebRequest.GET;
};
/**
* Exports
*/
export default {
determineLocalTarget,
isValidCandidate
};
/**
* Request Interceptor
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2016-04-06
* @license MPL 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import Constants from '../../data/constants.js';
import DomainExceptions from '../../data/domain/exceptions.js';
import Helpers from '../../utilities/helpers.js';
import RequestAnalyzer from './analyzer.js';
import ResourceEnvironment from '../../data/resource/environments.js';
import State from '../state.js';
import Storage from '../storage.js';
/**
* Private Functions
*/
const _handleMissingCandidate = async (requestUrl, preserveUrl) => {
let blockMissing, requestUrlSegments;
blockMissing = await Storage.getSetting(Constants.Setting.BLOCK_MISSING);
if (blockMissing === true) {
Storage.incrementStatistic(Constants.Statistic.AMOUNT_BLOCKED);
return {'cancel': true};
}
if (preserveUrl === true) {
return {'cancel': false};
}
requestUrlSegments = new URL(requestUrl);
if (requestUrlSegments.protocol === Constants.Address.HTTP) {
requestUrlSegments.protocol = Constants.Address.HTTPS;
requestUrl = requestUrlSegments.toString();
return {'redirectUrl': requestUrl};
} else {
return {'cancel': false};
}
};
const _handleRequest = async (requestDetails) => {
let tab, validCandidate, tabDomain, settings, isSilent, targetDetails, targetPath;
try {
tab = await chrome.tabs.get(requestDetails.tabId);
} catch {
tab = {};
}
validCandidate = await RequestAnalyzer.isValidCandidate(requestDetails, tab);
if (! validCandidate) {
return {'cancel': false};
}
tabDomain = Helpers.extractDomainFromUrl(tab.url);
if (tabDomain === null) {
tabDomain = Constants.Address.EXAMPLE;
}
settings = await Storage.getSettings();
isSilent = false;
if (requestDetails.type === Constants.WebRequestType.XHR) {
if (tabDomain === settings[Constants.Setting.XHR_TEST_DOMAIN]) {
isSilent = true;
} else {
return _handleMissingCandidate(requestDetails.url);
}
}
if (DomainExceptions.literal[tabDomain]) {
return _handleMissingCandidate(requestDetails.url, true);
}
for (const dynamicDomainException of DomainExceptions.dynamic) {
if (dynamicDomainException.test(tabDomain)) {
return _handleMissingCandidate(requestDetails.url, true);
};
}
targetDetails = RequestAnalyzer.determineLocalTarget(requestDetails);
targetPath = targetDetails?.path;
if (! targetPath) {
return _handleMissingCandidate(requestDetails.url);
}
if (! ResourceEnvironment[settings[Constants.ComputedSetting.ENVIRONMENT_NAME]][targetPath]) {
return _handleMissingCandidate(requestDetails.url);
}
State.createRequestContext(requestDetails.requestId, {
'tabIdentifier': requestDetails.tabId, 'targetDetails': targetDetails, 'isSilent': isSilent
});
return {'redirectUrl': await chrome.runtime.getURL(targetPath)};
};
/**
* Event Handlers
*/
chrome.webRequest.onBeforeRequest.addListener(
_handleRequest, {'urls': Helpers.determineValidHosts()},
[Constants.WebRequest.BLOCKING]
);
chrome.webRequest.onErrorOccurred.addListener((requestDetails) => {
State.deleteRequestContext(requestDetails.requestId);
}, {'urls': [Constants.Address.ANY]});
/**
* Request Sanitizer
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2018-01-10
* @license MPL 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import Constants from '../../data/constants.js';
import Helpers from '../../utilities/helpers.js';
import Storage from '../storage.js';
/**
* Private Functions
*/
const _stripMetadata = async (requestDetails) => {
const stripMetadata = await Storage.getSetting(Constants.Setting.STRIP_METADATA);
if (stripMetadata !== false) {
const sensitiveHeaders = [Constants.Header.COOKIE, Constants.Header.ORIGIN, Constants.Header.REFERER];
Storage.incrementStatistic(Constants.Statistic.AMOUNT_SANITIZED);
requestDetails.requestHeaders = requestDetails.requestHeaders.filter((header) => {
return ! sensitiveHeaders.includes(header.name);
});
}
return {
[Constants.WebRequest.HEADERS]: requestDetails.requestHeaders
};
};
/**
* Event Handlers
*/
chrome.webRequest.onBeforeSendHeaders.addListener(
_stripMetadata, {'urls': Helpers.determineValidHosts()},
[Constants.WebRequest.BLOCKING, Constants.WebRequest.HEADERS]
);
/**
* State Manager
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2017-03-10
* @license MPL 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import Constants from '../data/constants.js';
import Helpers from '../utilities/helpers.js';
import Storage from './storage.js';
import View from './view.js';
/**
* Private Constants
*/
const _requestContexts = {};
/**
* Private Functions
*/
const _registerInjection = async (requestIdentifier) => {
let tabIdentifier, targetDetails, injectionIdentifier, tabContext, injectionCount;
({tabIdentifier, targetDetails} = _requestContexts[requestIdentifier]);
injectionIdentifier = targetDetails.source + targetDetails.path + targetDetails.version;
tabContext = await Storage.getTabContext(tabIdentifier);
tabContext[Constants.TabContext.INJECTIONS][injectionIdentifier] = targetDetails;
injectionCount = Object.keys(tabContext[Constants.TabContext.INJECTIONS]).length;
Storage.incrementStatistic(Constants.Statistic.AMOUNT_INJECTED);
View.renderInjectionCount(tabIdentifier, injectionCount);
await Storage.updateTabContext(tabIdentifier, tabContext);
};
const _clearInjections = async (requestDetails) => {
if (requestDetails.frameId === Constants.WebRequest.MAIN_FRAME_ID) {
if (requestDetails.tabId !== chrome.tabs.TAB_ID_NONE) {
const tabContext = await Storage.getTabContext(requestDetails.tabId);
if (tabContext) {
tabContext[Constants.TabContext.INJECTIONS] = {};
Storage.updateTabContext(requestDetails.tabId, tabContext);
}
View.renderInitialTabState(requestDetails.tabId, requestDetails.url);
}
}
};
/**
* Public Functions
*/
const createRequestContext = (requestIdentifier, requestContext) => {
_requestContexts[requestIdentifier] = requestContext;
};
const deleteRequestContext = (requestIdentifier) => {
delete _requestContexts[requestIdentifier];
};
/**
* Initializations
*/
chrome.tabs.query({}, (tabs) => {
for (const tab of tabs) {
View.renderInitialTabState(tab.id, tab.url, false);
}
Storage.createTabContexts(tabs);
});
/**
* Event Handlers
*/
chrome.tabs.onCreated.addListener((tab) => {
Storage.createTabContexts([tab], true);
});
chrome.tabs.onRemoved.addListener((tabIdentifier) => {
Storage.clearTabContext(tabIdentifier);
});
chrome.webRequest.onBeforeRequest.addListener(async (requestDetails) => {
const tabContext = await Storage.getTabContext(requestDetails.tabId);
if (tabContext) {
const tabIdentifier = tabContext[Constants.TabContext.IDENTIFIER];
if (tabIdentifier !== chrome.tabs.TAB_ID_NONE) {
tabContext[Constants.TabContext.URL] = requestDetails.url;
await Storage.updateTabContext(tabIdentifier, tabContext);
}
}
}, {'types': [Constants.WebRequestType.MAIN_FRAME], 'urls': [Constants.Address.ANY]});
chrome.webNavigation.onErrorOccurred.addListener(_clearInjections, {'url': [{'urlContains': ':'}]});
chrome.webNavigation.onCommitted.addListener(_clearInjections, {'url': [{'urlContains': ':'}]});
chrome.webRequest.onBeforeRedirect.addListener((requestDetails) => {
if (typeof _requestContexts[requestDetails.requestId] === 'object') {
if (! _requestContexts[requestDetails.requestId].isSilent) {
_registerInjection(requestDetails.requestId);
}
deleteRequestContext(requestDetails.requestId);
}
}, {'urls': [Constants.Address.ANY]});
chrome.storage.onChanged.addListener((changes) => {
if (Object.keys(changes).includes(Constants.Setting.DISABLE_PREFETCH)) {
Helpers.setPrefetchDisabled(changes[Constants.Setting.DISABLE_PREFETCH].newValue);
}
});
/**
* Initializations
*/
(async () => {
Helpers.setPrefetchDisabled(await Storage.getSetting(Constants.Setting.DISABLE_PREFETCH));
})();
/**
* Exports
*/
export default {
createRequestContext,
deleteRequestContext
};
/**
* Storage Manager
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2024-10-09
* @license MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import Constants from '../data/constants.js';
import SettingDefaults from '../data/setting/defaults.js';
import StatisticDefaults from '../data/statistic/defaults.js';
/**
* Private Constants
*/
const _items = {
'tabContexts': null, 'settings': null, 'statistics': null
};
const _promises = {
'tabContexts': null, 'settings': null, 'statistics': null
};
const _timeouts = {
'tabContexts': null, 'settings': null, 'statistics': null
};
/**
* Private Functions
*/
const _getLocalSettings = async () => {
const localSettings = await chrome.storage.local.get(SettingDefaults);
return localSettings;
};
const _getManagedSettings = async () => {
let managedSettingKeys, managedSettings;
managedSettingKeys = Object.keys(Constants.Setting);
try {
managedSettings = await chrome.storage.managed.get(managedSettingKeys);
if (window.chrome.runtime.lastError) {
throw new Error(window.chrome.runtime.lastError);
}
} catch {
managedSettings = {};
}
return managedSettings;
};
/**
* Public Functions
*/
const getTabContexts = async () => {
if (! _items.tabContexts) {
_promises.tabContexts ??= chrome.storage.session.get(Constants.SessionItem.TAB_CONTEXTS);
const tabContents = (await _promises.tabContexts)?.[Constants.SessionItem.TAB_CONTEXTS];
_items.tabContexts = tabContents ?? {};
_promises.tabContexts = null;
}
return _items.tabContexts;
};
const getTabContext = async (key) => {
const tabContexts = await getTabContexts();
key = String(key);
return tabContexts[key] ?? null;
};
const createTabContexts = async (tabs, replaceExisting = false) => {
const tabContexts = await getTabContexts();
for (const tab of tabs) {
const key = String(tab.id);
if (replaceExisting === true || ! Object.keys(tabContexts).includes(key)) {
tabContexts[key] = {
[Constants.TabContext.IDENTIFIER]: Number(key),
[Constants.TabContext.INJECTIONS]: {},
[Constants.TabContext.URL]: tab.url ?? null
};
}
}
await chrome.storage.session.set({tabContexts});
};
const updateTabContext = async (key, value) => {
const tabContexts = await getTabContexts();
key = String(key);
tabContexts[key] = value;
await chrome.storage.session.set({tabContexts});
};
const clearTabContext = async (key) => {
const tabContexts = await getTabContexts();
key = String(key);
delete tabContexts[key];
await chrome.storage.session.set({tabContexts});
};
const getSettings = async (concise = true) => {
if (! _items.settings) {
let localSettings, managedSettings, settings, environmentName;
_promises.settings ??= Promise.all([_getLocalSettings(), _getManagedSettings()]);
[localSettings, managedSettings] = await _promises.settings;
settings = Object.keys({...localSettings, ...managedSettings}).reduce((accumulator, key) => {
accumulator[key] = {
'value': managedSettings[key] || localSettings[key],
'origin': 'local'
};
if (managedSettings[key]) {
accumulator[key].origin = 'managed';
}
return accumulator;
}, {});
if (settings.blockMissing.value === true || settings.enforceStaging.value === true) {
environmentName = Constants.Environment.STAGING;
} else {
environmentName = Constants.Environment.STABLE;
};
settings[Constants.ComputedSetting.ENVIRONMENT_NAME] = {
'value': environmentName, 'origin': 'computed'
};
_items.settings ??= settings;
_promises.settings = null;
}
if (concise) {
return {...Object.fromEntries(Object.entries(_items.settings)
.map(([key, setting]) => [key, setting.value]))};
}
return _items.settings;
};
const getSetting = async (key, concise = true) => {
const settings = await getSettings(false);
if (! Object.keys(settings).includes(key)) {
throw new Error(`Setting "${key}" does not exist.`);
}
if (concise) {
return settings[key].value;
}
return settings[key];
};
const updateSetting = async (key, value) => {
const settings = await getSettings(false);
if (! Object.keys(settings).includes(key)) {
throw new Error(`Setting "${key}" does not exist.`);
}
if (settings[key].origin === 'computed') {
throw new Error(`Setting "${key}" is read-only.`);
}
await chrome.storage.local.set({[key]: value});
};
const clearSetting = async (key) => {
const settings = await getSettings(false);
if (! Object.keys(settings).includes(key)) {
throw new Error(`Setting "${key}" does not exist.`);
}
if (settings[key].origin === 'computed') {
throw new Error(`Setting "${key}" is read-only.`);
}
chrome.storage.local.remove(key);
};
const getStatistics = async () => {
if (! _items.statistics) {
_promises.statistics ??= chrome.storage.local.get(StatisticDefaults);
const statistics = await _promises.statistics;
if (typeof statistics[Constants.Statistic.AMOUNT_INJECTED] === 'number') {
statistics[Constants.Statistic.AMOUNT_INJECTED] = {
'value': statistics[Constants.Statistic.AMOUNT_INJECTED],
'createdAt': null
};
}
_items.statistics ??= statistics;
_promises.statistics = null;
}
return _items.statistics;
};
const getStatistic = async (key) => {
const statistics = await getStatistics();
if (! Object.keys(statistics).includes(key)) {
throw new Error(`Statistic "${key}" does not exist.`);
}
return statistics[key];
};
const incrementStatistic = async (key) => {
const statistics = await getStatistics();
clearTimeout(_timeouts.statistics);
if (! Object.keys(statistics).includes(key)) {
throw new Error(`Setting "${key}" does not exist.`);
}
statistics[key].value = ++statistics[key].value;
_timeouts.statistics = setTimeout(() => {
chrome.storage.local.set(statistics);
}, 500);
};
const clearStatistic = async (key) => {
const statistics = await getStatistics();
if (! Object.keys(statistics).includes(key)) {
throw new Error(`Statistic "${key}" does not exist.`);
}
clearTimeout(_timeouts.statistics);
chrome.storage.local.remove(key);
};
/**
* Event Handlers
*/
chrome.storage.onChanged.addListener((changes) => {
let updatedKeys, statisticKeys, settingKeys;
updatedKeys = Object.keys(changes);
statisticKeys = Object.values(Constants.Statistic);
settingKeys = Object.values(Constants.Setting);
if (updatedKeys.some((key) => statisticKeys.includes(key))) {
_items.statistics = null;
}
if (updatedKeys.some((key) => settingKeys.includes(key))) {
_items.settings = null;
}
if (updatedKeys.includes(Constants.SessionItem.TAB_CONTEXTS)) {
_items.tabContexts = null;
}
});
/**
* Exports
*/
export default {
getTabContexts,
getTabContext,
createTabContexts,
updateTabContext,
clearTabContext,
getSettings,
getSetting,
updateSetting,
clearSetting,
getStatistics,
getStatistic,
incrementStatistic,
clearStatistic
};
/**
* View Manager
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2024-10-17
* @license MPL 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import Constants from '../data/constants.js';
import Helpers from '../utilities/helpers.js';
import Storage from './storage.js';
import Wrappers from '../utilities/wrappers.js';
/**
* Private Functions
*/
const _renderTabWhitelistState = async (tabIdentifier, url, injectionCount = 0) => {
let domain, domainIsWhitelisted;
domain = Helpers.extractDomainFromUrl(url);
domainIsWhitelisted = await Helpers.domainIsWhitelisted(domain);
if (domainIsWhitelisted) {
Wrappers.action.setIcon({'tabId': tabIdentifier, 'path': Helpers.determineIconPaths('disabled')});
} else {
Wrappers.action.setIcon({'tabId': tabIdentifier, 'path': Helpers.determineIconPaths('default')});
}
if (domainIsWhitelisted && injectionCount <= 0) {
Wrappers.action.setTitle({'tabId': tabIdentifier, 'title': 'Decentraleyes (–)'});
} else {
Wrappers.action.setTitle({'tabId': tabIdentifier, 'title': `Decentraleyes (${injectionCount})`});
}
};
const _clearBadgeText = (tabIdentifier) => {
Wrappers.action.setBadgeText({'tabId': tabIdentifier, 'text': ''});
};
/**
* Public Functions
*/
const renderInitialTabState = async (tabIdentifier, url, clearBadgeText = true) => {
const showIconBadge = await Storage.getSetting(Constants.Setting.SHOW_ICON_BADGE);
_renderTabWhitelistState(tabIdentifier, url);
if (showIconBadge === true && clearBadgeText === true) {
_clearBadgeText(tabIdentifier);
}
};
const renderInjectionCount = async (tabIdentifier, injectionCount) => {
const showIconBadge = await Storage.getSetting(Constants.Setting.SHOW_ICON_BADGE);
if (injectionCount > 0) {
Wrappers.action.setTitle({'tabId': tabIdentifier, 'title': `Decentraleyes (${injectionCount})`});
if (showIconBadge === true) {
Wrappers.action.setBadgeText({'tabId': tabIdentifier, 'text': injectionCount.toString()});
}
}
};
/**
* Event Handlers
*/
chrome.runtime.onInstalled.addListener(async (details) => {
let location, previousVersion, showReleaseNotes;
location = chrome.runtime.getURL('pages/welcome/welcome.html');
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL ||
details.reason === chrome.runtime.OnInstalledReason.UPDATE) {
previousVersion = details.previousVersion;
if (previousVersion && previousVersion.startsWith('3')) {
return; // Do not show release notes after minor updates.
}
if (details.temporary === true) {
return; // Only show release notes on full installations.
}
showReleaseNotes = await Storage.getSetting(Constants.Setting.SHOW_RELEASE_NOTES);
if (showReleaseNotes === true) {
chrome.tabs.create({'url': location, 'active': false});
}
}
});
chrome.storage.onChanged.addListener(async (changes) => {
if (Object.keys(changes).includes(Constants.Setting.SHOW_ICON_BADGE)) {
const tabContexts = await Storage.getTabContexts();
if (changes[Constants.Setting.SHOW_ICON_BADGE].newValue === true) {
for (const tabContext of Object.values(tabContexts)) {
let tabIdentifier, injectionCount;
tabIdentifier = tabContext[Constants.TabContext.IDENTIFIER];
injectionCount = Object.keys(tabContext[Constants.TabContext.INJECTIONS]).length;
renderInjectionCount(tabIdentifier, injectionCount);
}
} else {
for (const tabContext of Object.values(tabContexts)) {
_clearBadgeText(tabContext[Constants.TabContext.IDENTIFIER]);
}
}
}
if (Object.keys(changes).includes(Constants.Setting.WHITELISTED_DOMAINS)) {
const tabContexts = await Storage.getTabContexts();
for (const tabContext of Object.values(tabContexts)) {
_renderTabWhitelistState(
tabContext[Constants.TabContext.IDENTIFIER],
tabContext[Constants.TabContext.URL],
Object.keys(tabContext[Constants.TabContext.INJECTIONS]).length
);
}
}
});
/**
* Initializations
*/
Wrappers.action.setBadgeBackgroundColor({'color': 'rgb(74, 130, 108)'});
Wrappers.action.setBadgeTextColor({'color': 'rgb(255, 255, 255)'});
/**
* Exports
*/
export default {
renderInitialTabState,
renderInjectionCount
};
/**
* Global Constants
* Belongs to Decentraleyes.
*
* @author Thomas Rientjes
* @since 2017-10-27
* @license MPL 2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Public Constants
*/
const Address = {
'ANY': '*://*/*',
'ANY_PATH': '/*',
'ANY_PROTOCOL': '*://',
'CHROME': 'chrome:',
'DECENTRALEYES': 'decentraleyes.org',
'EXAMPLE': 'example.org',
'HTTP': 'http:',
'HTTPS': 'https:',
'WWW_PREFIX': 'www.'
};
const Environment = {
'STABLE': 'stable',
'STAGING': 'staging'
};
const Header = {
'COOKIE': 'Cookie',
'ORIGIN': 'Origin',
'REFERER': 'Referer'
};
const MessageResponse = {
'ASYNCHRONOUS': true,
'SYNCHRONOUS': false
};
const Resource = {
'MAPPING_EXPRESSION': /\.map$/i,
'VERSION_EXPRESSION': /(?:\d{1,2}\.){1,3}\d{1,2}/,
'VERSION_PLACEHOLDER': '{version}'
};
const SessionItem = {
'TAB_CONTEXTS': 'tabContexts'
};
const TabContext = {
'IDENTIFIER': 'identifier',
'INJECTIONS': 'injections',
'URL': 'url'
};
const Setting = {
'BLOCK_MISSING': 'blockMissing',
'DISABLE_PREFETCH': 'disablePrefetch',
'ENFORCE_STAGING': 'enforceStaging',
'SHOW_ICON_BADGE': 'showIconBadge',
'SHOW_RELEASE_NOTES': 'showReleaseNotes',
'STRIP_METADATA': 'stripMetadata',
'WHITELISTED_DOMAINS': 'whitelistedDomains',
'XHR_TEST_DOMAIN': 'xhrTestDomain'
};
const ComputedSetting = {
'ENVIRONMENT_NAME': 'environmentName'
};
const Statistic = {
'AMOUNT_INJECTED': 'amountInjected',
'AMOUNT_SANITIZED': 'amountSanitized',
'AMOUNT_BLOCKED': 'amountBlocked'
};
const WebRequest = {
'GET': 'GET',
'BLOCKING': 'blocking',
'HEADERS': 'requestHeaders',
'MAIN_FRAME_ID': 0
};
const WebRequestType = {
'MAIN_FRAME': 'main_frame',
'XHR': 'xmlhttprequest'
};
/**
* Exports
*/
export default {
Address,
Environment,
Header,
MessageResponse,
Resource,
SessionItem,
TabContext,
Setting,
ComputedSetting,
Statistic,
WebRequest,
WebRequestType
};