#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const { program } = require('commander'); // Safe Sender for Feishu Cards // Automatically handles temp file creation to prevent shell escaping issues. program .requiredOption('-t, --target ', 'Target User/Chat ID') .requiredOption('-x, --text ', 'Markdown content (will be saved to temp file)') .option('--title ', 'Card Title') .option('--color ', 'Header Color', 'blue'); program.parse(process.argv); const options = program.opts(); const tempDir = path.resolve(__dirname, '../../temp'); if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true }); const tempFile = path.join(tempDir, `msg_${Date.now()}_${Math.random().toString(36).substring(7)}.md`); try { // 1. Write content to temp file safely (Node.js writeFileSync avoids shell parsing of content) fs.writeFileSync(tempFile, options.text, 'utf8'); console.log(`[SafeSend] Written content to ${tempFile}`); // 2. Construct command for the real sender // Note: We use the absolute path to send.js const senderScript = path.resolve(__dirname, 'send.js'); // Build arguments array for spawn/exec // We construct the command string carefully. // Since we are invoking via execSync, we still need to quote arguments, // BUT the dangerous content is now inside a file, so we only quote the filename. let cmd = `node "${senderScript}" --target "${options.target}" --text-file "${tempFile}" --color "${options.color}"`; if (options.title) cmd += ` --title "${options.title}"`; console.log(`[SafeSend] Executing: ${cmd}`); execSync(cmd, { stdio: 'inherit' }); } catch (e) { console.error(`[SafeSend] Error: ${e.message}`); process.exit(1); } finally { // 3. Cleanup try { if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile); console.log(`[SafeSend] Cleaned up ${tempFile}`); } catch (e) {} }