RynnHub commited on
Commit
87be38f
·
0 Parent(s):
.dockerignore ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ node_modules
2
+ npm-debug.log
3
+ Dockerfile
4
+ .dockerignore
5
+ .git
6
+ .gitignore
7
+ README.md
8
+ .env
9
+ .env.*
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ node_modules
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # unknown
2
+ FROM node:latest
3
+
4
+ RUN apt-get update && apt-get install -y \
5
+ wget \
6
+ gnupg \
7
+ ca-certificates \
8
+ apt-transport-https \
9
+ chromium \
10
+ chromium-driver \
11
+ xvfb \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ ENV CHROME_BIN=/usr/bin/chromium
15
+
16
+ WORKDIR /app
17
+
18
+ COPY package*.json ./
19
+
20
+ RUN npm update
21
+ RUN npm install
22
+ # RUN npm i -g pm2
23
+ COPY . .
24
+
25
+ EXPOSE 7860
26
+
27
+ #CMD ["pm2-runtime", "src/index.js"]
28
+ CMD ["node", "src/index.js"]
LICENSE.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 - 2024 @zfcsoftware
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Cf
3
+ emoji: 💻
4
+ colorFrom: gray
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ short_description: idk
9
+ ---
10
+
11
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "cf-clearance-scraper",
3
+ "version": "2.1.3",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "start": "node src/index.js",
7
+ "test": "node --experimental-vm-modules ./node_modules/.bin/jest --detectOpenHandles --verbose"
8
+ },
9
+ "jest": {
10
+ "testMatch": [
11
+ "**/tests/**/*.js"
12
+ ],
13
+ "verbose": true
14
+ },
15
+ "keywords": [
16
+ "cf-clearance",
17
+ "cloudflare",
18
+ "waf",
19
+ "scraper",
20
+ "puppeteer",
21
+ "xvfb",
22
+ "turnstile",
23
+ "bypass",
24
+ "undetected",
25
+ "stealth"
26
+ ],
27
+ "author": "zfcsoftware",
28
+ "license": "ISC",
29
+ "description": "This package is an experimental and educational package created for Cloudflare protections.",
30
+ "dependencies": {
31
+ "ajv": "^8.17.1",
32
+ "ajv-formats": "^3.0.1",
33
+ "body-parser": "^1.20.3",
34
+ "cors": "^2.8.5",
35
+ "dotenv": "^16.4.5",
36
+ "express": "^4.21.0",
37
+ "jest": "^29.7.0",
38
+ "puppeteer-real-browser": "^1.4.0",
39
+ "supertest": "^7.0.0"
40
+ }
41
+ }
src/createBrowser.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { connect } = require("puppeteer-real-browser")
2
+ async function createBrowser() {
3
+ try {
4
+ if (global.finished == true) return
5
+
6
+ global.browser = null
7
+
8
+ // console.log('Launching the browser...');
9
+
10
+ const { browser } = await connect({
11
+ headless: false,
12
+ turnstile: true,
13
+ connectOption: { defaultViewport: null },
14
+ disableXvfb: false,
15
+ })
16
+
17
+ // console.log('Browser launched');
18
+
19
+ global.browser = browser;
20
+
21
+ browser.on('disconnected', async () => {
22
+ if (global.finished == true) return
23
+ console.log('Browser disconnected');
24
+ await new Promise(resolve => setTimeout(resolve, 3000));
25
+ await createBrowser();
26
+ })
27
+
28
+ } catch (e) {
29
+ console.log(e.message);
30
+ if (global.finished == true) return
31
+ await new Promise(resolve => setTimeout(resolve, 3000));
32
+ await createBrowser();
33
+ }
34
+ }
35
+ createBrowser()
src/fakePage.html ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title></title>
8
+ </head>
9
+
10
+ <body>
11
+ <div class="turnstile"></div>
12
+ <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
13
+ <script>
14
+ window.onloadTurnstileCallback = function () {
15
+ turnstile.render('.turnstile', {
16
+ sitekey: '<site-key>',
17
+ callback: function (token) {
18
+ var c = document.createElement('input');
19
+ c.type = 'hidden';
20
+ c.name = 'cf-response';
21
+ c.value = token;
22
+ document.body.appendChild(c);
23
+ },
24
+ });
25
+ };
26
+
27
+ </script>
28
+ </body>
29
+
30
+ </html>
src/getSource.js ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function getSource({ url, proxy }) {
2
+ return new Promise(async (resolve, reject) => {
3
+ if (!url) return reject("Missing url parameter");
4
+ const context = await global.browser
5
+ .createBrowserContext({
6
+ proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
7
+ })
8
+ .catch(() => null);
9
+ if (!context) return reject("Failed to create browser context");
10
+
11
+ let isResolved = false;
12
+
13
+ var cl = setTimeout(async () => {
14
+ if (!isResolved) {
15
+ await context.close();
16
+ reject("Timeout Error");
17
+ }
18
+ }, global.timeOut || 60000);
19
+
20
+ try {
21
+ const page = await context.newPage();
22
+
23
+ if (proxy?.username && proxy?.password)
24
+ await page.authenticate({
25
+ username: proxy.username,
26
+ password: proxy.password,
27
+ });
28
+
29
+ await page.setRequestInterception(true);
30
+ page.on("request", async (request) => request.continue());
31
+ page.on("response", async (res) => {
32
+ try {
33
+ if (
34
+ [200, 302].includes(res.status()) &&
35
+ [url, url + "/"].includes(res.url())
36
+ ) {
37
+ await page
38
+ .waitForNavigation({ waitUntil: "load", timeout: 5000 })
39
+ .catch(() => {});
40
+ const html = await page.content();
41
+ await context.close();
42
+ isResolved = true;
43
+ clearInterval(cl);
44
+ resolve(html);
45
+ }
46
+ } catch (e) {}
47
+ });
48
+ await page.goto(url, {
49
+ waitUntil: "domcontentloaded",
50
+ });
51
+ } catch (e) {
52
+ if (!isResolved) {
53
+ await context.close();
54
+ clearInterval(cl);
55
+ reject(e.message);
56
+ }
57
+ }
58
+ });
59
+ }
60
+ module.exports = getSource;
src/index.js ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express')
2
+ const app = express()
3
+ const port = process.env.PORT || 7860
4
+ const bodyParser = require('body-parser')
5
+ const authToken = process.env.authToken || null
6
+ const cors = require('cors')
7
+ const reqValidate = require('./reqValidate')
8
+
9
+ global.browserLength = 0
10
+ global.browserLimit = Number(process.env.browserLimit) || 20
11
+ global.timeOut = Number(process.env.timeOut || 60000)
12
+
13
+ app.use(bodyParser.json({}))
14
+ app.use(bodyParser.urlencoded({ extended: true }))
15
+ app.use(cors())
16
+ if (process.env.NODE_ENV !== 'development') {
17
+ let server = app.listen(port, () => { console.log(`Server running on port ${port}`) })
18
+ try {
19
+ server.timeout = global.timeOut
20
+ } catch (e) { }
21
+ }
22
+ if (process.env.SKIP_LAUNCH != 'true') require('./createBrowser')
23
+
24
+ const getSource = require('./getSource')
25
+ const solveTurnstileMin = require('./solveTurnstile.min')
26
+ const solveTurnstileMax = require('./solveTurnstile.max')
27
+ const wafSession = require('./wafSession')
28
+
29
+ app.get("/", (req, res) => {
30
+ res.send({ msg: "Hello World" })
31
+ })
32
+
33
+ app.post('/action', async (req, res) => {
34
+
35
+ const data = req.body
36
+
37
+ const check = reqValidate(data)
38
+
39
+ if (check !== true) return res.status(400).json({ code: 400, message: 'Bad Request', schema: check })
40
+
41
+ if (authToken && data.authToken !== authToken) return res.status(401).json({ code: 401, message: 'Unauthorized' })
42
+
43
+ if (global.browserLength >= global.browserLimit) return res.status(429).json({ code: 429, message: 'Too Many Requests' })
44
+
45
+ if (process.env.SKIP_LAUNCH != 'true' && !global.browser) return res.status(500).json({ code: 500, message: 'The scanner is not ready yet. Please try again a little later.' })
46
+
47
+ var result = { code: 500 }
48
+
49
+ global.browserLength++
50
+
51
+ switch (data.mode) {
52
+ case "source":
53
+ result = await getSource(data).then(res => { return { source: res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
54
+ break;
55
+ case "turnstile-min":
56
+ result = await solveTurnstileMin(data).then(res => { return { token: res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
57
+ break;
58
+ case "turnstile-max":
59
+ result = await solveTurnstileMax(data).then(res => { return { token: res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
60
+ break;
61
+ case "waf-session":
62
+ result = await wafSession(data).then(res => { return { ...res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
63
+ break;
64
+ }
65
+
66
+ global.browserLength--
67
+
68
+ res.status(result.code ?? 500).send(result)
69
+ })
70
+
71
+ app.use((req, res) => { res.status(404).json({ code: 404, message: 'Not Found' }) })
72
+
73
+ if (process.env.NODE_ENV == 'development') module.exports = app
src/reqValidate.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const Ajv = require("ajv")
2
+ const addFormats = require("ajv-formats")
3
+
4
+ const ajv = new Ajv()
5
+ addFormats(ajv)
6
+
7
+ const schema = {
8
+ "type": "object",
9
+ "properties": {
10
+ "mode": {
11
+ "type": "string",
12
+ "enum": ["source", "turnstile-min", "turnstile-max", "waf-session"],
13
+ },
14
+ "proxy": {
15
+ "type": "object",
16
+ "properties": {
17
+ "host": { "type": "string" },
18
+ "port": { "type": "integer" },
19
+ "username": { "type": "string" },
20
+ "password": { "type": "string" }
21
+ },
22
+ "additionalProperties": false
23
+ },
24
+ "url": {
25
+ "type": "string",
26
+ "format": "uri",
27
+ },
28
+ "authToken": {
29
+ "type": "string"
30
+ },
31
+ "siteKey": {
32
+ "type": "string"
33
+ }
34
+ },
35
+ "required": ["mode", "url"],
36
+ "additionalProperties": false
37
+ }
38
+
39
+ // const data = {
40
+ // mode: "source",
41
+ // url: "https://example.com",
42
+ // proxy: {
43
+ // host: "localhost",
44
+ // port: 8080,
45
+ // username: "test",
46
+ // password: "test"
47
+ // },
48
+ // authToken: "123456"
49
+ // }
50
+
51
+
52
+ function validate(data) {
53
+ const valid = ajv.validate(schema, data)
54
+ if (!valid) return ajv.errors
55
+ else return true
56
+ }
57
+
58
+ module.exports = validate
src/solveTurnstile.max.js ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require("fs");
2
+ function solveTurnstileMin({ url, proxy }) {
3
+ return new Promise(async (resolve, reject) => {
4
+ if (!url) return reject("Missing url parameter");
5
+
6
+ const context = await global.browser
7
+ .createBrowserContext({
8
+ proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
9
+ })
10
+ .catch(() => null);
11
+
12
+ if (!context) return reject("Failed to create browser context");
13
+
14
+ let isResolved = false;
15
+
16
+ var cl = setTimeout(async () => {
17
+ if (!isResolved) {
18
+ await context.close();
19
+ reject("Timeout Error");
20
+ }
21
+ }, global.timeOut || 60000);
22
+
23
+ try {
24
+ const page = await context.newPage();
25
+
26
+ if (proxy?.username && proxy?.password)
27
+ await page.authenticate({
28
+ username: proxy.username,
29
+ password: proxy.password,
30
+ });
31
+
32
+ await page.evaluateOnNewDocument(() => {
33
+ let token = null;
34
+ async function waitForToken() {
35
+ while (!token) {
36
+ try {
37
+ token = window.turnstile.getResponse();
38
+ } catch (e) {}
39
+ await new Promise((resolve) => setTimeout(resolve, 500));
40
+ }
41
+ var c = document.createElement("input");
42
+ c.type = "hidden";
43
+ c.name = "cf-response";
44
+ c.value = token;
45
+ document.body.appendChild(c);
46
+ }
47
+ waitForToken();
48
+ });
49
+
50
+ await page.goto(url, {
51
+ waitUntil: "domcontentloaded",
52
+ });
53
+
54
+ await page.waitForSelector('[name="cf-response"]', {
55
+ timeout: 60000,
56
+ });
57
+ const token = await page.evaluate(() => {
58
+ try {
59
+ return document.querySelector('[name="cf-response"]').value;
60
+ } catch (e) {
61
+ return null;
62
+ }
63
+ });
64
+ isResolved = true;
65
+ clearInterval(cl);
66
+ await context.close();
67
+ if (!token || token.length < 10) return reject("Failed to get token");
68
+ return resolve(token);
69
+ } catch (e) {
70
+ console.log(e);
71
+
72
+ if (!isResolved) {
73
+ await context.close();
74
+ clearInterval(cl);
75
+ reject(e.message);
76
+ }
77
+ }
78
+ });
79
+ }
80
+ module.exports = solveTurnstileMin;
src/solveTurnstile.min.js ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function solveTurnstileMin({ url, proxy, siteKey }) {
2
+ return new Promise(async (resolve, reject) => {
3
+ if (!url) return reject("Missing url parameter");
4
+ if (!siteKey) return reject("Missing siteKey parameter");
5
+
6
+ const context = await global.browser
7
+ .createBrowserContext({
8
+ proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
9
+ })
10
+ .catch(() => null);
11
+ if (!context) return reject("Failed to create browser context");
12
+
13
+ let isResolved = false;
14
+
15
+ var cl = setTimeout(async () => {
16
+ if (!isResolved) {
17
+ await context.close();
18
+ reject("Timeout Error");
19
+ }
20
+ }, global.timeOut || 60000);
21
+
22
+ try {
23
+ const page = await context.newPage();
24
+
25
+ if (proxy?.username && proxy?.password)
26
+ await page.authenticate({
27
+ username: proxy.username,
28
+ password: proxy.password,
29
+ });
30
+
31
+ await page.setRequestInterception(true);
32
+
33
+ page.on("request", async (request) => {
34
+ if (
35
+ [url, url + "/"].includes(request.url()) &&
36
+ request.resourceType() === "document"
37
+ ) {
38
+ const response = await request.respond({
39
+ status: 200,
40
+ contentType: "text/html",
41
+ body: String(
42
+ require("fs").readFileSync("./src/fakePage.html")
43
+ ).replace(/<site-key>/g, siteKey),
44
+ });
45
+ } else {
46
+ await request.continue();
47
+ }
48
+ });
49
+
50
+ await page.goto(url, {
51
+ waitUntil: "domcontentloaded",
52
+ });
53
+
54
+ await page.waitForSelector('[name="cf-response"]', {
55
+ timeout: 60000,
56
+ });
57
+
58
+ const token = await page.evaluate(() => {
59
+ try {
60
+ return document.querySelector('[name="cf-response"]').value;
61
+ } catch (e) {
62
+ return null;
63
+ }
64
+ });
65
+
66
+ isResolved = true;
67
+ clearInterval(cl);
68
+ await context.close();
69
+ if (!token || token.length < 10) return reject("Failed to get token");
70
+ return resolve(token);
71
+ } catch (e) {
72
+ if (!isResolved) {
73
+ await context.close();
74
+ clearInterval(cl);
75
+ reject(e.message);
76
+ }
77
+ }
78
+ });
79
+ }
80
+ module.exports = solveTurnstileMin;
src/wafSession.js ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ async function findAcceptLanguage(page) {
2
+ return await page.evaluate(async () => {
3
+ const result = await fetch("https://httpbin.org/get")
4
+ .then((res) => res.json())
5
+ .then(
6
+ (res) =>
7
+ res.headers["Accept-Language"] || res.headers["accept-language"]
8
+ )
9
+ .catch(() => null);
10
+ return result;
11
+ });
12
+ }
13
+
14
+ function getSource({ url, proxy }) {
15
+ return new Promise(async (resolve, reject) => {
16
+ if (!url) return reject("Missing url parameter");
17
+ const context = await global.browser
18
+ .createBrowserContext({
19
+ proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
20
+ })
21
+ .catch(() => null);
22
+ if (!context) return reject("Failed to create browser context");
23
+
24
+ let isResolved = false;
25
+
26
+ var cl = setTimeout(async () => {
27
+ if (!isResolved) {
28
+ await context.close();
29
+ reject("Timeout Error");
30
+ }
31
+ }, global.timeOut || 60000);
32
+
33
+ try {
34
+ const page = await context.newPage();
35
+
36
+ if (proxy?.username && proxy?.password)
37
+ await page.authenticate({
38
+ username: proxy.username,
39
+ password: proxy.password,
40
+ });
41
+ let acceptLanguage = await findAcceptLanguage(page);
42
+ await page.setRequestInterception(true);
43
+ page.on("request", async (request) => request.continue());
44
+ page.on("response", async (res) => {
45
+ try {
46
+ if (
47
+ [200, 302].includes(res.status()) &&
48
+ [url, url + "/"].includes(res.url())
49
+ ) {
50
+ await page
51
+ .waitForNavigation({ waitUntil: "load", timeout: 5000 })
52
+ .catch(() => {});
53
+ const cookies = await page.cookies();
54
+ let headers = await res.request().headers();
55
+ delete headers["content-type"];
56
+ delete headers["accept-encoding"];
57
+ delete headers["accept"];
58
+ delete headers["content-length"];
59
+ headers["accept-language"] = acceptLanguage;
60
+ await context.close();
61
+ isResolved = true;
62
+ clearInterval(cl);
63
+ resolve({ cookies, headers });
64
+ }
65
+ } catch (e) {}
66
+ });
67
+
68
+ await page.goto(url, {
69
+ waitUntil: "domcontentloaded",
70
+ });
71
+ } catch (e) {
72
+ if (!isResolved) {
73
+ await context.close();
74
+ clearInterval(cl);
75
+ reject(e.message);
76
+ }
77
+ }
78
+ });
79
+ }
80
+ module.exports = getSource;