- Updated environment variable names from JERAPAH_FLOW_* to JFLOW_* for consistency and clarity. - Adjusted database path handling in knexfile.js to reflect the new variable naming. - Modified logging and retention days configurations to use the new variable names. - Ensured that the JWT secret and secrets key checks are aligned with the new naming convention. - Updated workflow test storage prefix to match the new project branding. This refactor enhances the clarity of environment variable usage and aligns with the project's rebranding efforts.
74 lines
1.9 KiB
JavaScript
74 lines
1.9 KiB
JavaScript
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "node:crypto";
|
|
|
|
const DEV_DEFAULT = "jflow-dev-secrets-key";
|
|
const SCRYPT_SALT = Buffer.from("jflow-secrets-v1");
|
|
const KEY_LEN = 32;
|
|
const IV_LEN = 12;
|
|
const AUTH_TAG_LEN = 16;
|
|
|
|
/**
|
|
* @returns {string}
|
|
*/
|
|
export function resolveSecretsKeyMaterial() {
|
|
const raw =
|
|
process.env.JFLOW_SECRETS_KEY ??
|
|
(process.env.NODE_ENV === "production" ? "" : DEV_DEFAULT);
|
|
if (!raw) {
|
|
throw new Error("JFLOW_SECRETS_KEY is required in production");
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
/** @type {Buffer | null} */
|
|
let cachedKey = null;
|
|
|
|
/**
|
|
* @returns {Buffer}
|
|
*/
|
|
export function getMasterKey() {
|
|
if (cachedKey) return cachedKey;
|
|
const raw = resolveSecretsKeyMaterial();
|
|
cachedKey = /^[0-9a-fA-F]{64}$/.test(raw)
|
|
? Buffer.from(raw, "hex")
|
|
: scryptSync(raw, SCRYPT_SALT, KEY_LEN);
|
|
return cachedKey;
|
|
}
|
|
|
|
/**
|
|
* @param {string} plaintext
|
|
* @returns {{ ciphertext: string, iv: string, authTag: string }}
|
|
*/
|
|
export function encryptSecret(plaintext) {
|
|
const iv = randomBytes(IV_LEN);
|
|
const cipher = createCipheriv("aes-256-gcm", getMasterKey(), iv, {
|
|
authTagLength: AUTH_TAG_LEN,
|
|
});
|
|
const encrypted = Buffer.concat([
|
|
cipher.update(plaintext, "utf8"),
|
|
cipher.final(),
|
|
]);
|
|
return {
|
|
ciphertext: encrypted.toString("base64"),
|
|
iv: iv.toString("base64"),
|
|
authTag: cipher.getAuthTag().toString("base64"),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {{ ciphertext: string, iv: string, authTag: string }} row
|
|
* @returns {string}
|
|
*/
|
|
export function decryptSecret(row) {
|
|
const decipher = createDecipheriv(
|
|
"aes-256-gcm",
|
|
getMasterKey(),
|
|
Buffer.from(row.iv, "base64"),
|
|
{ authTagLength: AUTH_TAG_LEN },
|
|
);
|
|
decipher.setAuthTag(Buffer.from(row.authTag, "base64"));
|
|
return Buffer.concat([
|
|
decipher.update(Buffer.from(row.ciphertext, "base64")),
|
|
decipher.final(),
|
|
]).toString("utf8");
|
|
}
|