Created
December 30, 2024 10:25
-
-
Save nejdetckenobi/0967b2ed578772464d49fa218682325d to your computer and use it in GitHub Desktop.
`kdbx` deposunu, `pass` deposuna aktarmak için yazılmış bir araç. `-f` argümanına XML halinde dışa aktardığın parola listesinin yolunu vererek çalıştırabilirsin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! /usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| # | |
| # Script for importing KeeWeb (Keepass XML) data to pass. | |
| # This file is licensed under the GPLv2+. Please see COPYING for more information. | |
| import getopt | |
| import sys | |
| from subprocess import Popen, PIPE | |
| from xml.etree import ElementTree | |
| def pass_import_entry(path, data): | |
| """ Import new password entry to password-store using pass insert command """ | |
| proc = Popen(['pass', 'insert', '--multiline', path], stdin=PIPE, stdout=PIPE) | |
| proc.communicate(data.encode('utf-8')) | |
| proc.wait() | |
| def get_value(elements, node_text): | |
| """ Get value for a specific key from XML elements """ | |
| for element in elements: | |
| for child in element.findall('Key'): | |
| if child.text == node_text: | |
| value = element.find('Value') | |
| return value.text if value is not None else '' | |
| return '' | |
| def path_for(element, path=''): | |
| """ Generate path name from elements title and current path """ | |
| if element.tag == 'Entry': | |
| title = get_value(element.findall("String"), "Title") | |
| elif element.tag == 'Group': | |
| title = element.find('Name').text | |
| else: | |
| title = '' | |
| if path == '': | |
| return title | |
| else: | |
| return '/'.join([path, title]) | |
| def password_data(element, path=''): | |
| """ Return password data and additional info if available from password entry element """ | |
| data = "" | |
| password = get_value(element.findall('String'), 'Password') | |
| if password: | |
| data = password + "\n" | |
| else: | |
| print(f"[WARN] No password: {path_for(element, path)}") | |
| for field in ['UserName', 'URL', 'Notes']: | |
| value = get_value(element.findall('String'), field) | |
| if value: | |
| data += f"{field}: {value}\n" | |
| # OTP Secret için özel işlem | |
| otp_secret = get_value(element.findall('String'), 'otp') | |
| if otp_secret: | |
| # KeeWeb'den gelen OTP verisini pass-otp formatına dönüştür | |
| if otp_secret.startswith('otpauth://'): | |
| otp_entry = f"{otp_secret}" | |
| else: | |
| otp_entry = f"otpauth://totp/default?secret={otp_secret}" | |
| data += otp_entry | |
| return data | |
| def import_entry(entries, element, path=''): | |
| """ Import a single password entry """ | |
| element_path = path_for(element, path) | |
| if element_path in entries: | |
| print(f"[INFO] Duplicate needs merging: {element_path}") | |
| existing_data = entries[element_path] | |
| data = f"{existing_data}---------\nPassword: {password_data(element)}" | |
| else: | |
| data = password_data(element, path) | |
| entries[element_path] = data | |
| def import_group(entries, element, path='', npath=None): | |
| """ Import all entries and sub-groups from a given group """ | |
| if npath is None: | |
| npath = path_for(element, path) | |
| for group in element.findall('Group'): | |
| import_group(entries, group, npath) | |
| for entry in element.findall('Entry'): | |
| import_entry(entries, entry, npath) | |
| def import_passwords(xml_file, root_path=None): | |
| """ Parse given KeeWeb XML file and import password groups from it """ | |
| print(f"[>>>>] Importing passwords from file {xml_file}") | |
| print(f"[INFO] Root path: {root_path}") | |
| entries = {} | |
| with open(xml_file, 'r', encoding='utf-8') as xml: | |
| text = xml.read() | |
| xml_tree = ElementTree.XML(text) | |
| root = xml_tree.find('Root') | |
| root_group = root.find('Group') | |
| import_group(entries, root_group, '', root_path) | |
| password_count = 0 | |
| for path, data in sorted(entries.items()): | |
| sys.stdout.write(f"[>>>>] Importing {path} ... ") | |
| pass_import_entry(path, data) | |
| sys.stdout.write("OK\n") | |
| password_count += 1 | |
| print(f"[ OK ] Done. Imported {password_count} passwords.") | |
| def usage(): | |
| """ Print usage """ | |
| print(f"Usage: {sys.argv[0]} -f XML_FILE") | |
| print("Optional:") | |
| print(" -r ROOT_PATH Different root path to use than the one in xml file, use \"\" for none") | |
| def main(argv): | |
| """ Main function to handle command-line arguments and execute the script """ | |
| try: | |
| opts, args = getopt.gnu_getopt(argv, "f:r:") | |
| except getopt.GetoptError as err: | |
| print(str(err)) | |
| usage() | |
| sys.exit(2) | |
| xml_file = None | |
| root_path = None | |
| for opt, arg in opts: | |
| if opt == "-f": | |
| xml_file = arg | |
| if opt == "-r": | |
| root_path = arg | |
| if xml_file: | |
| import_passwords(xml_file, root_path) | |
| else: | |
| usage() | |
| sys.exit(2) | |
| if __name__ == '__main__': | |
| main(sys.argv[1:]) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment