Skip to content

Add cloudflare injection #14

@Mahkhmood8

Description

@Mahkhmood8

see this

https://github.com/botswin/BotBrowser/blob/main/examples/bot-script/cloudflare-turnstile.js```
```What Is --bot-script?
--bot-script executes JavaScript in a privileged, non‑extension context where the chrome.debugger API is available. This provides:

No framework dependencies - Pure Chrome DevTools Protocol access
Earlier intervention - Execute before page navigation
Privileged context - Full chrome.debugger API access
Reduced detection surface - No Playwright/Puppeteer artifacts
Usage
./chrome.exe --no-sandbox --bot-profile="/absolute/path/to/profile.enc" --bot-script="your-script.js"

example

import puppeteer from "rebrowser-puppeteer-core";
// import { pageController } from "./module/pageController.mjs";

import { createCursor } from 'ghost-cursor';
// import { checkTurnstile } from './turnstile.mjs';
import kill from 'tree-kill';

export const checkTurnstileOLD = ({ page }) => {
  return new Promise(async (resolve, reject) => {
    var waitInterval = setTimeout(() => { clearInterval(waitInterval); resolve(false) }, 5000);

    try {
      const elements = await page.$$('[name="cf-turnstile-response"]');
      if (elements.length <= 0) {

        const coordinates = await page.evaluate(() => {
          let coordinates = [];
          document.querySelectorAll('div').forEach(item => {
            try {
              let itemCoordinates = item.getBoundingClientRect()
              let itemCss = window.getComputedStyle(item)
              if (itemCss.margin == "0px" && itemCss.padding == "0px" && itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) {
                coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height })
              }
            } catch (err) { }
          });

          if (coordinates.length <= 0) {
            document.querySelectorAll('div').forEach(item => {
              try {
                let itemCoordinates = item.getBoundingClientRect()
                if (itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) {
                  coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height })
                }
              } catch (err) { }
            });

          }

          return coordinates
        })

        for (const item of coordinates) {
          try {
            let x = item.x + 30;
            let y = item.y + item.h / 2;
            await page.mouse.click(x, y);
          } catch (err) { }
        }
        return resolve(true)
      }

      for (const element of elements) {
        try {
          const parentElement = await element.evaluateHandle(el => el.parentElement);
          const box = await parentElement.boundingBox();
          let x = box.x + 30;
          let y = box.y + box.height / 2;
          await page.mouse.click(x, y);
        } catch (err) { }
      }
      clearInterval(waitInterval)
      resolve(true)
    } catch (err) {
      clearInterval(waitInterval)
      resolve(false)
    }
  })
}
const CLOUDFLARE_CHALLENGE_URL = 'challenges.cloudflare.com/cdn-cgi/challenge-platform';
const MONITORING_INTERVAL = 2000;
const CLICK_X = 30;
const CLICK_Y = 30;
const DEFAULT_TIMEOUT = 30000;

export const checkTurnstile = ({ page, timeout = DEFAULT_TIMEOUT } = {}) => {
  return new Promise(async (resolve, reject) => {
    if (!page) {
      return reject(new Error('Page object is required'));
    }

    const browser = page.browser();
    let monitoringActive = true;
    let activeTargets = new Set();
    let targetFrameFound = false;
    const startTime = Date.now();

    console.log("Starting Turnstile automation...");

    const sleep = (ms) => new Promise(r => setTimeout(r, ms));

    const setupMobileEmulation = async (targetId, cdpSession) => {
      try {
        const result = await cdpSession.send('Runtime.evaluate', {
          expression: `
                        (() => {
                            const mobileUserAgent = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
                            const hasTouchSupport = ('ontouchstart' in window);
                            
                            return {
                                isMobile: mobileUserAgent,
                                userAgent: navigator.userAgent,
                                innerWidth: window.innerWidth,
                                touchSupport: hasTouchSupport,
                                maxTouchPoints: navigator.maxTouchPoints
                            };
                        })()
                    `,
          returnByValue: true
        });

        if (result?.result?.value) {
          const deviceInfo = result.result.value;
          console.log("Device detection:", deviceInfo);

          if (deviceInfo.isMobile) {
            console.log("Mobile device detected, enabling touch events");
            await cdpSession.send('Emulation.setEmitTouchEventsForMouse', {
              enabled: true,
              configuration: 'mobile'
            });
            console.log("Mobile touch events enabled for target:", targetId);
          } else {
            console.log("Desktop device detected, using standard mouse events");
          }
        }
      } catch (error) {
        console.log("Could not detect device type, defaulting to desktop mode:", error.message);
      }
    };

    const performMouseClick = async (targetId, cdpSession, x, y) => {
      console.log("Performing mouse click at coordinates:", x, y);

      try {
        await cdpSession.send('Input.dispatchMouseEvent', {
          type: 'touchStart',///type: "mousePressed",   [[Chrome is very forgiving internally. Even if you send a touch-like type to dispatchMouseEvent, it might still trigger a DOM event (pointerdown / pointerup]]
          // type: 'mousePressed',///type: "mousePressed",   [[Chrome is very forgiving internally. Even if you send a touch-like type to dispatchMouseEvent, it might still trigger a DOM event (pointerdown / pointerup]]
          x: x,
          y: y,
          button: 'left',
          clickCount: 1
        });

        console.log("Mouse pressed at:", x, y);
        await sleep(100);///LMAO DOESNT NOT NEED THIS

        await cdpSession.send('Input.dispatchMouseEvent', {
          type: 'touchEnd',/// type: "mouseReleased",
          x: x,
          y: y,
          button: 'left',
          clickCount: 1
        });

        console.log("Mouse released. Click completed successfully!");
        activeTargets.delete(targetId);
        targetFrameFound = true;
      } catch (error) {
        console.error("Mouse event failed:", error.message);
        activeTargets.delete(targetId);
        throw error;
      }
    };

    const attachToTarget = async (target) => {
      const targetId = target._targetId;
      const url = target.url();

      if (activeTargets.has(targetId)) {
        return;
      }

      const isCloudflareChallenge = url.includes(CLOUDFLARE_CHALLENGE_URL);
      if (!isCloudflareChallenge) {
        return;
      }

      console.log("Found Cloudflare challenge frame:", url);
      activeTargets.add(targetId);

      try {
        let cdpSession;

        if (target.type() === 'page') {
          const targetPage = await target.page();
          if (targetPage) {
            cdpSession = await targetPage._client();
            console.log("Got CDP session from page");
          }
        } else {
          const browserClient = await page._client();
          const result = await browserClient.send('Target.attachToTarget', {
            targetId: targetId,
            flatten: true
          });
          console.log("Created new CDP session:", result.sessionId);

          let connection = browserClient.connection?.() || browserClient.connection || browserClient._connection;

          cdpSession = connection?.sessions?.get(result.sessionId) ||
            connection?._sessions?.get(result.sessionId) ||
            connection?._sessionMap?.get(result.sessionId);

          if (!cdpSession) {
            console.log("Using fallback session wrapper");
            cdpSession = {
              _sessionId: result.sessionId,
              _browserClient: browserClient,
              send: async function (method, params = {}) {
                console.log(`Sending command: ${method}`, params);
                try {
                  return await this._browserClient.send(method, params);
                } catch (error) {
                  console.error(`Error sending ${method}:`, error.message);
                  throw error;
                }
              }
            };
          }
        }

        console.log("Successfully attached to target:", targetId);

        await cdpSession.send('Runtime.enable');
        console.log("Runtime enabled for target:", targetId);

        await setupMobileEmulation(targetId, cdpSession);
        await sleep(500);
        await performMouseClick(targetId, cdpSession, CLICK_X, CLICK_Y);

      } catch (error) {
        console.error("Failed to attach to target:", targetId, error.message);
        activeTargets.delete(targetId);
      }
    };

    const checkForToken = async () => {
      try {
        const token = await page.evaluate(() => {
          try {
            let item = document.querySelector('[name="cf-turnstile-response"]')?.value;
            return item && item.length > 20 ? item : null;
          } catch (e) {
            return null;
          }
        });
        return token;
      } catch (e) {
        return null;
      }
    };

    const monitor = async () => {
      while (monitoringActive) {
        if (Date.now() - startTime > timeout) {
          monitoringActive = false;
          return reject(new Error('Turnstile challenge timeout'));
        }

        try {
          const targets = await browser.targets();

          for (const target of targets) {
            const type = target.type();
            const url = target.url();

            if ((type === 'page' || type === 'iframe' || type === 'other') &&
              url && url.includes('challenges.cloudflare.com')) {
              await attachToTarget(target);
            }
          }

          const token = await checkForToken();
          if (token) {
            console.log('Cloudflare Turnstile Token obtained:', token);
            monitoringActive = false;
            return resolve(token);
          }
        } catch (error) {
          console.error("Error during monitoring:", error.message);
        }

        await sleep(MONITORING_INTERVAL);
      }
    };

    try {
      await monitor();
    } catch (error) {
      reject(error);
    }
  });
};

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

export async function pageController({ browser, page, proxy, turnstile, xvfbsession, pid, plugins, killProcess = false, chrome }) {

  let solveStatus = turnstile

  page.on('close', () => {
    solveStatus = false
  });


  browser.on('disconnected', async () => {
    solveStatus = false
    if (killProcess === true) {
      if (xvfbsession) try { xvfbsession.stopSync() } catch (err) { }
      if (chrome) try { chrome.kill() } catch (err) { console.log(err); }
      if (pid) try { kill(pid, 'SIGKILL', () => { }) } catch (err) { }
    }
  });

  async function turnstileSolver() {
    while (solveStatus) {
      await checkTurnstile({ page }).catch(() => { });
      await new Promise(r => setTimeout(r, 1000));
    }
    return
  }

  turnstileSolver()

  if (proxy.username && proxy.password) await page.authenticate({ username: proxy.username, password: proxy.password });

  if (plugins.length > 0) {
    for (const plugin of plugins) {
      plugin.onPageCreated(page)
    }
  }

  await page.evaluateOnNewDocument(() => {
    Object.defineProperty(MouseEvent.prototype, 'screenX', {
      get: function () {
        return this.clientX + window.screenX;
      }
    });

    Object.defineProperty(MouseEvent.prototype, 'screenY', {
      get: function () {
        return this.clientY + window.screenY;
      }
    });

  });

  const cursor = createCursor(page);
  page.realCursor = cursor
  page.realClick = cursor.click
  return page
}

// process.env.REBROWSER_PATCHES_DEBUG=1
export async function connect({
  args = [],
  headless = true,
  customConfig = {},
  proxy = {},
  turnstile = false,
  connectOption = {},
  disableXvfb = false,
  plugins = [],
  ignoreAllFlags = false,
} = {}) {
  let xvfbsession = null;
  if (headless == "auto") headless = false;

  if (process.platform === "linux" && disableXvfb === false) {
    try {
      const { default: Xvfb } = await import("xvfb");
      xvfbsession = new Xvfb({
        silent: true,
        xvfb_args: ["-screen", "0", "1920x1080x24", "-ac"],
      });
      xvfbsession.startSync();
    } catch (err) {
      console.log(
        "You are running on a Linux platform but do not have xvfb installed. The browser can be captured. Please install it with the following command\n\nsudo apt-get install xvfb\n\n" +
        err.message
      );
    }
  }

  let chromeFlags;
  if (ignoreAllFlags === true) {
    chromeFlags = [
      ...args,
      ...(headless !== false ? [`--headless=${headless}`] : []),
      ...(proxy && proxy.host && proxy.port
        ? [`--proxy-server=${proxy.host}:${proxy.port}`]
        : []),
    ];
  } else {
    // Default flags: https://github.com/GoogleChrome/chrome-launcher/blob/main/src/flags.ts
    const flags = Launcher.defaultFlags();
    // Add AutomationControlled to "disable-features" flag
    const indexDisableFeatures = flags.findIndex((flag) => flag.startsWith('--disable-features'));
    flags[indexDisableFeatures] = `${flags[indexDisableFeatures]},AutomationControlled`;
    // Remove "disable-component-update" flag
    const indexComponentUpdateFlag = flags.findIndex((flag) => flag.startsWith('--disable-component-update'));
    flags.splice(indexComponentUpdateFlag, 1);
    chromeFlags = [
      ...flags,
      ...args,
      ...(headless !== false ? [`--headless=${headless}`] : []),
      ...(proxy && proxy.host && proxy.port
        ? [`--proxy-server=${proxy.host}:${proxy.port}`]
        : []),
      "--no-sandbox",
      "--disable-dev-shm-usage",
    ];
  }
  const chrome = await launch({
    executablePath: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
    ignoreDefaultFlags: true,
    chromeFlags,
    ...customConfig,
  });
  let pextra = null;
  if (plugins.length > 0) {
    const { addExtra } = await import("puppeteer-extra");

    pextra = addExtra(puppeteer);

    for (const item of plugins) {
      pextra.use(item);
    }
  }

  const browser = await (pextra ? pextra : puppeteer).connect({
    browserURL: `http://127.0.0.1:${chrome.port}`,
    ...connectOption,
  });

  let [page] = await browser.pages();

  let pageControllerConfig = {
    browser,
    page,
    proxy,
    turnstile,
    xvfbsession,
    pid: chrome.pid,
    plugins,
  };

  page = await pageController({
    ...pageControllerConfig,
    chrome,
    killProcess: true,
  });

  browser.on("targetcreated", async (target) => {
    if (target.type() === "page") {
      let newPage = await target.page();
      pageControllerConfig.page = newPage;
      newPage = await pageController(pageControllerConfig);
    }
  });

  return {
    browser,
    page,
  };
}

import test from 'node:test';
import assert from 'node:assert';
const realBrowserOption = {
  args: [
    '--no-sandbox',
    '--no-zygote',
    "--start-maximized",
    '--lang=en-US',
    '--disable-dev-shm-usage',
    '--disable-client-side-phishing-detection',
    '--fingerprint=' + ((Math.random() * 2 ** 32) >>> 0).toString(),
    // '--fingerprint=2333',
    // '--fingerprint-platform-version=11',
    // '--fingerprint-platform-version=141',//this is from  ///chrome 139, from https://github.com/adryfish/fingerprint-chromium
    // '--fingerprint-brand=Edge',
    '--fingerprint-platform=windows'// macos also works , slightly slower, TRY ON DOCKER, important for docker? Unknown I dont use docker
    // '--fingerprint-platform=macos'//important for docker? Unknown I dont use docker

  ],
  turnstile: false,
  headless: false,
  disableXvfb: true,
  ignoreAllFlags: false,
  // ignoreAllFlags:true,
  customConfig: {
    chromePath: `C:\\Users\\User\\mlx\\deps\\mimic_140.3\\chrome.exe`, ///chrome 139, from https://github.com/adryfish/fingerprint-chromium
  },
  connectOption: {
    defaultViewport: null
  },
  plugins: []
}

test('Cloudflare Turnstile', async () => {
  const { page, browser } = await connect(realBrowserOption)


  //detected with ///chrome 139, from https://github.com/adryfish/fingerprint-chromium
  // //IN CHROME WEB CONSOLE : (async () => { if (navigator.userAgentData) { try { const userAgentData = await navigator.userAgentData.getHighEntropyValues([ "architecture", "bitness", "brands", "fullVersionList", "mobile", "model", "platform", "platformVersion" ]); const formattedCode = ` await page.setUserAgent('${navigator.userAgent}',{ "architecture": "${userAgentData.architecture}", "bitness": "${userAgentData.bitness}", "brands": ${JSON.stringify(userAgentData.brands, null, 12)}, "fullVersionList": ${JSON.stringify(userAgentData.fullVersionList, null, 12)}, "mobile": ${userAgentData.mobile}, "model": "${userAgentData.model}", "platform": "${userAgentData.platform}", "platformVersion": "${userAgentData.platformVersion}" }); `; console.log(formattedCode); } catch (error) { console.error("Error retrieving user agent data:", error); console.log("Could not generate the complete code snippet. This might happen if you deny the permission prompt for more detailed information."); } } else { console.log("The navigator.userAgentData API is not available in this browser. The requested detailed format cannot be automatically generated."); console.log("You can use the basic user agent string as follows:"); console.log(`await page.setUserAgent('${navigator.userAgent}');`); } })();
  // await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0', {
  //   "architecture": "x86", "bitness": "64", "brands": [
  //     {
  //       "brand": "Microsoft Edge",
  //       "version": "141"
  //     },
  //     {
  //       "brand": "Not?A_Brand",
  //       "version": "8"
  //     },
  //     {
  //       "brand": "Chromium",
  //       "version": "141"
  //     }
  //   ], "fullVersionList": [
  //     {
  //       "brand": "Microsoft Edge",
  //       "version": "141.0.3537.71"
  //     },
  //     {
  //       "brand": "Not?A_Brand",
  //       "version": "8.0.0.0"
  //     },
  //     {
  //       "brand": "Chromium",
  //       "version": "141.0.7390.66"
  //     }
  //   ], "mobile": false, "model": "", "platform": "Windows", "platformVersion": "10.0.0"
  // });


  // await page.goto("https://abrahamjuliot.github.io/creepjs/");
  // await page.goto("https://bot-detector.rebrowser.net/");
  await page.goto("https://turnstile-demo.pages.dev/");
  // await page.goto("https://fingerprint.com/products/bot-detection/");

  // await page.waitForSelector('#email')
  let token = null
  let startDate = Date.now()
  console.log("Please complete the Turnstile challenge within 30 seconds...")
  while (!token && (Date.now() - startDate) < 30000) {
    token = await page.evaluate(() => {
      try {
        let item = document.querySelector('[name="cf-turnstile-response"]').value
        return item && item.length > 20 ? item : null
      } catch (e) {
        return null
      }
    })
    await new Promise(r => setTimeout(r, 1000));
  }
  await browser.close()
  // if (token !== null) console.log('Cloudflare Turnstile Token: ' + token);
  assert.strictEqual(token !== null, true, "Cloudflare turnstile test failed!")
})```

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @Mahkhmood8

        Issue actions

          Add cloudflare injection · Issue #14 · LoseNine/AutoWK