UNPKG

@larskarbo/gitops-secrets

Version:

SecretOps workflow for bundling encrypted secrets into your deployments to safely decrypt at runtime.

123 lines (122 loc) 5.53 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = require("fs"); const path = require("path"); const secrets_1 = require("./secrets"); const SECRETS_FOLDER = path.join(__dirname, "../.secrets"); const DEFAULT_JS_PATH = path.join(SECRETS_FOLDER, ".secrets.enc.js"); const DEFAULT_FILE_PATH = path.join(SECRETS_FOLDER, ".secrets.enc.json"); // eslint-disable-next-line security/detect-non-literal-fs-filename if (!(0, fs_1.existsSync)(SECRETS_FOLDER)) { // eslint-disable-next-line security/detect-non-literal-fs-filename (0, fs_1.mkdirSync)(SECRETS_FOLDER); } /** * Encapsulate encrypted secrets in a JS module for easy runtime access. * Use {options.path} to output module locally for when package level storage or non-literal imports are disallowed. * Use {options.cipherTextOnly} to limit the JS file to only exporting `CIPHER_TEXT` * @param {<Record<string, any>} payload * @param {{path: string, cipherTextOnly: boolean}} [options={path: null, cipherTextOnly: false}] */ function build(payload, options = { path: null, cipherTextOnly: false, typescript: false }) { return __awaiter(this, void 0, void 0, function* () { const cipherText = secrets_1.default.encrypt(payload); const filePath = options.path ? path.resolve(options.path) : DEFAULT_JS_PATH; const packageType = process.env.npm_package_type === "module" ? "esm" : "cjs"; const format = filePath === DEFAULT_JS_PATH ? "cjs" : packageType; let lines = ["/* eslint-disable */", "// This file was auto-generated by gitops-secrets"]; if (options.typescript) { lines = [ ...lines, `export const CIPHER_TEXT = "${cipherText}";`, ]; } else if (format === "esm") { if (!options.cipherTextOnly) { lines = [ ...lines, `import secrets from "gitops-secrets/no-fs";`, `const CIPHER_TEXT = "${cipherText}";`, "const loadSecrets = () => secrets.loadSecretsFromCipher(CIPHER_TEXT);", "export { CIPHER_TEXT, loadSecrets };", ]; } else { lines = [...lines, `const CIPHER_TEXT = "${cipherText}";`, "export { CIPHER_TEXT };"]; } } else if (format === "cjs") { if (!options.cipherTextOnly) { lines = [ ...lines, `const secrets = require("gitops-secrets/no-fs");`, `const CIPHER_TEXT = "${cipherText}";`, "const loadSecrets = () => secrets.loadSecretsFromCipher(CIPHER_TEXT);", "module.Object = { CIPHER_TEXT, loadSecrets };", ]; } else { lines = [...lines, `const CIPHER_TEXT = "${cipherText}";`, "module.Object = { CIPHER_TEXT };"]; } } writeFile(filePath, lines.join("\n")); }); } /** * Encrypt JSON-serializable payload to a static file * @param {Record<string,any>} payload * @param {{path: string}} [options={ path: null }] */ function encryptToFile(payload, options = { path: null }) { const cipherText = secrets_1.default.encrypt(payload); const filePath = options.path ? path.resolve(options.path) : DEFAULT_FILE_PATH; console.log('filePath: ', filePath); writeFile(filePath, cipherText); } /** * Decrypt JSON payload to object with option to merge with process.env * @param {string} filePath * @returns */ function decryptFromFile(filePath) { filePath = filePath ? path.resolve(filePath) : DEFAULT_FILE_PATH; try { // eslint-disable-next-line security/detect-non-literal-fs-filename const payload = secrets_1.default.decrypt((0, fs_1.readFileSync)(filePath, { encoding: "utf-8" })); return Object.assign(Object.assign({}, payload), { populateEnv: () => secrets_1.default.populateEnv(payload) }); } catch (error) { throw new Error(`Unable to read secrets from ${filePath}: ${error}`); } } function writeFile(filePath, fileContents) { try { // eslint-disable-next-line security/detect-non-literal-fs-filename (0, fs_1.writeFileSync)(filePath, fileContents, { encoding: "utf-8" }); } catch (error) { throw new Error(`Unable to write secrets to ${filePath}: ${error}`); } } function loadSecrets() { // eslint-disable-next-line security/detect-non-literal-require return require(DEFAULT_JS_PATH).loadSecrets(); } const exportObject = { build: build, encryptToFile: encryptToFile, decryptFromFile: decryptFromFile, loadSecrets: loadSecrets, DEFAULT_JS_PATH: DEFAULT_JS_PATH, DEFAULT_FILE_PATH: DEFAULT_FILE_PATH, }; exports.default = exportObject;