Created
September 24, 2018 12:29
-
-
Save kevinji/4bd6d133552a597bc24ee4aebed16cf2 to your computer and use it in GitHub Desktop.
Properly escape & from 1password passwords and URLs using the command-line tool, op.
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
import json | |
import subprocess | |
import sys | |
def read_json(*args): | |
output = subprocess.check_output(*args) | |
return json.loads(output) | |
def pp(obj): | |
""" | |
Debugging function used to pretty-print JSON. | |
""" | |
print(json.dumps(obj, indent=2)) | |
def find_login_uuid(): | |
templates = read_json(["op", "list", "templates"]) | |
for template in templates: | |
if template["name"] == "Login": | |
return template["uuid"] | |
sys.exit("uuid for Login not found") | |
def update_password(login): | |
""" | |
Replace & with & in passwords. | |
Returns whether the password was updated. | |
""" | |
for field in login["details"]["fields"]: | |
# Use `.get()` since designation may not exist. | |
if field.get("designation") != "password": | |
continue | |
new_password = field["value"].replace("&", "&") | |
if field["value"] != new_password: | |
field["value"] = new_password | |
return True | |
return False | |
return False | |
def update_url(login): | |
""" | |
Replace & with & in URLs. | |
Returns whether the URL was updated. | |
""" | |
new_url = login["overview"]["url"].replace("&", "&") | |
if login["overview"]["url"] != new_url: | |
login["overview"]["url"] = new_url | |
return True | |
return False | |
def encode(login): | |
# The `.rstrip()` is quite important: it strips out the trailing newline | |
# from `subprocess.check_output()`. Without it, `op create item Login <data>` | |
# raises the error "illegal base64 data at input byte _". | |
return subprocess.check_output(["op", "encode"], | |
input=json.dumps(login["details"], | |
separators=(",", ":")), | |
text=True).rstrip() | |
def run(*args): | |
subprocess.run(*args, check=True, stdout=subprocess.DEVNULL) | |
def main(): | |
login_uuid = find_login_uuid() | |
items = read_json(["op", "list", "items"]) | |
uuids = (item["uuid"] for item in items) | |
for uuid in uuids: | |
login = read_json(["op", "get", "item", uuid]) | |
if login["templateUuid"] != login_uuid or login["trashed"] != "N": | |
continue | |
password_updated = update_password(login) | |
url_updated = update_url(login) | |
what_to_update = [] | |
what_to_update += ["password"] if password_updated else [] | |
what_to_update += ["url"] if url_updated else [] | |
if not what_to_update: | |
continue | |
what_to_update = ", ".join(what_to_update) | |
print("Updating {} for {}...".format(what_to_update, | |
login["overview"]["title"])) | |
encoded_details = encode(login) | |
run(["op", "create", "item", "Login", | |
"--title={}".format(login["overview"]["title"]), | |
"--url={}".format(login["overview"]["url"]), | |
encoded_details]) | |
# Delete the old version to avoid leaving a duplicate entry. | |
print("Deleting old version of {}...".format(login["overview"]["title"])) | |
run(["op", "delete", "item", uuid]) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment