up
Browse files- .dockerignore +9 -0
- .gitattributes +0 -35
- .gitignore +1 -0
- Dockerfile +28 -3
- LICENSE.md +21 -0
- README.md +3 -2
- package-lock.json +0 -0
- package.json +41 -0
- src/createBrowser.js +35 -0
- src/fakePage.html +30 -0
- src/getSource.js +60 -0
- src/index.js +73 -0
- src/reqValidate.js +58 -0
- src/solveTurnstile.max.js +80 -0
- src/solveTurnstile.min.js +80 -0
- src/wafSession.js +80 -0
.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.*
|
.gitattributes
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
node_modules
|
Dockerfile
CHANGED
|
@@ -1,3 +1,28 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
emoji: 💻
|
| 4 |
colorFrom: gray
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 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;
|