UNPKG

tops-bmad

Version:

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

99 lines (88 loc) 3.37 kB
import AdmZip from "adm-zip"; import fs from "fs-extra"; import path from "path"; import { fileURLToPath } from "url"; import { decryptFile } from "./encrypt.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const TEMP_DIR = path.join(process.cwd(), "bmad-temp"); const LOCAL_ZIP = path.join(__dirname, "..", "bmad-package.zip"); const DECRYPTED_ZIP = path.join(process.cwd(), "bmad-temp-decrypted.zip"); /** * Validates the secret key by attempting to decrypt the package * @param {string} secretKey - The secret key to validate * @returns {Promise<void>} */ export async function validateSecretKey(secretKey) { try { // Check if local zip file exists if (!await fs.pathExists(LOCAL_ZIP)) { throw new Error(`BMAD package not found at: ${LOCAL_ZIP}`); } // Attempt to decrypt to a temporary location await fs.ensureDir(TEMP_DIR); const TEST_DECRYPTED_ZIP = path.join(process.cwd(), "bmad-temp-test-decrypt.zip"); try { await decryptFile(LOCAL_ZIP, TEST_DECRYPTED_ZIP, secretKey); // Verify the decrypted file is a valid zip try { const zip = new AdmZip(TEST_DECRYPTED_ZIP); zip.getEntries(); // This will throw if not a valid zip } catch (error) { throw new Error("Invalid secret key. The decrypted file is not a valid zip."); } } finally { // Clean up test decrypted file if (await fs.pathExists(TEST_DECRYPTED_ZIP)) { await fs.remove(TEST_DECRYPTED_ZIP).catch(() => {}); } } } catch (error) { // Provide user-friendly error messages if (error.message.includes("Invalid secret key") || 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."); } throw error; } } /** * Extracts the local BMAD package * @param {string} secretKey - The secret key to decrypt the zip file * @returns {Promise<void>} */ export async function downloadBMAD(secretKey) { try { console.log("📦 Locating BMAD package..."); // Check if local zip file exists if (!await fs.pathExists(LOCAL_ZIP)) { throw new Error(`BMAD package not found at: ${LOCAL_ZIP}`); } console.log("🔓 Decrypting BMAD package..."); // Decrypt the zip file to a temporary location await fs.ensureDir(TEMP_DIR); await decryptFile(LOCAL_ZIP, DECRYPTED_ZIP, secretKey); console.log("📦 Extracting BMAD package..."); // Verify the decrypted file is a valid zip before extracting let zip; try { zip = new AdmZip(DECRYPTED_ZIP); zip.getEntries(); // This will throw if not a valid zip } catch (error) { throw new Error("Decrypted file is not a valid zip. The encryption key may be incorrect or the file may be corrupted."); } zip.extractAllTo(TEMP_DIR, true); // Clean up decrypted zip file for security if (await fs.pathExists(DECRYPTED_ZIP)) { await fs.remove(DECRYPTED_ZIP); } } catch (error) { // Clean up decrypted zip file on error if (await fs.pathExists(DECRYPTED_ZIP)) { await fs.remove(DECRYPTED_ZIP).catch(() => {}); } console.error("❌ Error downloading BMAD package:", error.message); throw error; } }