UNPKG

tops-bmad

Version:

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

165 lines (138 loc) โ€ข 5.95 kB
#!/usr/bin/env node import inquirer from "inquirer"; import fs from "fs-extra"; import path from "path"; import { fileURLToPath } from "url"; import { decryptFile } from "../lib/encrypt.js"; import AdmZip from "adm-zip"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PROJECT_ROOT = path.join(__dirname, ".."); const ENCRYPTED_ZIP = path.join(PROJECT_ROOT, "bmad-package.zip"); const TEST_DECRYPTED_ZIP = path.join(PROJECT_ROOT, "test-decrypted.zip"); const TEST_EXTRACT_DIR = path.join(PROJECT_ROOT, "test-extract"); console.log("\n๐Ÿ” BMAD Package Decryption Test Tool\n"); (async () => { try { // Check if encrypted zip exists if (!await fs.pathExists(ENCRYPTED_ZIP)) { console.error(`โŒ Error: Encrypted zip file not found at: ${ENCRYPTED_ZIP}`); console.log("๐Ÿ’ก Make sure you have encrypted the package first using: npm run encrypt-zip"); process.exit(1); } const fileStats = await fs.stat(ENCRYPTED_ZIP); console.log(`๐Ÿ“ฆ Found encrypted file: ${ENCRYPTED_ZIP}`); console.log(` Size: ${(fileStats.size / 1024).toFixed(2)} KB\n`); const answers = await inquirer.prompt([ { name: "secretKey", type: "password", message: "Enter the secret key to test decryption:", mask: "*", validate: v => v.trim() !== "" || "Secret key cannot be empty!" } ]); console.log("\n๐Ÿ”“ Attempting to decrypt..."); // Decrypt the file await decryptFile(ENCRYPTED_ZIP, TEST_DECRYPTED_ZIP, answers.secretKey); console.log("โœ… Decryption successful!"); // Verify the decrypted file exists and has content const decryptedStats = await fs.stat(TEST_DECRYPTED_ZIP); console.log(`๐Ÿ“ฆ Decrypted file size: ${(decryptedStats.size / 1024).toFixed(2)} KB`); // Try to open it as a zip file to verify it's valid console.log("\n๐Ÿ” Verifying zip file integrity..."); try { const zip = new AdmZip(TEST_DECRYPTED_ZIP); const zipEntries = zip.getEntries(); console.log(`โœ… Zip file is valid!`); console.log(` Number of entries: ${zipEntries.length}`); // Show first few entries if (zipEntries.length > 0) { console.log("\n๐Ÿ“„ Sample entries:"); zipEntries.slice(0, 5).forEach((entry, index) => { const size = entry.header.size > 0 ? `${(entry.header.size / 1024).toFixed(2)} KB` : "dir"; console.log(` ${index + 1}. ${entry.entryName} (${size})`); }); if (zipEntries.length > 5) { console.log(` ... and ${zipEntries.length - 5} more entries`); } } // Optionally extract to verify contents const { extract } = await inquirer.prompt([ { name: "extract", type: "confirm", message: "\nDo you want to extract the decrypted zip to verify contents?", default: false } ]); if (extract) { console.log("\n๐Ÿ“‚ Extracting zip file..."); await fs.ensureDir(TEST_EXTRACT_DIR); zip.extractAllTo(TEST_EXTRACT_DIR, true); // List extracted contents const extractContents = await fs.readdir(TEST_EXTRACT_DIR); console.log(`โœ… Extraction successful!`); console.log(` Extracted ${extractContents.length} items to: ${TEST_EXTRACT_DIR}`); // Show directory structure console.log("\n๐Ÿ“ Directory structure:"); async function listDir(dirPath, prefix = "") { const items = await fs.readdir(dirPath); for (let i = 0; i < items.length; i++) { const item = items[i]; const itemPath = path.join(dirPath, item); const stats = await fs.stat(itemPath); const isLast = i === items.length - 1; const currentPrefix = isLast ? "โ””โ”€โ”€ " : "โ”œโ”€โ”€ "; console.log(`${prefix}${currentPrefix}${item}${stats.isDirectory() ? "/" : ""}`); if (stats.isDirectory() && items.length <= 10) { await listDir(itemPath, prefix + (isLast ? " " : "โ”‚ ")); } } } await listDir(TEST_EXTRACT_DIR); } } catch (zipError) { console.error("โŒ Error: Decrypted file is not a valid zip file!"); console.error(` ${zipError.message}`); console.log("\nโš ๏ธ This might indicate:"); console.log(" - Wrong secret key was used"); console.log(" - File was corrupted during encryption/decryption"); throw zipError; } console.log("\nโœจ Decryption test completed successfully!"); console.log("\n๐Ÿงน Cleaning up test files..."); // Clean up test files if (await fs.pathExists(TEST_DECRYPTED_ZIP)) { await fs.remove(TEST_DECRYPTED_ZIP); console.log(" โœ“ Removed test-decrypted.zip"); } if (await fs.pathExists(TEST_EXTRACT_DIR)) { const { keepExtracted } = await inquirer.prompt([ { name: "keepExtracted", type: "confirm", message: "Keep extracted test files?", default: false } ]); if (!keepExtracted) { await fs.remove(TEST_EXTRACT_DIR); console.log(" โœ“ Removed test-extract directory"); } else { console.log(` โ„น๏ธ Test files kept at: ${TEST_EXTRACT_DIR}`); } } console.log("\nโœ… All tests passed! Your encryption/decryption is working correctly."); } catch (error) { // Clean up on error if (await fs.pathExists(TEST_DECRYPTED_ZIP)) { await fs.remove(TEST_DECRYPTED_ZIP).catch(() => {}); } if (await fs.pathExists(TEST_EXTRACT_DIR)) { await fs.remove(TEST_EXTRACT_DIR).catch(() => {}).catch(() => {}); } console.error("\nโŒ Decryption test failed:", error.message); process.exit(1); } })();