Skip to content

Instantly share code, notes, and snippets.

@nbonamy
Last active August 23, 2025 22:24
Show Gist options
  • Save nbonamy/55b56d19a37b6153206fd75e91a0f8f1 to your computer and use it in GitHub Desktop.
Save nbonamy/55b56d19a37b6153206fd75e91a0f8f1 to your computer and use it in GitHub Desktop.
Script to dump all login items from Bitwarden browser extension
//
// In case you have lost your master password but can log in the extension using TouchID or similar
// This was tested in Edge so should be working in Chrome
// Open the extension window and detach it
// Filter to show only logins as this script was meant for that only :)
// Then press shortcut to open developer tools
// Copy/Paste script in console and be patient!
// NOTE: the script will fail if you have two items that have the same title and username (cleanup before running it!)
//
// config
const startItem = null
const startIndex = 0
const maxItems = -1
// constants
const scrollBase = 40
const scrollInc = 59
// result
let allItems = []
// disable emitter warning
const oldWarn = console.warn
console.warn = function(arguments) {
if (!arguments.join(' ').includes('NG0953')) {
oldWarn.apply(console, arguments)
}
}
// get the item count
const itemCount = parseInt(document.querySelector('app-vault-list-items-container[id=allItems] > bit-section span[slot=end]').textContent.trim())
console.log(`➡️ Total items: ${itemCount}`)
// intialize for 1st item
let prev = startItem
document.querySelector('[data-testid=popup-layout-scroll-region]').scrollTo(0, scrollBase + startIndex * scrollInc)
await new Promise(resolve => setTimeout(resolve, 1000));
try {
while (true) {
// find all the items
let items = document.querySelectorAll('app-vault-list-items-container[id=allItems] bit-item > bit-item-action button')
if (items.length === 0) break
// find previous
if (prev == null) {
index = 0
} else {
index = Array.from(items).findIndex(
item => item.textContent.trim() === prev
);
if (index !== -1) index++;
else {
console.warn('Item not found:', prev);
break;
}
}
// get item and click it
let item = items[index]
if (!item) continue
prev = item.textContent.trim()
item.click()
await new Promise(resolve => setTimeout(resolve, 500));
// get data
const title = document.querySelector('h2[data-testid=item-name]') ? document.querySelector('h2[data-testid=item-name]').textContent.trim() : ''
const userName = document.querySelector('input[id=userName]') ? document.querySelector('input[id=userName]').value.trim() : ''
const password = document.querySelector('input[id=password]') ? document.querySelector('input[id=password]').value.trim() : ''
const websites = []
document.querySelectorAll('input[data-testid=login-website]').forEach(input => {
websites.push(input.value.trim())
});
// totp is on Edit page
let totp = ''
if (document.querySelector('input[id=totp]')) {
document.querySelector('popup-footer button[buttontype=primary]').click()
await new Promise(resolve => setTimeout(resolve, 500));
if (document.querySelector('input[formcontrolname=totp]')) {
totp = document.querySelector('input[formcontrolname=totp]').value
}
document.querySelector('popup-footer button[buttontype=secondary]').click()
} else {
document.querySelector('button[title=Back]').click()
}
// skip items without websites (list not filtered so bringing back notes and other stuff)
if (websites.length) {
allItems.push({
type: 1,
name: title,
login: {
uris: websites.map(website => ({ match: null, uri: website })),
username: userName,
password: password,
...(totp.length ? { totp } : {})
}
})
console.log(`✅ ${title} (${userName})`);
}
// stop if needed
if (maxItems > 0 && allItems.length >= maxItems) {
break
}
// now wait for back animation
await new Promise(resolve => setTimeout(resolve, 750));
// and scroll to current item
const scrollTo = scrollBase + scrollInc * (startIndex + allItems.length - 1 )
document.querySelector('[data-testid=popup-layout-scroll-region]').scrollTo(0, scrollTo)
await new Promise(resolve => setTimeout(resolve, 500));
}
} catch (e) {
console.error(e);
}
// restore
console.warn = oldWarn
// done!
console.log({
items: allItems
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment