"""Importer for Nord Pass in CSV format.""" name = 'nordpass' url = 'https://nordpass.com/' hexport = 'Settings > Export Items' himport = 'pass import nordpass file.csv' keys = { 'title': 'name', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'note', 'group': 'folder' } def parse(self): super().parse() # NordPass exports individual folders as their own # empty rows. This code removes the extra folder entries # from the parsed data. groups = [] for entry in self.data: if entry['group']: groups.append(entry['group']) self.data = list( filter(lambda x: not (x['title'] in groups and not x['group']), self.data)) register_managers(NordPassCSV)
html_header = 'body/div/div/textarea' keys = {'login': '******'} def parse(self): """Parse Clipperz HTML+JSON file.""" # Extract the json from the html file. tree = ElementTree.XML(self.file.read()) found = tree.find(self.html_header) if found is None: raise FormatError() # Parse JSON data keys = self.invkeys() for item in json.loads(found.text): entry = dict() label = item.get('label', ' \ue009').split(' \ue009') entry['title'] = label[0] if len(label) > 1: entry['group'] = label[1] fields = item.get('currentVersion', {}).get('fields', {}) for uid in fields: label = fields[uid].get('label', '') entry[keys.get(label, label)] = fields[uid].get('value', '') entry['comments'] = item.get('data', {}).get('notes', '') self.data.append(entry) register_managers(ClipperzHTML)
from pass_import.core import register_managers from pass_import.formats.csv import CSV class Buttercup(CSV): """Importer for Buttercup in CSV format.""" name = 'buttercup' url = 'https://buttercup.pw' hexport = 'File > Export > Export File to CSV' himport = 'pass import buttercup file.csv' ignore = {'!group_id', 'id'} keys = { 'title': 'title', 'password': '******', 'login': '******', 'url': 'URL', 'comments': 'Notes', 'group': '!group_name' } def parse(self): """Parse Buttercup CSV file.""" super(Buttercup, self).parse() for entry in self.data: for key in self.ignore: entry.pop(key, None) register_managers(Buttercup)
name = 'passpack' url = 'https://www.passpack.com' hexport = 'Settings > Export > Save to CSV' keys = { 'title': 'Entry Name', 'password': '******', 'login': '******', 'url': 'URL', 'email': 'Email', 'comments': 'Notes', 'group': 'Tags' } def parse(self): """Parse Passpack CSV file.""" super(Passpack, self).parse() for entry in self.data: groups = json.loads(entry.pop('group', '')).get('tags', []) for item in groups: field = json.loads(item) entry['group'] = field.get('tag', '') extra = json.loads(entry.pop('Extra Fields', '')).get('extraFields', []) for item in extra: field = json.loads(item) entry[field.get('name', '')] = field.get('data', '') register_managers(Passpack)
for item in collection.get_all_items(): entry = {} entry['group'] = group entry['title'] = item.get_label() entry['password'] = item.get_secret().decode('utf-8') entry['modified'] = item.get_modified() entry['created'] = item.get_created() for key, value in item.get_attributes().items(): entry[keys.get(key, key)] = value self.data.append(entry) # Context manager methods def exist(self): """Nothing to do.""" return True @classmethod def isvalid(cls): """Nothing to do.""" return True def open(self): """Nothing to open.""" def close(self): """Nothing to close.""" register_managers(GnomeKeyring)
from pass_import.core import register_managers from pass_import.formats.csv import CSV from pass_import.formats.kdbx import KDBX class Keepassx2CSV(CSV): """Importer for KeepassX2 in CSV format.""" name = 'keepassx2' default = False url = 'https://www.keepassx.org' hexport = 'Database > Export to CSV File' himport = 'pass import keepassx2 file.csv' keys = { 'title': 'Title', 'password': '******', 'login': '******', 'url': 'URL', 'comments': 'Notes', 'group': 'Group' } class Keepassx2KDBX(KDBX): """Importer for KeepassX2 encrypted KDBX format.""" name = 'keepassx2' url = 'https://www.keepassx.org' himport = 'pass import keepassx2 file.kdbx' register_managers(Keepassx2CSV, Keepassx2KDBX)
'password': str, 'url': str, 'icon': dict, 'custom_fields': list, 'otp': dict, 'compromised': bool }] def parse(self): """Parse Passman JSON file.""" ignore = {'custom_fields', 'icon', 'tags'} keys = self.invkeys() jsons = json.loads(self.file.read()) for item in jsons: entry = {} if item['tags']: group = item['tags'][0]['text'] entry['group'] = group.replace('\\', os.sep) custom_fields = item.get('custom_fields', []) for field in custom_fields: item.update({field['label']: field['value']}) for key, value in item.items(): if key not in ignore: entry[keys.get(key, key)] = value self.data.append(entry) register_managers(PassmanCSV, PassmanJSON)
@classmethod def _getpath(cls, element, path=''): """Generate path name from elements title and current path.""" title = '' if element.tag == 'Group': title = element.find('Name').text if title is None: title = '' return os.path.join(path, title) @classmethod def _getvalue(cls, element): xmlkey = value = '' for child in element.findall('Key'): xmlkey = child.text value = element.find('Value').text return xmlkey, value def _import(self, element, path=''): path = self._getpath(element, path) for group in element.findall(self.group): self._import(group, path) for xmlentry in element.findall(self.entry): entry = self._getentry(xmlentry) entry['group'] = path self.data.append(entry) register_managers(Keepass, KeepassCSV, KeepassXML)
# -*- encoding: utf-8 -*- # pass import - Passwords importer swiss army knife # Copyright (C) 2017-2020 Alexandre PUJOL <*****@*****.**>. # from pass_import.core import register_managers from pass_import.formats.otp import OTP class AndOTP(OTP): """Importer for AndOTP plain or encrypted JSON format.""" name = 'andotp' format = 'json' url = 'https://github.com/andOTP/andOTP' hexport = 'Backups> Backup plain' himport = 'pass import andotp file.json' json_header = [{ 'secret': str, 'label': str, 'digits': int, 'type': str, 'algorithm': str, 'thumbnail': str, 'last_used': int, 'tags': list }] register_managers(AndOTP)
# -*- encoding: utf-8 -*- # pass import - Passwords importer swiss army knife # Copyright (C) 2017-2020 Alexandre PUJOL <*****@*****.**>. # from pass_import.core import register_managers from pass_import.formats.csv import CSV class SaferPass(CSV): """Importer for SaferPass in CSV format.""" name = 'saferpass' url = 'https://saferpass.net' hexport = 'Settings > Export Data: Export data' himport = 'pass import saferpass file.csv' encoding = 'utf-8-sig' keys = { 'title': 'title', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'notes', 'favorite': 'favorite', 'text': 'text', 'modelType': 'modelType', 'color': 'color' } register_managers(SaferPass)
# Format recognition methods def is_format(self): """Check keychain file format.""" try: self.yamls = self.keychain2yaml(self.file) if isinstance(self.yamls, str): return False except (yaml.scanner.ScannerError, yaml.parser.ParserError, UnicodeDecodeError): return False return True def checkheader(self, header, only=False): """Check keychain format.""" if isinstance(self.yamls, list): self.yamls = self.yamls[0] for yamlkey in header: if yamlkey not in self.yamls: return False return True @classmethod def header(cls): """Get keychain format header.""" return cls.keychain_format register_managers(AppleKeychain) register_detecters(AppleKeychain)
hexport = 'See this guide: https://support.1password.com/export' himport = 'pass import 1password file.csv' keys = { 'title': 'title', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'notes' } class OnePassword4PIF(PIF): """Importer for 1password 4 in PIF format.""" name = '1password' default = False version = '4' url = 'https://1password.com' hexport = 'See this guide: https://support.1password.com/export' himport = 'pass import 1password file.1pif' keys = { 'title': 'title', 'password': '******', 'login': '******', 'url': 'location', 'comments': 'notesPlain', 'group': 'folderUuid' } register_managers(OnePasswordCSV, OnePassword4CSV, OnePassword4PIF)
keys are ignored. Binary attachment is not supported. """ if self.all: self.writer.writerow(entry) else: res = dict() for key in self.keyslist: res[key] = entry.get(key, '') self.writer.writerow(res) # Context manager method def open(self): """Create/Re-create CSV exported file.""" if self.action is Cap.IMPORT: super(GenericCSV, self).open() else: if os.path.isfile(self.prefix): if self.force: self.file = open(self.prefix, 'w', encoding=self.encoding) else: raise PMError("%s is already a file." % self.prefix) else: self.file = open(self.prefix, 'w', encoding=self.encoding) register_managers(GenericCSV)
# -*- encoding: utf-8 -*- # pass import - Passwords importer swiss army knife # Copyright (C) 2017-2020 Alexandre PUJOL <*****@*****.**>. # from pass_import.core import register_managers from pass_import.managers import PasswordStore class Gopass(PasswordStore): """Importer & Exporter for gopass.""" name = 'gopass' format = 'gopass' command = 'gopass' url = 'https://www.gopass.pw/' himport = 'pass import gopass path/to/store' register_managers(Gopass)
cipher = AESGCM(key) param = slot['key_params'] try: nonce = bytes.fromhex(param['nonce']) data = bytes.fromhex(slot['key']) + bytes.fromhex(param['tag']) master_key = cipher.decrypt(nonce=nonce, data=data, associated_data=None) except InvalidTag: # pragma: no cover pass if master_key is None: # pragma: no cover raise FormatError("unable to decrypt the master key.") cipher = AESGCM(master_key) param = jsons['header']['params'] content = base64.b64decode(jsons['db']) + bytes.fromhex(param['tag']) plain = cipher.decrypt(nonce=bytes.fromhex(param['nonce']), data=content, associated_data=None) return plain.decode('utf-8') def parse(self): """Parse Aegis encrypted JSON file.""" self.content = self.decrypt(json.loads(self.content)) super(AegisCipher, self).parse() register_managers(Aegis, AegisCipher)
class DashlaneJSON(JSON): """Importer for Dashlane in JSON format.""" name = 'dashlane' default = False url = 'https://www.dashlane.com' hexport = 'File > Export > Unsecured Archive in JSON' himport = 'pass import dashlane file.json' keys = { 'title': 'title', 'password': '******', 'email': 'email', 'login': '******', 'url': 'domain', 'comments': 'note', } json_header = {'AUTHENTIFIANT': list, 'EMAIL': list} def parse(self): """Parse Dashlane JSON file.""" jsons = json.loads(self.file.read()) keys = self.invkeys() for item in jsons.get('AUTHENTIFIANT', {}): entry = dict() for key, value in item.items(): entry[keys.get(key, key)] = value self.data.append(entry) register_managers(DashlaneCSV, DashlaneJSON)
# -*- encoding: utf-8 -*- # pass import - Passwords importer swiss army knife # Copyright (C) 2017-2020 Alexandre PUJOL <*****@*****.**>. # from pass_import.core import register_managers from pass_import.formats.csv import CSV class Myki(CSV): """Importer for Myki in CSV format.""" name = 'myki' url = 'https://myki.com' hexport = ('See this guide: https://support.myki.com/myki-app/export' 'ing-your-passwords-from-the-myki-app/how-to-export-your' '-passwords-account-data-from-myki') himport = 'pass import myki file.csv' keys = { 'title': 'Name', 'password': '******', 'login': '******', 'url': 'Url', 'comments': 'Extra', 'group': 'Grouping' } register_managers(Myki)
url = 'https://support.google.com/chrome' hexport = ('See this guide: https://support.google.com/chrome/' 'answer/95606#see') himport = 'pass import chrome file.csv' only = True keys = { 'title': 'name', 'password': '******', 'login': '******', 'url': 'url' } class ChromeCSVSQLite(CSV): """Importer for Chrome SQLite in CSV format.""" name = 'chrome' default = False url = 'https://support.google.com/chrome' hexport = ('See this guide: https://support.google.com/chrome/' 'answer/95606#see') himport = 'pass import chrome file.csv' keys = { 'title': 'display_name', 'password': '******', 'login': '******', 'url': 'origin_url' } register_managers(ChromeCSV, ChromeCSVSQLite)
for field in custom_fields: name = field['name'] value = field['value'] if name in destination_entry.keys(): name = f'{name}_' if value: destination_entry[name] = value class BitwardenOrgJSON(BitwardenJSON): """Importer for Bitwarden Organisation in JSON format.""" key_group = 'collections' key_group_id = 'collectionIds' json_header = { 'encrypted': False, 'collections': list, 'items': [{ 'id': str, 'type': int, 'name': str, 'favorite': bool, 'collectionIds': list, }], } register_managers(BitwardenCSV, BitwardenJSON, BitwardenOrgCSV, BitwardenOrgJSON)
json_header = { 'tokenOrder': list, 'tokens': [{ 'algo': str, 'digits': int, 'issuerExt': str, 'label': str, 'secret': list, 'type': str }] } def parse(self): """Parse FreeOTP+ JSON file.""" jsons = json.loads(self.content) for item in jsons['tokens']: item['label'] = item['issuerExt'] item['algorithm'] = item['algo'] item['secret'] = base64.b32encode( bytes(x & 0xff for x in item['secret'])).decode("utf8") entry = dict() entry['title'] = item['issuerExt'] entry['otpauth'] = self._otp(item) entry['type'] = item['type'].lower() self.data.append(entry) register_managers(FreeOTPPlus)
import re from pass_import.core import register_managers from pass_import.formats.csv import CSV class Gorilla(CSV): """Importer for Gorilla in CSV format.""" name = 'gorilla' url = 'https://github.com/zdia/gorilla/wiki' hexport = 'File > Export: Yes: CSV Files' himport = 'pass import gorilla file.csv' keys = { 'title': 'title', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'notes', 'group': 'group' } def parse(self): """Parse Gorilla CSV file.""" super(Gorilla, self).parse() for entry in self.data: group = re.sub(r'(?<=[^\\])\.', os.sep, entry.get('group', '')) entry['group'] = re.sub(r'\\.', '.', group) register_managers(Gorilla)
# -*- encoding: utf-8 -*- # pass import - Passwords importer swiss army knife # Copyright (C) 2017-2020 Alexandre PUJOL <*****@*****.**>. # from pass_import.core import register_managers from pass_import.formats.csv import CSV class UPM(CSV): """Importer for Universal Password Manager (UPM) in CSV format.""" name = 'upm' url = 'http://upm.sourceforge.net' hexport = 'Database > Export' himport = 'pass import upm file.csv' fieldnames = ['title', 'login', 'password', 'url', 'comments'] keys = { 'title': 'title', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'comments' } register_managers(UPM)
# -*- encoding: utf-8 -*- # pass import - Passwords importer swiss army knife # Copyright (C) 2017-2020 Alexandre PUJOL <*****@*****.**>. # from pass_import.core import register_managers from pass_import.formats.kdbx import KDBX from pass_import.managers.keepassx2 import Keepassx2CSV class KeepassxcCSV(Keepassx2CSV): """Importer for KeepassXC in CSV format.""" name = 'keepassxc' default = False url = 'https://keepassxc.org' hexport = 'Database > Export to CSV File' himport = 'pass import keepassxc file.csv' class KeepassxcKDBX(KDBX): """Importer for KeepassXC encrypted KDBX format.""" name = 'keepassxc' url = 'https://keepassxc.org' himport = 'pass import keepassxc file.kdbx' register_managers(KeepassxcCSV, KeepassxcKDBX)
# import os from pass_import.core import register_managers from pass_import.formats.csv import CSV class LastpassCSV(CSV): """Importer for Lastpass in CSV format.""" name = 'lastpass' url = 'https://www.lastpass.com' hexport = 'More Options > Advanced > Export' keys = { 'title': 'name', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'extra', 'group': 'grouping' } def parse(self): """Parse Lastpass CSV file.""" super(LastpassCSV, self).parse() for entry in self.data: entry['group'] = entry.get('group', '').replace('\\', os.sep) register_managers(LastpassCSV)
'doctype': '<!DOCTYPE KEEPASSX_DATABASE>', 'root': 'database' } keys = { 'title': 'title', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'comment' } @classmethod def _getpath(cls, element, path=''): title = '' if element.tag != 'database': if element.find('title').text: title = element.find('title').text return os.path.join(path, title) def _import(self, element, path=''): path = self._getpath(element, path) for group in element.findall(self.group): self._import(group, path) for xmlentry in element.findall(self.entry): entry = self._getentry(xmlentry) entry['group'] = path self.data.append(entry) register_managers(KeepassxXML)
import os from pass_import.core import register_managers from pass_import.formats.csv import CSV class PadlockCSV(CSV): """Importer for Padloc CSV format.""" name = 'padlock' url = 'https://padloc.app' hexport = 'Settings > Export Data and copy text into a .csv file' himport = 'pass import padlock file.csv' keys = { 'title': 'name', 'password': '******', 'login': '******', 'url': 'url', 'comments': 'notes', 'group': 'tags' } def parse(self): """Parse Padloc CSV file.""" super(PadlockCSV, self).parse() for entry in self.data: entry['group'] = entry.get('group', '').replace('\\', os.sep) register_managers(PadlockCSV)
def close(self): """Close all the opened files.""" for file in self.files: file.close() # Format recognition methods def is_format(self): """Return True if the prefix has same format than the pm.""" try: for file in self.files: ini = configparser.ConfigParser() ini.read_file(file) except: # noqa return False return True def checkheader(self, header, only=False): """No header check is needed.""" return True @classmethod def header(cls): """No header for NetworkManager.""" return '' register_managers(NetworkManager) register_detecters(NetworkManager)
return key, element.text def _import(self, element, path=''): for xmlentry in element.findall('entry'): if xmlentry.attrib.get('type', '') == 'folder': _path = os.path.join(path, xmlentry.find('name').text) self._import(xmlentry, _path) else: entry = self._getentry(xmlentry) entry['group'] = path host = entry.get('host', None) # Fix older Revelation storing Websites in Generic entries if host and 'url' not in entry: for protocol in ['http://', 'https://']: if host.startswith(protocol): entry['url'] = entry.pop('host') break domain = entry.pop('hostdomain', None) if domain: if host: if not host.endswith(domain): entry['host'] = '%s.%s' % (host, domain) else: entry['host'] = domain self.data.append(entry) register_managers(Revelation)
def open(self): """Ensure prefix is a path to a password repository.""" if not os.path.isdir(self.prefix): raise PMError("%s is not a password repository." % self.prefix) def close(self): """There is no file to close.""" # Format recognition methods def is_format(self): """Ensure the prefix is a directory than contain a .gpg-id file.""" if os.path.isdir(self.prefix): path = os.path.join(self.prefix, '.gpg-id') if os.path.isfile(path): return True return False def checkheader(self, header, only=False): """No header check is needed.""" return True @classmethod def header(cls): """No header for pass.""" return '' register_managers(PasswordStore) register_detecters(PasswordStore)
# -*- encoding: utf-8 -*- # pass import - Passwords importer swiss army knife # Copyright (C) 2017-2020 Alexandre PUJOL <*****@*****.**>. # from pass_import.core import register_managers from pass_import.formats.yaml import YAML class Passpie(YAML): """Importer for Passpie in YAML format.""" name = 'passpie' version = '1.0' url = 'https://www.enpass.io' hexport = '`passpie export file.yml`' himport = 'pass import passpie file.yml' yml_format = {'handler': 'passpie', 'version': 1.0} rootkey = 'credentials' keys = { 'title': 'name', 'password': '******', 'login': '******', 'comments': 'comment' } register_managers(Passpie)