From 9bef7e5745e4f4cadff50e87c21ef097764143de Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Mon, 12 Jan 2026 18:36:41 -0500 Subject: [PATCH 1/7] fix: add support for log file using split state format --- app/lib.js | 65 ++++++++----- app/lib.test.js | 5 + bundle.js | 110 +++++++++++++--------- test/fixtures/chrome-windows-2/000004.log | Bin 0 -> 34664 bytes 4 files changed, 115 insertions(+), 65 deletions(-) create mode 100644 test/fixtures/chrome-windows-2/000004.log diff --git a/app/lib.js b/app/lib.js index 9bc2630..78b2cf3 100644 --- a/app/lib.js +++ b/app/lib.js @@ -116,27 +116,52 @@ function extractVaultFromFile (data) { } } - // attempt 6: chromium 000005.ldb on windows - const matchRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/gu - const captureRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/u - const ivRegex = /\\"iv.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,40}=*)/u - const dataRegex = /\\"[^":,is]*\\":\\"([A-Za-z0-9+\/]*=*)/u - const saltRegex = /,\\"salt.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,100}=*)/u - const vaults = dedupe(data.match(matchRegex)?.map(m => m.match(captureRegex)[1]) - .map(s => [dataRegex, ivRegex, saltRegex].map(r => s.match(r))) - .filter(([d,i,s]) => d&&d.length>1 && i&&i.length>1 && s&&s.length>1) - .map(([d,i,s]) => ({ - data: d[1], - iv: i[1], - salt: s[1], - }))) - if (!vaults.length) { - return null + { + // attempt 6: chromium 000005.ldb on windows + const matchRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/gu + const captureRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/u + const ivRegex = /\\"iv.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,40}=*)/u + const dataRegex = /\\"[^":,is]*\\":\\"([A-Za-z0-9+\/]*=*)/u + const saltRegex = /,\\"salt.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,100}=*)/u + const vaults = dedupe(data.match(matchRegex)?.map(m => m.match(captureRegex)[1]) + .map(s => [dataRegex, ivRegex, saltRegex].map(r => s.match(r))) + .filter(([d,i,s]) => d&&d.length>1 && i&&i.length>1 && s&&s.length>1) + .map(([d,i,s]) => ({ + data: d[1], + iv: i[1], + salt: s[1], + }))) + if (vaults.length) { + if (vaults.length > 1) { + console.log('Found multiple vaults!', vaults) + } + return vaults[0] + } } - if (vaults.length > 1) { - console.log('Found multiple vaults!', vaults) + { + // attempt 7: log file using split state format, chromium 000004.log on windows-2 + const vaultRegex = /KeyringController[\s\S]*?"vault":"((?:[^"\\]|\\.)*)"/g + const vaults = [] + let match + + while ((match = vaultRegex.exec(data)) !== null) { + try { + const vaultString = JSON.parse(`"${match[1]}"`) + vaults.push(JSON.parse(vaultString)) + } catch (err) { + // Not valid JSON: continue + } + } + + const dedupedVaults = dedupe(vaults) + if (dedupedVaults.length) { + if (dedupedVaults.length > 1) { + console.log('Found multiple vaults!', dedupedVaults) + } + return dedupedVaults[0] + } } - return vaults[0] + return null } @@ -176,5 +201,3 @@ module.exports = { extractVaultFromFile, isVaultValid, } - - diff --git a/app/lib.test.js b/app/lib.test.js index 4ef47e2..f2dd20d 100644 --- a/app/lib.test.js +++ b/app/lib.test.js @@ -50,6 +50,11 @@ const FIXTURES = [ 'live pupil slab senior boy release lyrics shaft lazy renew potato simple', passphrase: 'correct horse battery staple', }, + { + path: 'chrome-windows-2/000004.log', + mnemonic: 'please injury labor shift much city general enemy vivid bench pottery picture', + passphrase: 'correct horse battery staple', + }, ] const VAULTS = [ diff --git a/bundle.js b/bundle.js index dde8269..b80405d 100644 --- a/bundle.js +++ b/bundle.js @@ -36,7 +36,6 @@ function decodeMnemonic(mnemonic) { } } function extractVaultFromFile(data) { - var _data$match; var vaultBody; try { // attempt 1: raw json @@ -75,11 +74,11 @@ function extractVaultFromFile(data) { if (_matches2 && _matches2.length) { try { var keyringControllerStateFragment = _matches2[1]; - var _dataRegex = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; - var _ivRegex = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; - var _saltRegex = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; + var dataRegex = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; + var ivRegex = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; + var saltRegex = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; var keyMetaRegex = /,\\"keyMetadata\\":(.*}})/; - var vaultParts = [_dataRegex, _ivRegex, _saltRegex, keyMetaRegex].map(function (reg) { + var vaultParts = [dataRegex, ivRegex, saltRegex, keyMetaRegex].map(function (reg) { return keyringControllerStateFragment.match(reg); }).map(function (match) { return match[1]; @@ -102,11 +101,11 @@ function extractVaultFromFile(data) { if (_matches3 && _matches3.length) { try { var _keyringControllerStateFragment = _matches3[1]; - var _dataRegex2 = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; - var _ivRegex2 = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; - var _saltRegex2 = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; + var _dataRegex = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; + var _ivRegex = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; + var _saltRegex = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; var _keyMetaRegex = /,\\"keyMetadata\\":(.*}})/; - var _vaultParts = [_dataRegex2, _ivRegex2, _saltRegex2, _keyMetaRegex].map(function (reg) { + var _vaultParts = [_dataRegex, _ivRegex, _saltRegex, _keyMetaRegex].map(function (reg) { return _keyringControllerStateFragment.match(reg); }).map(function (match) { return match[1]; @@ -122,43 +121,66 @@ function extractVaultFromFile(data) { } } } - - // attempt 6: chromium 000005.ldb on windows - var matchRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/g; - var captureRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/; - var ivRegex = /\\"iv(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,40}=*)/; - var dataRegex = /\\"(?:[\0-!#-\+\x2D-9;-hj-rt-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\":\\"([\+\/-9A-Za-z]*=*)/; - var saltRegex = /,\\"salt(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,100}=*)/; - var vaults = dedupe((_data$match = data.match(matchRegex)) === null || _data$match === void 0 ? void 0 : _data$match.map(function (m) { - return m.match(captureRegex)[1]; - }).map(function (s) { - return [dataRegex, ivRegex, saltRegex].map(function (r) { - return s.match(r); - }); - }).filter(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 3), - d = _ref4[0], - i = _ref4[1], - s = _ref4[2]; - return d && d.length > 1 && i && i.length > 1 && s && s.length > 1; - }).map(function (_ref5) { - var _ref6 = _slicedToArray(_ref5, 3), - d = _ref6[0], - i = _ref6[1], - s = _ref6[2]; - return { - data: d[1], - iv: i[1], - salt: s[1] - }; - })); - if (!vaults.length) { - return null; + { + var _data$match; + // attempt 6: chromium 000005.ldb on windows + var matchRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/g; + var captureRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/; + var _ivRegex2 = /\\"iv(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,40}=*)/; + var _dataRegex2 = /\\"(?:[\0-!#-\+\x2D-9;-hj-rt-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\":\\"([\+\/-9A-Za-z]*=*)/; + var _saltRegex2 = /,\\"salt(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,100}=*)/; + var vaults = dedupe((_data$match = data.match(matchRegex)) === null || _data$match === void 0 ? void 0 : _data$match.map(function (m) { + return m.match(captureRegex)[1]; + }).map(function (s) { + return [_dataRegex2, _ivRegex2, _saltRegex2].map(function (r) { + return s.match(r); + }); + }).filter(function (_ref3) { + var _ref4 = _slicedToArray(_ref3, 3), + d = _ref4[0], + i = _ref4[1], + s = _ref4[2]; + return d && d.length > 1 && i && i.length > 1 && s && s.length > 1; + }).map(function (_ref5) { + var _ref6 = _slicedToArray(_ref5, 3), + d = _ref6[0], + i = _ref6[1], + s = _ref6[2]; + return { + data: d[1], + iv: i[1], + salt: s[1] + }; + })); + if (vaults.length) { + if (vaults.length > 1) { + console.log('Found multiple vaults!', vaults); + } + return vaults[0]; + } } - if (vaults.length > 1) { - console.log('Found multiple vaults!', vaults); + { + // attempt 7: log file using split state format + var vaultRegex = /KeyringController[\s\S]*?"vault":"((?:[^"\\]|\\.)*)"/g; + var _vaults = []; + var match; + while ((match = vaultRegex.exec(data)) !== null) { + try { + var vaultString = JSON.parse("\"".concat(match[1], "\"")); + _vaults.push(JSON.parse(vaultString)); + } catch (err) { + // Not valid JSON: continue + } + } + var dedupedVaults = dedupe(_vaults); + if (dedupedVaults.length) { + if (dedupedVaults.length > 1) { + console.log('Found multiple vaults!', dedupedVaults); + } + return dedupedVaults[0]; + } } - return vaults[0]; + return null; } function isVaultValid(vault) { return _typeof(vault) === 'object' && ['data', 'iv', 'salt'].every(function (e) { diff --git a/test/fixtures/chrome-windows-2/000004.log b/test/fixtures/chrome-windows-2/000004.log new file mode 100644 index 0000000000000000000000000000000000000000..fc2262040972ec8428f081abf0ef986b1d3e807e GIT binary patch literal 34664 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 Date: Mon, 12 Jan 2026 18:39:56 -0500 Subject: [PATCH 2/7] ignore informational log lines in console --- app/lib.js | 2 ++ jest.config.js | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/lib.js b/app/lib.js index 78b2cf3..0760ca8 100644 --- a/app/lib.js +++ b/app/lib.js @@ -132,6 +132,7 @@ function extractVaultFromFile (data) { salt: s[1], }))) if (vaults.length) { + /* istanbul ignore next */ if (vaults.length > 1) { console.log('Found multiple vaults!', vaults) } @@ -155,6 +156,7 @@ function extractVaultFromFile (data) { const dedupedVaults = dedupe(vaults) if (dedupedVaults.length) { + /* istanbul ignore next */ if (dedupedVaults.length > 1) { console.log('Found multiple vaults!', dedupedVaults) } diff --git a/jest.config.js b/jest.config.js index 0bb81cf..b7098b7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -41,10 +41,10 @@ module.exports = { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 95.23, + branches: 97.61, functions: 100, - lines: 98.57, - statements: 98.64, + lines: 100, + statements: 100, }, }, From 6ee5e483484b9da390af1ec3ed23ce90a35982dd Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Mon, 12 Jan 2026 18:40:08 -0500 Subject: [PATCH 3/7] update bundle --- bundle.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundle.js b/bundle.js index b80405d..e6ebc09 100644 --- a/bundle.js +++ b/bundle.js @@ -153,6 +153,7 @@ function extractVaultFromFile(data) { }; })); if (vaults.length) { + /* istanbul ignore next */ if (vaults.length > 1) { console.log('Found multiple vaults!', vaults); } @@ -160,7 +161,7 @@ function extractVaultFromFile(data) { } } { - // attempt 7: log file using split state format + // attempt 7: log file using split state format, chromium 000004.log on windows-2 var vaultRegex = /KeyringController[\s\S]*?"vault":"((?:[^"\\]|\\.)*)"/g; var _vaults = []; var match; @@ -174,6 +175,7 @@ function extractVaultFromFile(data) { } var dedupedVaults = dedupe(_vaults); if (dedupedVaults.length) { + /* istanbul ignore next */ if (dedupedVaults.length > 1) { console.log('Found multiple vaults!', dedupedVaults); } From 03aaa70291efc99b1400c687b961df2e4feb5747 Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:40:23 -0500 Subject: [PATCH 4/7] don't push a `null` vault into dedupe, which will crash the function --- app/lib.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/lib.js b/app/lib.js index 0760ca8..8ce3de3 100644 --- a/app/lib.js +++ b/app/lib.js @@ -148,7 +148,10 @@ function extractVaultFromFile (data) { while ((match = vaultRegex.exec(data)) !== null) { try { const vaultString = JSON.parse(`"${match[1]}"`) - vaults.push(JSON.parse(vaultString)) + const json = JSON.parse(vaultString) + if (json !== null) { + vaults.push(json) + } } catch (err) { // Not valid JSON: continue } From c3c1e02b8718b3829f33f2c742fcf2b63a1e6def Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:40:41 -0500 Subject: [PATCH 5/7] rebuild --- bundle.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bundle.js b/bundle.js index e6ebc09..8999803 100644 --- a/bundle.js +++ b/bundle.js @@ -168,7 +168,10 @@ function extractVaultFromFile(data) { while ((match = vaultRegex.exec(data)) !== null) { try { var vaultString = JSON.parse("\"".concat(match[1], "\"")); - _vaults.push(JSON.parse(vaultString)); + var json = JSON.parse(vaultString); + if (json !== null) { + _vaults.push(json); + } } catch (err) { // Not valid JSON: continue } From 64a020a1d773a110d421fa1b1abed5beb57d3a28 Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:43:38 -0500 Subject: [PATCH 6/7] coverage --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index b7098b7..4e1674e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -41,7 +41,7 @@ module.exports = { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 97.61, + branches: 95.45, functions: 100, lines: 100, statements: 100, From f8ad6fe27ee60d74cfa129e7df2bbcf67c42c916 Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:01:10 -0500 Subject: [PATCH 7/7] rerereview --- app/lib.js | 7 ++++--- app/lib.test.js | 6 +++--- bundle.js | 7 ++++--- jest.config.js | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/lib.js b/app/lib.js index 8ce3de3..015c4c2 100644 --- a/app/lib.js +++ b/app/lib.js @@ -4,6 +4,9 @@ const passworder = require('@metamask/browser-passworder') function dedupe (arr) { const result = [] arr?.forEach(x => { + if (x == null) { + return + } if (!result.find(y => Object.keys(x).length === Object.keys(y).length && Object.entries(x).every(([k,ex]) => y[k] === ex ))) { result.push(x) } @@ -149,9 +152,7 @@ function extractVaultFromFile (data) { try { const vaultString = JSON.parse(`"${match[1]}"`) const json = JSON.parse(vaultString) - if (json !== null) { - vaults.push(json) - } + vaults.push(json) } catch (err) { // Not valid JSON: continue } diff --git a/app/lib.test.js b/app/lib.test.js index f2dd20d..b3dcaf3 100644 --- a/app/lib.test.js +++ b/app/lib.test.js @@ -62,13 +62,13 @@ const VAULTS = [ variant: 'vault with no key metadata', vaultData: '{"data":"s6TpYjlUNsn7ifhEFTkuDGBUM1GyOlPrim7JSjtfIxgTt8/6MiXgiR/CtFfR4dWW2xhq85/NGIBYEeWrZThGdKGarBzeIqBfLFhw9n509jprzJ0zc2Rf+9HVFGLw+xxC4xPxgCS0IIWeAJQ+XtGcHmn0UZXriXm8Ja4kdlow6SWinB7sr/WM3R0+frYs4WgllkwggDf2/Tv6VHygvLnhtzp6hIJFyTjh+l/KnyJTyZW1TkZhDaNDzX3SCOHT","iv":"FbeHDAW5afeWNORfNJBR0Q==","salt":"TxZ+WbCW6891C9LK/hbMAoUsSEW1E8pyGLVBU6x5KR8="}', mnemonic: 'spread raise short crane omit tent fringe mandate neglect detail suspect cradle', - passphrase: 'correct horse battery staple', + passphrase: 'correct horse battery staple', }, { variant: 'vault with key metadata and 600_000 iterations', vaultData: '{"data":"WHaP1FrrtV4zUonudIppDifsLHF39g6oPkVksAIdWAHBRzax1uy1asfAJprR7u72t4/HuYz5yPIFQrnNnv+hwQu9GRuty88VKMnvMy+sq8MNtoXI+C54bZpWa8r4iUQfa0Mj/cfJbpFpzOdF1ZYXahTfTcU5WsrHwvJew842CiJR4B2jmCHHXfm/DxLK3WazsVQwXJGx/U71UelGoOOrT8NI28EKrAwgPn+7Xmv0j92gmhau30N7Bo2fr6Zv","iv":"LfD8/tY1EjXzxuemSmDVdA==","keyMetadata":{"algorithm":"PBKDF2","params":{"iterations":600000}},"salt":"nk4xdpmMR+1s5BYe4Vnk++XAQwrISI2bCtbMg7V1wUA="}', mnemonic: 'spread raise short crane omit tent fringe mandate neglect detail suspect cradle', - passphrase: 'correct horse battery staple', + passphrase: 'correct horse battery staple', }, ] @@ -93,7 +93,7 @@ describe('decryptVault', () => { VAULTS.forEach((vault) => { it(`decrypts ${vault.variant}`, async () => { const decrypted = await decryptVault( - vault.passphrase, + vault.passphrase, JSON.parse(vault.vaultData) ); diff --git a/bundle.js b/bundle.js index 8999803..b3601a7 100644 --- a/bundle.js +++ b/bundle.js @@ -15,6 +15,9 @@ var passworder = require('@metamask/browser-passworder'); function dedupe(arr) { var result = []; arr === null || arr === void 0 ? void 0 : arr.forEach(function (x) { + if (x == null) { + return; + } if (!result.find(function (y) { return Object.keys(x).length === Object.keys(y).length && Object.entries(x).every(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), @@ -169,9 +172,7 @@ function extractVaultFromFile(data) { try { var vaultString = JSON.parse("\"".concat(match[1], "\"")); var json = JSON.parse(vaultString); - if (json !== null) { - _vaults.push(json); - } + _vaults.push(json); } catch (err) { // Not valid JSON: continue } diff --git a/jest.config.js b/jest.config.js index 4e1674e..694ee40 100644 --- a/jest.config.js +++ b/jest.config.js @@ -43,8 +43,8 @@ module.exports = { global: { branches: 95.45, functions: 100, - lines: 100, - statements: 100, + lines: 98.75, + statements: 98.8, }, },