7 Commits

Author SHA1 Message Date
estebanthilliez
772ed5a176 add cookie wait endpoint
Some checks failed
CI / release (push) Failing after 1m21s
2026-04-24 22:21:24 +02:00
estebanthi
dcd929a4c5 Harden Docker production build
Some checks failed
CI / release (push) Failing after 37s
2026-01-18 11:38:14 +01:00
estebanthi
ca5402d15f fixed build
Some checks failed
CI / release (push) Failing after 2m54s
2026-01-18 08:51:15 +01:00
estebanthi
86ed863e59 fixed build
Some checks failed
CI / release (push) Failing after 51s
2026-01-18 08:48:30 +01:00
estebanthi
71aa54057f build
All checks were successful
CI / release (push) Successful in 18s
2026-01-17 16:26:12 +01:00
estebanthi
a7c78ba1b6 build 2026-01-17 16:24:55 +01:00
estebanthi
4586494580 updated ua 2026-01-17 16:20:57 +01:00
10 changed files with 1922 additions and 25 deletions

5
.dockerignore Normal file
View File

@@ -0,0 +1,5 @@
node_modules
cache
*.log
.DS_Store
.git

View File

@@ -0,0 +1,28 @@
name: CI
on:
push:
branches:
- 'main'
tags:
- 'v*.*.*'
jobs:
release:
uses: Wavyzz/ci-workflows/.github/workflows/docker-build-publish.yml@v1
with:
registry_host: ${{ secrets.REGISTRY_HOST }}
images: |
[
{
"name": "cf-bypass-fast",
"cache_ref": "cf-bypass-fast:buildcache",
"dockerfile": "./Dockerfile",
"context": "./"
}
]
secrets:
registry_user: ${{ secrets.REGISTRY_USER }}
registry_password: ${{ secrets.REGISTRY_PASSWORD }}
ci_token: ${{ secrets.CI_GITEA_TOKEN }}

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
node_modules/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
.DS_Store
.env
.env.*
!.env.example

View File

@@ -1,29 +1,43 @@
FROM node:20-slim
FROM node:20.12.2-slim
ENV NODE_ENV=production
# Install Chrome and dependencies
RUN apt update && apt install -y \
wget gnupg ca-certificates xvfb \
fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 \
libatk1.0-0 libxss1 libnss3 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
&& wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
&& apt install -y ./google-chrome-stable_current_amd64.deb \
&& rm google-chrome-stable_current_amd64.deb
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates wget gnupg xvfb fonts-liberation \
&& wget -qO- https://dl.google.com/linux/linux_signing_key.pub \
| gpg --dearmor -o /usr/share/keyrings/google-linux-signing-keyring.gpg \
&& echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-linux-signing-keyring.gpg] https://dl.google.com/linux/chrome/deb/ stable main" \
> /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends google-chrome-stable \
&& apt-get purge -y --auto-remove wget gnupg \
&& rm -rf /var/lib/apt/lists/*
RUN corepack enable
# Create a non-root user for running the app
RUN useradd --create-home --home-dir /app --shell /bin/sh appuser
# Set working directory
WORKDIR /app
# Entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh
# Copy and install dependencies
COPY package*.json ./
RUN npm install
COPY --chown=appuser:appuser package.json pnpm-lock.yaml ./
USER appuser
RUN corepack prepare pnpm@9.0.0 --activate \
&& pnpm install --frozen-lockfile --prod \
&& pnpm store prune
# Copy app code
COPY . .
COPY --chown=appuser:appuser . .
# Expose port (match your app's port)
EXPOSE 10000
# Start Xvfb and run the bot
CMD rm -f /tmp/.X99-lock && \
Xvfb :99 -screen 0 1024x768x24 & \
export DISPLAY=:99 && \
npm start
CMD ["/usr/local/bin/docker-entrypoint.sh"]

View File

@@ -6,6 +6,7 @@ A Node.js service that automates Chromium to bypass **Cloudflare IUAM** and **Tu
- **Cloudflare IUAM Bypass**: Automatically solves "I'm Under Attack Mode" challenges super fast within 3.337 seconds 💖
- **Turnstile Challenge Solver**: Handles Cloudflare Turnstile captchas
- **Generic Cookie Waiter**: Opens a page, waits for a named cookie, and returns it
- **Proxy Support**: Full HTTP proxy integration with authentication
- **Smart Timeout**: 20-second timeout with graceful null responses
- **Browser Management**: Concurrent browser limit control
@@ -75,12 +76,14 @@ Bypass Cloudflare protection and get cookies/tokens.
{
"mode": "iuam",
"domain": "https://olamovies.watch/generate",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
```
#### Parameters
- **`mode`** (required): `"iuam"` or `"turnstile"`
- **`domain`** (required): Target website URL
- **`user_agent`** (optional): User-Agent to use for the browser session
- **`proxy`** (optional): Proxy configuration object
#### Response (Success)
@@ -108,6 +111,48 @@ Bypass Cloudflare protection and get cookies/tokens.
}
```
### POST /cookie
Open a page and wait for a specific cookie to appear.
#### Request Body
```json
{
"domain": "https://example.com/",
"cookieName": "session_id"
}
```
#### Parameters
- **`domain`** (required): Target website URL
- **`cookieName`** (required): Cookie name to wait for
- **`user_agent`** (optional): User-Agent to use for the browser session
- **`proxy`** (optional): Proxy configuration object
#### Response (Success)
```json
{
"cookie_name": "session_id",
"cookie_value": "abc123",
"cookie_domain": ".example.com",
"cookie_path": "/",
"http_only": true,
"secure": true,
"user_agent": "Mozilla/5.0...",
"elapsed_time": 1.42
}
```
#### Response (No Cookie Found)
```json
{
"cookie_name": "session_id",
"cookie_value": null,
"user_agent": "Mozilla/5.0...",
"elapsed_time": 60.0
}
```
## 🌐 Proxy Configuration
The service supports HTTP proxies with authentication:
@@ -139,6 +184,7 @@ python api_test.py
cf-bypass/
├── endpoints/
│ ├── cloudflare.js # IUAM bypass logic
│ ├── cookie.js # Generic cookie waiter
│ └── turnstile.js # Turnstile solver
├── cache/
│ └── cache.json # Response cache

View File

@@ -12,15 +12,24 @@ async def main():
)
print(resp1.json())
resp2 = await client.post(
"http://localhost:8080/cloudflare",
json={
"domain": "https://lksfy.com/",
"siteKey": "0x4AAAAAAA49NnPZwQijgRoi",
"mode": "turnstile",
},
)
print(resp2.json())
resp2 = await client.post(
"http://localhost:8080/cloudflare",
json={
"domain": "https://lksfy.com/",
"siteKey": "0x4AAAAAAA49NnPZwQijgRoi",
"mode": "turnstile",
},
)
print(resp2.json())
resp3 = await client.post(
"http://localhost:8080/cookie",
json={
"domain": "https://example.com/",
"cookieName": "session_id",
},
)
print(resp3.json())
if __name__ == "__main__":
asyncio.run(main())

33
docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,33 @@
#!/bin/sh
rm -f /tmp/.X99-lock
Xvfb :99 -screen 0 1024x768x24 &
xvfb_pid=$!
export DISPLAY=:99
npm start &
app_pid=$!
term_handler() {
kill "$app_pid" 2>/dev/null || true
kill "$xvfb_pid" 2>/dev/null || true
}
trap term_handler INT TERM
while kill -0 "$app_pid" 2>/dev/null; do
if ! kill -0 "$xvfb_pid" 2>/dev/null; then
echo "Xvfb exited; stopping app." >&2
kill "$app_pid" 2>/dev/null || true
wait "$app_pid" 2>/dev/null || true
exit 1
fi
sleep 1
done
wait "$app_pid"
app_status=$?
kill "$xvfb_pid" 2>/dev/null || true
wait "$xvfb_pid" 2>/dev/null || true
exit "$app_status"

62
endpoints/cookie.js Normal file
View File

@@ -0,0 +1,62 @@
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
function logBrowserCookies({ domain, cookieName, cookies }) {
console.log(
`[cookie] Browser cookies before return for ${domain} (${cookieName}): ${JSON.stringify(cookies)}`
)
}
async function waitForCookie({ domain, proxy, cookieName }, page) {
if (!domain) throw new Error("Missing domain parameter")
if (!cookieName) throw new Error("Missing cookieName parameter")
const timeout = global.timeOut || 60000
const pollInterval = 250
const startTime = Date.now()
if (proxy?.username && proxy?.password) {
await page.authenticate({
username: proxy.username,
password: proxy.password,
})
}
await page.goto(domain, { waitUntil: "domcontentloaded" })
const userAgent = await page.evaluate(() => navigator.userAgent)
while (Date.now() - startTime < timeout) {
const cookies = await page.cookies()
const match = cookies.find((cookie) => cookie.name === cookieName)
if (match) {
logBrowserCookies({ domain, cookieName, cookies })
return {
cookie_name: match.name,
cookie_value: match.value,
cookie_domain: match.domain,
cookie_path: match.path,
http_only: match.httpOnly,
secure: match.secure,
user_agent: userAgent,
elapsed_time: (Date.now() - startTime) / 1000,
}
}
await sleep(pollInterval)
}
const cookies = await page.cookies()
logBrowserCookies({ domain, cookieName, cookies })
return {
cookie_name: cookieName,
cookie_value: null,
user_agent: userAgent,
elapsed_time: (Date.now() - startTime) / 1000,
}
}
module.exports = waitForCookie

View File

@@ -91,12 +91,16 @@ async function createBrowser(proxyServer = null) {
const turnstile = require('./endpoints/turnstile')
const cloudflare = require('./endpoints/cloudflare')
const waitForCookie = require('./endpoints/cookie')
app.post('/cloudflare', async (req, res) => {
const data = req.body
if (!data || typeof data.mode !== 'string') {
return res.status(400).json({ message: 'Bad Request: missing or invalid mode' })
}
if (data.user_agent && typeof data.user_agent !== 'string') {
return res.status(400).json({ message: 'Bad Request: invalid user_agent' })
}
if (authToken && data.authToken !== authToken) {
return res.status(401).json({ message: 'Unauthorized' })
}
@@ -126,6 +130,9 @@ app.post('/cloudflare', async (req, res) => {
page = ctx.page
await page.goto('about:blank')
if (data.user_agent) {
await page.setUserAgent(data.user_agent)
}
switch (data.mode) {
case "turnstile":
@@ -161,10 +168,56 @@ app.post('/cloudflare', async (req, res) => {
res.status(result.code ?? 200).json(result)
})
app.post('/cookie', async (req, res) => {
const data = req.body
if (!data || typeof data.domain !== 'string' || typeof data.cookieName !== 'string') {
return res.status(400).json({ message: 'Bad Request: missing or invalid domain/cookieName' })
}
if (data.user_agent && typeof data.user_agent !== 'string') {
return res.status(400).json({ message: 'Bad Request: invalid user_agent' })
}
if (authToken && data.authToken !== authToken) {
return res.status(401).json({ message: 'Unauthorized' })
}
if (global.browserLimit <= 0) {
return res.status(429).json({ message: 'Too Many Requests' })
}
global.browserLimit--
let result
let browser, page
try {
const proxyServer = data.proxy ? `${data.proxy.hostname}:${data.proxy.port}` : null
const ctx = await createBrowser(proxyServer)
browser = ctx.browser
page = ctx.page
await page.goto('about:blank')
if (data.user_agent) {
await page.setUserAgent(data.user_agent)
}
result = await waitForCookie(data, page)
.then((cookie) => ({ ...cookie }))
.catch((err) => ({ code: 500, message: err.message }))
} catch (err) {
result = { code: 500, message: err.message }
} finally {
if (browser) {
try { await browser.close() } catch {}
}
global.browserLimit++
}
res.status(result.code ?? 200).json(result)
})
app.use((req, res) => {
res.status(404).json({ message: 'Not Found' })
})
if (process.env.NODE_ENV === 'development') {
module.exports = app
}
}

1637
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff