diff --git a/login_resources/generate-totp-standalone.mjs b/login_resources/generate-totp-standalone.mjs deleted file mode 100644 index caeb5dc..0000000 --- a/login_resources/generate-totp-standalone.mjs +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env node - -import { createHmac } from 'crypto'; - -/** - * Standalone TOTP generator that doesn't require external dependencies - * Based on RFC 6238 (TOTP: Time-Based One-Time Password Algorithm) - */ - -function parseArgs() { - const args = {}; - for (let i = 2; i < process.argv.length; i++) { - if (process.argv[i] === '--secret' && i + 1 < process.argv.length) { - args.secret = process.argv[i + 1]; - i++; // Skip the next argument since it's the value - } else if (process.argv[i] === '--help' || process.argv[i] === '-h') { - args.help = true; - } - } - return args; -} - -function showHelp() { - console.log(` -Usage: node generate-totp-standalone.mjs --secret - -Generate a Time-based One-Time Password (TOTP) from a secret key. -This standalone version doesn't require external dependencies. - -Options: - --secret The base32-encoded TOTP secret key (required) - --help, -h Show this help message - -Examples: - node generate-totp-standalone.mjs --secret "JBSWY3DPEHPK3PXP" - node generate-totp-standalone.mjs --secret "u4e2ewg3d6w7gya3p7plgkef6zgfzo23" - -Output: - A 6-digit TOTP code (e.g., 123456) -`); -} - -// Base32 decoding function -function base32Decode(encoded) { - const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; - const cleanInput = encoded.toUpperCase().replace(/[^A-Z2-7]/g, ''); - - if (cleanInput.length === 0) { - return Buffer.alloc(0); - } - - const output = []; - let bits = 0; - let value = 0; - - for (const char of cleanInput) { - const index = alphabet.indexOf(char); - if (index === -1) { - throw new Error(`Invalid base32 character: ${char}`); - } - - value = (value << 5) | index; - bits += 5; - - if (bits >= 8) { - output.push((value >>> (bits - 8)) & 255); - bits -= 8; - } - } - - return Buffer.from(output); -} - -// HOTP implementation (RFC 4226) -function generateHOTP(secret, counter, digits = 6) { - const key = base32Decode(secret); - - // Convert counter to 8-byte buffer (big-endian) - const counterBuffer = Buffer.alloc(8); - counterBuffer.writeBigUInt64BE(BigInt(counter)); - - // Generate HMAC-SHA1 - const hmac = createHmac('sha1', key); - hmac.update(counterBuffer); - const hash = hmac.digest(); - - // Dynamic truncation - const offset = hash[hash.length - 1] & 0x0f; - const code = ( - ((hash[offset] & 0x7f) << 24) | - ((hash[offset + 1] & 0xff) << 16) | - ((hash[offset + 2] & 0xff) << 8) | - (hash[offset + 3] & 0xff) - ); - - // Generate digits - const otp = (code % Math.pow(10, digits)).toString().padStart(digits, '0'); - return otp; -} - -// TOTP implementation (RFC 6238) -function generateTOTP(secret, timeStep = 30, digits = 6) { - const currentTime = Math.floor(Date.now() / 1000); - const counter = Math.floor(currentTime / timeStep); - return generateHOTP(secret, counter, digits); -} - -function main() { - const args = parseArgs(); - - if (args.help) { - showHelp(); - return; - } - - if (!args.secret) { - console.error('Error: --secret parameter is required'); - console.error('Use --help for usage information'); - process.exit(1); - } - - try { - const totpCode = generateTOTP(args.secret); - console.log(totpCode); - } catch (error) { - console.error(`Error: ${error.message}`); - process.exit(1); - } -} - -main(); \ No newline at end of file diff --git a/mcp-server/src/index.js b/mcp-server/src/index.js index 699f313..18e1fe0 100644 --- a/mcp-server/src/index.js +++ b/mcp-server/src/index.js @@ -28,16 +28,6 @@ export function createShannonHelperServer(targetDir) { }); } -/** - * Legacy export for backward compatibility - * @deprecated Use createShannonHelperServer(targetDir) instead - */ -export const shannonHelperServer = createSdkMcpServer({ - name: 'shannon-helper', - version: '1.0.0', - tools: [saveDeliverableTool, generateTotpTool], -}); - // Export tools for direct usage if needed export { saveDeliverableTool, generateTotpTool }; diff --git a/mcp-server/src/tools/index.js b/mcp-server/src/tools/index.js deleted file mode 100644 index 7d50008..0000000 --- a/mcp-server/src/tools/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * MCP Tools barrel export - */ - -export * from './save-deliverable.js'; -export * from './generate-totp.js'; diff --git a/mcp-server/src/utils/error-formatter.js b/mcp-server/src/utils/error-formatter.js index d811e51..0f5883c 100644 --- a/mcp-server/src/utils/error-formatter.js +++ b/mcp-server/src/utils/error-formatter.js @@ -31,24 +31,6 @@ export function createValidationError(message, retryable = true, context) { }; } -/** - * Create a file system error response - * - * @param {string} message - * @param {boolean} [retryable=false] - * @param {Record} [context] - * @returns {ErrorResponse} - */ -export function createFileSystemError(message, retryable = false, context) { - return { - status: 'error', - message, - errorType: 'FileSystemError', - retryable, - context, - }; -} - /** * Create a crypto error response * diff --git a/mcp-server/src/utils/index.js b/mcp-server/src/utils/index.js deleted file mode 100644 index cdf4357..0000000 --- a/mcp-server/src/utils/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Utilities barrel export - */ - -export * from './file-operations.js'; -export * from './error-formatter.js'; diff --git a/mcp-server/src/validation/index.js b/mcp-server/src/validation/index.js deleted file mode 100644 index adf3caa..0000000 --- a/mcp-server/src/validation/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Validation layer barrel export - */ - -export * from './queue-validator.js'; -export * from './totp-validator.js'; diff --git a/src/agent-status.js b/src/agent-status.js deleted file mode 100644 index aa3ba7f..0000000 --- a/src/agent-status.js +++ /dev/null @@ -1,309 +0,0 @@ -import chalk from 'chalk'; -import { path } from 'zx'; - -export class AgentStatusManager { - constructor(options = {}) { - this.mode = options.mode || 'parallel'; // 'parallel' or 'single' - this.activeStatuses = new Map(); - this.lastStatusLine = ''; - this.hiddenOperationCount = 0; - this.lastSummaryCount = 0; - this.summaryInterval = options.summaryInterval || 10; - this.showTodos = options.showTodos !== false; - - // Tools to completely hide in output - this.suppressedTools = new Set([ - 'Read', 'Write', 'Edit', 'MultiEdit', - 'Grep', 'Glob', 'LS' - ]); - - // Tools that might be noisy bash commands to hide - this.hiddenBashCommands = new Set([ - 'pwd', 'echo', 'ls', 'cd' - ]); - } - - /** - * Update status for an agent based on its current turn data - */ - updateAgentStatus(agentName, turnData) { - if (this.mode === 'single') { - this.handleSingleAgentOutput(agentName, turnData); - } else { - const status = this.extractMeaningfulStatus(turnData); - if (status) { - this.activeStatuses.set(agentName, status); - this.redrawStatusLine(); - } - } - } - - /** - * Handle output for single agent mode with clean formatting - */ - handleSingleAgentOutput(agentName, turnData) { - const toolUse = turnData.tool_use; - const text = turnData.assistant_text; - const turnCount = turnData.turnCount; - - // Check if this is a tool we should hide - if (toolUse && this.shouldHideTool(toolUse)) { - this.hiddenOperationCount++; - - // Show summary every N hidden operations - if (this.hiddenOperationCount - this.lastSummaryCount >= this.summaryInterval) { - const operationCount = this.hiddenOperationCount - this.lastSummaryCount; - console.log(chalk.gray(` [${operationCount} file operations...]`)); - this.lastSummaryCount = this.hiddenOperationCount; - } - return; - } - - // Format and show meaningful tools - if (toolUse) { - const formatted = this.formatMeaningfulTool(toolUse); - if (formatted) { - console.log(`๐Ÿค– ${formatted}`); - return; - } - } - - // For turns without tool use, just ignore them silently - // These are planning/thinking turns that don't need any output - } - - /** - * Check if a tool should be hidden from output - */ - shouldHideTool(toolUse) { - const toolName = toolUse.name; - - // Always hide these tools - if (this.suppressedTools.has(toolName)) { - return true; - } - - // Hide TodoWrite unless we're configured to show todos - if (toolName === 'TodoWrite' && !this.showTodos) { - return true; - } - - // Hide simple bash commands - if (toolName === 'Bash') { - const command = toolUse.input?.command || ''; - const simpleCommand = command.split(' ')[0]; - return this.hiddenBashCommands.has(simpleCommand); - } - - return false; - } - - /** - * Format meaningful tools for single agent display - */ - formatMeaningfulTool(toolUse) { - const toolName = toolUse.name; - const input = toolUse.input || {}; - - switch (toolName) { - case 'Task': - const description = input.description || 'analysis agent'; - return `๐Ÿš€ Launching ${description}`; - - case 'TodoWrite': - if (this.showTodos) { - return this.formatTodoUpdate(input); - } - return null; - - case 'WebFetch': - const domain = this.extractDomain(input.url || ''); - return `๐ŸŒ Fetching ${domain}`; - - case 'Bash': - // Only show meaningful bash commands - const command = input.command || ''; - if (command.includes('nmap') || command.includes('subfinder') || command.includes('whatweb')) { - const tool = command.split(' ')[0]; - return `๐Ÿ” Running ${tool}`; - } - return null; - - // Browser tools (keep existing formatting) - default: - if (toolName.startsWith('mcp__playwright__browser_')) { - return this.extractBrowserAction(toolUse); - } - } - - return null; - } - - /** - * Format TodoWrite updates for display - */ - formatTodoUpdate(input) { - if (!input.todos || !Array.isArray(input.todos)) { - return null; - } - - const todos = input.todos; - const inProgress = todos.filter(t => t.status === 'in_progress'); - const completed = todos.filter(t => t.status === 'completed'); - - if (completed.length > 0) { - const recent = completed[completed.length - 1]; - return `โœ… ${recent.content.slice(0, 50)}${recent.content.length > 50 ? '...' : ''}`; - } - - if (inProgress.length > 0) { - const current = inProgress[0]; - return `๐Ÿ”„ ${current.content.slice(0, 50)}${current.content.length > 50 ? '...' : ''}`; - } - - return null; - } - - /** - * Extract meaningful status from turn data, suppressing internal operations - */ - extractMeaningfulStatus(turnData) { - // Check for tool use first - if (turnData.tool_use?.name) { - // Suppress internal operations completely - if (this.suppressedTools.has(turnData.tool_use.name)) { - return null; - } - - // Show browser testing actions - if (turnData.tool_use.name.startsWith('mcp__playwright__browser_')) { - return this.extractBrowserAction(turnData.tool_use); - } - - // Show Task agent launches - if (turnData.tool_use.name === 'Task') { - const description = turnData.tool_use.input?.description || 'analysis'; - return `๐Ÿš€ ${description.slice(0, 40)}`; - } - } - - // Parse assistant text for progress milestones - if (turnData.assistant_text) { - return this.extractProgressFromText(turnData.assistant_text); - } - - return null; // Suppress everything else - } - - /** - * Extract browser action details - */ - extractBrowserAction(toolUse) { - const actionType = toolUse.name.split('_').pop(); - - switch (actionType) { - case 'navigate': - const url = toolUse.input?.url || ''; - const domain = this.extractDomain(url); - return `๐ŸŒ Testing ${domain}`; - - case 'click': - const element = toolUse.input?.element || 'element'; - return `๐Ÿ–ฑ๏ธ Clicking ${element.slice(0, 20)}`; - - case 'fill': - case 'form': - return `๐Ÿ“ Testing form inputs`; - - case 'snapshot': - return `๐Ÿ“ธ Capturing page state`; - - case 'type': - return `โŒจ๏ธ Testing input fields`; - - default: - return `๐ŸŒ Browser: ${actionType}`; - } - } - - /** - * Extract meaningful progress from assistant text (single-agent mode only) - */ - extractProgressFromText(text) { - // Only extract progress for single agents, not parallel ones - if (this.mode !== 'single') { - return null; - } - - // For single agents, be very conservative about what we show - // Most progress should come from tool formatting, not text parsing - return null; - } - - /** - * Extract domain from URL for display - */ - extractDomain(url) { - try { - const urlObj = new URL(url); - return urlObj.hostname || url.slice(0, 30); - } catch { - return url.slice(0, 30); - } - } - - /** - * Redraw the status line showing all active agents - */ - redrawStatusLine() { - // Clear previous line - if (this.lastStatusLine) { - process.stdout.write('\r' + ' '.repeat(this.lastStatusLine.length) + '\r'); - } - - // Build new status line - const statusEntries = Array.from(this.activeStatuses.entries()) - .map(([agent, status]) => `[${chalk.cyan(agent)}] ${status}`) - .join(' | '); - - if (statusEntries) { - process.stdout.write(statusEntries); - this.lastStatusLine = statusEntries.replace(/\u001b\[[0-9;]*m/g, ''); // Remove ANSI codes for length calc - } - } - - /** - * Clear status for a specific agent - */ - clearAgentStatus(agentName) { - this.activeStatuses.delete(agentName); - this.redrawStatusLine(); - } - - /** - * Clear all statuses and finish the status line - */ - finishStatusLine() { - if (this.lastStatusLine) { - process.stdout.write('\n'); // Move to next line - this.lastStatusLine = ''; - this.activeStatuses.clear(); - } - } - - /** - * Parse JSON tool use from message content - */ - parseToolUse(content) { - try { - // Look for JSON tool use patterns - const jsonMatch = content.match(/\{"type":"tool_use".*?\}/s); - if (jsonMatch) { - return JSON.parse(jsonMatch[0]); - } - } catch (error) { - // Ignore parsing errors - } - return null; - } -} \ No newline at end of file diff --git a/src/checkpoint-manager.js b/src/checkpoint-manager.js index f2783af..0685ad4 100644 --- a/src/checkpoint-manager.js +++ b/src/checkpoint-manager.js @@ -76,7 +76,7 @@ const rollbackGitToCommit = async (targetRepo, commitHash) => { }; // Run a single agent with retry logic and checkpointing -export const runSingleAgent = async (agentName, session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt, allowRerun = false, skipWorkspaceClean = false) => { +const runSingleAgent = async (agentName, session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt, allowRerun = false, skipWorkspaceClean = false) => { // Validate agent first const agent = validateAgent(agentName); @@ -299,7 +299,7 @@ export const runSingleAgent = async (agentName, session, pipelineTestingMode, ru }; // Run multiple agents in sequence -export const runAgentRange = async (startAgent, endAgent, session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt) => { +const runAgentRange = async (startAgent, endAgent, session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt) => { const agents = validateAgentRange(startAgent, endAgent); console.log(chalk.cyan(`\n๐Ÿ”„ Running agent range: ${startAgent} to ${endAgent} (${agents.length} agents)`)); @@ -323,7 +323,7 @@ export const runAgentRange = async (startAgent, endAgent, session, pipelineTesti }; // Run vulnerability agents in parallel -export const runParallelVuln = async (session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt) => { +const runParallelVuln = async (session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt) => { const vulnAgents = ['injection-vuln', 'xss-vuln', 'auth-vuln', 'ssrf-vuln', 'authz-vuln']; const activeAgents = vulnAgents.filter(agent => !session.completedAgents.includes(agent)); @@ -421,7 +421,7 @@ export const runParallelVuln = async (session, pipelineTestingMode, runClaudePro }; // Run exploitation agents in parallel -export const runParallelExploit = async (session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt) => { +const runParallelExploit = async (session, pipelineTestingMode, runClaudePromptWithRetry, loadPrompt) => { const exploitAgents = ['injection-exploit', 'xss-exploit', 'auth-exploit', 'ssrf-exploit', 'authz-exploit']; // Get fresh session data to ensure we have the latest vulnerability analysis results diff --git a/src/utils/git-manager.js b/src/utils/git-manager.js index 83617a8..32ef3d8 100644 --- a/src/utils/git-manager.js +++ b/src/utils/git-manager.js @@ -72,7 +72,7 @@ export const executeGitCommandWithRetry = async (commandArgs, sourceDir, descrip }; // Pure functions for Git workspace management -export const cleanWorkspace = async (sourceDir, reason = 'clean start') => { +const cleanWorkspace = async (sourceDir, reason = 'clean start') => { console.log(chalk.blue(` ๐Ÿงน Cleaning workspace for ${reason}`)); try { // Check for uncommitted changes