diff --git a/.gitignore b/.gitignore index c7c1ff6..01b3d69 100644 --- a/.gitignore +++ b/.gitignore @@ -177,3 +177,4 @@ dist # confidant env.ts test +executables diff --git a/bun.lockb b/bun.lockb index 4a345c0..f7a9370 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/confidant.ts b/confidant.ts index 8348768..67ab5aa 100755 --- a/confidant.ts +++ b/confidant.ts @@ -1,7 +1,6 @@ -#!/bin/env bun +#!/bin/env node import { input, password } from "@inquirer/prompts"; -import { $ } from "bun"; import chalk from "chalk-template"; import { Command } from "commander"; import { decrypt_vault, encrypt_vault, initialize, reset } from "./src/main"; @@ -18,7 +17,6 @@ import { const program = new Command(); const { exit } = process; -$.nothrow(); program.name("confidant").description("Creates a very secure file vault."); diff --git a/package.json b/package.json index c7a877b..cf128a0 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "confidant", - "version": "1.0.0", + "version": "1.1.0", "module": "confidant.ts", "type": "module", "scripts": { "init": "bun ./scripts/init.ts", - "sh": "bun ./scripts/sh.ts", - "build": "bun ./scripts/build.ts" + "build": "bun build --minify ./confidant.ts --target node --outfile ./dist/confidant.mjs", + "build-exec": "bun build --compile --minify ./confidant.ts --outfile ./dist/confidant" }, "bin": { - "confidant": "./confidant.ts" + "confidant": "./dist/confidant.mjs" }, "devDependencies": { "@types/bun": "latest" @@ -20,9 +20,11 @@ "dependencies": { "@iarna/toml": "^2.2.5", "@inquirer/prompts": "^6.0.1", + "@types/adm-zip": "^0.5.5", "@types/commander": "^2.12.2", "@types/crypto-js": "^4.2.2", "@types/eccrypto": "^1.1.6", + "adm-zip": "^0.5.16", "argon2": "^0.41.1", "chalk": "^5.3.0", "chalk-template": "^1.1.0", diff --git a/scripts/build.ts b/scripts/build.ts deleted file mode 100644 index dc3b11c..0000000 --- a/scripts/build.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { $ } from "bun"; -import data from "../package.json"; - -const { version: ver } = data; - -await $` -rm -rf ./dist -bun build --compile --minify --target=bun-linux-x64 ./confidant.ts --outfile ./dist/confidant_${ver}_linux_x64 -bun build --compile --minify --target=bun-windows-x64 ./confidant.ts --outfile ./dist/confidant_${ver}_win_x64.exe -bun build --compile --minify --target=bun-darwin-arm64 ./confidant.ts --outfile ./dist/confidant_${ver}_darwin_arm64 -`; diff --git a/scripts/init.ts b/scripts/init.ts index 583c04f..fc987f8 100644 --- a/scripts/init.ts +++ b/scripts/init.ts @@ -1,7 +1,7 @@ -import { $ } from "bun"; import { stringy } from "../src/utils"; import { randomBytes } from "crypto"; import chalkTemplate from "chalk-template"; +import { writeFileSync } from "fs"; const envstring = `// env.ts const env = { @@ -15,5 +15,5 @@ const env = { export default env; `; -await $`echo ${envstring} > env.ts`; +writeFileSync("./env.ts", envstring); console.log(chalkTemplate`{green env.ts initialized sucessfully!}`); diff --git a/src/main.ts b/src/main.ts index 8d4e321..fbc0a83 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,7 @@ import { parse, stringify } from "@iarna/toml"; -import { $ } from "bun"; import { pbkdf2Sync as pbkdf2, randomBytes } from "crypto"; import { derive, getPublic } from "eccrypto"; -import { readFileSync, writeFileSync } from "fs"; +import { readFileSync, rmSync, writeFileSync } from "fs"; import { buffer, decrypt, @@ -24,6 +23,7 @@ import env from "../env"; import { AES, enc, HmacSHA256 } from "crypto-js"; import chalk from "chalk-template"; import { input, password } from "@inquirer/prompts"; +import AdmZip from "adm-zip"; console.info = function () {}; const { exit } = process; @@ -36,8 +36,10 @@ export async function initialize(password: string, dirname: string) { const salt = randomBytes(32); const code = random(10000, 100000); const D = pbkdf2(S_AB, salt, code, 64, "sha256"); - await $`zip -r9 confidant.zip ${dirname} > /dev/null`; - await $`rm -rf ${dirname}`; + const zip = new AdmZip(); + zip.addLocalFolder("./" + dirname); + zip.writeZip("./confidant.zip"); + rmSync(dirname, { recursive: true }); const Z = readFileSync("confidant.zip"); const E_Z = encrypt_file(Z, D); @@ -84,13 +86,12 @@ export async function initialize(password: string, dirname: string) { console.log(`Recovery phrase:`); console.log(chalk`{magenta ${recovery_phrase}}`); - await $`rm confidant.zip`; + rmSync("confidant.zip"); // create .gitignore const gitignore = `# .gitignore -*.con -*.fid +*.key *_recovery.txt confidant.zip *.confidant @@ -132,7 +133,9 @@ export async function decrypt_vault(password: string, dirname: string) { const Z = decrypt_file(E_Z, D); writeFileSync(`confidant.zip`, Z); writeFileSync(`.${dirname}.confidant`, encrypt(D, buffer(env.AUTH_KEY))); - await $`unzip confidant.zip > /dev/null && rm confidant.zip`; + const unzip = new AdmZip("./confidant.zip"); + unzip.extractAllTo(`./${dirname}`, true); + rmSync("./confidant.zip"); } catch (e) { panic`Error decrypting vault. The files could be corrupted.`; } @@ -145,8 +148,10 @@ export async function encrypt_vault(dirname: string) { buffer(env.AUTH_KEY), ); // create zip file and encrypt it - await $`zip -r9 confidant.zip ${dirname} > /dev/null`; - await $`rm -rf ${dirname}`; + const zip = new AdmZip(); + zip.addLocalFolder("./" + dirname); + zip.writeZip("./confidant.zip"); + rmSync(dirname, { recursive: true }); const Z = readFileSync("confidant.zip"); const E_Z = encrypt_file(Z, D); @@ -159,7 +164,8 @@ export async function encrypt_vault(dirname: string) { `${dirname}.vault`, Buffer.concat([vaultfile.subarray(0, index), separator, E_Z]), ); - await $`rm confidant.zip .${dirname}.confidant`; + rmSync("./confidant.zip"); + rmSync(`./.${dirname}.confidant`); } catch (e) { panic`Error encrypting vault. The files could be corrupted.`; } @@ -231,7 +237,7 @@ export async function reset(dirname: string, recoverystring: string) { ]), ); - writeFileSync(`${dirname}_recovery.txt`, newrecphrase); + writeFileSync(`${dirname}_recovery.txt`, newrecphrase + "\n"); console.log(`New recovery phrase:`); console.log(chalk`{magenta ${newrecphrase}}`); } catch (e) { diff --git a/src/utils.ts b/src/utils.ts index 9226a1e..d144fa8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,7 +3,6 @@ import { createHmac } from "crypto"; import { generatePrivate, getPublic } from "eccrypto"; import { generate } from "random-words"; import chalk from "chalk-template"; -import { $ } from "bun"; import { existsSync, readdirSync } from "fs"; import { select } from "@inquirer/prompts"; @@ -61,15 +60,6 @@ export function decrypt_file(input: Buffer, key: Buffer) { return Buffer.from(result.toString(enc.Utf8), "base64"); } -export async function exists(dir: string) { - try { - await $`test -d ${dir}`; - return true; - } catch (e) { - return false; - } -} - export function log( str: TemplateStringsArray, ...placeholders: unknown[] @@ -109,13 +99,17 @@ export function print(key: string, value: string) { export class Files { data: string[] | null = null; - constructor(regex?: RegExp | string) { + constructor(regex?: RegExp | string | string[]) { if (!regex) { return this; } try { - const data = readdirSync(".").filter((x) => x.match(regex)); - this.data = data ? data.map((x) => x.replace(regex, "$1")) : null; + if (Array.isArray(regex)) { + this.data = regex; + } else { + const data = readdirSync(".").filter((x) => x.match(regex)); + this.data = data ? data.map((x) => x.replace(regex, "$1")) : null; + } } catch (error) { console.error("Error executing command:", error); this.data = null; @@ -130,51 +124,57 @@ export class Files { return `[ ${this.data ? this.data.join(", ") : "empty"} ]`; } - intersection(a: Files) { + intersection(a: Files): Files { const obj = new Files(); - obj.data = Array.from(new Set(this.data).intersection(new Set(a.data))); + if (this.data && a.data) { + obj.data = this.data.filter((x) => a.data!.includes(x)); + } return obj; } - difference(a: Files) { + difference(a: Files): Files { const obj = new Files(); - obj.data = Array.from(new Set(this.data).difference(new Set(a.data))); + if (this.data && a.data) { + obj.data = this.data.filter((x) => !a.data!.includes(x)); + } return obj; } } export async function getDirectoryNames() { - const dirlist = (await $`ls -d */`.text()).trim(); + try { + const dirlist = readdirSync(".", { withFileTypes: true }) + .filter((file) => file.isDirectory()) + .map((file) => file.name); - if (!dirlist) { - panic`No directories found in current directory, please create one and run "init" again.`; - return; + if (dirlist.length === 0) { + panic`No directories found in current directory, please create one and run "init" again.`; + return; + } + const dirs = new Files(dirlist); + const vaults = new Files(/.*\.vault/g); + const usableDirs = dirs.difference(vaults); + + if (usableDirs.data && usableDirs.data.length === 0) { + panic`No usable directories found in current directory, please create one and run "init" again.`; + return; + } else if (usableDirs.data && usableDirs.data.length === 1) { + return usableDirs.data[0]; + } + + const dirname = await select({ + message: "Select a directory to use:", + choices: usableDirs.data!.map((x) => ({ + name: x, + value: x, + })), + }); + + return dirname; + } catch (e) { + console.log(chalk`{red ${e.message}}`); + process.exit(1); } - let dirs: string[]; - const vaultList = (await $`ls *.vault`.text()).trim().match(/.*\.vault/g); - if (vaultList) { - const vaults = vaultList.map((x) => x.replace(".vault", "")); - dirs = Array.from(new Set(dirlist.split("\n")).difference(new Set(vaults))); - } else { - dirs = dirlist.split("\n"); - } - - if (dirs.length === 0) { - panic`No usable directories found in current directory, please create one and run "init" again.`; - return; - } else if (dirs.length === 1) { - return dirs[0]; - } - - const dirname = await select({ - message: "Select a directory to use:", - choices: dirs.map((x) => ({ - name: x, - value: x, - })), - }); - - return dirname; } export async function getVaultName() { @@ -252,7 +252,7 @@ export async function getDecryptedName() { export function getRandomPassword(length: number) { const STRING = - "0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + "0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*ABCDEFGHIJKLMNOPQRSTUVWXYZ"; let pass = ""; while (pass.length < length) {