Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade to Playwright 0.12.1 and update other deps #71

Merged
merged 10 commits into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules/
.DS_Store
.env
data/
*.sqlite
241 changes: 92 additions & 149 deletions backend/package-lock.json

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
"author": "Max Schmitt <max@schmitt.mx>",
"license": "MIT",
"dependencies": {
"@sentry/node": "^5.13.0",
"@sentry/node": "^5.15.0",
"bottleneck": "^2.19.5",
"express": "^4.17.1",
"ffmpeg-static": "^4.0.1",
"mime-types": "^2.1.26",
"playwright": "^0.11.1-next.1583993157193",
"playwright-core": "^0.11.1-next.1583993157193",
"playwright-video": "^0.5.0",
"sqlite": "^3.0.3",
"playwright": "^0.12.1",
"playwright-core": "^0.12.1",
"playwright-video": "^0.12.0",
"sqlite": "^3.0.6",
"sqlite3": "^4.1.1",
"typescript": "^3.8.3",
"uuid": "^7.0.1",
"vm2": "^3.8.4"
"uuid": "^7.0.2",
"vm2": "^3.9.0"
},
"scripts": {
"build": "tsc",
Expand All @@ -29,12 +29,12 @@
"test": "jest"
},
"devDependencies": {
"@sentry/types": "5.14.2",
"@sentry/types": "5.15.0",
"@types/debug": "4.1.5",
"@types/express": "4.17.3",
"@types/jest": "25.1.4",
"@types/mime-types": "2.1.0",
"@types/node": "13.9.1",
"@types/node": "13.9.3",
"@types/sqlite3": "3.1.6",
"@types/uuid": "7.0.2",
"jest": "25.1.0",
Expand Down
53 changes: 30 additions & 23 deletions backend/src/playwright.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import path from 'path'
import { EventEmitter } from 'events'
import { v4 as uuidv4 } from 'uuid'
import { Browser, WebKitBrowser } from 'playwright-core'
import playwright from 'playwright-core'
import { Browser, WebKitBrowser, BrowserTypeLaunchOptions, PagePdfOptions, PageScreenshotOptions, ChromiumBrowser, FirefoxBrowser, Page as PageType } from 'playwright-core'
// @ts-ignore
import { Playwright } from 'playwright-core/lib/server/playwright'
import { CRBrowser } from 'playwright-core/lib/chromium/crBrowser';
// @ts-ignore
import { CRPage } from 'playwright-core/lib/chromium/crPage';
import { Page, FirefoxBrowser } from 'playwright-core/lib/api';
import { ScreenshotOptions, PDFOptions } from 'playwright-core/lib/types';
import { BufferType } from 'playwright-core/lib/platform';
import { LaunchOptions } from 'playwright-core/lib/server/browserType';
// @ts-ignore
import { Page } from 'playwright-core/lib/api';
// @ts-ignore
import { downloadOptionsFromENV } from 'playwright-core/download-browser';

import { saveVideo, PageVideoCapture } from 'playwright-video'

const BROWSER_ID = Symbol('BROWSER_ID');
Expand All @@ -27,7 +30,7 @@ export const registerFileListener = (browserId: string): (() => FileWrapper[]) =
}
}

const superScreenshot = Page.prototype.screenshot;
const superScreenshot: PageType["screenshot"] = Page.prototype.screenshot;

export const emitNewFile = (browserId: string, originalFileName: string): string => {
const ext = path.extname(originalFileName)
Expand All @@ -41,7 +44,7 @@ export const emitNewFile = (browserId: string, originalFileName: string): string
return publicPath
}

Page.prototype.screenshot = async function (options?: ScreenshotOptions): Promise<BufferType> {
Page.prototype.screenshot = async function (this: PageType, options?: PageScreenshotOptions): Promise<Buffer> {
if (options?.path) {
// @ts-ignore
const browserId = this.context()._browser[BROWSER_ID];
Expand All @@ -55,12 +58,12 @@ Page.prototype.screenshot = async function (options?: ScreenshotOptions): Promis
return Buffer.from([]);
}

const superCRPDF = CRPage.prototype.pdf;
const superCRPDF: PageType["pdf"] = CRPage.prototype.pdf;

CRPage.prototype.pdf = async function (options?: PDFOptions): Promise<BufferType> {
CRPage.prototype.pdf = async function (this: PageType, options?: PagePdfOptions): Promise<Buffer> {
if (options?.path && superCRPDF) {
// @ts-ignore
const browserId = this.page().context()._browser[BROWSER_ID];
const browserId = this._page.context()._browser[BROWSER_ID];
const publicPath = emitNewFile(browserId, options.path)
const buffer = await superCRPDF.call(this, {
...options,
Expand All @@ -82,16 +85,24 @@ const preBrowserLaunch = async (browser: Browser, id: string): Promise<void> =>
browser[BROWSER_ID] = id
}

export const getPlaywright = (id: string): Playwright => {
const pw = new Playwright({
downloadPath: path.join(__dirname, "..", "node_modules", "playwright"),
const pwDirname = path.join(__dirname, "..", "node_modules", "playwright")
const crExecutablePath = downloadOptionsFromENV(pwDirname, 'chromium').executablePath
const ffExecutablePath = downloadOptionsFromENV(pwDirname, 'firefox').executablePath
const wkExecutablePath = downloadOptionsFromENV(pwDirname, 'webkit').executablePath

export const getPlaywright = (id: string): typeof playwright => {
const pw: typeof playwright = new Playwright({
browsers: ['webkit', 'chromium', 'firefox'],
respectEnvironmentVariables: false,
});
// @ts-ignore
const originalChromiumLaunch = pw.chromium.launch
pw.chromium._executablePath = crExecutablePath;
// @ts-ignore
pw.chromium.launch = async (options: LaunchOptions = {}): Promise<CRBrowser> => {
pw.webkit._executablePath = wkExecutablePath;
// @ts-ignore
pw.firefox._executablePath = ffExecutablePath;

const originalChromiumLaunch = pw.chromium.launch
pw.chromium.launch = async (options: BrowserTypeLaunchOptions = {}): Promise<ChromiumBrowser> => {
const browser = await originalChromiumLaunch.apply(pw.chromium, [{
...options,
args: [...(options.args !== undefined ? options.args : []), "--no-sandbox"]
Expand All @@ -100,19 +111,15 @@ export const getPlaywright = (id: string): Playwright => {
return browser
}

// @ts-ignore
const originalWebKitLaunch = pw.webkit.launch
// @ts-ignore
pw.webkit.launch = async (options: LaunchOptions = {}): Promise<WebKitBrowser> => {
pw.webkit.launch = async (options: BrowserTypeLaunchOptions = {}): Promise<WebKitBrowser> => {
const browser = await originalWebKitLaunch.apply(pw.webkit, [options])
await preBrowserLaunch(browser, id)
return browser
}

// @ts-ignore
const originalFirefoxLaunch = pw.firefox.launch
// @ts-ignore
pw.firefox.launch = async (options: LaunchOptions = {}): Promise<FirefoxBrowser> => {
pw.firefox.launch = async (options: BrowserTypeLaunchOptions = {}): Promise<FirefoxBrowser> => {
const browser = await originalFirefoxLaunch.apply(pw.firefox, [options])
await preBrowserLaunch(browser, id)
return browser
Expand Down
21 changes: 0 additions & 21 deletions backend/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,6 @@ const limiter = new Bottleneck({
maxConcurrent: 5
});

// https://github.com/patriksimek/vm2/issues/268#issuecomment-593536163
const vm2SecurityPatch = (vm: VM): void => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// @ts-ignore
const internal = vm._internal;
const old = internal.Decontextify.object;
const handler = { __proto__: null };
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
internal.Decontextify.object = (object, traps, deepTraps, flags, mock) => {
const value = old(object, traps, deepTraps, flags, mock);
// @ts-ignore
const better = new Proxy(value, handler);
internal.Decontextify.proxies.set(object, better);
internal.Contextify.proxies.set(better, object);
return better;
};
};


export const runUntrustedCode = async (code: string): Promise<APIResponse> => {
if (!code) {
throw new Error("no code specified")
Expand Down Expand Up @@ -94,8 +75,6 @@ export const runUntrustedCode = async (code: string): Promise<APIResponse> => {
sandbox,
})

vm2SecurityPatch(vm)

await limiter.schedule(() => vm.run(code));

const files = getFiles()
Expand Down