Skip to content

Instantly share code, notes, and snippets.

@lainiwa
Created March 18, 2019 12:27
Show Gist options
  • Select an option

  • Save lainiwa/b617fc2df978116a7483437bdab2444c to your computer and use it in GitHub Desktop.

Select an option

Save lainiwa/b617fc2df978116a7483437bdab2444c to your computer and use it in GitHub Desktop.
Import chromium passwords to passwordstore
"""Import data to passwordstore from chromium."""
import os.path
import re
import sqlite3
import sys
from subprocess import PIPE, Popen
from urllib.parse import urlparse
def get_db(path):
"""Get database connection and cursor."""
connection = sqlite3.connect(os.path.expanduser(path))
try:
cursor = connection.execute('SELECT * FROM logins')
except sqlite3.OperationalError:
print('Could not open chromium DB. Did you close the browser first?',
file=sys.stderr)
exit(1)
return connection, cursor
def cursor_to_dicts(cursor, gets=None, renames=None):
"""Fetch data into a iterator of dictionaries."""
names = [x[0] for x in cursor.description]
if renames is None:
renames = dict()
if gets is None:
gets = names
for values in cursor:
yield {
renames.get(name, name): value
for name, value in zip(names, values)
if name in gets
}
def pass_import_entry(path, data):
"""Import new password entry to passwordstore."""
proc = Popen(['pass', 'insert', '--multiline', path], stdin=PIPE,
stdout=PIPE)
proc.communicate(data.encode('utf8'))
proc.wait()
def pprint_row(row, data):
"""Print added entry with asterisk'ed passwords."""
max_url = max([len(row['url']) for row in data])
max_name = max([len(row['username_value']) for row in data])
max_pass = max([len(row['password_value']) for row in data])
fmt = f'%{max_url}s %{max_name}s %{max_pass}s'
print(fmt % (
row['url'],
row['username_value'],
'*' * len(row['password_value'])
))
# Connect to DB and get cursor
connection, cursor = get_db('~/.config/chromium/Default/Login Data')
# Get passwords with the desired additional fields
data = cursor_to_dicts(
cursor, ['signon_realm', 'password_value', 'username_value'])
# Remove dupes
data = [dict(t) for t in {tuple(row.items()) for row in data}]
# Remove empty passwords
data = [row for row in data if row['password_value'] != b'']
# Change signon_realm into a "basic url" and a port
for row in data:
row['url'] = urlparse(row['signon_realm']).hostname
port = urlparse(row['signon_realm']).port
if port is not None:
row['port'] = port
del row['signon_realm']
# Remove www from urls
data = [{
name: (re.sub('^www.', '', value) if name == 'url' else value)
for name, value in row.items()
} for row in data]
# Sorted by website
data = sorted(data, key=lambda row: row['url'])
# Add passwords to www folder
for row in data:
pprint_row(row, data)
lines = [
row['password_value'].decode('utf-8'),
'Username: ' + row['username_value']
]
if port in row:
lines.append('Port: ' + row['port'])
lines.append('')
pass_import_entry('www/' + row['url'], '\n'.join(lines))
# Close all stuff
connection.commit()
connection.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment