mirror of
https://github.com/silicoflare/confidant.git
synced 2026-05-26 12:45:24 +05:30
feat: remove complete dependency on bun to be able to compile to .js
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -177,3 +177,4 @@ dist
|
|||||||
# confidant
|
# confidant
|
||||||
env.ts
|
env.ts
|
||||||
test
|
test
|
||||||
|
executables
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/bin/env bun
|
#!/bin/env node
|
||||||
|
|
||||||
import { input, password } from "@inquirer/prompts";
|
import { input, password } from "@inquirer/prompts";
|
||||||
import { $ } from "bun";
|
|
||||||
import chalk from "chalk-template";
|
import chalk from "chalk-template";
|
||||||
import { Command } from "commander";
|
import { Command } from "commander";
|
||||||
import { decrypt_vault, encrypt_vault, initialize, reset } from "./src/main";
|
import { decrypt_vault, encrypt_vault, initialize, reset } from "./src/main";
|
||||||
@@ -18,7 +17,6 @@ import {
|
|||||||
|
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
const { exit } = process;
|
const { exit } = process;
|
||||||
$.nothrow();
|
|
||||||
|
|
||||||
program.name("confidant").description("Creates a very secure file vault.");
|
program.name("confidant").description("Creates a very secure file vault.");
|
||||||
|
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "confidant",
|
"name": "confidant",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"module": "confidant.ts",
|
"module": "confidant.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"init": "bun ./scripts/init.ts",
|
"init": "bun ./scripts/init.ts",
|
||||||
"sh": "bun ./scripts/sh.ts",
|
"build": "bun build --minify ./confidant.ts --target node --outfile ./dist/confidant.mjs",
|
||||||
"build": "bun ./scripts/build.ts"
|
"build-exec": "bun build --compile --minify ./confidant.ts --outfile ./dist/confidant"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"confidant": "./confidant.ts"
|
"confidant": "./dist/confidant.mjs"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest"
|
"@types/bun": "latest"
|
||||||
@@ -20,9 +20,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@inquirer/prompts": "^6.0.1",
|
"@inquirer/prompts": "^6.0.1",
|
||||||
|
"@types/adm-zip": "^0.5.5",
|
||||||
"@types/commander": "^2.12.2",
|
"@types/commander": "^2.12.2",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/eccrypto": "^1.1.6",
|
"@types/eccrypto": "^1.1.6",
|
||||||
|
"adm-zip": "^0.5.16",
|
||||||
"argon2": "^0.41.1",
|
"argon2": "^0.41.1",
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
"chalk-template": "^1.1.0",
|
"chalk-template": "^1.1.0",
|
||||||
|
|||||||
@@ -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
|
|
||||||
`;
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { $ } from "bun";
|
|
||||||
import { stringy } from "../src/utils";
|
import { stringy } from "../src/utils";
|
||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto";
|
||||||
import chalkTemplate from "chalk-template";
|
import chalkTemplate from "chalk-template";
|
||||||
|
import { writeFileSync } from "fs";
|
||||||
|
|
||||||
const envstring = `// env.ts
|
const envstring = `// env.ts
|
||||||
const env = {
|
const env = {
|
||||||
@@ -15,5 +15,5 @@ const env = {
|
|||||||
export default env;
|
export default env;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await $`echo ${envstring} > env.ts`;
|
writeFileSync("./env.ts", envstring);
|
||||||
console.log(chalkTemplate`{green env.ts initialized sucessfully!}`);
|
console.log(chalkTemplate`{green env.ts initialized sucessfully!}`);
|
||||||
|
|||||||
30
src/main.ts
30
src/main.ts
@@ -1,8 +1,7 @@
|
|||||||
import { parse, stringify } from "@iarna/toml";
|
import { parse, stringify } from "@iarna/toml";
|
||||||
import { $ } from "bun";
|
|
||||||
import { pbkdf2Sync as pbkdf2, randomBytes } from "crypto";
|
import { pbkdf2Sync as pbkdf2, randomBytes } from "crypto";
|
||||||
import { derive, getPublic } from "eccrypto";
|
import { derive, getPublic } from "eccrypto";
|
||||||
import { readFileSync, writeFileSync } from "fs";
|
import { readFileSync, rmSync, writeFileSync } from "fs";
|
||||||
import {
|
import {
|
||||||
buffer,
|
buffer,
|
||||||
decrypt,
|
decrypt,
|
||||||
@@ -24,6 +23,7 @@ import env from "../env";
|
|||||||
import { AES, enc, HmacSHA256 } from "crypto-js";
|
import { AES, enc, HmacSHA256 } from "crypto-js";
|
||||||
import chalk from "chalk-template";
|
import chalk from "chalk-template";
|
||||||
import { input, password } from "@inquirer/prompts";
|
import { input, password } from "@inquirer/prompts";
|
||||||
|
import AdmZip from "adm-zip";
|
||||||
|
|
||||||
console.info = function () {};
|
console.info = function () {};
|
||||||
const { exit } = process;
|
const { exit } = process;
|
||||||
@@ -36,8 +36,10 @@ export async function initialize(password: string, dirname: string) {
|
|||||||
const salt = randomBytes(32);
|
const salt = randomBytes(32);
|
||||||
const code = random(10000, 100000);
|
const code = random(10000, 100000);
|
||||||
const D = pbkdf2(S_AB, salt, code, 64, "sha256");
|
const D = pbkdf2(S_AB, salt, code, 64, "sha256");
|
||||||
await $`zip -r9 confidant.zip ${dirname} > /dev/null`;
|
const zip = new AdmZip();
|
||||||
await $`rm -rf ${dirname}`;
|
zip.addLocalFolder("./" + dirname);
|
||||||
|
zip.writeZip("./confidant.zip");
|
||||||
|
rmSync(dirname, { recursive: true });
|
||||||
const Z = readFileSync("confidant.zip");
|
const Z = readFileSync("confidant.zip");
|
||||||
const E_Z = encrypt_file(Z, D);
|
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(`Recovery phrase:`);
|
||||||
console.log(chalk`{magenta ${recovery_phrase}}`);
|
console.log(chalk`{magenta ${recovery_phrase}}`);
|
||||||
await $`rm confidant.zip`;
|
rmSync("confidant.zip");
|
||||||
|
|
||||||
// create .gitignore
|
// create .gitignore
|
||||||
const gitignore = `# .gitignore
|
const gitignore = `# .gitignore
|
||||||
|
|
||||||
*.con
|
*.key
|
||||||
*.fid
|
|
||||||
*_recovery.txt
|
*_recovery.txt
|
||||||
confidant.zip
|
confidant.zip
|
||||||
*.confidant
|
*.confidant
|
||||||
@@ -132,7 +133,9 @@ export async function decrypt_vault(password: string, dirname: string) {
|
|||||||
const Z = decrypt_file(E_Z, D);
|
const Z = decrypt_file(E_Z, D);
|
||||||
writeFileSync(`confidant.zip`, Z);
|
writeFileSync(`confidant.zip`, Z);
|
||||||
writeFileSync(`.${dirname}.confidant`, encrypt(D, buffer(env.AUTH_KEY)));
|
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) {
|
} catch (e) {
|
||||||
panic`Error decrypting vault. The files could be corrupted.`;
|
panic`Error decrypting vault. The files could be corrupted.`;
|
||||||
}
|
}
|
||||||
@@ -145,8 +148,10 @@ export async function encrypt_vault(dirname: string) {
|
|||||||
buffer(env.AUTH_KEY),
|
buffer(env.AUTH_KEY),
|
||||||
);
|
);
|
||||||
// create zip file and encrypt it
|
// create zip file and encrypt it
|
||||||
await $`zip -r9 confidant.zip ${dirname} > /dev/null`;
|
const zip = new AdmZip();
|
||||||
await $`rm -rf ${dirname}`;
|
zip.addLocalFolder("./" + dirname);
|
||||||
|
zip.writeZip("./confidant.zip");
|
||||||
|
rmSync(dirname, { recursive: true });
|
||||||
const Z = readFileSync("confidant.zip");
|
const Z = readFileSync("confidant.zip");
|
||||||
const E_Z = encrypt_file(Z, D);
|
const E_Z = encrypt_file(Z, D);
|
||||||
|
|
||||||
@@ -159,7 +164,8 @@ export async function encrypt_vault(dirname: string) {
|
|||||||
`${dirname}.vault`,
|
`${dirname}.vault`,
|
||||||
Buffer.concat([vaultfile.subarray(0, index), separator, E_Z]),
|
Buffer.concat([vaultfile.subarray(0, index), separator, E_Z]),
|
||||||
);
|
);
|
||||||
await $`rm confidant.zip .${dirname}.confidant`;
|
rmSync("./confidant.zip");
|
||||||
|
rmSync(`./.${dirname}.confidant`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
panic`Error encrypting vault. The files could be corrupted.`;
|
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(`New recovery phrase:`);
|
||||||
console.log(chalk`{magenta ${newrecphrase}}`);
|
console.log(chalk`{magenta ${newrecphrase}}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
94
src/utils.ts
94
src/utils.ts
@@ -3,7 +3,6 @@ import { createHmac } from "crypto";
|
|||||||
import { generatePrivate, getPublic } from "eccrypto";
|
import { generatePrivate, getPublic } from "eccrypto";
|
||||||
import { generate } from "random-words";
|
import { generate } from "random-words";
|
||||||
import chalk from "chalk-template";
|
import chalk from "chalk-template";
|
||||||
import { $ } from "bun";
|
|
||||||
import { existsSync, readdirSync } from "fs";
|
import { existsSync, readdirSync } from "fs";
|
||||||
import { select } from "@inquirer/prompts";
|
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");
|
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(
|
export function log(
|
||||||
str: TemplateStringsArray,
|
str: TemplateStringsArray,
|
||||||
...placeholders: unknown[]
|
...placeholders: unknown[]
|
||||||
@@ -109,13 +99,17 @@ export function print(key: string, value: string) {
|
|||||||
export class Files {
|
export class Files {
|
||||||
data: string[] | null = null;
|
data: string[] | null = null;
|
||||||
|
|
||||||
constructor(regex?: RegExp | string) {
|
constructor(regex?: RegExp | string | string[]) {
|
||||||
if (!regex) {
|
if (!regex) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const data = readdirSync(".").filter((x) => x.match(regex));
|
if (Array.isArray(regex)) {
|
||||||
this.data = data ? data.map((x) => x.replace(regex, "$1")) : null;
|
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) {
|
} catch (error) {
|
||||||
console.error("Error executing command:", error);
|
console.error("Error executing command:", error);
|
||||||
this.data = null;
|
this.data = null;
|
||||||
@@ -130,51 +124,57 @@ export class Files {
|
|||||||
return `[ ${this.data ? this.data.join(", ") : "empty"} ]`;
|
return `[ ${this.data ? this.data.join(", ") : "empty"} ]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
intersection(a: Files) {
|
intersection(a: Files): Files {
|
||||||
const obj = new 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;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
difference(a: Files) {
|
difference(a: Files): Files {
|
||||||
const obj = new 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;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDirectoryNames() {
|
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) {
|
if (dirlist.length === 0) {
|
||||||
panic`No directories found in current directory, please create one and run "init" again.`;
|
panic`No directories found in current directory, please create one and run "init" again.`;
|
||||||
return;
|
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() {
|
export async function getVaultName() {
|
||||||
@@ -252,7 +252,7 @@ export async function getDecryptedName() {
|
|||||||
|
|
||||||
export function getRandomPassword(length: number) {
|
export function getRandomPassword(length: number) {
|
||||||
const STRING =
|
const STRING =
|
||||||
"0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
"0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
let pass = "";
|
let pass = "";
|
||||||
|
|
||||||
while (pass.length < length) {
|
while (pass.length < length) {
|
||||||
|
|||||||
Reference in New Issue
Block a user