Files
BridgeSwarm/extension/origin-allowlist.js
T
snxraven 693814f46b
CI / Build & Test (push) Failing after 52s
Add Discordjs
2026-09-04 22:10:35 -04:00

164 lines
5.3 KiB
JavaScript

/**
* Capability origin allowlist. Shared by the MV3 service worker (importScripts)
* and Node unit tests (module.exports).
*
* Default allow: the examples server origin(s). There is no "allow all" mode —
* an empty user list still includes the examples origin.
*/
(function (root, factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory();
} else {
root.BridgeSwarmOriginAllowlist = factory();
}
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
const DEFAULT_EXAMPLES_PORT = 4173;
const CAP_PACK_PREFIX =
/^(media|fs|sqlite|net|qvac|agent|discord)\.[a-zA-Z0-9_-]+$/;
function defaultOrigins(examplesPort) {
const port = Number(examplesPort) > 0 ? Number(examplesPort) : DEFAULT_EXAMPLES_PORT;
return [
'http://127.0.0.1:' + port,
'http://localhost:' + port,
'http://[::1]:' + port,
];
}
function originFromUrl(raw) {
if (!raw || typeof raw !== 'string') return '';
try {
const u = new URL(raw);
if (u.protocol === 'http:' || u.protocol === 'https:') return u.origin;
if (u.protocol === 'chrome-extension:' || u.protocol === 'moz-extension:') {
return u.origin;
}
if (u.protocol === 'file:') return 'file://';
return u.origin || '';
} catch (_) {
return '';
}
}
function normalizeOrigin(value) {
if (!value || typeof value !== 'string') return '';
const trimmed = value.trim().replace(/\/+$/, '');
if (!trimmed) return '';
if (trimmed === 'file://') return 'file://';
try {
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed)) {
return originFromUrl(trimmed) || trimmed;
}
return originFromUrl('https://' + trimmed) || trimmed;
} catch (_) {
return trimmed;
}
}
function parseOriginList(raw) {
if (Array.isArray(raw)) {
return raw.map(normalizeOrigin).filter(Boolean);
}
if (typeof raw !== 'string') return [];
return raw
.split(/\r?\n/)
.map(normalizeOrigin)
.filter(Boolean);
}
function allowedOrigins(settings) {
const s = settings || {};
const port = s.examplesServerPort;
const extras = parseOriginList(s.capabilityOrigins);
const seen = new Set();
const out = [];
for (const o of defaultOrigins(port).concat(extras)) {
if (!seen.has(o)) {
seen.add(o);
out.push(o);
}
}
return out;
}
function isExtensionOrigin(origin, extensionOrigin) {
if (!origin) return false;
if (extensionOrigin && origin === extensionOrigin) return true;
return origin.startsWith('chrome-extension://') || origin.startsWith('moz-extension://');
}
function isOriginAllowed(origin, settings, opts) {
const o = normalizeOrigin(origin);
if (!o) return false;
if (isExtensionOrigin(o, opts && opts.extensionOrigin)) return true;
const list = allowedOrigins(settings);
if (list.indexOf(o) !== -1) return true;
return false;
}
function isAlwaysApproveOrigin(origin, settings) {
const o = normalizeOrigin(origin);
if (!o) return false;
const list = parseOriginList(settings && settings.agentAlwaysApproveOrigins);
return list.indexOf(o) !== -1;
}
function isCapabilityType(type) {
if (type === 'capability') return true;
return typeof type === 'string' && CAP_PACK_PREFIX.test(type);
}
const QVAC_META_CMDS = { detect: 1, status: 1, catalog: 1, openaiStatus: 1 };
const AGENT_META_CMDS = { status: 1, setGrants: 1, list: 1 };
const DISCORD_META_CMDS = { surface: 1, status: 1 };
function qvacPackAndCmd(msg) {
if (!msg || !msg.type) return null;
if (msg.type === 'capability' && msg.payload) {
return { pack: msg.payload.pack, cmd: msg.payload.cmd };
}
const m = typeof msg.type === 'string' && msg.type.match(/^(qvac|agent|discord)\.([a-zA-Z0-9_-]+)$/);
if (m) return { pack: m[1], cmd: m[2] };
return null;
}
/** True when QVAC/agent inference must be blocked (user has not enabled QVAC).
* qvac.setEnabled is never allowed from pages (background send bypasses this gate). */
function isQvacDisabled(msg, settings) {
const pc = qvacPackAndCmd(msg);
if (pc && pc.pack === 'qvac' && pc.cmd === 'setEnabled') return true;
if (settings && settings.qvacEnabled === true) return false;
if (!pc || (pc.pack !== 'qvac' && pc.pack !== 'agent')) return false;
if (pc.pack === 'qvac' && QVAC_META_CMDS[pc.cmd]) return false;
if (pc.pack === 'agent' && AGENT_META_CMDS[pc.cmd]) return false;
return true;
}
/** True when Discord construct/login must be blocked (user has not enabled Discord).
* discord.setEnabled is never allowed from pages. surface/status stay available. */
function isDiscordDisabled(msg, settings) {
const pc = qvacPackAndCmd(msg);
if (pc && pc.pack === 'discord' && pc.cmd === 'setEnabled') return true;
if (settings && settings.discordEnabled === true) return false;
if (!pc || pc.pack !== 'discord') return false;
if (DISCORD_META_CMDS[pc.cmd]) return false;
return true;
}
return {
DEFAULT_EXAMPLES_PORT,
defaultOrigins,
originFromUrl,
normalizeOrigin,
parseOriginList,
allowedOrigins,
isExtensionOrigin,
isOriginAllowed,
isAlwaysApproveOrigin,
isCapabilityType,
qvacPackAndCmd,
isQvacDisabled,
isDiscordDisabled,
};
});