UNPKG

tops-bmad

Version:

CLI tool to install BMAD workflow files into any project with integrated Shai-Hulud 2.0 security scanning

84 lines (72 loc) 2.9 kB
import crypto from "crypto"; import fs from "fs-extra"; const ALGORITHM = "aes-256-cbc"; const IV_LENGTH = 16; // For AES, this is always 16 /** * Derives a key from a password using PBKDF2 * @param {string} password - The plain text password * @returns {Buffer} - The derived key */ function deriveKey(password) { // Use a fixed salt for consistency (in production, you might want to store salt with encrypted data) const salt = crypto.createHash("sha256").update("bmad-cli-salt").digest(); return crypto.pbkdf2Sync(password, salt, 100000, 32, "sha256"); } /** * Encrypts a file using AES-256-CBC * @param {string} inputPath - Path to the file to encrypt * @param {string} outputPath - Path where encrypted file will be saved * @param {string} password - Plain text password * @returns {Promise<void>} */ export async function encryptFile(inputPath, outputPath, password) { try { const key = deriveKey(password); const iv = crypto.randomBytes(IV_LENGTH); const cipher = crypto.createCipheriv(ALGORITHM, key, iv); const input = await fs.readFile(inputPath); const encrypted = Buffer.concat([ iv, // Prepend IV to the encrypted data cipher.update(input), cipher.final() ]); await fs.writeFile(outputPath, encrypted); console.log(`✅ File encrypted successfully: ${outputPath}`); } catch (error) { console.error("❌ Error encrypting file:", error.message); throw error; } } /** * Decrypts a file using AES-256-CBC * @param {string} inputPath - Path to the encrypted file * @param {string} outputPath - Path where decrypted file will be saved * @param {string} password - Plain text password * @returns {Promise<void>} */ export async function decryptFile(inputPath, outputPath, password) { try { const key = deriveKey(password); const encrypted = await fs.readFile(inputPath); // Extract IV from the beginning of the encrypted data const iv = encrypted.slice(0, IV_LENGTH); const encryptedData = encrypted.slice(IV_LENGTH); const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); const decrypted = Buffer.concat([ decipher.update(encryptedData), decipher.final() ]); await fs.writeFile(outputPath, decrypted); } catch (error) { // If decryption fails, it's likely due to wrong password if (error.message.includes("bad decrypt") || error.message.includes("wrong final block length")) { throw new Error("Invalid secret key. Please check your password and try again."); } // If the file is not encrypted or corrupted if (error.message.includes("Invalid") || error.message.includes("unsupported")) { throw new Error("Failed to decrypt file. The file may be corrupted or encrypted with a different key."); } console.error("❌ Error decrypting file:", error.message); throw error; } }