feat: password-gated recovery phrase display for HD wallets (closes #161)
Some checks failed
check / check (push) Has been cancelled
Some checks failed
check / check (push) Has been cancelled
This commit was merged in pull request #215.
This commit is contained in:
94
tests/showPhrase.test.js
Normal file
94
tests/showPhrase.test.js
Normal file
@@ -0,0 +1,94 @@
|
||||
// Tests for the recovery phrase display (issue #161).
|
||||
//
|
||||
// These cover the parts that do not need a DOM: which wallet types may be
|
||||
// offered the action at all, the exclusion of the screen from the set of
|
||||
// views the popup may reopen onto, and the absence of any path from this
|
||||
// module to the logger. The DOM behaviour it guards — nothing rendered
|
||||
// before the password is accepted, a wrong password revealing nothing, and
|
||||
// the wipe on leaving — is driven against the real popup in a real browser
|
||||
// by tests/e2e/run.js, which is where every other view behaviour is tested.
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const { walletHasRecoveryPhrase } = require("../src/shared/wallet");
|
||||
const { RESTORABLE_VIEWS } = require("../src/popup/restorableViews");
|
||||
|
||||
const SHOW_PHRASE_VIEW = "show-phrase";
|
||||
|
||||
// helpers.js pulls in state.js, which reads chrome.storage.local at load.
|
||||
function loadHelpers() {
|
||||
globalThis.chrome = {
|
||||
storage: { local: { get: async () => ({}), set: async () => {} } },
|
||||
};
|
||||
return require("../src/popup/views/helpers");
|
||||
}
|
||||
|
||||
describe("which wallets have a recovery phrase", () => {
|
||||
test("an HD wallet does", () => {
|
||||
expect(walletHasRecoveryPhrase({ type: "hd" })).toBe(true);
|
||||
});
|
||||
|
||||
// A key wallet holds a bare private key and an xprv wallet an extended
|
||||
// private key. Neither can be turned back into words, so neither may be
|
||||
// offered the action.
|
||||
test("a key wallet does not", () => {
|
||||
expect(walletHasRecoveryPhrase({ type: "key" })).toBe(false);
|
||||
});
|
||||
|
||||
test("an xprv wallet does not", () => {
|
||||
expect(walletHasRecoveryPhrase({ type: "xprv" })).toBe(false);
|
||||
});
|
||||
|
||||
test("an unknown or missing wallet type does not", () => {
|
||||
expect(walletHasRecoveryPhrase({ type: "something-new" })).toBe(false);
|
||||
expect(walletHasRecoveryPhrase({})).toBe(false);
|
||||
expect(walletHasRecoveryPhrase(undefined)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("views the popup may reopen onto", () => {
|
||||
// Restoring onto a secret screen would put the phrase on screen with no
|
||||
// password prompt in front of it, on a popup the user may have reopened
|
||||
// by accident.
|
||||
test("the recovery phrase screen is not restorable", () => {
|
||||
expect(RESTORABLE_VIEWS.has(SHOW_PHRASE_VIEW)).toBe(false);
|
||||
});
|
||||
|
||||
test("the private key export screen is not restorable either", () => {
|
||||
expect(RESTORABLE_VIEWS.has("export-privkey")).toBe(false);
|
||||
});
|
||||
|
||||
test("the recovery phrase screen is still a registered view", () => {
|
||||
const { VIEWS } = loadHelpers();
|
||||
expect(VIEWS).toContain(SHOW_PHRASE_VIEW);
|
||||
});
|
||||
|
||||
// Guards the other direction: a restorable name that is not a real view
|
||||
// would leave restoreView() showing nothing at all.
|
||||
test("every restorable view is a registered view", () => {
|
||||
const { VIEWS } = loadHelpers();
|
||||
for (const view of RESTORABLE_VIEWS) {
|
||||
expect(VIEWS).toContain(view);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("the phrase cannot reach the logger", () => {
|
||||
const source = fs.readFileSync(
|
||||
path.join(__dirname, "..", "src", "popup", "views", "showPhrase.js"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
// The decrypted phrase only ever lives in a local and in the DOM node
|
||||
// that displays it. The module has no logger to hand it to, and this
|
||||
// pins that: src/shared/log.js writes to the console, and a console
|
||||
// record of a recovery phrase outlives the popup.
|
||||
test("the view does not import src/shared/log.js", () => {
|
||||
expect(source).not.toMatch(/require\(["'][^"']*shared\/log["']\)/);
|
||||
});
|
||||
|
||||
test("the view calls no logger method", () => {
|
||||
expect(source).not.toMatch(/\blog\.(debugf|infof|warnf|errorf)\b/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user