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',
|
'app/scripts/lockdown-more.js',
|
||||||
'development/**/*.js',
|
'development/**/*.js',
|
||||||
'test/e2e/**/*.js',
|
'test/e2e/**/*.js',
|
||||||
'test/lib/wait-until-called.js',
|
|
||||||
'test/env.js',
|
'test/env.js',
|
||||||
'test/setup.js',
|
'test/setup.js',
|
||||||
|
'test/helpers/protect-intrinsics-helpers.js',
|
||||||
|
'test/lib/wait-until-called.js',
|
||||||
'jest.config.js',
|
'jest.config.js',
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
@ -204,6 +205,7 @@ module.exports = {
|
|||||||
files: [
|
files: [
|
||||||
'app/scripts/lockdown-run.js',
|
'app/scripts/lockdown-run.js',
|
||||||
'app/scripts/lockdown-more.js',
|
'app/scripts/lockdown-more.js',
|
||||||
|
'test/helpers/protect-intrinsics-helpers.js',
|
||||||
'test/unit-global/protect-intrinsics.test.js',
|
'test/unit-global/protect-intrinsics.test.js',
|
||||||
],
|
],
|
||||||
globals: {
|
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) {
|
buildLocator(locator) {
|
||||||
if (typeof locator === 'string') {
|
if (typeof locator === 'string') {
|
||||||
// If locator is a string we assume its a css selector
|
// If locator is a string we assume its a css selector
|
||||||
@ -388,6 +392,7 @@ function collectMetrics() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Driver.PAGES = {
|
Driver.PAGES = {
|
||||||
|
BACKGROUND: 'background',
|
||||||
HOME: 'home',
|
HOME: 'home',
|
||||||
NOTIFICATION: 'notification',
|
NOTIFICATION: 'notification',
|
||||||
POPUP: 'popup',
|
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 'ses/lockdown';
|
||||||
import '../../app/scripts/lockdown-run';
|
import '../../app/scripts/lockdown-run';
|
||||||
import '../../app/scripts/lockdown-more';
|
import '../../app/scripts/lockdown-more';
|
||||||
import { strict as assert } from 'assert';
|
import {
|
||||||
|
getGlobalProperties,
|
||||||
// These are Agoric inventions, and we don't care about them.
|
testIntrinsic,
|
||||||
const ignoreList = new Set([
|
} from '../helpers/protect-intrinsics-helpers';
|
||||||
'Compartment',
|
|
||||||
'HandledPromise',
|
|
||||||
'StaticModuleRecord',
|
|
||||||
]);
|
|
||||||
|
|
||||||
describe('non-modifiable intrinsics', function () {
|
describe('non-modifiable intrinsics', function () {
|
||||||
const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis);
|
getGlobalProperties().forEach((propertyName) => {
|
||||||
|
|
||||||
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) => {
|
|
||||||
it(`intrinsic globalThis["${propertyName}"]`, function () {
|
it(`intrinsic globalThis["${propertyName}"]`, function () {
|
||||||
const descriptor = Reflect.getOwnPropertyDescriptor(
|
testIntrinsic(propertyName);
|
||||||
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`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user