Created
November 29, 2021 12:46
-
-
Save bysse/c374b31dc69ef8ef1cd0f08843adaa95 to your computer and use it in GitHub Desktop.
Convert Dashlane CSV to Bitwarden CSV format
This file contains 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
#!/bin/python3 | |
import sys | |
import urllib.parse | |
# Folder for uncategorized credentials | |
import_folder = "Dashlane" | |
with open("credentials.csv") as fp: | |
data = [[y.strip() for y in x.strip().split(",")] for x in fp.readlines()] | |
dashlane_cols = ["username","username2","username3","title","password","note","url","category","otpSecret"] | |
bitwarden_cols = ["folder","favorite","type","name","notes","fields","reprompt","login_uri","login_username","login_password","login_totp"] | |
def validate(description, valid): | |
print("# {:50} [{}]".format(description, "OK" if valid else "ERROR")) | |
if not valid: | |
raise Exception("Validation failed: " + description) | |
def col(name): | |
return dashlane_cols.index(name) | |
def writeCSV(fp, array): | |
fp.write(",".join(array)) | |
fp.write("\n") | |
def main(): | |
# verify CSV headers | |
header = data[0] | |
validate("Byte order in CSV export", header[0][0] == "\ufeff") | |
header[0] = header[0][1:] | |
validate("Dashlane CSV column names", header == dashlane_cols) | |
# convert each line | |
result = [] | |
print("# Converting records") | |
for record in data[1:]: | |
converted = {} | |
title = record[col("title")] | |
entryurl = record[col("url")] | |
if not title and entryurl: | |
# use the hostname when the title is missing but a url is present | |
try: | |
title = urllib.parse.urlparse(entryurl).netloc | |
except Exception as e: | |
pass | |
converted["name"] = title | |
converted["login_uri"] = entryurl | |
converted["login_username"] = record[col("username")] | |
# Convert the username2 fild | |
if username2 := record[col("username2")]: | |
converted["fields"] = "username2:" + username2 | |
if username3 := record[col("username3")]: | |
print("WARNING: username3 is not supported by this converter") | |
# Convert password, notes and OTP | |
converted["login_password"] = record[col("password")] | |
converted["notes"] = record[col("note")] | |
converted["login_totp"] = record[col("otpSecret")] | |
# Convert the category and use the default if it's missing | |
if category := record[col("category")]: | |
converted["folder"] = category | |
else: | |
converted["folder"] = import_folder | |
# Add missing columns | |
converted["favorite"] = 0 | |
converted["type"] = "login" | |
converted["reprompt"] = 0 | |
if not "fields" in converted: | |
converted["fields"] = "" | |
# Add the line to the converted data set | |
result.append(converted) | |
print(" -", len(result), "records") | |
# Export the converted CSV | |
print("# Writing records") | |
with open("bitwarden_credentials.csv", "w") as fp: | |
writeCSV(fp, bitwarden_cols) | |
for record in sorted(result, key = lambda x: x["name"].lower()): | |
line = [] | |
for column in bitwarden_cols: | |
line.append(str(record[column])) | |
writeCSV(fp, line) | |
print(" -", len(result), "records") | |
try: | |
main() | |
except Exception as e: | |
print("ERROR: Conversion failed with message", e) | |
sys.exit(1) |
This file contains 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
#!/bin/python3 | |
import sys | |
# Folder for ntoes | |
import_folder = "Dashlane" | |
with open("securenotes.csv") as fp: | |
data = [[y.strip() for y in x.strip().split(",")] for x in fp.readlines()] | |
dashlane_cols = ["title","note"] | |
bitwarden_cols = ["folder","favorite","type","name","notes","fields","reprompt","login_uri","login_username","login_password","login_totp"] | |
def validate(description, valid): | |
print("# {:50} [{}]".format(description, "OK" if valid else "ERROR")) | |
if not valid: | |
raise Exception("Validation failed: " + description) | |
def col(name): | |
return dashlane_cols.index(name) | |
def writeCSV(fp, array): | |
fp.write(",".join(array)) | |
fp.write("\n") | |
def main(): | |
# verify CSV headers | |
header = data[0] | |
validate("Byte order in CSV export", header[0][0] == "\ufeff") | |
header[0] = header[0][1:] | |
validate("Dashlane CSV column names", header == dashlane_cols) | |
# convert each line | |
result = [] | |
print("# Converting records") | |
for record in data[1:]: | |
converted = {} | |
# create all columns | |
for column in bitwarden_cols: | |
converted[column] = "" | |
converted["type"] = "note" | |
converted["name"] = record[col("title")] | |
converted["notes"] = record[col("note")] | |
# Add the line to the converted data set | |
result.append(converted) | |
print(" -", len(result), "records") | |
# Export the converted CSV | |
print("# Writing records") | |
with open("bitwarden_securenotes.csv", "w") as fp: | |
writeCSV(fp, bitwarden_cols) | |
for record in sorted(result, key = lambda x: x["name"].lower()): | |
line = [] | |
for column in bitwarden_cols: | |
line.append(str(record[column])) | |
writeCSV(fp, line) | |
print(" -", len(result), "records") | |
try: | |
main() | |
except Exception as e: | |
print("ERROR: Conversion failed with message", e) | |
sys.exit(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment