Skip to content

Instantly share code, notes, and snippets.

@simojo
Created January 20, 2023 20:01
Show Gist options
  • Save simojo/345e3f9537ae00081bcc2ce4a5b37ed4 to your computer and use it in GitHub Desktop.
Save simojo/345e3f9537ae00081bcc2ce4a5b37ed4 to your computer and use it in GitHub Desktop.
// migrating from password-store (aka pass) to bitwarden.
// by Simon Jones (2023)
//
// note: I do not use bitwarden, this is for you to use.
// this script assumes you have:
// 1. decrypted all of your password-store gpg encrypted files
// and put them in a folder named 'passDecryptedFiles'.
// 2. a directory structure as follows:
// ./
// ├ migratePassToBitwarden.js
// └ passDecryptedFiles/
// ├ file1.gpg.txt
// ├ file2.gpg.txt
// ├ file3.gpg.txt
// ├ file4.gpg.txt
// ├ ...
// └ fileN.gpg.txt
//
// good luck.
const fs = require("fs");
const path = require("path");
const directoryName = "passDecryptedFiles";
const fullPath = path.join(__dirname, directoryName)
fs.readdir(fullPath, (error1, files) => {
if (error1) throw error1;
let passwordObjs = files.map(f => {
let passwordObj;
passwordObj = handleFileText(
f,
fs.readFileSync(path.join(__dirname, directoryName, f), {encoding: "utf8", flag: "r"})
);
return passwordObj;
});
fs.writeFile("OUTPUT.txt", JSON.stringify({
"encrypted": false,
"folders": [],
"items": passwordObjs
}), (e) => { if (e) throw e; });
});
function createUUID() {
// http://www.ietf.org/rfc/rfc4122.txt
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
function handleFileText(fname, text) {
let splits = text.split("\n");
let p = splits[0];
if (p === "") {
let res= {
"id": createUUID(),
"organizationId": null,
"folderId": null,
"type": 1,
"reprompt": 0,
"name": fname.replace(".gpg.txt", ""),
"notes": text,
"favorite": false,
"fields": [],
"login": {
"uris": [],
"username": "",
"password": "",
"totp": null
},
"collectionIds": null
};
return res;
}
let keyvalues = splits.slice(1)
.filter(x => /^.*?:\s.+$/.test(x) === true)
.map(x => ({
key: x.split(":")[0].trim(),
value: x.split(":").slice(1).join(":").trim()
}));
let username = keyvalues.filter(x => x.key === "username")[0]?.value;
if (username != null) keyvalues = keyvalues.filter(x => x.key !== "username");
let notes = keyvalues.filter(x => x.key === "notes")[0]?.value;
if (notes != null) keyvalues = keyvalues.filter(x => x.key !== "notes");
let url = keyvalues.filter(x => x.key === "url")[0]?.value;
if (url != null) keyvalues = keyvalues.filter(x => x.key !== "url");
let res= {
"id": createUUID(),
"organizationId": null,
"folderId": null,
"type": 1,
"reprompt": 0,
"name": fname.replace(".gpg.txt", ""),
"notes": notes,
"favorite": false,
"fields": keyvalues.map(x => ({
"name": x.key,
"value": x.value,
"type": 0,
"linkedId": null
})),
"login": {
"uris": (url == null ? [] : [
{
"match": null,
"uri": url
}
]),
"username": username,
"password": p,
"totp": null
},
"collectionIds": null
};
return res;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment