[Python] Cannot capture navigator.runAdAuction() parameters from seller td.doubleclick.net,...

Discussão em 'Python' iniciado por Stack, Agosto 6, 2025.

  1. Stack

    Stack Membro Participativo

    I’m working on a research scanner for Google’s Privacy Sandbox APIs using Playwright and Chromium. The goal is to detect real-world usage of APIs like navigator.runAdAuction, navigator.joinAdInterestGroup, and document.browsingTopics.

    To detect FLEDGE activity, I inject the following wrapper into all pages:

    if (navigator.runAdAuction) {
    const origRun = navigator.runAdAuction;
    navigator.runAdAuction = async (cfg) => {
    const urn = await origRun.call(navigator, cfg);
    const payload = { config: cfg, timestamp: Date.now(), urn };
    console.log("[runAdAuction captured]", JSON.stringify(payload));
    window.top?.postMessage({ type: "RunAdAuctionPayload", payload }, "*");
    return urn;
    };
    }


    Then in Playwright, I expose a handler for the postMessage:

    await page.expose_function("logRunAdAuctionParams", lambda payload: runAdAuctionParams.append(payload))


    This works fine for many FLEDGE sellers — I’m able to capture config objects from: t.seedtag.com lucead.com

    The Problem: I cannot capture any auction payloads from td.doubleclick.net. There’s no JS message, no log, and no payload reaching my wrapper. Which is very odd because I'm also trying to use my crawler on some known websites which has auction payloads from doubleclick which other people managed to capture, not working.

    Verified

    navigator.runAdAuction is defined in accessible frames.

    My wrapper fires correctly on many sites and logs to terminal as expected.

    window.top.postMessage(...) works for other sellers.

    Chrome is launched with all relevant flags:

    --enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,BiddingAndScoringDebugReportingAPI,AllowURNsInIframes,BrowsingTopics
    --disable-features=EnforcePrivacySandboxAttestations,FledgeEnforceKAnonymity,FencedFrames


    My logic:

    1. Patching navigator.runAdAuction

    A JavaScript snippet is injected to replace the browser's native navigator.runAdAuction function with a custom wrapper.

    Store the Original: The original runAdAuction function is saved to a variable (origRun).

    Create a Wrapper: navigator.runAdAuction is overwritten with a new async function. This new function does the following:

    Records the timestamp of the call using Date.now().

    Executes the original runAdAuction with the provided configuration (cfg) and awaits the result (urn).

    Bundles the auction config, the timestamp, and the resulting urn into a single payload object.

    Calls a function, window.logRunAdAuctionParams(payload), to send the captured data back to the Python script.

    Includes a fallback using window.top?.postMessage to handle cases where the call originates from a cross-origin iframe that cannot directly access functions on the main page.

    Logs the captured payload to the browser's developer console for real-time debugging.

    // Injected via add_init_script
    if (navigator.runAdAuction) {
    const origRun = navigator.runAdAuction;
    navigator.runAdAuction = async (cfg) => {
    const ts = Date.now();
    try {
    const urn = await origRun.call(navigator, cfg);
    const payload = {
    config: cfg,
    timestamp: ts,
    urn: urn
    };

    // Primary communication method
    if (typeof window.logRunAdAuctionParams === "function") {
    window.logRunAdAuctionParams(payload);
    } else {
    // Fallback for iframes
    try {
    window.top?.postMessage({
    type: "RunAdAuctionPayload",
    payload: payload
    }, "*");
    } catch (e) {
    console.warn("postMessage to parent failed:", e);
    }
    }

    console.log("[runAdAuction captured]", JSON.stringify(payload));
    return urn;
    } catch (e) {
    console.log("[runAdAuction error]", e.message);
    throw e;
    }
    };
    }

    1. Communication from Browser to Python

    The script establishes a two-way communication channel between the browser's JavaScript environment and the Python backend.

    Exposing a Python Function: The script uses page.expose_function("logRunAdAuctionParams", ...) to create a function named logRunAdAuctionParams in the browser's window object. When this function is called from JavaScript, it executes a Python lambda function.

    Storing Data: The Python lambda function receives the payload object and appends it to the RunAdAuction_Params list within the main result dictionary for the scanned URL.

    await page.expose_function("logRunAdAuctionParams", lambda params: (
    result["RunAdAuction_Params"].append(params)
    ))


    Handling iFrame Messages: A separate message event listener is added to the page to catch the postMessage fallback from iframes, ensuring that auction calls from sandboxed or cross-origin frames are also captured.

    // Injected via add_init_script
    window.addEventListener("message", (event) => {
    if (event.data && event.data.type === "RunAdAuctionPayload") {
    if (typeof window.logRunAdAuctionParams === "function") {
    window.logRunAdAuctionParams(event.data.payload);
    }
    }
    });

    1. Capturing Availability and Opt-Out Policies

    In addition to capturing active calls, I also gather contextual information about the API.

    API Availability: It checks if navigator.runAdAuction is a function to determine if the API is available in the environment. This result is stored in result["RunAdAuction_Available"].

    Permissions-Policy Header: A response listener (page.on("response", ...)) inspects the permissions-policy HTTP header of every network response. It uses a regular expression (re.search(r'run-ad-auction=(([^)]*))', header)) to find directives related to run-ad-auction. If a policy like run-ad-auction=() is found, it indicates an opt-out, which is stored in result["RunAdAuction_Opt_Out"]. Any help or suggestions are appreciated — this has been a blocker for me despite working detection logic elsewhere.

    Sadly I cannot capture any auction payloads from td.doubleclick.net. Any help would be appreciated!!!

    Continue reading...

Compartilhe esta Página