From fa8f3f9465e87d499f7d6428323f496a884b7818 Mon Sep 17 00:00:00 2001 From: Elizabeth Alexander Hunt Date: Sat, 10 May 2025 16:57:03 -0700 Subject: initial commit --- utils/deno.json | 5 +++++ utils/env.ts | 5 +++++ utils/mod.ts | 4 ++++ utils/run.ts | 21 ++++++++++++++++++++ utils/secret.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++ utils/validate_identifier.ts | 3 +++ 6 files changed, 84 insertions(+) create mode 100644 utils/deno.json create mode 100644 utils/env.ts create mode 100644 utils/mod.ts create mode 100644 utils/run.ts create mode 100644 utils/secret.ts create mode 100644 utils/validate_identifier.ts (limited to 'utils') diff --git a/utils/deno.json b/utils/deno.json new file mode 100644 index 0000000..b85c47f --- /dev/null +++ b/utils/deno.json @@ -0,0 +1,5 @@ +{ + "name": "@liz-ci/utils", + "version": "0.1.0", + "exports": "./mod.ts" +} diff --git a/utils/env.ts b/utils/env.ts new file mode 100644 index 0000000..c0cf447 --- /dev/null +++ b/utils/env.ts @@ -0,0 +1,5 @@ +export const getRequiredEnv = (name: string): string => { + const value = Deno.env.get(name); + if (!value) throw new Error(`${name} environment variable is required`); + return value; +}; diff --git a/utils/mod.ts b/utils/mod.ts new file mode 100644 index 0000000..93457f0 --- /dev/null +++ b/utils/mod.ts @@ -0,0 +1,4 @@ +export * from "./env.ts"; +export * from "./run.ts"; +export * from "./secret.ts"; +export * from "./validate_identifier.ts"; diff --git a/utils/run.ts b/utils/run.ts new file mode 100644 index 0000000..f3ce3d3 --- /dev/null +++ b/utils/run.ts @@ -0,0 +1,21 @@ +export const getStdout = async ( + cmd: string[] | string, + options: Deno.CommandOptions = {}, +): Promise => { + const [exec, ...args] = (typeof cmd === "string") ? cmd.split(" ") : cmd; + const command = new Deno.Command(exec, { + args, + stdout: "piped", + stderr: "piped", + ...options, + }); + + const { code, stdout, stderr } = await command.output(); + + const stdoutText = new TextDecoder().decode(stdout); + const stderrText = new TextDecoder().decode(stderr); + + if (code !== 0) throw new Error(`Command failed: ${cmd}\n${stderrText}`); + + return stdoutText; +}; diff --git a/utils/secret.ts b/utils/secret.ts new file mode 100644 index 0000000..9847aa6 --- /dev/null +++ b/utils/secret.ts @@ -0,0 +1,46 @@ +import { getRequiredEnv, getStdout } from "./mod.ts"; + +export class BitwardenSession { + private readonly sessionInitializer: Promise; + + constructor(server = getRequiredEnv("BW_SERVER")) { + ["BW_CLIENTID", "BW_CLIENTSECRET"].forEach(getRequiredEnv); + + this.sessionInitializer = getStdout( + `bw config server ${server} --quiet`, + ).then(() => getStdout(`bw login --apikey --quiet`)) + .then(() => getStdout(`bw unlock --passwordenv BW_PASSWORD --raw`)) + .then((session) => session.trim()); + } + + public async getItem( + secretName: string, + ): Promise { + return await this.sessionInitializer.then((session) => + getStdout(`bw list items`, { + env: { + BW_SESSION: session, + }, + }) + ).then((items) => JSON.parse(items)).then((items) => + items.find(({ name }: { name: string }) => name === secretName) + ); + } + + async close(): Promise { + return await this.sessionInitializer.then((session) => + getStdout(`bw lock`, { env: { BW_SESSION: session } }) + ).then(() => {}); + } +} + +export type LoginItem = { + login: { + username: string; + password: string; + }; +}; + +export type SecureNote = { + notes: string; +}; diff --git a/utils/validate_identifier.ts b/utils/validate_identifier.ts new file mode 100644 index 0000000..c8a5213 --- /dev/null +++ b/utils/validate_identifier.ts @@ -0,0 +1,3 @@ +export const validateIdentifier = (token: string) => { + return (/^[a-zA-Z0-9_\-:. ]+$/).test(token) && !token.includes(".."); +}; -- cgit v1.2.3-70-g09d2