import fs from "node:fs";
|
import vm from "node:vm";
|
|
const source = fs.readFileSync(process.argv[2], "utf8");
|
const extensionId = "oidmclckpdmpabbfedplkbdplmfcenbb";
|
const sidepanelUrl = `chrome-extension://${extensionId}/sidepanel.html`;
|
let runtimeListener = null;
|
let pageValid = true;
|
let cookieReads = 0;
|
let cookieStoreReads = 0;
|
let nativeConnects = 0;
|
let prepareError = null;
|
let cookieStoreDelayMs = 0;
|
const nativeMessages = [];
|
|
function response(type, phase = "READY", errorCode = null, prepareId = null) {
|
return {
|
schema: 2,
|
type,
|
host_build: "project-info-bili-auth-native-host/1.0.0+20260805.v002",
|
target: "BV1HA3o6oEJJ",
|
phase,
|
progress: 0,
|
error_code: errorCode,
|
formal_filename: null,
|
mapping_filename: null,
|
prepare_id: prepareId
|
};
|
}
|
|
function nativePort() {
|
nativeConnects += 1;
|
let messageListener = null;
|
let disconnectListener = null;
|
return {
|
onMessage: {addListener(listener) { messageListener = listener; }},
|
onDisconnect: {addListener(listener) { disconnectListener = listener; }},
|
postMessage(message) {
|
nativeMessages.push(structuredClone(message));
|
if (message.type === "hello") {
|
setTimeout(() => messageListener(response("hello")), 0);
|
} else if (message.type === "status") {
|
setTimeout(() => messageListener(response("status")), 0);
|
} else if (message.type === "prepare") {
|
setTimeout(() => messageListener(response(
|
"prepare",
|
prepareError ? "FAILED" : "READY",
|
prepareError,
|
message.prepare_id
|
)), 0);
|
}
|
},
|
disconnect() {
|
if (disconnectListener) setTimeout(disconnectListener, 0);
|
}
|
};
|
}
|
|
const chrome = {
|
runtime: {
|
id: extensionId,
|
getURL(path) { return `chrome-extension://${extensionId}/${path}`; },
|
onInstalled: {addListener() {}},
|
onMessage: {addListener(listener) { runtimeListener = listener; }},
|
connectNative() { return nativePort(); }
|
},
|
sidePanel: {setPanelBehavior() {}},
|
tabs: {
|
async query() {
|
return [{id: 41, url: "https://www.bilibili.com/video/BV1HA3o6oEJJ"}];
|
}
|
},
|
scripting: {
|
async executeScript(options) {
|
if (!pageValid) return [{result: null}];
|
const [target, canonicalUrl, nonce] = options.args;
|
return [{result: {
|
target,
|
canonical_url: canonicalUrl,
|
task_nonce: nonce,
|
observed_at_unix_ms: Date.now(),
|
observed_duration_ms: 3133950,
|
video_width: 2560,
|
video_height: 1440,
|
ready_state: 4,
|
eme_present: false
|
}}];
|
}
|
},
|
cookies: {
|
async getAllCookieStores() {
|
cookieStoreReads += 1;
|
if (cookieStoreDelayMs) await new Promise(resolve => setTimeout(resolve, cookieStoreDelayMs));
|
return [{id: "0", tabIds: [41]}];
|
},
|
async getAll() {
|
cookieReads += 1;
|
return [{
|
name: "SYNTHETIC_NAME",
|
value: "SYNTHETIC_VALUE",
|
domain: ".bilibili.com",
|
hostOnly: false,
|
path: "/",
|
secure: true,
|
httpOnly: true,
|
sameSite: "unspecified",
|
session: true
|
}];
|
}
|
}
|
};
|
|
vm.runInNewContext(source, {
|
chrome,
|
crypto: globalThis.crypto,
|
URL,
|
Date,
|
Error,
|
Number,
|
Object,
|
Array,
|
Set,
|
String,
|
Boolean,
|
Math,
|
RegExp,
|
Promise,
|
structuredClone,
|
setTimeout,
|
clearTimeout
|
}, {filename: "background.js"});
|
|
if (!runtimeListener) throw new Error("runtime listener missing");
|
|
function dispatch(action, sender = {id: extensionId, url: sidepanelUrl}) {
|
return new Promise(resolve => {
|
const keepAlive = runtimeListener({action}, sender, resolve);
|
if (!keepAlive) resolve(undefined);
|
});
|
}
|
|
pageValid = false;
|
let result = await dispatch("validate");
|
if (result.ok || cookieReads !== 0 || nativeConnects !== 0) {
|
throw new Error("invalid page crossed the external-action boundary");
|
}
|
|
pageValid = true;
|
result = await dispatch("validate");
|
if (!result.ok || result.state.phase !== "READY" || cookieReads !== 0 || nativeConnects !== 1) {
|
throw new Error("validate did not stop before Cookie access");
|
}
|
|
prepareError = "E_CONFIG_HASH";
|
result = await dispatch("start");
|
if (result.ok || result.error_code !== "E_CONFIG_HASH" || cookieReads !== 0) {
|
throw new Error("fresh prepare failure did not stop before Cookie access");
|
}
|
const failedPrepare = nativeMessages.find(message => message.type === "prepare");
|
if (!failedPrepare || Object.prototype.hasOwnProperty.call(failedPrepare, "cookies")) {
|
throw new Error("prepare was not secret-free");
|
}
|
|
prepareError = null;
|
cookieStoreDelayMs = 20;
|
const concurrent = await Promise.all([dispatch("start"), dispatch("start")]);
|
const successes = concurrent.filter(item => item?.ok);
|
const busy = concurrent.filter(item => !item?.ok && item?.error_code === "E_BUSY");
|
if (successes.length !== 1 || busy.length !== 1 ||
|
successes[0].state.phase !== "CHECKING" || cookieStoreReads !== 1 || cookieReads !== 1) {
|
throw new Error("concurrent start did not fail closed before the second Cookie read");
|
}
|
cookieStoreDelayMs = 0;
|
const prepareIndex = nativeMessages.findIndex(message => message.type === "prepare" && message.page_proof);
|
const startIndex = nativeMessages.findIndex(message => message.type === "start");
|
if (prepareIndex < 0 || startIndex <= prepareIndex) {
|
throw new Error("start secret crossed before fresh prepare");
|
}
|
const firstStart = nativeMessages.find(message => message.type === "start");
|
if (!firstStart || firstStart.cookies.length !== 1 || firstStart.cookies[0].value !== "SYNTHETIC_VALUE") {
|
throw new Error("projected Cookie schema was not delivered");
|
}
|
if (!/^[0-9a-f]{32}$/.test(firstStart.prepare_id) ||
|
firstStart.prepare_id !== nativeMessages.find(message => message.type === "prepare" && message.prepare_id === firstStart.prepare_id)?.prepare_id ||
|
nativeMessages.filter(message => message.type === "start").length !== 1) {
|
throw new Error("prepare lease was not correlated and consumed exactly once");
|
}
|
if (Object.keys(firstStart.cookies[0]).sort().join("|") !== [
|
"domain", "expiration_unix", "host_only", "http_only", "name", "partition_key",
|
"path", "same_site", "secure", "session", "store_id", "value"
|
].sort().join("|")) {
|
throw new Error("Cookie projection contains missing or extra fields");
|
}
|
|
result = await dispatch("retry");
|
if (!result.ok || cookieStoreReads !== 2 || cookieReads !== 2 || nativeConnects !== 2) {
|
throw new Error("retry did not revalidate, reconnect, and reread Cookie once");
|
}
|
|
const rejected = await dispatch("validate", {id: "wrong", url: sidepanelUrl});
|
if (rejected !== undefined) throw new Error("untrusted sender was accepted");
|
|
process.stdout.write("BACKGROUND_CONTRACT_PASS\n");
|