UNPKG

bbpro

Version:

BrowserBox - remote browser isolation tool

377 lines (347 loc) 10.4 kB
import fs from 'fs'; import os from 'os'; import path from 'path'; import {execSync} from 'child_process'; import {fileURLToPath} from 'url'; import isDocker from 'is-docker'; import {FRAME_CONTROL} from './public/voodoo/src/common.js'; import {APP_ROOT as app_root} from './root.js'; export * from './args.js'; export const T2_MINUTES = 2 * 60; // 2 minutes in seconds export const EXPEDITE = new Set([ "Page.navigate", "Page.navigateToHistoryEntry", "Runtime.evaluate", "Emulation.setUserAgentOverride", "Input.dispatchMouseEvent", ]); export const LOG_FILE = { Commands: new Set(), FileHandle: null }; export const DEBUG = Object.freeze({ events: false, commands: false, adBlock: true, debugAddr: true, debugScaledUpCoViewport: false, debugInterception: false, noCastMaxDims: false, debugAckBlast: false, allowAckBlastOnStart: !process.env.TORBB && true, dontSendActivate: false, ALL_FLAGS: false, // turn on all chrome flags listed in MISC_STABILITY_RELATED_FLAGS_THAT_REDUCE_SECURITY localTestRTT: !process.env.TORBB && process.platform == "darwin" && true, debugCast: false, showTargetSessionMap: false, debugFileDownload: false, debugFileUpload: false, useNewAsgardHeadless: false, showFlags: false, allowExternalChrome: true, debugChromeStart: false, logFileCommands: false, showTodos: false, showViewportChanges: false, showResizeEvents: false, logRestartCast: false, showErrorSources: false, showNoSessionIdWarnings: false, showBlockedCaptureScreenshots: false, coords: false, scrollbars: true, get ensureUptimeBeforeRestart() { return this.mode !== 'dev' }, debugRestart: true, debugUntilTrue: false, debugUntilForever: false, debugViewportDimensions: false, debugDevtoolsServer: false, /* peer and websocket connections */ cnx: false, debugClicks: false, debugNoticeSignal: false, throttleIntentPrompts: false, // this rewdraws every mouse event which produces too many frames for slow Tor showMousePosition: !process.env.TORBB && true, debugConnect: false, debugCommandResponses: false, dataDebug: false, debugHistory: false, debugFaviconsSend: false, neverWait: true, /* for commands */ attachImmediately: true, manuallyInjectIntoEveryCreatedContext: true, ignoreCertificateErrors: true, debugNavigator: false, showContextIdCalls: false, debugCopyPaste: false, watchFrameStack: false, enableClientsToSetURL: true, debugModals: false, debugSendResult: false, metaDebug: false, channelDebug: false, debugCookie: false, waitLonger: true, useLoopbackIP: true, debugAuth: false, pausedDebug: false, useWebRTC: !process.env.TORBB && true, binaryFrames: true, sendImmediate: !process.env.TORBB && true, chooseFastest: !process.env.TORBB && true, logCastOutOfOrderFrames: false, noSecurityHeaders: false, mode: 'prod', // prod or dev (whether to bundle frontend code or not) showOrigin: false, useFlashEmu: process.env.USE_FLASH == 'true' ? true : false, showFlash: false, /* debug flash */ loadSPLFreshEachLogin: false, frameDebug: false, adaptiveImagery: true, debugAdaptiveImagery: false, useGL: true, disableGL: false, disable3D: process.platform == 'linux', // this logs resize related data, probably rename resizeDebug or logResize resize: false, logFastest: false, showConsoleMessages: false, get debugConsoleMessages() { /* || other condition || some other condition ... etc ... */ return this.debugFavicon || this.showConsoleMessages; }, debugFavicon: false, worldDebug: false, bufSend: true, acks: false, // actually this doesn't "turn on" acks. They are on by default // this flag just turns on acks logging. It should probably be called // logAcks (like it is in the front end) socDebug: false, fontDebug: false, useHash: false, cwebp: false, sendFramesWhenTheyArrive: !process.env.TORBB && true, onlySendOnAck: process.env.TORBB || false, goSecure: true, noAudio: false, legacyShots: !FRAME_CONTROL, /* until enableBeginFrameControl can be set for any target whether created with createTarget or simply spawning, we must use legacy shots */ get dontShowFetchDomain() { return this.commands && true; }, shotDebug: false, noShot: false, dev: false, val: 0, low: 1, med: 3, high: 5 }); console.log(DEBUG); export const ALLOWED_3RD_PARTY_EMBEDDERS = [ "https://users.dosyago.com", "https://cloudtabs.net", "https://*.cloudtabs.net", "https://browserbox.pro", "https://*.browserbox.pro", "https://localhost:*", ...( process.env.DOMAIN ? [ `https://${process.env.DOMAIN}:*`, ] : []) ]; export const FLASH_FORMATS = new Set([ 'swf', 'fla', 'as', 'jsfl', ]); export const CONFIG = Object.freeze({ useTorProxy: process.env.TOR_PROXY || false, useScaledUpCoViewport: true, useCappedScaling: true, mobileMaxWidth: 414, // CSS pixels mobileMaxHeight: 736, // CSS pixels castSyncsWithActive: true, doAckBlast: !process.env.TORBB && true, SHORT_TIMEOUT: 30, useLayerTreeDomain: false, tailShots: false, alwaysRestartCast: true, blockAllCaptureScreenshots: true, setAlternateBackgroundColor: false, screencastOnly: true, baseDir: path.resolve(os.homedir(), '.config', 'dosyago', 'bbpro'), darkMode: false, forceDarkContentMode: false, audioDropPossiblySilentFrames: true, sslcerts: port => { if ( process.env.TORBB ) { DEBUG.debugAddr && console.log('Cert file for', process.env[`ADDR_${port}`]); return path.join(process.env.SSLCERTS_DIR, process.env[`ADDR_${port}`]); } else { return process.env.SSLCERTS_DIR ? process.env.SSLCERTS_DIR : 'sslcerts'; } }, reniceValue: process.env.RENICE_VALUE || -15, sleepMax: 20000, maxTimers: 800, FORBIDDEN: new Set([ 'javascript:', 'file:', 'vbscript:' ]), connectivityTests: [ "https://1.1.1.1", "https://dns.google", "https://www.akamai.com", "https://www.lumen.com", "https://www.equinix.com", "https://www.f5.com", "https://www.cogentco.com", "https://www.he.net", "https://www.arista.com", ] }); export const localBlockList = process.platform == 'darwin' || process.env.GITHUB_ACTIONS || isDocker() ? [] : [ /^localhost/, /^::1/, /^127.0/, /^192.168./, /^169.254./, /^10./, /^172.(1[6-9]|2[0-9]|3[01])./, ]; const Timers = new Set(); if ( ! DEBUG.useWebRTC ) { console.warn(`Warning!! Not using web rtc`); } if ( DEBUG.logFileCommands ) { LOG_FILE.FileHandle = fs.openSync(path.resolve(CONFIG.baseDir, 'watch-commands.log'), 'a'); process.on('beforeExit', () => { fs.closeSync(LOG_FILE.FileHandle); }); } export const SignalNotices = path.resolve(CONFIG.baseDir, 'notices'); export const NoticeFile = 'text'; export const noticeFilePath = path.resolve(SignalNotices, NoticeFile); export const NOTICE_SIGNAL = 'SIGPIPE'; export const CONNECTION_ID_URL = "data:text,DoNotDeleteMe"; export const MAX_TABS = 15; const MIN_HEIGHT = 300; const MIN_WAIT = 10; const MAX_WAITS = 200; export const COMMAND_MAX_WAIT = DEBUG.waitLonger ? 5000 : 333; /* don't wait very long, but wait longer in debug */ // test for webpack export const APP_ROOT = app_root; if ( DEBUG.noSecurityHeaders ) { console.warn(`Security headers are switched off! Audio will fail.`); } //export const APP_ROOT = APP_ROOT; export const GO_SECURE = fs.existsSync(path.resolve(os.homedir(), CONFIG.sslcerts(process.env.APP_PORT), 'privkey.pem')); export const version = 'v1'; export const COOKIENAME = `litewait-${version}-userauth-${GO_SECURE?'sec':'nonsec'}`; export const SECURE_VIEW_SCRIPT = path.join(APP_ROOT, 'zombie-lord', 'scripts', 'get_download_view_url.sh'); fs.mkdirSync(CONFIG.baseDir, {recursive: true}); fs.mkdirSync(SignalNotices, {recursive:true}); try { execSync(`chmod 757 ${SignalNotices}`); } catch(e) { console.warn(`Running chmod on notices file failed.`); } export async function throwAfter(ms, command, port, Aborter) { // could make this sleep cancellable await sleep(ms, Aborter); //DEBUG.metaDebug && console.log(`Throwing after`); // let's not throw but instead return a warning, that we could collect on the client // or server if we warned in another way // rather than just exploding //throw new Error(`Timed out after ${ms}. ${port} : ${JSON.stringify(command,null,2)}`); return { meta: [ { timeoutWarning: true, timedOutAfter: ms, originalCommand: command, port } ] }; } export function consolelog(...args) { setTimeout(() => console.log(...args), 50); } export async function sleep(ms, Aborter) { if ( Timers.size > CONFIG.maxTimers ) { throw new Error(`Too many timers created!`); } let timer; ms = Math.min(ms, CONFIG.sleepMax); const pr = new Promise(res => timer = setTimeout(() => { if ( Aborter ) { Aborter.do = () => void 0; } Timers.delete(pr); res(); }, ms)); Timers.add(pr); if ( Aborter ) { Aborter.do = () => { clearTimeout(timer); Timers.delete(pr); }; } return pr; } export async function untilTrue(pred, waitOverride = MIN_WAIT, maxWaits = MAX_WAITS) { let waitCount = 0; let resolve; const pr = new Promise(res => resolve = res); setTimeout(checkPred, 0); return pr; async function checkPred() { DEBUG.debugUntilTrue && console.log('Checking', pred); if ( await pred() ) { return resolve(true); } else { waitCount++; if ( waitCount < maxWaits ) { setTimeout(checkPred, waitOverride); } } } } export async function untilForever(pred, waitOverride = 5000) { let resolve; const pr = new Promise(res => resolve = res); setTimeout(checkPred, 0); return pr; function checkPred() { DEBUG.debugUntilForever && console.log('Checking', pred); if ( pred() ) { return resolve(true); } else { setTimeout(checkPred, waitOverride); } } } // leading edge throttle export function throttle(func, wait) { let timeout; const throttled = (...args) => { if ( ! timeout ) { timeout = setTimeout(() => timeout = false, wait); return func(...args); } } return throttled; }