// Reading the operator's browser login for X, and the status the Settings // section's "Check" shows (release 16 slice XL). Every store here is a fixture // written through node:sqlite (__fixtures__/firefoxCookieStore.ts) under a temp // HOME — no test reads a real browser profile. // // Run with: pnpm --filter yt-dlp-transcript-common exec tsx --test social/xBrowserLogin.test.ts import { test, after } from "node:test"; import assert from "node:assert/strict"; import { mkdtempSync } from "node:fs"; import { mkdir, readdir, rm, stat, utimes, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import type { Paths } from "../lib/paths"; import { firefoxExpirySeconds, isOldDomainAuth, isXComAuth, readFirefoxXCookies, readFirefoxXStore, readXLoginStatus, xCookiesFromBrowser, } from "./xBrowserLogin"; import { xCookieFile, xProfileDir } from "./xSessionBroker"; import { insertFixtureCookies, writeFirefoxCookieStore, type FixtureCookie, } from "./__fixtures__/firefoxCookieStore"; const TMP = mkdtempSync(path.join(os.tmpdir(), "xl-xbrowserlogin-")); after(() => rm(TMP, { recursive: true, force: true })); let n = 0; const fresh = (label: string) => path.join(TMP, `${label}-${++n}`); const NOW_S = Math.floor(Date.now() / 1000); const YEAR = 86_400 * 365; const LAST_USED_US = Date.UTC(2026, 9, 1, 8, 30, 0) * 1000; // 2026-10-01 08:30 UTC const X_LOGIN: FixtureCookie[] = [ { host: ".x.com", name: "auth_token", value: "fixture-auth", lastAccessed: LAST_USED_US }, { host: ".x.com", name: "ct0", value: "fixture-csrf", isHttpOnly: 0, sameSite: 1 }, { host: "x.com", name: "lang", value: "en", isSecure: 0, isHttpOnly: 0, sameSite: 0 }, { host: ".twitter.com", name: "guest_id", value: "v1%3A1", sameSite: 2 }, // Not X: never selected. { host: ".example.com", name: "session", value: "not-x" }, { host: "notx.com", name: "auth_token", value: "lookalike" }, // Expired: skipped. { host: ".x.com", name: "old", value: "gone", expiry: NOW_S - 60 }, ]; test("reads X's cookies only, mapped to the shape Playwright and cookies.txt take", async () => { const dir = fresh("profile"); const { file } = await writeFirefoxCookieStore(dir, X_LOGIN); const cookies = await readFirefoxXCookies(file, { tmpRoot: TMP }); const byName = new Map(cookies.map((c) => [c.name, c])); assert.deepEqual([...byName.keys()].sort(), ["auth_token", "ct0", "guest_id", "lang"]); const auth = byName.get("auth_token")!; assert.equal(auth.value, "fixture-auth"); assert.equal(auth.domain, ".x.com"); assert.equal(auth.path, "/"); assert.equal(auth.secure, true); assert.equal(auth.httpOnly, true); assert.equal(auth.sameSite, "None"); assert.equal(auth.lastAccessedMs, LAST_USED_US / 1000); assert.ok(auth.expires > NOW_S); assert.equal(byName.get("ct0")!.sameSite, "Lax"); assert.equal(byName.get("ct0")!.httpOnly, false); assert.equal(byName.get("guest_id")!.sameSite, "Strict"); // SameSite=None on a cookie that is not secure is not kept by a browser. assert.equal(byName.get("lang")!.sameSite, "Lax"); assert.equal(byName.get("lang")!.domain, "x.com"); }); test("a login still in the WAL of a running Firefox is read", async () => { const dir = fresh("profile-wal"); const { file, db } = await writeFirefoxCookieStore(dir, X_LOGIN, { keepOpen: true }); try { const listed = await readdir(dir); assert.ok(listed.includes("cookies.sqlite-wal"), "the fixture leaves its rows in the WAL"); const cookies = await readFirefoxXCookies(file, { tmpRoot: TMP }); assert.ok(cookies.some((c) => c.name === "auth_token" && c.value === "fixture-auth")); } finally { db?.close(); } }); test("the browser's store is never written: no file in the profile changes, no temp is left", async () => { const dir = fresh("profile-ro"); const { file, db } = await writeFirefoxCookieStore(dir, X_LOGIN, { keepOpen: true }); try { const before = await Promise.all( (await readdir(dir)).sort().map(async (f) => { const st = await stat(path.join(dir, f)); return `${f}:${st.size}:${st.mtimeMs}`; }), ); const tmpRoot = fresh("tmproot"); await mkdir(tmpRoot); await readFirefoxXCookies(file, { tmpRoot }); const afterList = await Promise.all( (await readdir(dir)).sort().map(async (f) => { const st = await stat(path.join(dir, f)); return `${f}:${st.size}:${st.mtimeMs}`; }), ); assert.deepEqual(afterList, before); assert.deepEqual(await readdir(tmpRoot), [], "the private copy is removed"); } finally { db?.close(); } }); test("containers are read as gallery-dl reads them: none by default, ::all, or one by name", async () => { const dir = fresh("profile-containers"); const { file } = await writeFirefoxCookieStore(dir, [ { host: ".x.com", name: "auth_token", value: "default" }, { host: ".x.com", name: "auth_token", value: "work", originAttributes: "^userContextId=2" }, { host: ".x.com", name: "auth_token", value: "work-fpd", originAttributes: "^firstPartyDomain=x.com&userContextId=2&x=1" }, { host: ".x.com", name: "auth_token", value: "personal", originAttributes: "^userContextId=1" }, { host: ".x.com", name: "auth_token", value: "shopping", originAttributes: "^userContextId=12" }, ]); await writeFile( path.join(dir, "containers.json"), JSON.stringify({ version: 5, identities: [ { userContextId: 1, public: true, l10nID: "userContextPersonal.label" }, { userContextId: 2, public: true, name: "Work" }, { userContextId: 12, public: true, l10nId: "user-context-shopping" }, ], }), ); const values = async (container?: string) => (await readFirefoxXCookies(file, { container, tmpRoot: TMP })).map((c) => c.value).sort(); // No container in the spec: ONLY cookies outside every container (gallery-dl's // default; yt-dlp would read them all). assert.deepEqual(await values(), ["default"]); assert.deepEqual(await values("none"), ["default"]); assert.deepEqual(await values("all"), ["default", "personal", "shopping", "work", "work-fpd"]); // By name (`name`), by `l10nID` (userContext.label) and by `l10nId` // (user-context-) — case-sensitive, as gallery-dl matches. Container 2 does // not pick up container 12. assert.deepEqual(await values("Work"), ["work", "work-fpd"]); assert.deepEqual(await values("Personal"), ["personal"]); assert.deepEqual(await values("shopping"), ["shopping"]); await assert.rejects(values("personal"), /No Firefox container named "personal"/); await assert.rejects(values("Banking"), /No Firefox container named "Banking"/); }); test("the store read has two views: with the WAL (Firefox now) and the main file alone (gallery-dl's)", async () => { const dir = fresh("profile-views"); const { file, db } = await writeFirefoxCookieStore(dir, [ { host: ".x.com", name: "guest_id", value: "g" }, ]); // The schema and guest_id are in the main file; the login lands in the WAL // of the still-open store, as a fresh login does in a running Firefox. assert.equal(db, undefined); const live = await writeFirefoxCookieStore(fresh("profile-views-wal"), [], { keepOpen: true }); try { insertFixtureCookies(live.db!, [{ host: ".x.com", name: "auth_token", value: "fresh" }]); const read = await readFirefoxXStore(live.file, { tmpRoot: TMP }); assert.deepEqual(read.cookies.map((c) => c.value), ["fresh"]); assert.deepEqual(read.mainFile, []); } finally { live.db?.close(); } const settled = await readFirefoxXStore(file, { tmpRoot: TMP }); assert.deepEqual(settled.cookies.map((c) => c.name), ["guest_id"]); assert.deepEqual(settled.mainFile.map((c) => c.name), ["guest_id"]); }); test("a login counts on x.com only; twitter.com's auth_token is the old domain's", () => { assert.equal(isXComAuth({ name: "auth_token", domain: ".x.com" }), true); assert.equal(isXComAuth({ name: "auth_token", domain: "x.com" }), true); assert.equal(isXComAuth({ name: "auth_token", domain: "api.x.com" }), true); assert.equal(isXComAuth({ name: "auth_token", domain: ".twitter.com" }), false); assert.equal(isXComAuth({ name: "auth_token", domain: ".notx.com" }), false); assert.equal(isXComAuth({ name: "ct0", domain: ".x.com" }), false); assert.equal(isOldDomainAuth({ name: "auth_token", domain: ".twitter.com" }), true); assert.equal(isOldDomainAuth({ name: "auth_token", domain: ".x.com" }), false); }); test("Firefox's expiry reads as seconds, or as milliseconds when too large for seconds", () => { assert.equal(firefoxExpirySeconds(1_800_000_000), 1_800_000_000); assert.equal(firefoxExpirySeconds(1_800_000_000_123), 1_800_000_000); assert.equal(firefoxExpirySeconds(0), -1); assert.equal(firefoxExpirySeconds(null), -1); }); test("discovery: the most recently used store under Firefox's roots, as gallery-dl picks it", async () => { const home = fresh("home"); const root = path.join(home, ".mozilla", "firefox"); const older = await writeFirefoxCookieStore(path.join(root, "aaa.default"), [ { host: ".x.com", name: "auth_token", value: "older-profile" }, ]); const newer = await writeFirefoxCookieStore(path.join(root, "bbb.default-release"), [ { host: ".x.com", name: "auth_token", value: "newer-profile" }, ]); const t = Date.now() / 1000; await utimes(older.file, t - 3_600, t - 3_600); await utimes(newer.file, t, t); const read = await xCookiesFromBrowser("firefox", { home, tmpRoot: TMP }); assert.equal(read.ok, true); assert.ok(read.ok && read.store === newer.file); assert.ok(read.ok && read.cookies.some((c) => c.value === "newer-profile")); // A profile NAME is looked up under the roots; a PATH is used as given. const named = await xCookiesFromBrowser("firefox:aaa.default", { home, tmpRoot: TMP }); assert.ok(named.ok && named.store === older.file); const byPath = await xCookiesFromBrowser(`firefox:${path.join(root, "aaa.default")}`, { home, tmpRoot: TMP, }); assert.ok(byPath.ok && byPath.store === older.file); }); test("what xCookiesFromBrowser will not read, it says", async () => { const home = fresh("home-empty"); await mkdir(home); const none = await xCookiesFromBrowser("", { home }); assert.equal(none.ok, false); assert.ok(!none.ok && none.reason === "no-spec"); const chromium = await xCookiesFromBrowser("chromium:Default", { home }); assert.ok(!chromium.ok && chromium.reason === "unsupported"); assert.match(!chromium.ok ? chromium.message : "", /gallery-dl/); const missing = await xCookiesFromBrowser("firefox", { home }); assert.ok(!missing.ok && missing.reason === "not-found"); assert.match(!missing.ok ? missing.message : "", /\.mozilla\/firefox/); }); // --- The status --------------------------------------------------------------- function pathsFor(transcriptsDir: string): Paths { return { transcriptsDir } as Paths; } async function connectProfile(paths: Paths, authed: boolean) { await mkdir(xProfileDir(paths), { recursive: true }); await writeFile( xCookieFile(paths), "# Netscape HTTP Cookie File\n" + (authed ? ".x.com\tTRUE\t/\tTRUE\t1900000000\tauth_token\tjar\n" : ".x.com\tTRUE\t/\tTRUE\t1900000000\tct0\tjar\n"), ); } test("status — browser source by default: the login is visible, with when the browser last used it", async () => { const home = fresh("home-status"); await writeFirefoxCookieStore(path.join(home, ".mozilla", "firefox", "p.default"), X_LOGIN); const paths = pathsFor(fresh("transcripts")); const s = await readXLoginStatus(paths, { cookiesFromBrowser: "firefox" }, { home, tmpRoot: TMP }); assert.equal(s.source, "browser"); assert.equal(s.chosen, false); assert.equal(s.label, "Browser login (firefox)"); assert.equal(s.authTokenVisible, true); assert.equal(s.lastSeenAt, "2026-10-01T08:30:00.000Z"); assert.equal(s.walOnly, false); assert.equal(s.oldDomainCookie, false); assert.equal( s.summary, "An X login is visible in firefox, outside its containers (where gallery-dl reads); " + "its auth_token was last used 2026-10-01 08:30:00 UTC.", ); }); test("status — a login only in Firefox's write-ahead log says gallery-dl will not see it yet", async () => { const home = fresh("home-wal"); const live = await writeFirefoxCookieStore(path.join(home, ".mozilla", "firefox", "p.default"), X_LOGIN, { keepOpen: true, }); try { const s = await readXLoginStatus(pathsFor(fresh("transcripts")), { cookiesFromBrowser: "firefox" }, { home, tmpRoot: TMP }); assert.equal(s.authTokenVisible, true); assert.equal(s.walOnly, true); assert.match( s.summary, /Seen in Firefox's write-ahead log only; gallery-dl will see it after Firefox checkpoints \(closing Firefox does it\)\.$/, ); } finally { live.db?.close(); } }); test("status — twitter.com's auth_token is reported as the old domain's, never counted", async () => { const home = fresh("home-olddomain"); await writeFirefoxCookieStore(path.join(home, ".mozilla", "firefox", "p.default"), [ { host: ".twitter.com", name: "auth_token", value: "old" }, ]); const s = await readXLoginStatus(pathsFor(fresh("transcripts")), { cookiesFromBrowser: "firefox" }, { home, tmpRoot: TMP }); assert.equal(s.authTokenVisible, false); assert.equal(s.oldDomainCookie, true); assert.match(s.summary, /^No X login in firefox, outside its containers \(where gallery-dl reads\): no auth_token cookie for x\.com\./); assert.match(s.summary, /An old-domain cookie is present too \(an auth_token for twitter\.com\); gallery-dl does not use it\.$/); }); test("status — a login only inside a container is not counted, and the Check says where it is", async () => { const home = fresh("home-container"); await writeFirefoxCookieStore(path.join(home, ".mozilla", "firefox", "p.default"), [ { host: ".x.com", name: "auth_token", value: "in-a-container", originAttributes: "^userContextId=3" }, ]); const paths = pathsFor(fresh("transcripts")); const s = await readXLoginStatus(paths, { cookiesFromBrowser: "firefox" }, { home, tmpRoot: TMP }); assert.equal(s.authTokenVisible, false); assert.equal(s.otherContainerLogin, true); assert.match(s.summary, /One is in another Firefox container; gallery-dl reads it only when cookiesFromBrowser names that container \(firefox::\) or firefox::all\./); const all = await readXLoginStatus(paths, { cookiesFromBrowser: "firefox::all" }, { home, tmpRoot: TMP }); assert.equal(all.authTokenVisible, true); assert.match(all.summary, /^An X login is visible in firefox, in any container/); }); test("status — browser source with no X login in the browser", async () => { const home = fresh("home-nologin"); await writeFirefoxCookieStore(path.join(home, ".mozilla", "firefox", "p.default"), [ { host: ".x.com", name: "guest_id", value: "g" }, ]); const s = await readXLoginStatus(pathsFor(fresh("transcripts")), { cookiesFromBrowser: "firefox" }, { home, tmpRoot: TMP }); assert.equal(s.authTokenVisible, false); assert.match(s.summary, /No X login in firefox/); }); test("status — a browser that cannot be read here reports null, not false", async () => { const s = await readXLoginStatus( pathsFor(fresh("transcripts")), { cookiesFromBrowser: "chromium", social: { x: { cookieSource: "browser" } } }, { home: fresh("home-x") }, ); assert.equal(s.source, "browser"); assert.equal(s.chosen, true); assert.equal(s.authTokenVisible, null); }); test("status — the profile: chosen by default when no browser is set, and when one is connected", async () => { const paths = pathsFor(fresh("transcripts")); const none = await readXLoginStatus(paths, { cookiesFromBrowser: "" }); assert.equal(none.source, "profile"); assert.equal(none.label, "Connected profile"); assert.equal(none.authTokenVisible, false); assert.match(none.summary, /No profile is connected/); await connectProfile(paths, false); const notLoggedIn = await readXLoginStatus(paths, { cookiesFromBrowser: "" }); assert.equal(notLoggedIn.authTokenVisible, false); assert.match(notLoggedIn.summary, /not logged in to X/); // A connected profile turns the default to "profile" even with a browser set. await connectProfile(paths, true); const connected = await readXLoginStatus(paths, { cookiesFromBrowser: "firefox" }, { home: fresh("home-y") }); assert.equal(connected.source, "profile"); assert.equal(connected.chosen, false); assert.equal(connected.authTokenVisible, true); assert.ok(connected.lastSeenAt); assert.match(connected.summary, /^The connected profile holds an X login \(cookies exported /); });