-
Notifications
You must be signed in to change notification settings - Fork 885
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch back to the WEB client for the local API with potokens
- Loading branch information
Showing
11 changed files
with
283 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const path = require('path') | ||
|
||
/** @type {import('webpack').Configuration} */ | ||
module.exports = { | ||
name: 'botGuardScript', | ||
// Always use production mode, as we use the output as a function body and the debug output doesn't work for that | ||
mode: 'production', | ||
devtool: false, | ||
target: 'web', | ||
entry: { | ||
botGuardScript: path.join(__dirname, '../src/botGuardScript.js'), | ||
}, | ||
output: { | ||
filename: '[name].js', | ||
path: path.join(__dirname, '../dist'), | ||
library: { | ||
type: 'modern-module' | ||
} | ||
}, | ||
experiments: { | ||
outputModule: true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { BG } from 'bgutils-js' | ||
|
||
// This script has it's own webpack config, as it gets passed as a string to Electron's evaluateJavaScript function | ||
// in src/main/poTokenGenerator.js | ||
export default async function(visitorData) { | ||
const requestKey = 'O43z0dpjhgX20SCx4KAo' | ||
|
||
const bgConfig = { | ||
fetch: (input, init) => fetch(input, init), | ||
requestKey, | ||
globalObj: window, | ||
identifier: visitorData | ||
} | ||
|
||
const challenge = await BG.Challenge.create(bgConfig) | ||
|
||
if (!challenge) { | ||
throw new Error('Could not get challenge') | ||
} | ||
|
||
const interpreterJavascript = challenge.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue | ||
|
||
if (interpreterJavascript) { | ||
// eslint-disable-next-line no-new-func | ||
new Function(interpreterJavascript)() | ||
} else { | ||
console.warn('Unable to load VM.') | ||
} | ||
|
||
const poTokenResult = await BG.PoToken.generate({ | ||
program: challenge.program, | ||
globalName: challenge.globalName, | ||
bgConfig | ||
}) | ||
|
||
return poTokenResult.poToken | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { session, WebContentsView } from 'electron' | ||
import { readFile } from 'fs/promises' | ||
import { join } from 'path' | ||
|
||
/** | ||
* Generates a poToken (proof of origin token) using `bgutils-js`. | ||
* The script to generate it is `src/botGuardScript.js` | ||
* | ||
* This is intentionally split out into it's own thing, with it's own temporary in-memory session, | ||
* as the BotGuard stuff accesses the global `document` and `window` objects and also requires making some requests. | ||
* So we definitely don't want it running in the same places as the rest of the FreeTube code with the user data. | ||
* @param {string} visitorData | ||
* @returns {Promise<string>} | ||
*/ | ||
export async function generatePoToken(visitorData) { | ||
const sessionUuid = crypto.randomUUID() | ||
|
||
const theSession = session.fromPartition(`potoken-${sessionUuid}`, { cache: false }) | ||
|
||
theSession.setPermissionCheckHandler(() => false) | ||
// eslint-disable-next-line n/no-callback-literal | ||
theSession.setPermissionRequestHandler((webContents, permission, callback) => callback(false)) | ||
|
||
theSession.setUserAgent( | ||
theSession.getUserAgent() | ||
.split(' ') | ||
.filter(part => !part.includes('Electron')) | ||
.join(' ') | ||
) | ||
|
||
const webContentsView = new WebContentsView({ | ||
webPreferences: { | ||
backgroundThrottling: false, | ||
safeDialogs: true, | ||
sandbox: true, | ||
v8CacheOptions: 'none', | ||
session: theSession, | ||
offscreen: true | ||
} | ||
}) | ||
|
||
webContentsView.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) | ||
|
||
webContentsView.webContents.setAudioMuted(true) | ||
webContentsView.setBounds({ | ||
x: 0, | ||
y: 0, | ||
width: 1920, | ||
height: 1080 | ||
}) | ||
|
||
webContentsView.webContents.debugger.attach() | ||
|
||
await webContentsView.webContents.loadURL('data:text/html,', { | ||
baseURLForDataURL: 'https://www.youtube.com' | ||
}) | ||
|
||
await webContentsView.webContents.debugger.sendCommand('Emulation.setUserAgentOverride', { | ||
userAgent: theSession.getUserAgent(), | ||
acceptLanguage: 'en-US', | ||
platform: 'Win32', | ||
userAgentMetadata: { | ||
brands: [ | ||
{ | ||
brand: 'Not/A)Brand', | ||
version: '99' | ||
}, | ||
{ | ||
brand: 'Chromium', | ||
version: process.versions.chrome.split('.')[0] | ||
} | ||
], | ||
fullVersionList: [ | ||
{ | ||
brand: 'Not/A)Brand', | ||
version: '99.0.0.0' | ||
}, | ||
{ | ||
brand: 'Chromium', | ||
version: process.versions.chrome | ||
} | ||
], | ||
platform: 'Windows', | ||
platformVersion: '10.0.0', | ||
architecture: 'x86', | ||
model: '', | ||
mobile: false, | ||
bitness: '64', | ||
wow64: false | ||
} | ||
}) | ||
|
||
await webContentsView.webContents.debugger.sendCommand('Emulation.setDeviceMetricsOverride', { | ||
width: 1920, | ||
height: 1080, | ||
deviceScaleFactor: 1, | ||
mobile: false, | ||
screenWidth: 1920, | ||
screenHeight: 1080, | ||
positionX: 0, | ||
positionY: 0, | ||
screenOrientation: { | ||
type: 'landscapePrimary', | ||
angle: 0 | ||
} | ||
}) | ||
|
||
const script = await getScript(visitorData) | ||
|
||
const response = await webContentsView.webContents.executeJavaScript(script) | ||
|
||
webContentsView.webContents.close({ waitForBeforeUnload: false }) | ||
await theSession.closeAllConnections() | ||
|
||
return response | ||
} | ||
|
||
let cachedScript | ||
|
||
/** | ||
* @param {string} visitorData | ||
*/ | ||
async function getScript(visitorData) { | ||
if (!cachedScript) { | ||
const pathToScript = process.env.NODE_ENV === 'development' | ||
? join(__dirname, '../../dist/botGuardScript.js') | ||
/* eslint-disable-next-line n/no-path-concat */ | ||
: `${__dirname}/botGuardScript.js` | ||
|
||
const content = await readFile(pathToScript, 'utf-8') | ||
|
||
const match = content.match(/export{(\w+) as default};/) | ||
|
||
const functionName = match[1] | ||
|
||
cachedScript = content.replace(match[0], `;${functionName}("FT_VISITOR_DATA")`) | ||
} | ||
|
||
return cachedScript.replace('FT_VISITOR_DATA', visitorData) | ||
} |
Oops, something went wrong.