mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Add lockdown e2e test (#12562)
This PR adds an e2e test to ensure that the background and UI environments are locked down. It reuses the logic from the `protect-intrinsics.test.js`, and runs in both Chrome and Firefox.
This commit is contained in:
parent
1298a8cdc6
commit
5f0fd9d1c2
@ -191,9 +191,10 @@ module.exports = {
|
||||
'app/scripts/lockdown-more.js',
|
||||
'development/**/*.js',
|
||||
'test/e2e/**/*.js',
|
||||
'test/lib/wait-until-called.js',
|
||||
'test/env.js',
|
||||
'test/setup.js',
|
||||
'test/helpers/protect-intrinsics-helpers.js',
|
||||
'test/lib/wait-until-called.js',
|
||||
'jest.config.js',
|
||||
],
|
||||
parserOptions: {
|
||||
@ -204,6 +205,7 @@ module.exports = {
|
||||
files: [
|
||||
'app/scripts/lockdown-run.js',
|
||||
'app/scripts/lockdown-more.js',
|
||||
'test/helpers/protect-intrinsics-helpers.js',
|
||||
'test/unit-global/protect-intrinsics.test.js',
|
||||
],
|
||||
globals: {
|
||||
|
89
test/e2e/tests/lockdown.spec.js
Normal file
89
test/e2e/tests/lockdown.spec.js
Normal file
@ -0,0 +1,89 @@
|
||||
const { strict: assert } = require('assert');
|
||||
const { Browser } = require('selenium-webdriver');
|
||||
const {
|
||||
getGlobalProperties,
|
||||
testIntrinsic,
|
||||
} = require('../../helpers/protect-intrinsics-helpers');
|
||||
const { withFixtures } = require('../helpers');
|
||||
const { PAGES } = require('../webdriver/driver');
|
||||
|
||||
const isFirefox = process.env.SELENIUM_BROWSER === Browser.FIREFOX;
|
||||
|
||||
/**
|
||||
* This script iterates over all named intrinsics and tests that they are locked
|
||||
* down per ses/lockdown.
|
||||
*
|
||||
* We set globalThis to window in Firefox because the test fails otherwise.
|
||||
* We believe this is due to some Selenium-related shenanigans. In the browser,
|
||||
* this behavior is not a problem.
|
||||
*/
|
||||
const lockdownTestScript = `
|
||||
${isFirefox ? 'globalThis = window;' : ''}
|
||||
|
||||
const assert = {
|
||||
equal: (value, comparison, message) => {
|
||||
if (value !== comparison) {
|
||||
throw new Error(message || 'not equal');
|
||||
}
|
||||
},
|
||||
ok: (value, message) => {
|
||||
if (!value) {
|
||||
throw new Error(message || 'not ok');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
${getGlobalProperties.toString()}
|
||||
|
||||
${testIntrinsic.toString()}
|
||||
|
||||
try {
|
||||
getGlobalProperties().forEach((propertyName) => {
|
||||
console.log('Testing intrinsic:', propertyName);
|
||||
testIntrinsic(propertyName);
|
||||
})
|
||||
console.log('Lockdown test successful!');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log('Lockdown test failed.', error);
|
||||
return false;
|
||||
}
|
||||
`;
|
||||
|
||||
describe('lockdown', function () {
|
||||
const ganacheOptions = {
|
||||
accounts: [
|
||||
{
|
||||
secretKey:
|
||||
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
|
||||
balance: 25000000000000000000,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('the UI and background environments are locked down', async function () {
|
||||
await withFixtures(
|
||||
{
|
||||
// The fixtures used here is arbitrary. Any fixture would do.
|
||||
fixtures: 'imported-account',
|
||||
ganacheOptions,
|
||||
title: this.test.title,
|
||||
},
|
||||
async ({ driver }) => {
|
||||
await driver.navigate(PAGES.HOME);
|
||||
assert.equal(
|
||||
await driver.executeScript(lockdownTestScript),
|
||||
true,
|
||||
'The UI environment should be locked down.',
|
||||
);
|
||||
|
||||
await driver.navigate(PAGES.BACKGROUND);
|
||||
assert.equal(
|
||||
await driver.executeScript(lockdownTestScript),
|
||||
true,
|
||||
'The background environment should be locked down.',
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
@ -49,6 +49,10 @@ class Driver {
|
||||
};
|
||||
}
|
||||
|
||||
async executeScript(script, ...args) {
|
||||
return this.driver.executeScript(script, args);
|
||||
}
|
||||
|
||||
buildLocator(locator) {
|
||||
if (typeof locator === 'string') {
|
||||
// If locator is a string we assume its a css selector
|
||||
@ -388,6 +392,7 @@ function collectMetrics() {
|
||||
}
|
||||
|
||||
Driver.PAGES = {
|
||||
BACKGROUND: 'background',
|
||||
HOME: 'home',
|
||||
NOTIFICATION: 'notification',
|
||||
POPUP: 'popup',
|
||||
|
81
test/helpers/protect-intrinsics-helpers.js
Normal file
81
test/helpers/protect-intrinsics-helpers.js
Normal file
@ -0,0 +1,81 @@
|
||||
const { strict: assert } = require('assert');
|
||||
|
||||
module.exports = {
|
||||
getGlobalProperties,
|
||||
testIntrinsic,
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the global intrinsic property names in a locked down environemnt.
|
||||
*
|
||||
* @returns {Set<string>} All global intrinsic property names.
|
||||
*/
|
||||
function getGlobalProperties() {
|
||||
// These are Agoric inventions, and we don't care about them.
|
||||
const ignoreList = new Set([
|
||||
'Compartment',
|
||||
'HandledPromise',
|
||||
'StaticModuleRecord',
|
||||
]);
|
||||
|
||||
const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis);
|
||||
|
||||
return new Set(
|
||||
[
|
||||
// Added to global scope by ses/dist/lockdown.cjs.
|
||||
...namedIntrinsics,
|
||||
|
||||
// TODO: Also include the named platform globals
|
||||
// This grabs every enumerable property on globalThis.
|
||||
// ...Object.keys(globalThis),
|
||||
].filter((propertyName) => !ignoreList.has(propertyName)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a number of assertions on the specified intrinsic property to
|
||||
* ensure that the environment is locked down properly.
|
||||
* Throws if any assertion fails.
|
||||
*
|
||||
* @param {string} propertyName - The name of the intrinsic property to test.
|
||||
*/
|
||||
function testIntrinsic(propertyName) {
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(globalThis, propertyName);
|
||||
|
||||
assert.ok(
|
||||
descriptor,
|
||||
`globalThis["${propertyName}"] should have a descriptor`,
|
||||
);
|
||||
|
||||
// As long as Object.isFrozen is the true Object.isFrozen, the object
|
||||
// it is called with cannot lie about being frozen.
|
||||
const value = globalThis[propertyName];
|
||||
if (value !== globalThis) {
|
||||
assert.equal(
|
||||
Object.isFrozen(value),
|
||||
true,
|
||||
`value of universal property globalThis["${propertyName}"] should be frozen`,
|
||||
);
|
||||
}
|
||||
|
||||
// The writability of properties with accessors cannot be modified.
|
||||
if ('set' in descriptor || 'get' in descriptor) {
|
||||
assert.equal(
|
||||
descriptor.configurable,
|
||||
false,
|
||||
`globalThis["${propertyName}"] should be non-configurable`,
|
||||
);
|
||||
} else {
|
||||
assert.equal(
|
||||
descriptor.configurable,
|
||||
false,
|
||||
`globalThis["${propertyName}"] should be non-configurable`,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
descriptor.writable,
|
||||
false,
|
||||
`globalThis["${propertyName}"] should be non-writable`,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,72 +1,15 @@
|
||||
import 'ses/lockdown';
|
||||
import '../../app/scripts/lockdown-run';
|
||||
import '../../app/scripts/lockdown-more';
|
||||
import { strict as assert } from 'assert';
|
||||
|
||||
// These are Agoric inventions, and we don't care about them.
|
||||
const ignoreList = new Set([
|
||||
'Compartment',
|
||||
'HandledPromise',
|
||||
'StaticModuleRecord',
|
||||
]);
|
||||
import {
|
||||
getGlobalProperties,
|
||||
testIntrinsic,
|
||||
} from '../helpers/protect-intrinsics-helpers';
|
||||
|
||||
describe('non-modifiable intrinsics', function () {
|
||||
const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis);
|
||||
|
||||
const globalProperties = new Set(
|
||||
[
|
||||
// Added to global scope by ses/dist/lockdown.cjs.
|
||||
...namedIntrinsics,
|
||||
|
||||
// TODO: Also include the named platform globals
|
||||
// This grabs every enumerable property on globalThis.
|
||||
// ...Object.keys(globalThis),
|
||||
].filter((propertyName) => !ignoreList.has(propertyName)),
|
||||
);
|
||||
|
||||
globalProperties.forEach((propertyName) => {
|
||||
getGlobalProperties().forEach((propertyName) => {
|
||||
it(`intrinsic globalThis["${propertyName}"]`, function () {
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(
|
||||
globalThis,
|
||||
propertyName,
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
descriptor,
|
||||
`globalThis["${propertyName}"] should have a descriptor`,
|
||||
);
|
||||
|
||||
// As long as Object.isFrozen is the true Object.isFrozen, the object
|
||||
// it is called with cannot lie about being frozen.
|
||||
const value = globalThis[propertyName];
|
||||
if (value !== globalThis) {
|
||||
assert.equal(
|
||||
Object.isFrozen(value),
|
||||
true,
|
||||
`value of universal property globalThis["${propertyName}"] should be frozen`,
|
||||
);
|
||||
}
|
||||
|
||||
// The writability of properties with accessors cannot be modified.
|
||||
if ('set' in descriptor || 'get' in descriptor) {
|
||||
assert.equal(
|
||||
descriptor.configurable,
|
||||
false,
|
||||
`globalThis["${propertyName}"] should be non-configurable`,
|
||||
);
|
||||
} else {
|
||||
assert.equal(
|
||||
descriptor.configurable,
|
||||
false,
|
||||
`globalThis["${propertyName}"] should be non-configurable`,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
descriptor.writable,
|
||||
false,
|
||||
`globalThis["${propertyName}"] should be non-writable`,
|
||||
);
|
||||
}
|
||||
testIntrinsic(propertyName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user