Socket
Socket
Sign inDemoInstall

@ctrl/cloudflare

Package Overview
Dependencies
31
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 3.1.0 to 3.1.1

dist/catchCloudflare.d.ts

20

dist/index.d.ts

@@ -1,19 +0,1 @@

/// <reference types="node" />
import { Response } from 'got';
export declare function isCloudflareChallenge(statusCode: number, headers: any, body: string): boolean;
export declare function isCloudflareCaptcha(body: string): boolean;
export declare function solveChallenge(body: string, domain: string): {
ms: number;
answer: number;
};
export declare function jschlValue(body: string): string;
export declare function passValue(body: string): string;
export declare function sValue(body: string): string;
export declare function getRValue(body: string): object;
export declare function getMethod(body: string): string;
/**
* sets headers.accept and headers.user-agent if not exist
* @param headers existing headers
*/
export declare function setupHeaders(headers?: any): any;
export declare function catchCloudflare<T extends Buffer | string | object>(error: any, options: any, attempts?: number): Promise<Response<T>>;
export { catchCloudflare, CaptchaError, CloudflareMaxAttemptsError } from './catchCloudflare';
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const crypto_1 = __importDefault(require("crypto"));
const delay_1 = __importDefault(require("delay"));
const got_1 = __importDefault(require("got"));
const https_1 = __importDefault(require("https"));
const vm_1 = __importDefault(require("vm"));
const url_1 = require("url");
const BUG_REPORT = `\
Cloudflare may have changed their technique, or there may be a bug in the script.
Please read https://github.com/typectrl/cloudflare#updates, then file a \
bug report at https://github.com/typectrl/cloudflare/issues"\
`;
function isCloudflareChallenge(statusCode, headers, body) {
const { server } = headers;
return (statusCode === 503 &&
server.startsWith('cloudflare') &&
body.includes('jschl_vc') &&
body.includes('jschl_answer'));
}
exports.isCloudflareChallenge = isCloudflareChallenge;
function isCloudflareCaptcha(body) {
if (body.includes('why_captcha') || /cdn-cgi\/l\/chk_captcha/i.test(body)) {
return true;
}
return false;
}
exports.isCloudflareCaptcha = isCloudflareCaptcha;
function solveChallenge(body, domain) {
// regex without selecting setTimeout delay ms for reference
// const timeoutReg = /setTimeout\(function\(\){\s+(var s,t,o,p,b,r,e,a,k,i,n,g,f.+?\r?\n[\s\S]+?a\.value =.+?)\r?\n/;
const timeoutReg = /setTimeout\(\s*function\s*\(\){\s*(var\s+(?:[a-z]\s*,\s*)(?:[a-z]\s*,\s*)+(?:[a-z]\s*,\s*).+?\r?\n[\s\S]+?a\.value\s*=.+?)\r?\n[\s\S]+?,\s*(\d{4,5})\);\s*(?:\/\*eoc\*\/)?/;
const timeout = timeoutReg.exec(body);
let js = timeout && timeout.length ? timeout[1] : '';
js = js.replace(/a\.value = (.+\.toFixed\(10\);).+", r"\1/i, '$1');
js = js.replace(/(e\s=\sfunction\(s\)\s{[\s\S]*};)/g, '');
js = js.replace(/\s{3,}[a-z](?: = |\.).+/g, '');
js = js.replace(/'; \d+'/g, '');
// Strip characters that could be used to exit the string context
// These characters are not currently used in Cloudflare's arithmetic snippet
js = js.replace(/[\n\\']/, '');
js = js.replace('; 121', '');
if (!js.includes('toFixed')) {
throw new Error(`Error parsing Cloudflare IUAM Javascript challenge. ${BUG_REPORT}`);
}
// get setTimeout length that cloudflare has mandated
const ms = timeout && timeout.length > 1 ? Number(timeout[2]) : 6000;
// 2019-03-20: Cloudflare sometimes stores part of the challenge in a div which is later
// added using document.getElementById(x).innerHTML, so it is necessary to simulate that
// method and value.
let k = '';
let val = '';
const kReg = /k\s+=\s+'([^']+)';/g;
try {
// Find the id of the div in the javascript code.
const kResult = kReg.exec(body);
k = kResult && kResult.length ? kResult[1] : '';
const valReg = new RegExp(`<div(.*)id="${k}"(.*)>(.*)</div>`, 'g');
const valResult = valReg.exec(body);
val = valResult && valResult.length ? valResult[3] : '';
}
catch (_a) {
// If not available, either the code has been modified again, or the old style challenge is used.
// ignore errors
}
const dom = `var t = "${domain}";`;
const a = 'var a = {};';
const o = 'var o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";';
const e = `var e = function(s) {{
s += "==".slice(2 - (s.length & 3));
var bm, r = "", r1, r2, i = 0;
for (; i < s.length;) {{
bm = o.indexOf(s.charAt(i++)) << 18 | o.indexOf(s.charAt(i++)) << 12 | (r1 = o.indexOf(s.charAt(i++))) << 6 | (r2 = o.indexOf(s.charAt(i++)));
r += r1 === 64 ? g(bm >> 16 & 255) : r2 === 64 ? g(bm >> 16 & 255, bm >> 8 & 255) : g(bm >> 16 & 255, bm >> 8 & 255, bm & 255);
}}
return r;
}};`;
const g = 'var g = String.fromCharCode;';
const document = `var document= {getElementById: function(x) { return {innerHTML:"${val}"};}};`;
const atob = 'var atob = function(str) {return Buffer.from(str, "base64").toString("binary");};';
const jsx = a + o + e + dom + atob + g + document + js;
const str = vm_1.default.runInNewContext(jsx, { Buffer, g: String.fromCharCode }, { timeout: 5000 });
// must eval javascript - potentially very unsafe
const answer = Number(str);
try {
return { ms, answer };
}
catch (err) {
throw new Error(`Error occurred during evaluation: ${err.message}`);
}
}
exports.solveChallenge = solveChallenge;
function jschlValue(body) {
const lineReg = /^.*name="jschl_vc".*$/gm;
const line = lineReg.exec(body);
if (!line) {
throw new Error('Failed to find jschl_vc');
}
const valueReg = /\svalue="(\w+)"/g;
const result = valueReg.exec(line[0]);
if (!result || result.length === 0) {
throw new Error('Failed to parse value from jschl');
}
return result[1];
}
exports.jschlValue = jschlValue;
function passValue(body) {
const lineReg = /^.*name="pass".*$/gm;
const line = lineReg.exec(body);
if (!line) {
throw new Error('Failed to find pass field');
}
const valueReg = /\svalue="(.+?)"/g;
const result = valueReg.exec(line[0]);
if (!result || result.length === 0) {
throw new Error('Failed to parse value from pass field');
}
return result[1];
}
exports.passValue = passValue;
function sValue(body) {
const sReg = /name="s"\svalue="([^"]+)/g;
const s = sReg.exec(body);
return s && s.length ? s[1] : '';
}
exports.sValue = sValue;
function getRValue(body) {
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
const match = body.match(/name="(.+?)" value="(.+?)"/);
if (match) {
const hiddenInputName = match[1];
return { [hiddenInputName]: match[2] };
}
return {};
}
exports.getRValue = getRValue;
function getMethod(body) {
const methodReg = /method="(.+?)"/g;
const s = methodReg.exec(body);
return s && s.length ? s[1] : 'GET';
}
exports.getMethod = getMethod;
/**
* sets headers.accept and headers.user-agent if not exist
* @param headers existing headers
*/
function setupHeaders(headers = {}) {
return {
accept: headers.accept || 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
'upgrade-insecure-requests': '1',
'accept-language': 'en-US,en;q=0.9',
connection: 'keep-alive',
'Cache-Control': 'max-age=0',
};
}
exports.setupHeaders = setupHeaders;
class CaptchaError extends Error {
constructor(message) {
super(message); // 'Error' breaks prototype chain here
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
}
}
class CloudflareMaxAttemptsError extends Error {
constructor(message) {
super(message); // 'Error' breaks prototype chain here
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
}
}
async function catchCloudflare(error, options, attempts = 1) {
const config = { ...options };
// must have body present
if (!error.response || !error.response.body) {
throw error;
}
// must have cookiejar
if (!config.cookieJar) {
throw new Error('options.cookieJar required');
}
const { body } = error.response;
// recaptcha captcha encountered
if (isCloudflareCaptcha(body)) {
throw new CaptchaError('Cloudflare captcha encountered');
}
// error is not cloudflare related - rethrow
if (!isCloudflareChallenge(error.response.statusCode, error.response.headers, body)) {
throw error;
}
// max attempt error
if (attempts === 4) {
throw new CloudflareMaxAttemptsError(`Unable to bypass cloudflare attempts: ${attempts}`);
}
const newHeaders = setupHeaders(config.headers);
config.headers = { ...config.headers, ...newHeaders } || newHeaders;
// console.log(error.options);
config.headers.referer = `${error.options.url.href}${error.options.pathname || ''}`;
config.headers['cache-control'] = config.headers['cache-control'] || 'private';
const retry = {
statusCodes: [408, 413, 429, 500, 502, 504],
limit: 0,
};
config.retry = retry;
// get form field values
const jschlVc = jschlValue(body);
const pass = passValue(body);
const s = sValue(body);
const rValue = getRValue(body);
const method = getMethod(body);
// solve js challenge
const challenge = solveChallenge(body, error.options.url.hostname);
// defaults to 6 seconds or ms found in html
await delay_1.default(challenge.ms);
// make request with answer
config.prefixUrl = `${error.options.url.protocol}//${error.options.url.hostname}/`;
if (config.url.startsWith(config.prefixUrl)) {
config.url = config.url.replace(config.prefixUrl, '');
}
const payload = {
...rValue,
jschl_vc: jschlVc,
pass,
jschl_answer: challenge.answer,
};
if (method.toUpperCase() === 'GET') {
config.method = 'GET';
config.url = 'cdn-cgi/l/chk_jschl';
payload.s = s;
config.searchParams = new url_1.URLSearchParams(payload).toString().replace(/&amp;/g, '&');
}
else {
config.method = 'POST';
const queryMatch = body.match(/id="challenge-form" action="(.+?)" method="(.+?)"/);
config.searchParams = queryMatch[1].replace('/?__cf_chl_jschl_tk__', '__cf_chl_jschl_tk__').replace(/&amp;/g, '&');
const params = new url_1.URLSearchParams();
for (const entry of Object.entries(payload)) {
params.append(entry[0], entry[1]);
}
config.body = params.toString();
config.headers = {
...config.headers,
'content-type': 'application/x-www-form-urlencoded',
};
config.followRedirect = true;
}
if (!config.agent) {
config.agent = {
https: new https_1.default.Agent({
ciphers: crypto_1.default.constants.defaultCipherList + ':!ECDHE+SHA:!AES128-SHA',
}),
};
}
try {
return await got_1.default(config);
}
catch (err) {
// eslint-disable-next-line no-return-await
return await catchCloudflare(err, config, attempts + 1);
}
}
exports.catchCloudflare = catchCloudflare;
var catchCloudflare_1 = require("./catchCloudflare");
exports.catchCloudflare = catchCloudflare_1.catchCloudflare;
exports.CaptchaError = catchCloudflare_1.CaptchaError;
exports.CloudflareMaxAttemptsError = catchCloudflare_1.CloudflareMaxAttemptsError;
//# sourceMappingURL=index.js.map
{
"name": "@ctrl/cloudflare",
"version": "3.1.0",
"version": "3.1.1",
"description": "Bypass Cloudflare's anti-bot page also known as I'm Under Attack Mode",

@@ -59,3 +59,3 @@ "author": "Scott Cooper <scttcper@gmail.com>",

"eslint-plugin-import": "2.20.2",
"jest": "25.5.2",
"jest": "25.5.3",
"jest-junit": "10.0.0",

@@ -62,0 +62,0 @@ "nock": "12.0.3",

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc