Created
September 29, 2020 21:31
-
-
Save julian-klode/39d128acba970f20ae95e3504047cca3 to your computer and use it in GitHub Desktop.
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/python3 | |
# | |
# Copyright (C) 2020 Julian Andres Klode <[email protected]> | |
# | |
# SPDX-License-Identifier: AGPL-3.0-or-later | |
"""Takes a Chrome CSV on stdin, and produces a Bitwarden JSON export on stdout. | |
The output only contains relevant fields, and logins are merged together based | |
on user name and password. | |
""" | |
import csv, sys, uuid, json | |
def remove_prefix(s, prefix): | |
"""Remove a prefix from a string.""" | |
return s[len(prefix) :] if s.startswith(prefix) else s | |
def clean_name(name, x): | |
"""Produce a clean name without useless noise like www in it""" | |
if not name and x["url"].startswith("android://"): | |
name = ".".join(reversed(x["url"].split("/")[2].split("@")[-1].split("."))) | |
print("Found android app", name, file=sys.stderr) | |
if not any(c.isalpha() for c in name): | |
print("Ignoring IP", name, file=sys.stderr) | |
return name | |
inp = name | |
name = remove_prefix(name, "www.") | |
name = remove_prefix(name, "login.") | |
name = remove_prefix(name, "signin.") | |
name = remove_prefix(name, "passport.") | |
name = remove_prefix(name, "auth.") | |
name = remove_prefix(name, "console.") | |
name = remove_prefix(name, "config.") | |
name = remove_prefix(name, "app.") | |
name = remove_prefix(name, "m.") | |
name = remove_prefix(name, "mobile.") | |
name = remove_prefix(name, "my.") | |
name = remove_prefix(name, "id.") | |
name = remove_prefix(name, "account.") | |
name = remove_prefix(name, "shop.") | |
name = remove_prefix(name, "accounts.login.idm.") | |
name = remove_prefix(name, "identity.") | |
name = remove_prefix(name, "co2offset.") | |
name = remove_prefix(name, "sso.") | |
if name.endswith(".ikea.com"): | |
name = "ikea.com" | |
if name.endswith(".auth0.com"): | |
name = name.replace(".auth0.com", "") | |
if name.endswith(".shopgate.com"): | |
name = name.replace(".shopgate.com", "") | |
if name.endswith("wikipedia.org"): | |
name = "wikipedia.org" | |
if name.endswith("tutanota.com"): | |
name = "tutanota.com" | |
out = ".".join(name.split(".")[-2:]) | |
pre = ".".join(name.split(".")[:-2]) | |
if pre: | |
out = f"{out} - {pre}" | |
print("Replacing", pre, "in", name, "=>", out, file=sys.stderr) | |
return out | |
def convert(fobj=sys.stdin): | |
"""Convert Chrome CSV file in fobj to BitWarden JSON file.""" | |
passwds = list(csv.DictReader(fobj)) | |
by_login = [((k["username"], k["password"]), k) for k in passwds] | |
by_login1 = {k: [v1 for k1, v1 in by_login if k1 == k] for k, v in by_login} | |
return { | |
"folders": [], | |
"items": [ | |
{ | |
"type": 1, | |
"name": (", ".join(set(clean_name(x["name"], x) for x in v))), | |
"login": { | |
"uris": [{"match": None, "uri": x["url"]} for x in v], | |
"username": v[0]["username"], | |
"password": v[0]["password"], | |
}, | |
} | |
for v in by_login1.values() | |
], | |
} | |
print(json.dumps(convert(), indent=4,)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment