Skip to content

Commit

Permalink
fix(emulate): mask widevine checks
Browse files Browse the repository at this point in the history
  • Loading branch information
blakebyrnes committed Mar 25, 2021
1 parent cb49d1e commit 65e8655
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 19 deletions.
1 change: 1 addition & 0 deletions core-interfaces/IBrowserEngine.ts
Expand Up @@ -4,6 +4,7 @@ export default interface IBrowserEngine {
executablePath: string;
executablePathEnvVar: string;
extraLaunchArgs?: string[];
isHeaded?: boolean;
}

export type IBrowserEngineConfig = Pick<
Expand Down
16 changes: 8 additions & 8 deletions core/index.ts
Expand Up @@ -151,13 +151,13 @@ export default class Core {
});
});

if (process.env.NODE_ENV !== 'test') {
process.on('uncaughtExceptionMonitor', async (error: Error) => {
await Core.logUnhandledError(error, true);
process.on('uncaughtExceptionMonitor', async (error: Error) => {
await Core.logUnhandledError(error, true);
if (process.env.NODE_ENV !== 'test') {
await Core.shutdown();
});
}
});

process.on('unhandledRejection', async (error: Error) => {
await Core.logUnhandledError(error, false);
});
}
process.on('unhandledRejection', async (error: Error) => {
await Core.logUnhandledError(error, false);
});
2 changes: 0 additions & 2 deletions core/lib/GlobalPool.ts
Expand Up @@ -106,10 +106,8 @@ export default class GlobalPool {
const puppet = new Puppet(engine);
this.puppets.push(puppet);

const showBrowser = Boolean(JSON.parse(process.env.SHOW_BROWSER ?? 'false'));
const browserOrError = await puppet.start({
proxyPort: this.mitmServer.port,
showBrowser,
});
if (browserOrError instanceof Error) throw browserOrError;
return puppet;
Expand Down
4 changes: 4 additions & 0 deletions emulate-browsers/base/index.ts
Expand Up @@ -25,6 +25,10 @@ function getEngine(
executablePathEnvVar,
).toJSON();

engineFetcher.isHeaded = Boolean(
JSON.parse(process.env.SA_SHOW_BROWSER ?? process.env.SHOW_BROWSER ?? 'false'),
);

log.stats('Browser.getEngine', {
sessionId: null,
engineFetcher,
Expand Down
19 changes: 19 additions & 0 deletions emulate-browsers/base/injected-scripts/navigator.ts
Expand Up @@ -38,3 +38,22 @@ if ('clearAppBadge' in self.navigator) {
return Promise.resolve(undefined);
});
}

if (args.headless === true && 'requestMediaKeySystemAccess' in self.navigator) {
proxyFunction(self.navigator, 'requestMediaKeySystemAccess', (target, thisArg, argArray) => {
if (argArray.length < 2) {
return ProxyOverride.callOriginal;
}
const [keySystem, configs] = argArray;
if (keySystem !== 'com.widevine.alpha' || [...configs].length < 1) {
return ProxyOverride.callOriginal;
}
return target
.call(thisArg, 'org.w3.clearkey', configs)
.then(x => {
proxyGetter(x, 'keySystem', () => keySystem);
return x;
})
.catch(err => cleanErrorStack(err));
});
}
1 change: 1 addition & 0 deletions emulate-browsers/chrome-80/index.ts
Expand Up @@ -130,6 +130,7 @@ export default class Chrome80 {
domOverrides.add('navigator', {
userAgentString: this.userAgentString,
platform: this.osPlatform,
headless: Chrome80.engine.isHeaded !== true,
});

domOverrides.add('MediaDevices.prototype.enumerateDevices', {
Expand Down
1 change: 1 addition & 0 deletions emulate-browsers/chrome-81/index.ts
Expand Up @@ -130,6 +130,7 @@ export default class Chrome81 {
domOverrides.add('navigator', {
userAgentString: this.userAgentString,
platform: this.osPlatform,
headless: Chrome81.engine.isHeaded !== true,
});

domOverrides.add('MediaDevices.prototype.enumerateDevices', {
Expand Down
1 change: 1 addition & 0 deletions emulate-browsers/chrome-83/index.ts
Expand Up @@ -130,6 +130,7 @@ export default class Chrome83 {
domOverrides.add('navigator', {
userAgentString: this.userAgentString,
platform: this.osPlatform,
headless: Chrome83.engine.isHeaded !== true,
});

domOverrides.add('MediaDevices.prototype.enumerateDevices', {
Expand Down
1 change: 1 addition & 0 deletions emulate-browsers/safari-13/index.ts
Expand Up @@ -178,6 +178,7 @@ export default class Safari13 {
domOverrides.add('navigator', {
userAgentString: this.userAgentString,
platform: this.osPlatform,
headless: Safari13.engine.isHeaded !== true,
});

domOverrides.add('MediaDevices.prototype.enumerateDevices', {
Expand Down
36 changes: 36 additions & 0 deletions full-client/test/detection.test.ts
Expand Up @@ -67,6 +67,35 @@ beforeAll(async () => {
afterAll(Helpers.afterAll, 30e3);
afterEach(Helpers.afterEach, 30e3);

test('widevine detection', async () => {
const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
await agent.goto(koaServer.baseUrl);

const accessKey = await agent
.getJsValue(
`navigator.requestMediaKeySystemAccess('com.widevine.alpha', [{
initDataTypes: ['cenc'],
audioCapabilities: [
{
contentType: 'audio/mp4;codecs="mp4a.40.2"',
},
],
videoCapabilities: [
{
contentType: 'video/mp4;codecs="avc1.42E01E"',
},
],
},
]).then(x => {
if (x.keySystem !== 'com.widevine.alpha') throw new Error('Wrong keysystem ' + x.keySystem);
return x.createMediaKeys();
})`,
)
.catch(err => err);
expect(accessKey.type).toBe('MediaKeys');
});

test('should pass FpScanner', async () => {
const analyzePromise = new Promise(resolve => {
koaServer.post('/analyze', async ctx => {
Expand All @@ -81,6 +110,7 @@ test('should pass FpScanner', async () => {
});

const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
await agent.goto(`${koaServer.baseUrl}/collect`);

const data = await analyzePromise;
Expand All @@ -97,6 +127,7 @@ test('should pass FpScanner', async () => {

test('should not be denied for notifications but prompt for permissions', async () => {
const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
await agent.goto(`${koaServer.baseUrl}`);
const activeTab = await agent.activeTab;
const tabId = await activeTab.tabId;
Expand All @@ -120,6 +151,7 @@ test('should not be denied for notifications but prompt for permissions', async

test('should not leave markers on permissions.query.toString', async () => {
const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
const tabId = await agent.activeTab.tabId;
await agent.goto(`${koaServer.baseUrl}`);
const sessionId = await agent.sessionId;
Expand All @@ -146,6 +178,7 @@ test('should not leave markers on permissions.query.toString', async () => {

test('should not recurse the toString function', async () => {
const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
await agent.goto(`${koaServer.baseUrl}`);
const tabId = await agent.activeTab.tabId;
const sessionId = await agent.sessionId;
Expand All @@ -166,6 +199,7 @@ test('should not recurse the toString function', async () => {

test('should properly maintain stack traces in toString', async () => {
const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
await agent.goto(`${koaServer.baseUrl}`);
const tabId = await agent.activeTab.tabId;
const sessionId = await agent.sessionId;
Expand Down Expand Up @@ -201,6 +235,7 @@ test('should properly maintain stack traces in toString', async () => {
// https://github.com/digitalhurricane-io/puppeteer-detection-100-percent
test('should not leave stack trace markers when calling getJsValue', async () => {
const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
const tabId = await agent.activeTab.tabId;
await agent.goto(koaServer.baseUrl);
const sessionId = await agent.sessionId;
Expand All @@ -224,6 +259,7 @@ document.querySelector = (function (orig) {

test('should not leave stack trace markers when calling in page functions', async () => {
const agent = await handler.createAgent();
Helpers.needsClosing.push(agent);
koaServer.get('/marker', ctx => {
ctx.body = `
<body>
Expand Down
3 changes: 2 additions & 1 deletion puppet-chrome/index.ts
Expand Up @@ -96,7 +96,6 @@ const defaultArgs = [
'--disable-breakpad', // Disable crashdump collection (reporting is already disabled in Chromium)
'--disable-client-side-phishing-detection', // Disables client-side phishing detection.
'--disable-domain-reliability', // Disables Domain Reliability Monitoring, which tracks whether the browser has difficulty contacting Google-owned sites and uploads reports to Google.
'--disable-component-update', // Don't update the browser 'components' listed at chrome://components/
'--disable-component-extensions-with-background-pages', // Disable some built-in extensions that aren't affected by --disable-extensions
'--disable-default-apps', // Disable installation of default apps on first run
'--disable-dev-shm-usage', // https://github.com/GoogleChrome/puppeteer/issues/1834
Expand All @@ -116,6 +115,8 @@ const defaultArgs = [

'--incognito',

'--use-fake-device-for-media-stream',

'--no-default-browser-check', // Disable the default browser check, do not prompt to set it as such
'--metrics-recording-only', // Disable reporting to UMA, but allows for collection
'--no-first-run', // Skip first run wizards
Expand Down
14 changes: 6 additions & 8 deletions puppet/index.ts
Expand Up @@ -35,11 +35,7 @@ export default class Puppet {
puppBrowserCounter += 1;
}

public start(
args: ILaunchArgs = {
showBrowser: false,
},
): Promise<IPuppetBrowser | Error> {
public start(args: ILaunchArgs = {}): Promise<IPuppetBrowser | Error> {
if (this.browserOrError) {
return this.browserOrError;
}
Expand Down Expand Up @@ -88,8 +84,11 @@ export default class Puppet {
}

try {
const { proxyPort, showBrowser } = args;
const launchArgs = launcher.getLaunchArgs({ showBrowser, proxyPort });
const { proxyPort } = args;
const launchArgs = launcher.getLaunchArgs({
showBrowser: !!this.engine.isHeaded,
proxyPort,
});

// exists, but can't launch, try to launch
await validateHostRequirements(this.engine);
Expand Down Expand Up @@ -136,5 +135,4 @@ ${remedyMessage}`);

interface ILaunchArgs {
proxyPort?: number;
showBrowser?: boolean;
}

0 comments on commit 65e8655

Please sign in to comment.