Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 50 additions & 21 deletions app/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const passworder = require('@metamask/browser-passworder')
function dedupe (arr) {
const result = []
arr?.forEach(x => {
if (x == null) {
return
}
Comment on lines +7 to +9
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review caught this bug that could cause dedupe to throw.

if (!result.find(y => Object.keys(x).length === Object.keys(y).length && Object.entries(x).every(([k,ex]) => y[k] === ex ))) {
result.push(x)
}
Expand Down Expand Up @@ -116,27 +119,55 @@ 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) {
/* istanbul ignore next */
if (vaults.length > 1) {
console.log('Found multiple vaults!', vaults)
}
return vaults[0]
}
Comment on lines +122 to +143
Copy link
Contributor Author

@davidmurdoch davidmurdoch Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this hasn't changed, other than putting it in its own lexical block now, and it doesn't return null if there are no vaults found.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide whitespace changes removes this from the diff

}
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the vault is stored as a top-level key now, and the top-level keys aren't JSON serialized, so they aren't wrapped with quotes.

const vaults = []
let match

while ((match = vaultRegex.exec(data)) !== null) {
try {
const vaultString = JSON.parse(`"${match[1]}"`)
const json = JSON.parse(vaultString)
vaults.push(json)
} catch (err) {
// Not valid JSON: continue
}
}

const dedupedVaults = dedupe(vaults)
if (dedupedVaults.length) {
/* istanbul ignore next */
if (dedupedVaults.length > 1) {
console.log('Found multiple vaults!', dedupedVaults)
}
return dedupedVaults[0]
}
}
return vaults[0]
return null
}


Expand Down Expand Up @@ -176,5 +207,3 @@ module.exports = {
extractVaultFromFile,
isVaultValid,
}


11 changes: 8 additions & 3 deletions app/lib.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,25 @@ 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 = [
{
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',
},
]

Expand All @@ -88,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)
);

Expand Down
116 changes: 72 additions & 44 deletions bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes in the file are just from rebuilding and committing it.

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),
Expand All @@ -36,7 +39,6 @@ function decodeMnemonic(mnemonic) {
}
}
function extractVaultFromFile(data) {
var _data$match;
var vaultBody;
try {
// attempt 1: raw json
Expand Down Expand Up @@ -75,11 +77,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];
Expand All @@ -102,11 +104,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];
Expand All @@ -122,43 +124,69 @@ 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) {
/* istanbul ignore next */
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
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], "\""));
var json = JSON.parse(vaultString);
_vaults.push(json);
} catch (err) {
// Not valid JSON: continue
}
}
var dedupedVaults = dedupe(_vaults);
if (dedupedVaults.length) {
/* istanbul ignore next */
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) {
Expand Down
6 changes: 3 additions & 3 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 95.23,
branches: 95.45,
functions: 100,
lines: 98.57,
statements: 98.64,
lines: 98.75,
statements: 98.8,
},
},

Expand Down
Binary file added test/fixtures/chrome-windows-2/000004.log
Binary file not shown.
Loading