Is SetupVPN - Lifetime Free VPN safe?

High risk

SetupVPN encrypts all its API traffic with a per-request XOR key, making it impossible to inspect what data is being sent.

Code analysis found that all communication with the extension's backend is XOR-encrypted using a key included in each request header, rendering standard traffic inspection ineffective. The extension also contains a Go-compiled WebAssembly module with a hardcoded AES key. The content of the transmitted data is technically recoverable but has not yet been analyzed.

SetupVPN Incv4.0.9Chrome Web Store
75Risk
Who publishes it

SetupVPN Inc - 2 other listings from the same operator, none carrying a finding

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
SetupVPN Inc
Declared legal entity
SetupVPN Inc
Registered address
815 Ponce de Leon Suite 210, Miami, FL 33134-3038, US
Registered contact
Osman Taskin

Same operator - 2 listings

Published under a different store account, but sharing the registered address, contact or declared legal entity this one gave the store.

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Remote Config: 7-Mirror CDN Rotates All API Infrastructure Every 6 Hours

SetupVPN doesn't hardcode servers.

It fetches tierssv.json from one of 7 CDN mirrors every 6h, listing domains to use next, letting operators swap endpoints with no update.

Mirrors: DigitalOcean, S3, R2, Vultr, Linode, GitHub, Bitbucket.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You install SetupVPN or open Chrome after installing it.

The config fetch fires once at install and then every 6 hours via a Chrome alarm, no user interaction needed.

The extension did this

The extension downloads a server config file from one of 7 CDN mirrors and replaces all API endpoints.

The new mainbase and tierbase domain lists immediately take effect for all future requests. The operator can push completely different infrastructure to 9 million users within 6 hours.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://tierbase3.fra1.cdn.digitaloceanspaces.com/tierssv.json
200 OK, JSON, ~2 KB, contains mainbase[] (12 domains), tierbase[] (5 domains), update_interval_hours: 6
Headers
Originchrome-extension://oofgbpoabipfcfjapgnbbjjaenockbdp
Sec-Fetch-Modecors
Sec-Fetch-Sitecross-site
03EvidenceSTORAGE DUMP
What's stored on your device

The live server config from CDN. mainbase lists the 12 domains the extension sends data to; the operator can push a new list any time.

Locationchrome.storage.local key 'ext_serversjson'
Contents
{
 "retcode": 200,
 "data": {
 "version": 4,
 "update_interval_hours": 6,
 "settings": {
 "tierbase_search_timeout": 15000,
 "mainbase_search_timeout": 20000,
 "tierbase_api_timeout": 35000,
 "mainbase_api_timeout": 30000,
 "proxy_search_timeout": 30000,
 "mirror_timeout": 20000,
 "base_ttl_days": 90,
 "bs_consequtive_fails": 42
 },
 "mainbase": [
 "https://lllm.scanners.fun",
 "https://uabh.talked.run",
 "https://mjgu.figure.run",
 "https://xcxx.pointed.cc",
 "https://uaia.scanners.fun",
 "https://icax.figure.run",
 "https://1.foreground.work",
 "https://1.awakened.work",
 "https://1.6912044.cc",
 "https://1.default2024.uk",
 "https://1.sahi.uk",
 "https://1.area9.uk"
 ],
 "tierbase": [
 "https://sxkk.pointed.cc",
 "https://api.keepthisdomain.com",
 "https://1.allnine.uk",
 "https://ksho.uk",
 "https://3245.uk"
 ],
 "uibase": [
 "https://user1.setupvpn.com",
 "https://user2.setupvpn.com",
 "https://user3.setupvpn.com",
 "https://user4.setupvpn.com",
 "https://user5.setupvpn.com",
 "https://user6.setupvpn.com",
 "https://user7.setupvpn.com",
 "https://user8.setupvpn.com",
 "https://user9.setupvpn.com",
 "https://user10.setupvpn.com",
 "https://user11.setupvpn.com",
 "https://user13.setupvpn.com",
 "https://user14.setupvpn.com",
 "https://user15.setupvpn.com"
 ],
 "mirrors": [
 "https://tierbase3.fra1.cdn.digitaloceanspaces.com/tierssv.json",
 "https://tierbase4.s3.amazonaws.com/tierssv.json",
 "https://pub-8029ed10cf4e4db0b3757e6b82ef7a40.r2.dev/tierssv.json",
 "https://ams1.vultrobjects.com/tierupdate2/tierssv.json",
 "https://mirror4.es-mad-1.linodeobjects.com/tierssv.json",
 "https://raw.githubusercontent.com/the7c/update/master/master/ui/data.json",
 "https://bitbucket.org/the7c/update/raw/master/edge/pub/data.json"
 ],
 "t": 1736093012935
 }
}
04EvidenceCODE COMPARE
The code that does this

Mirror fetch and server config dispatch (background.bundle.js)

What it actually does
Fallback config and mirror URLs — readable
// UPDATE_SERVERSJSON is the Redux action type that replaces all API endpoints.
const UPDATE_SERVERSJSON = 'UPDATE_SERVERSJSON';

// Hardcoded fallback config — used only if no CDN mirror is reachable.
// In practice, getTiers() fires at startup and CDN response overwrites this immediately.
const DEFAULT_SERVER_CONFIG = {
 retcode: 200,
 data: {
 version: 4,
 update_interval_hours: 6, // server can change this
 mainbase: [ // 12 API endpoints — all fingerprinted requests go here
 'https://lllm.scanners.fun',
 'https://uabh.talked.run',
 'https://mjgu.figure.run',
 'https://xcxx.pointed.cc',
 'https://uaia.scanners.fun',
 'https://icax.figure.run',
 'https://1.foreground.work',
 'https://1.awakened.work',
 'https://1.6912044.cc',
 'https://1.default2024.uk',
 'https://1.sahi.uk',
 'https://1.area9.uk'
 ],
 tierbase: [ // 5 secondary endpoints
 'https://sxkk.pointed.cc',
 'https://api.keepthisdomain.com',
 'https://1.allnine.uk',
 'https://ksho.uk',
 'https://3245.uk'
 ],
 mirrors: [ // 7 CDN mirrors tried in order
 'https://tierbase3.fra1.cdn.digitaloceanspaces.com/tierssv.json',
 'https://tierbase4.s3.amazonaws.com/tierssv.json',
 'https://pub-8029ed10cf4e4db0b3757e6b82ef7a40.r2.dev/tierssv.json',
 'https://ams1.vultrobjects.com/tierupdate2/tierssv.json',
 'https://mirror4.es-mad-1.linodeobjects.com/tierssv.json',
 'https://raw.githubusercontent.com/the7c/update/master/master/ui/data.json',
 'https://bitbucket.org/the7c/update/raw/master/edge/pub/data.json'
 ]
 }
};
getTiers interval setup — readable
// On startup: create a repeating alarm named 'int1' tied to getTiers().
// updateIntervalTime is derived from update_interval_hours in the last fetched config.
this.updateInterval = new IntervalTimer(
 this.getTiers.bind(this),
 this.updateIntervalTime, // default 6 hours = 21600000ms; server-adjustable
 'int1' // alarm name visible in chrome.alarms
);
this.updateInterval.start();
this.getTiers(); // also fires immediately at startup

// isValidTierResponse() accepts the response only if:
// retcode === 200
// mainbase.length > 2
// tierbase.length > 2
// update_interval_hours must be integer 1 < x < 168 (1 week max)
05EvidenceTHIRD PARTY LIST
CDN mirrors hosting the server config
  • tierbase3.fra1.cdn.digitaloceanspaces.com

    Primary CDN mirror (DigitalOcean Spaces, Frankfurt). Confirmed fetched in dynamic analysis session. Hosts tierssv.json, the extension infrastructure config.

  • tierbase4.s3.amazonaws.com

    AWS S3 mirror. Fallback if DigitalOcean mirror is unreachable. Bucket name: tierbase4.

  • pub-8029ed10cf4e4db0b3757e6b82ef7a40.r2.dev

    Cloudflare R2 object storage mirror. The hex ID is the R2 bucket public access subdomain.

  • ams1.vultrobjects.com

    Vultr Object Storage mirror (Amsterdam). Path: /tierupdate2/tierssv.json.

  • mirror4.es-mad-1.linodeobjects.com

    Linode Object Storage mirror (Madrid). Five-CDN redundancy makes the config highly available and resistant to domain blocking.

  • raw.githubusercontent.com

    GitHub raw file mirror. Repository: the7c/update, path master/master/ui/data.json. Public repo acts as a free, highly available CDN fallback.

  • bitbucket.org

    Bitbucket raw file mirror. Repository: the7c/update, path master/edge/pub/data.json. Same operator (user the7c) controls both GitHub and Bitbucket mirrors.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

WASM Fingerprint: Device + Browser Profiling on Every API Call

SetupVPN embeds a Go WASM binary (534KB) fingerprinting the browser at startup, no prompt: screen size, storage quota, heap size, mouse movement, incognito state, DevTools/timezone/Selenium detection, on every POST, XOR-encrypted, claim135.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open Chrome with SetupVPN installed.

No action is required; the fingerprint is collected automatically at extension startup.

The extension did this

The extension builds a device fingerprint and attaches it to every request to SetupVPN servers.

A Go-compiled WASM binary exports 9 fingerprinting symbols that are called by the background service worker. The results are merged into the params for all POST requests via po().

02EvidenceFIELD TABLE
Fingerprint data collected by the WASM module
FieldValueWhy it matters
Screen dimensions
outerWidth=1920, innerWidth=1904, outerHeight=1080, innerHeight=969Outer and inner width/height of your browser window, helps uniquely identify your display configuration.
Storage quota
12884901888 bytes (12 GB)How much temporary storage your browser has allocated, a stable, hard-to-spoof device fingerprint signal.
JS heap size limit
4294705152 bytes (4 GB)Maximum memory your browser process can allocate, varies by OS, Chrome version, and hardware.
Mouse movement data
mouseMovement=[{x:413,y:208,t:104},{x:414,y:209,t:118}]The speed and pattern of your mouse movements, used as a biometric signal and bot-detection measure.
Incognito / private mode
falseWhether you are browsing in an incognito window, changes the risk profile of your session for the server.
DevTools open
falseWhether you have the browser developer tools open, extension uses this to detect researchers or power users.
Selenium / automation detection
falseWhether your browser is being controlled by automated test software, used to detect security researchers.
Timezone mismatch
falseWhether your system timezone differs from what the browser reports, a VPN or proxy detection signal.
OS and platform
os=mac, platform=desktop, brand=ChromeYour operating system type and Chrome build variant, narrows down your device further.
03EvidenceCODE COMPARE
The code that does this

WASM fingerprint proxy + request construction (background.bundle.js)

What it actually does
WASM instantiation — readable
// Loads main.wasm and wraps all exported Go functions in a Promise-based proxy.
const zi = (() => {
  let wasmReady = false;
  async function initWasm(wasmBytesPromise) {
    const go = new Go();  // Go WASM runtime helper (wasm_exec.js)
    const bytes = await wasmBytesPromise;
    const instance = await WebAssembly.instantiate(bytes, go.importObject);
    go.run(instance.instance);
    wasmReady = true;
  }
  initWasm(fetch('/main.wasm').then(r => r.arrayBuffer()));
  return new Proxy({}, {
    get(_, methodName) {
      return (...args) => new Promise((resolve, reject) => {
        const poll = () => {
          if (!wasmReady) { setTimeout(poll, 10); return; }
          window[methodName](...args, (err, ...result) => {
            if (err) reject(err); else resolve(result);
          });
        };
        poll();
      });
    }
  });
})();
Request pipeline — readable
// Every POST request to SetupVPN servers goes through po().
async function po(endpoint, params, { signal, baselink, requestUrl, timeout, defaults } = {}) {
  const key = generateRandomKey();          // go(): 3-8 random alphanumeric chars
  const headers = buildAuthHeader(key);     // mo(): {Content-Type, Authorization: Basic}
  const requestOptions = { method: 'POST', headers };

  const defaultParams = defaults ? defaults : await getOsDefaults(); // vo()
  const mergedParams = { ...params, ...defaultParams };

  requestOptions.body = encodeBody(mergedParams, key); // bo(): url-encode -> XOR -> base64

  const controller = new AbortController();
  requestOptions.signal = controller.signal;
  const timer = setTimeout(() => controller.abort(), timeout);

  const response = await fetch(requestUrl || (baselink + endpoint), requestOptions);
  clearTimeout(timer);
  return response;
}

// XOR cipher (Eo)
function xorCipher(text, key) {
  const t = text.split('');
  const k = key.split('');
  for (let i = 0; i < t.length; i++)
    t[i] = String.fromCharCode(t[i].charCodeAt(0) ^ k[i % k.length].charCodeAt(0));
  return t.join('');
}

function encodeBody(params, key) {
  const urlEncoded = Object.keys(params)
    .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
    .join('&');
  return btoa(xorCipher(urlEncoded, key));
}

function decodeResponse(body, key) {
  return xorCipher(decodeURIComponent(escape(atob(body))), key);
}
04EvidenceTHIRD PARTY LIST
SetupVPN mainbase domains receiving fingerprinted requests
  • lllm.scanners.fun

    Primary mainbase API endpoint. Receives all user telemetry, auth, and fingerprinted payloads. Domain registered via Namecheap; no obvious business identity.

  • uabh.talked.run

    Secondary mainbase endpoint. Same API as lllm.scanners.fun. Used as failover when primary is unreachable.

  • mjgu.figure.run

    Tertiary mainbase endpoint. Obfuscated subdomain on figure.run.

  • xcxx.pointed.cc

    Mainbase endpoint on pointed.cc infrastructure. Confirmed contacted in DA traffic.

  • uaia.scanners.fun

    Additional scanners.fun mainbase endpoint.

  • icax.figure.run

    Additional figure.run mainbase endpoint.

  • 1.foreground.work

    Mainbase endpoint on foreground.work.

  • 1.awakened.work

    Mainbase endpoint on awakened.work.

  • 1.6912044.cc

    Mainbase endpoint, numeric.cc domain.

  • 1.default2024.uk

    Mainbase endpoint on default2024.uk.

  • 1.sahi.uk

    Mainbase endpoint on sahi.uk.

  • 1.area9.uk

    Mainbase endpoint on area9.uk.

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI SANDBOX

Dual-Layer Encryption Hides All Traffic: XOR + Go WASM AES

SetupVPN wraps every request in two layers.

The outer is a rolling XOR cipher: a random key scrambles the body and travels with it in the same header, reversible by anyone intercepting both. main.wasm adds a Go AES layer with a 44-char key.

01EvidenceCAUSE EFFECT
What actually happens
You did this

The extension prepares any POST request to a SetupVPN server.

This applies to every API call, authentication, server selection, telemetry, and config fetches.

The extension did this

The request body is XOR-encrypted with a per-request random key, and the key is embedded in the Authorization header.

A Go-compiled WASM binary applies an additional AES cipher layer using a 44-character key hardcoded in the binary: 'JHn7tK7OjOWMHHPm2wEgeS3E8Gbq3PPgZx3dnj3Xx?q!!'

02EvidenceOPAQUE REVEAL
Why you can't catch this in DevTools

All POST bodies look like random base64 strings in network tools. Decoding requires extracting the XOR key from the Authorization header.

What's actually being sent
login=user%40example.com&hpassword=<redacted>&os=mac&cv=4.7.2.2&platform=desktop&brand=Chrome
03EvidenceCODE COMPARE
The code that does this

The XOR cipher and encoding pipeline, shipped vs readable

What it actually does
XOR cipher and encoding pipeline — readable
// Eo(text, key) — character-level XOR cipher with a repeating key.
function xorCipher(text, key) {
 const textChars = text.split('');
 const keyChars = key.split('');
 for (let i = 0; i < textChars.length; i++) {
 textChars[i] = String.fromCharCode(
 textChars[i].charCodeAt(0) ^ keyChars[i % keyChars.length].charCodeAt(0)
 );
 }
 return textChars.join('');
}

// go() — generates a random 3-65 char alphanumeric key (3-8 when used in mo())
function generateKey(length) {
 const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
 return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
}

// mo(key) — builds request headers; the XOR key goes in Authorization: Basic
function buildHeaders(key) {
 const suffix = generateKey(Math.round(3 + 5 * Math.random()));
 return {
 'Content-Type': 'text/plain',
 'Authorization': 'Basic ' + btoa(key + ':' + suffix)
 };
}

// bo(params, key) — encodes the request body
function encodeBody(params, key) {
 const urlEncoded = Object.keys(params)
 .filter(k => Object.prototype.hasOwnProperty.call(params, k))
 .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
 .join('&');
 return btoa(xorCipher(urlEncoded, key));
}

// wo(body, key) — decodes a response body
function decodeBody(encodedBody, key) {
 return xorCipher(decodeURIComponent(escape(atob(encodedBody))), key);
}
04EvidenceARTIFACT
Reproduce it yourself

Decodes any captured SetupVPN POST request body using the XOR key extracted from its Authorization header. Paste in the captured Authorization header value and POST body, run the script, and it prints the plaintext params sent by the extension.

RequiresNode.js 16+
setupvpn-xor-decode.js · js
#!/usr/bin/env node
// setupvpn-xor-decode.js
//
// Decodes a captured SetupVPN POST request body.
//
// How SetupVPN encodes requests (from background.bundle.js functions Eo/bo/mo/go/wo):
// 1. Generate a random key K (3-8 alphanumeric chars) — function go()
// 2. URL-encode all request params into a query string P — inside bo()
// 3. XOR every character of P with repeating K -> C — function Eo()
// 4. base64-encode C -> wire body — btoa() in bo()
// 5. Put K in the Authorization: Basic base64(K:randomSuffix) header — function mo()
//
// To decode: atob(body) -> unescape(escape(result)) -> XOR(result, K)
// To get K: atob(authHeaderValue.replace("Basic ", "")).split(":")[0]
//
// Usage:
// 1. Capture a POST to any mainbase domain (lllm.scanners.fun, uabh.talked.run, etc.)
// 2. Copy the value from the Authorization header (everything after "Basic ")
// 3. Copy the raw base64 POST body
// 4. Paste them below as CAPTURED_AUTH_HEADER and CAPTURED_BODY
// 5. node setupvpn-xor-decode.js
//
// Requirements: Node.js 16+ (no npm packages needed)

// ─── PASTE YOUR CAPTURED VALUES HERE ────────────────────────────────────────

// The value from the Authorization header (everything after "Basic "):
const CAPTURED_AUTH_HEADER = 'bVEzazo4QTZi';
// Example: base64 decodes to "mQ3k:8A6b" -> XOR key = "mQ3k"

// The raw POST body (base64 string from the request):
const CAPTURED_BODY = 'S3VHcGpXT1FaUFBVUFFUUVZZV1VUUVZaUVlZWVVQUFZXWVRRWFBVUVZWVQ==';
// Example body encoded with key "mQ3k"

// ─────────────────────────────────────────────────────────────────────────────

/**
 * XOR cipher — exact match of SetupVPN's Eo() function in background.bundle.js.
 * Both text and key are plain JS strings. The key repeats cyclically.
 */
function xorCipher(text, key) {
 const textChars = text.split('');
 const keyChars = key.split('');
 for (let i = 0; i < textChars.length; i++) {
 textChars[i] = String.fromCharCode(
 textChars[i].charCodeAt(0) ^ keyChars[i % keyChars.length].charCodeAt(0)
 );
 }
 return textChars.join('');
}

/**
 * Extract the XOR key from the Authorization: Basic header value.
 * Header format (after base64-decoding): "<xorKey>:<randomSuffix>"
 * Only the part before the first colon is the XOR key.
 */
function extractKey(authHeaderValue) {
 const b64 = authHeaderValue.replace(/^Basic\s+/i, '').trim();
 const decoded = Buffer.from(b64, 'base64').toString('utf8');
 const key = decoded.split(':')[0];
 if (!key) throw new Error('Could not extract XOR key from Authorization header');
 return key;
}

/**
 * Decode a SetupVPN POST body.
 * Mirrors SetupVPN's wo() function: atob -> unescape(escape(s)) -> XOR
 *
 * The unescape/escape step converts latin-1 byte sequences (produced after
 * XOR) back to valid UTF-8 — required for any non-ASCII bytes in the params.
 */
function decodeBody(encodedBody, xorKey) {
 // base64-decode to binary string (latin-1 byte values)
 const rawBytes = Buffer.from(encodedBody, 'base64').toString('binary');
 // unescape(escape(s)) equivalent: percent-encode each byte, then decode as UTF-8
 const unescaped = decodeURIComponent(encodeURIComponent(rawBytes));
 return xorCipher(unescaped, xorKey);
}

/**
 * Parse URL-encoded query string into a plain object.
 */
function parseQueryString(qs) {
 const params = {};
 for (const part of qs.split('&')) {
 if (!part) continue;
 const eqIdx = part.indexOf('=');
 if (eqIdx === -1) { params[decodeURIComponent(part)] = ''; continue; }
 const k = decodeURIComponent(part.slice(0, eqIdx));
 const v = decodeURIComponent(part.slice(eqIdx + 1));
 params[k] = v;
 }
 return params;
}

// ─── Main ─────────────────────────────────────────────────────────────────────

let xorKey;
try {
 xorKey = extractKey(CAPTURED_AUTH_HEADER);
} catch (err) {
 console.error('ERROR extracting XOR key:', err.message);
 console.error('Make sure CAPTURED_AUTH_HEADER is the base64 value from the Authorization header,');
 console.error('NOT including the "Basic " prefix — or paste the full header value and the script');
 console.error('will strip the prefix automatically.');
 process.exit(1);
}

console.log('Authorization header : ' + CAPTURED_AUTH_HEADER);
console.log('Decoded header value : ' + Buffer.from(CAPTURED_AUTH_HEADER.replace(/^Basic\s+/i,''), 'base64').toString('utf8'));
console.log('XOR key extracted : ' + JSON.stringify(xorKey));
console.log('');

let decoded;
try {
 decoded = decodeBody(CAPTURED_BODY, xorKey);
} catch (err) {
 console.error('ERROR decoding body:', err.message);
 console.error('Make sure CAPTURED_BODY is the raw base64 string from the POST body.');
 process.exit(1);
}

console.log('Decoded query string:');
console.log(decoded);
console.log('');

let params;
try {
 params = parseQueryString(decoded);
} catch (err) {
 console.error('Parsed as raw string only (not URL-encoded params):', err.message);
 params = null;
}

if (params && Object.keys(params).length > 0) {
 console.log('Parsed params:');
 const maxKeyLen = Math.max(...Object.keys(params).map(k => k.length));
 for (const [k, v] of Object.entries(params)) {
 console.log(' ' + k.padEnd(maxKeyLen + 2) + '= ' + v);
 }
} else if (params !== null) {
 console.log('(No URL-encoded params found — decoded value shown above is the plaintext)');
}
How to run it
  1. 1
    node setupvpn-xor-decode.js
05EvidenceFIELD TABLE
Hardcoded keys found in main.wasm
FieldValueWhy it matters
Hardcoded AES key / seed
JHn7tK7OjOWMHHPm2wEgeS3E8Gbq3PPgZx3dnj3Xx?q!!A 44-character string baked directly into the compiled WASM binary, cannot be changed without releasing a new extension version.
Secondary key candidate
X2skj39mcLmc2!jsA 16-character string adjacent to AES symbols in the WASM data segment, matches standard AES-128 key length.

What it can do

Permissions this extension asks for, as declared in version 4.0.9. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Route all of your browsing through a server of its choosing

    proxy

  • Store data in your browser

    storage

  • Watch every request your browser makes

    webRequest

  • Show you desktop notifications

    notifications

  • See the address and title of every tab you have open

    tabs

  • See, disable and uninstall your other extensions, including your security ones

    management

  • Schedule its own background tasks

    alarms

  • Block and redirect the requests your browser makes

    declarativeNetRequest

webRequestAuthProvider
Updated 30 September 2026oofgbpoabipfcfjapgnbbjjaenockbdp