mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Add periodic user agent string updates
This commit is contained in:
9
docs/ua.json
Normal file
9
docs/ua.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./ua.schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"platforms": {
|
||||||
|
"win": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
||||||
|
"mac": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
||||||
|
"linux": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
docs/ua.schema.json
Normal file
22
docs/ua.schema.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$defs": {
|
||||||
|
"ua_string": { "type": "string", "minLength": 1 }
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"$schema": { "type": "string" },
|
||||||
|
"version": { "type": "integer", "const": 1 },
|
||||||
|
"platforms": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"win": { "$ref": "#/$defs/ua_string" },
|
||||||
|
"mac": { "$ref": "#/$defs/ua_string" },
|
||||||
|
"linux": { "$ref": "#/$defs/ua_string" }
|
||||||
|
},
|
||||||
|
"required": ["win", "mac", "linux"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["version", "platforms"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import deviceManager from "./deviceManager";
|
|||||||
import { initAction } from "./action";
|
import { initAction } from "./action";
|
||||||
import { initMenus } from "./menus";
|
import { initMenus } from "./menus";
|
||||||
import { initWhitelist } from "./whitelist";
|
import { initWhitelist } from "./whitelist";
|
||||||
|
import { cacheUaInfo, getChromeUserAgentString } from "../lib/userAgents";
|
||||||
|
|
||||||
const _ = browser.i18n.getMessage;
|
const _ = browser.i18n.getMessage;
|
||||||
|
|
||||||
@@ -143,5 +144,6 @@ async function init() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cacheUaInfo();
|
||||||
cacheBaseConfig();
|
cacheBaseConfig();
|
||||||
init();
|
init();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logger from "../lib/logger";
|
import logger from "../lib/logger";
|
||||||
import options from "../lib/options";
|
import options from "../lib/options";
|
||||||
|
|
||||||
import { getChromeUserAgent } from "../lib/userAgents";
|
import { cacheUaInfo, getChromeUserAgentString } from "../lib/userAgents";
|
||||||
import { RemoteMatchPattern } from "../lib/matchPattern";
|
import { RemoteMatchPattern } from "../lib/matchPattern";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -41,11 +41,17 @@ let customUserAgent: string | undefined;
|
|||||||
export async function initWhitelist() {
|
export async function initWhitelist() {
|
||||||
logger.info("init (whitelist)");
|
logger.info("init (whitelist)");
|
||||||
|
|
||||||
|
await cacheUaInfo();
|
||||||
|
|
||||||
if (!platform) {
|
if (!platform) {
|
||||||
|
const browserInfo = await browser.runtime.getBrowserInfo();
|
||||||
|
|
||||||
// TODO: Allow hybrid UA to be configurable
|
// TODO: Allow hybrid UA to be configurable
|
||||||
platform = (await browser.runtime.getPlatformInfo()).os;
|
platform = (await browser.runtime.getPlatformInfo()).os;
|
||||||
chromeUserAgent = getChromeUserAgent(platform);
|
chromeUserAgent = await getChromeUserAgentString(platform);
|
||||||
chromeUserAgentHybrid = getChromeUserAgent(platform, true);
|
chromeUserAgentHybrid = await getChromeUserAgentString(platform, {
|
||||||
|
hybridFirefoxVersion: browserInfo.version
|
||||||
|
});
|
||||||
if (!chromeUserAgent) {
|
if (!chromeUserAgent) {
|
||||||
throw logger.error("Failed to get Chrome UA string");
|
throw logger.error("Failed to get Chrome UA string");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,4 +106,4 @@ export default {
|
|||||||
siteWhitelistCustomUserAgent: "",
|
siteWhitelistCustomUserAgent: "",
|
||||||
|
|
||||||
showAdvancedOptions: false
|
showAdvancedOptions: false
|
||||||
} as Options;
|
} satisfies Options as Options;
|
||||||
|
|||||||
@@ -1,25 +1,90 @@
|
|||||||
const PLATFORM_MAC = "Macintosh; Intel Mac OS X 12_5";
|
import logger from "./logger";
|
||||||
const PLATFORM_MAC_HYBRID = "Macintosh; Intel Mac OS X 12_5; rv:103.0";
|
import { TypedStorageArea } from "./TypedStorageArea";
|
||||||
const PLATFORM_WIN = "Windows NT 10.0; Win64; x64";
|
|
||||||
const PLATFORM_LINUX = "X11; Linux x86_64";
|
|
||||||
|
|
||||||
const UA_CHROME =
|
// Bundle UA info at time of build (as a fallback)
|
||||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36";
|
import bundledUaInfo from "../../../docs/ua.json";
|
||||||
const UA_HYBRID = "Chrome/104.0.0.0 Gecko/20100101 Firefox/103.0";
|
|
||||||
|
|
||||||
export function getChromeUserAgent(platform: string, hybrid = false) {
|
const UA_INFO_ENDPOINT = "https://hensm.github.io/fx_cast/ua.json";
|
||||||
let platformComponent: string;
|
|
||||||
if (platform === "mac") {
|
interface UaInfo {
|
||||||
platformComponent = hybrid ? PLATFORM_MAC_HYBRID : PLATFORM_MAC;
|
version: 1;
|
||||||
} else if (platform === "win") {
|
platforms: {
|
||||||
platformComponent = PLATFORM_WIN;
|
[key: string]: string;
|
||||||
} else if (platform === "linux") {
|
};
|
||||||
platformComponent = PLATFORM_LINUX;
|
}
|
||||||
|
|
||||||
|
const uaInfoStorage = new TypedStorageArea<{
|
||||||
|
uaInfo: UaInfo | undefined;
|
||||||
|
uaInfoUpdated: number | undefined;
|
||||||
|
}>(browser.storage.local);
|
||||||
|
|
||||||
|
async function fetchUaInfo(): Promise<UaInfo | null> {
|
||||||
|
try {
|
||||||
|
const res: UaInfo = await fetch(UA_INFO_ENDPOINT).then(r => r.json());
|
||||||
|
if (typeof res.version !== "number" || res.version !== 1) {
|
||||||
|
throw logger.error(
|
||||||
|
`UA info version mismatch (got ${res.version}, expected 1)`
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
typeof res.platforms !== "object" ||
|
||||||
|
res.platforms === null
|
||||||
|
) {
|
||||||
|
throw logger.error("UA info platforms missing or invalid");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("Failed to fetch UA info:", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cacheUaInfo() {
|
||||||
|
const { uaInfoUpdated } = await uaInfoStorage.get("uaInfoUpdated");
|
||||||
|
|
||||||
|
// If never updated or updated more than 24 hours ago
|
||||||
|
if (!uaInfoUpdated || (Date.now() - uaInfoUpdated) / 1000 >= 86400) {
|
||||||
|
logger.info("Fetching updated Chrome User-Agent strings...");
|
||||||
|
const uaInfo = await fetchUaInfo();
|
||||||
|
if (uaInfo) {
|
||||||
|
await uaInfoStorage.set({ uaInfo, uaInfoUpdated: Date.now() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a chrome user */
|
||||||
|
export async function getChromeUserAgentString(
|
||||||
|
platform: string,
|
||||||
|
opts?: { hybridFirefoxVersion?: string }
|
||||||
|
) {
|
||||||
|
const { uaInfo } = await uaInfoStorage.get("uaInfo");
|
||||||
|
if (!uaInfo) {
|
||||||
|
logger.warn("UA info not cached (using bundled version)");
|
||||||
|
}
|
||||||
|
const maybeBundledUaInfo = uaInfo ?? (bundledUaInfo as UaInfo);
|
||||||
|
const userAgentString = maybeBundledUaInfo.platforms[platform];
|
||||||
|
if (userAgentString) {
|
||||||
|
// Return hybrid UA (mostly Firefox UA) if requested
|
||||||
|
if (opts?.hybridFirefoxVersion) {
|
||||||
|
// Sans-patch (e.g. "145.0.1" -> "145.0")
|
||||||
|
const firefoxMajorMinorVersionMatch =
|
||||||
|
opts.hybridFirefoxVersion.match(/^\d+\.\d+/);
|
||||||
|
const componentMatch = userAgentString.match(
|
||||||
|
/Mozilla\/5.0 \(([^\(]+)\) (.+)/
|
||||||
|
);
|
||||||
|
if (firefoxMajorMinorVersionMatch && componentMatch) {
|
||||||
|
const [rv] = firefoxMajorMinorVersionMatch;
|
||||||
|
const [, platformComponent, browserComponent] = componentMatch;
|
||||||
|
// Extract Chrome/xxx part
|
||||||
|
const chromeVersionMatch = browserComponent.match(
|
||||||
|
/(?<= )Chrome\/[^ ]+(?= )/
|
||||||
|
);
|
||||||
|
if (chromeVersionMatch) {
|
||||||
|
const [chromeVersion] = chromeVersionMatch;
|
||||||
|
return `Mozilla/5.0 (${platformComponent}; rv:${rv}) ${chromeVersion} Gecko/20100101 Firefox/${rv}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return;
|
return userAgentString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const browserComponent = hybrid ? UA_HYBRID : UA_CHROME;
|
|
||||||
|
|
||||||
return `Mozilla/5.0 (${platformComponent}) ${browserComponent}`;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import options, { Options } from "../../lib/options";
|
import options, { Options } from "../../lib/options";
|
||||||
import defaultOptions from "../../defaultOptions";
|
import defaultOptions from "../../defaultOptions";
|
||||||
|
|
||||||
import { getChromeUserAgent } from "../../lib/userAgents";
|
import { getChromeUserAgentString } from "../../lib/userAgents";
|
||||||
|
|
||||||
const _ = browser.i18n.getMessage;
|
const _ = browser.i18n.getMessage;
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
let opts: Options | undefined;
|
let opts: Options | undefined;
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const platform = (await browser.runtime.getPlatformInfo()).os;
|
const platform = (await browser.runtime.getPlatformInfo()).os;
|
||||||
defaultUserAgent = getChromeUserAgent(platform);
|
defaultUserAgent = await getChromeUserAgentString(platform);
|
||||||
|
|
||||||
opts = await options.getAll();
|
opts = await options.getAll();
|
||||||
options.addEventListener("changed", async () => {
|
options.addEventListener("changed", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user