diff --git a/add-on/src/lib/redirect-handler/blockOrObserve.ts b/add-on/src/lib/redirect-handler/blockOrObserve.ts index f8f23789d..5065bb088 100644 --- a/add-on/src/lib/redirect-handler/blockOrObserve.ts +++ b/add-on/src/lib/redirect-handler/blockOrObserve.ts @@ -1,6 +1,8 @@ import debug from 'debug' +import isIPFS from 'is-ipfs' import browser from 'webextension-polyfill' import { CompanionState } from '../../types/companion.js' +import { brave } from '../ipfs-client/brave.js' import { IFilter, IRegexFilter, RegexFilter } from './baseRegexFilter.js' import { CommonPatternRedirectRegexFilter } from './commonPatternRedirectRegexFilter.js' import { NamespaceRedirectRegexFilter } from './namespaceRedirectRegexFilter.js' @@ -24,13 +26,25 @@ interface regexFilterMap { regexSubstitution: string } -interface redirectHandlerInput { - originUrl: string +interface redirectPair { + originUrl: string, redirectUrl: string getPort: (state: CompanionState) => string } +interface redirectRegexPair { + regexSubstitution: string, + regexFilter: string +} + +interface redirectHandlerInput extends Omit { + priority?: number + getRedirectUrl: (port: string) => string + getPort: (state: CompanionState) => string +} + type messageToSelfType = typeof GLOBAL_STATE_OPTION_CHANGE | typeof DELETE_RULE_REQUEST + interface messageToSelf { type: messageToSelfType value?: string | Record @@ -86,19 +100,25 @@ const savedRegexFilters: Map = new Map() const DEFAULT_LOCAL_RULES: redirectHandlerInput[] = [ { originUrl: 'http://127.0.0.1', - redirectUrl: 'http://localhost', + getRedirectUrl: (port): string => `http://localhost:${port}/\\1/\\2`, getPort: ({ gwURLString }): string => new URL(gwURLString).port }, { originUrl: 'http://[::1]', - redirectUrl: 'http://localhost', + getRedirectUrl: (port): string => `http://localhost:${port}/\\1/\\2`, getPort: ({ gwURLString }): string => new URL(gwURLString).port }, { originUrl: 'http://localhost', - redirectUrl: 'http://127.0.0.1', + getRedirectUrl: (port): string => `http://localhost:${port}/\\1/\\2`, getPort: ({ apiURL }): string => new URL(apiURL).port - } + }, + ...(brave ? [{ + originUrl: 'http://localhost', + getRedirectUrl: (): string => `\\1://\\2`, + getPort: ({ gwURLString }): string => new URL(gwURLString).port, + priority: 1 + }] : []) as redirectHandlerInput[] ] /** @@ -126,6 +146,18 @@ export function escapeURLRegex (str: string): string { return str.replace(ALLOWED_CHARS_URL_REGEX, '\\$1') } +/** + * Compute the namespace from the URL. This finds the first path segment. + * e.g. http:////path/to/file/or/cid + * + * @param url string + */ +function computeNamespaceFromUrl (url: string): string { + const { pathname } = new URL(url) + // regex to match the first path segment. + return (/\/([^/]+)\//i.exec(pathname)?.[1] ?? '').toLowerCase() +} + /** * Construct a regex filter and substitution for a redirect. * @@ -133,7 +165,7 @@ export function escapeURLRegex (str: string): string { * @param redirectUrl * @returns */ -function constructRegexFilter ({ originUrl, redirectUrl }: IRegexFilter): IFilter { +function constructRegexFilter ({ originUrl, redirectUrl }: redirectPair): redirectRegexPair { // the order is very important here, because we want to match the best possible filter. const filtersToTryInOrder: Array = [ SubdomainRedirectRegexFilter, @@ -248,19 +280,19 @@ async function reconcileRulesAndRemoveOld (state: CompanionState): Promise if (rules.length === 0) { // we need to populate old rules. for (const [regexFilter, { regexSubstitution, id }] of savedRegexFilters.entries()) { - addRules.push(generateAddRule(id, regexFilter, regexSubstitution)) + addRules.push(generateAddRule({ id, regexFilter, regexSubstitution })) } } // make sure that the default rules are added. - for (const { originUrl, redirectUrl, getPort } of DEFAULT_LOCAL_RULES) { + for (const { originUrl, getRedirectUrl, getPort, priority } of DEFAULT_LOCAL_RULES) { const port = getPort(state) const regexFilter = `^${escapeURLRegex(`${originUrl}:${port}`)}\\/${defaultNSRegexStr}\\/${RULE_REGEX_ENDING}` - const regexSubstitution = `${redirectUrl}:${port}/\\1/\\2` + const regexSubstitution = getRedirectUrl(port) if (!savedRegexFilters.has(regexFilter)) { // We need to add the new rule. - addRules.push(saveAndGenerateRule(regexFilter, regexSubstitution)) + addRules.push(saveAndGenerateRule({ regexFilter, regexSubstitution, priority })) } } @@ -276,16 +308,20 @@ async function reconcileRulesAndRemoveOld (state: CompanionState): Promise * @param excludedInitiatorDomains - The domains that are excluded from the rule. * @returns */ -function saveAndGenerateRule ( - regexFilter: string, - regexSubstitution: string, - excludedInitiatorDomains: string[] = [] -): browser.DeclarativeNetRequest.Rule { +function saveAndGenerateRule ({ + regexFilter, + regexSubstitution, + excludedInitiatorDomains = [], + priority +}: redirectRegexPair & { + excludedInitiatorDomains?: string[], + priority?: number +}): browser.DeclarativeNetRequest.Rule { // We need to generate a random ID for the rule. const id = Math.floor(Math.random() * 29999) // We need to save the regex filter and ID to check if the rule already exists later. savedRegexFilters.set(regexFilter, { id, regexSubstitution }) - return generateAddRule(id, regexFilter, regexSubstitution, excludedInitiatorDomains) + return generateAddRule({ id, regexFilter, regexSubstitution, excludedInitiatorDomains, priority }) } /** @@ -296,15 +332,20 @@ function saveAndGenerateRule ( * @param excludedInitiatorDomains - The domains that are excluded from the rule. * @returns */ -export function generateAddRule ( +export function generateAddRule ({ + id, + priority = 10, + regexFilter, + regexSubstitution, + excludedInitiatorDomains = [] +}: redirectRegexPair & { id: number, - regexFilter: string, - regexSubstitution: string, - excludedInitiatorDomains: string[] = [] -): browser.DeclarativeNetRequest.Rule { + priority?: number, + excludedInitiatorDomains?: string[] +}): browser.DeclarativeNetRequest.Rule { return { id, - priority: 1, + priority, action: { type: 'redirect', redirect: { regexSubstitution } @@ -340,7 +381,8 @@ export function generateAddRule ( * @returns {Promise} */ export function addRuleToDynamicRuleSetGenerator ( - getState: () => CompanionState): (input: redirectHandlerInput) => Promise { + getState: () => CompanionState +): (input: redirectPair) => Promise { // setup listeners for the extension. setupListeners({ [GLOBAL_STATE_OPTION_CHANGE]: async (): Promise => { @@ -359,7 +401,7 @@ export function addRuleToDynamicRuleSetGenerator ( }) // returning a closure to avoid passing `getState` as an argument to `addRuleToDynamicRuleSet`. - return async function ({ originUrl, redirectUrl }: redirectHandlerInput): Promise { + return async function ({ originUrl, redirectUrl }: redirectPair): Promise { // update the rules so that the next request is handled correctly. const state = getState() const redirectIsOrigin = originUrl === redirectUrl @@ -390,7 +432,7 @@ export function addRuleToDynamicRuleSetGenerator ( await browser.declarativeNetRequest.updateDynamicRules( { // We need to add the new rule. - addRules: [saveAndGenerateRule(regexFilter, regexSubstitution)], + addRules: [saveAndGenerateRule({ regexFilter, regexSubstitution })], // We need to remove the old rules. removeRuleIds } diff --git a/add-on/src/lib/state.js b/add-on/src/lib/state.js index 3f06a7785..860fb689a 100644 --- a/add-on/src/lib/state.js +++ b/add-on/src/lib/state.js @@ -4,7 +4,7 @@ import { isHostname, safeURL } from './options.js' export const offlinePeerCount = -1 -export const POSSIBLE_NODE_TYPES = ['external', 'brave'] +export const POSSIBLE_NODE_TYPES = ['external', 'external:brave'] /** * diff --git a/add-on/src/options/page.js b/add-on/src/options/page.js index dedbe8473..3c6789444 100644 --- a/add-on/src/options/page.js +++ b/add-on/src/options/page.js @@ -115,12 +115,13 @@ export default function optionsPage (state, emit) { ${resetForm({ onOptionsReset })} - ${supportsBlock + ${supportsBlock() ? '' : redirectRuleForm({ - redirectRules: state.redirectRules, - emit - })} + redirectRules: state.redirectRules, + emit + }) + } ` } diff --git a/test/helpers/mv3-test-helper.ts b/test/helpers/mv3-test-helper.ts index 71b1f1563..dd84846e7 100644 --- a/test/helpers/mv3-test-helper.ts +++ b/test/helpers/mv3-test-helper.ts @@ -28,11 +28,11 @@ export function ensureCallRedirected ({ }): void { if (isManifestV3) { const [args] = browser.declarativeNetRequest.updateDynamicRules.firstCall.args - expect(args.addRules[0]).to.deep.equal(generateAddRule( - args.addRules[0].id, - MV3Expectation.origin + RULE_REGEX_ENDING, - MV3Expectation.destination - )) + expect(args.addRules[0]).to.deep.equal(generateAddRule({ + id: args.addRules[0].id, + regexFilter: MV3Expectation.origin + RULE_REGEX_ENDING, + regexSubstitution: MV3Expectation.destination + })) } else { expect(modifiedRequestCallResp.redirectUrl).to.equal(MV2Expectation) }