claude-api.js
1 /** 2 * Claude API Utility (DEPRECATED) 3 * 4 * ⚠️ DEPRECATED: This module is deprecated in favor of agent-claude-api.js 5 * which uses the Anthropic API directly with Claude Max subscription. 6 * 7 * Migration guide: 8 * - Use agent-claude-api.js instead 9 * - All agents now use Anthropic API (not OpenRouter) 10 * - OpenRouter is only used by pipeline stages (scoring, proposals, enrichment) 11 * 12 * @deprecated Use agent-claude-api.js instead 13 */ 14 15 import Logger from '../../utils/logger.js'; 16 17 const logger = new Logger('ClaudeAPI'); 18 19 /** 20 * Call Claude API via OpenRouter 21 * 22 * @param {Object} options - API call options 23 * @param {string} options.prompt - The prompt to send to Claude 24 * @param {string} [options.model='anthropic/claude-sonnet-4-6'] - Model to use 25 * @param {number} [options.maxTokens=4000] - Maximum tokens in response 26 * @param {number} [options.temperature=0.3] - Sampling temperature (0-1) 27 * @param {string} [options.systemPrompt] - System prompt (optional) 28 * @returns {Promise<string>} - Claude's response 29 */ 30 export async function callClaude({ 31 prompt, 32 model = 'anthropic/claude-sonnet-4-6', 33 maxTokens = 4000, 34 temperature = 0.3, 35 systemPrompt = null, 36 }) { 37 if (!prompt) { 38 throw new Error('Prompt is required'); 39 } 40 41 if (!process.env.OPENROUTER_API_KEY) { 42 throw new Error('OPENROUTER_API_KEY not configured'); 43 } 44 45 const messages = [{ role: 'user', content: prompt }]; 46 47 const requestBody = { 48 model, 49 messages, 50 max_tokens: maxTokens, 51 temperature, 52 }; 53 54 if (systemPrompt) { 55 requestBody.system = systemPrompt; 56 } 57 58 try { 59 const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { 60 method: 'POST', 61 headers: { 62 Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`, 63 'Content-Type': 'application/json', 64 'HTTP-Referer': 65 process.env.OPENROUTER_REFERER || 'https://github.com/jasonpaulneu/333Method', 66 'X-Title': process.env.OPENROUTER_TITLE || '333 Method Agent System', 67 }, 68 body: JSON.stringify(requestBody), 69 }); 70 71 if (!response.ok) { 72 const errorText = await response.text(); 73 throw new Error(`OpenRouter API error: ${response.status} - ${errorText}`); 74 } 75 76 const data = await response.json(); 77 78 if (!data.choices || data.choices.length === 0) { 79 throw new Error('No response from Claude API'); 80 } 81 82 return data.choices[0].message.content; 83 } catch (error) { 84 logger.error('Claude API call failed', { error: error.message }); 85 throw error; 86 } 87 } 88 89 /** 90 * Analyze code for security vulnerabilities using Claude 91 * 92 * @param {string} code - Code to analyze 93 * @param {string} [focusArea] - Specific security focus (sql_injection, xss, command_injection, secrets) 94 * @param {string} [fileName] - File name for context 95 * @returns {Promise<Object>} - Analysis result with findings 96 */ 97 export async function analyzeCodeSecurity(code, focusArea = null, fileName = null) { 98 const focusInstruction = focusArea 99 ? `Focus specifically on: ${focusArea.replace(/_/g, ' ')}` 100 : 'Check all security aspects'; 101 102 const fileContext = fileName ? `File: ${fileName}\n\n` : ''; 103 104 const prompt = `Analyze this code for security vulnerabilities. 105 106 ${focusInstruction} 107 108 ${fileContext}Code: 109 \`\`\` 110 ${code} 111 \`\`\` 112 113 Provide analysis in JSON format: 114 { 115 "findings": [ 116 { 117 "type": "vulnerability_type", 118 "severity": "critical|high|medium|low", 119 "line": line_number, 120 "description": "detailed description", 121 "recommendation": "how to fix", 122 "cwe_id": "CWE-XXX (if applicable)" 123 } 124 ], 125 "summary": "overall security assessment" 126 }`; 127 128 const systemPrompt = `You are a security expert analyzing code for vulnerabilities. 129 Be thorough but practical. Focus on real exploitable issues, not theoretical concerns. 130 Classify severity accurately: 131 - critical: Direct exploitable vulnerability with high impact 132 - high: Exploitable with some conditions, or severe impact 133 - medium: Security weakness requiring specific conditions 134 - low: Minor issue or best practice violation`; 135 136 const response = await callClaude({ 137 prompt, 138 systemPrompt, 139 temperature: 0.2, 140 maxTokens: 3000, 141 }); 142 143 try { 144 // Extract JSON from response (handle markdown code blocks) 145 const jsonMatch = 146 response.match(/```json\s*([\s\S]*?)\s*```/) || response.match(/```\s*([\s\S]*?)\s*```/); 147 const jsonStr = jsonMatch ? jsonMatch[1] : response; 148 149 return JSON.parse(jsonStr); 150 } catch (error) { 151 logger.error('Failed to parse Claude security analysis', { error: error.message, response }); 152 throw new Error(`Failed to parse security analysis: ${error.message}`); 153 } 154 } 155 156 /** 157 * Generate a secure fix for a vulnerability using Claude 158 * 159 * @param {Object} options - Fix generation options 160 * @param {string} options.code - Original code with vulnerability 161 * @param {Object} options.finding - Security finding object 162 * @param {string} [options.fileName] - File name for context 163 * @returns {Promise<Object>} - Fix result with old_string, new_string, explanation 164 */ 165 export async function generateSecureFix({ code, finding, fileName = null }) { 166 const fileContext = fileName ? `File: ${fileName}\n\n` : ''; 167 168 const prompt = `Fix this security vulnerability in the code. 169 170 Vulnerability: 171 - Type: ${finding.type} 172 - Severity: ${finding.severity} 173 - Line: ${finding.line || 'unknown'} 174 - Description: ${finding.description} 175 - Recommendation: ${finding.recommendation} 176 177 ${fileContext}Original Code: 178 \`\`\` 179 ${code} 180 \`\`\` 181 182 Provide the fix in JSON format: 183 { 184 "old_string": "exact string to replace (must match code exactly)", 185 "new_string": "secure replacement code", 186 "explanation": "why this fix is secure", 187 "testing_notes": "how to test the fix" 188 } 189 190 IMPORTANT: 191 - old_string must match the vulnerable code EXACTLY (including whitespace, indentation) 192 - new_string should maintain the same functionality but be secure 193 - Preserve code style and formatting 194 - Keep the fix minimal - only change what's necessary`; 195 196 const systemPrompt = `You are a security expert who fixes code vulnerabilities. 197 Provide practical, secure fixes that maintain functionality. 198 Ensure old_string matches the original code EXACTLY. 199 Follow secure coding best practices for the language.`; 200 201 const response = await callClaude({ 202 prompt, 203 systemPrompt, 204 temperature: 0.2, 205 maxTokens: 2000, 206 }); 207 208 try { 209 // Extract JSON from response 210 const jsonMatch = 211 response.match(/```json\s*([\s\S]*?)\s*```/) || response.match(/```\s*([\s\S]*?)\s*```/); 212 const jsonStr = jsonMatch ? jsonMatch[1] : response; 213 214 const fix = JSON.parse(jsonStr); 215 216 // Validate fix has required fields 217 if (!fix.old_string || !fix.new_string) { 218 throw new Error('Fix missing old_string or new_string'); 219 } 220 221 return fix; 222 } catch (error) { 223 logger.error('Failed to parse Claude fix generation', { error: error.message, response }); 224 throw new Error(`Failed to generate secure fix: ${error.message}`); 225 } 226 } 227 228 /** 229 * Perform STRIDE threat modeling on a system component 230 * 231 * @param {Object} options - Threat modeling options 232 * @param {string} options.component - Component description or code 233 * @param {string} [options.componentType] - Type (api, database, auth, file_upload, etc) 234 * @param {string} [options.dataFlow] - Description of data flow 235 * @returns {Promise<Object>} - Threat model with STRIDE analysis and DREAD scores 236 */ 237 export async function performThreatModeling({ 238 component, 239 componentType = 'general', 240 dataFlow = null, 241 }) { 242 const dataFlowContext = dataFlow ? `\n\nData Flow:\n${dataFlow}` : ''; 243 244 const prompt = `Perform STRIDE threat modeling on this component. 245 246 Component Type: ${componentType} 247 ${dataFlowContext} 248 249 Component: 250 \`\`\` 251 ${component} 252 \`\`\` 253 254 Analyze using STRIDE methodology: 255 - Spoofing: Can identity be faked? 256 - Tampering: Can data be modified? 257 - Repudiation: Can actions be denied? 258 - Information Disclosure: Can sensitive data leak? 259 - Denial of Service: Can availability be disrupted? 260 - Elevation of Privilege: Can permissions be bypassed? 261 262 For each threat found, calculate DREAD score: 263 - Damage potential (1-10) 264 - Reproducibility (1-10) 265 - Exploitability (1-10) 266 - Affected users (1-10) 267 - Discoverability (1-10) 268 269 Provide analysis in JSON format: 270 { 271 "threats": [ 272 { 273 "stride_category": "Spoofing|Tampering|Repudiation|InformationDisclosure|DoS|ElevationOfPrivilege", 274 "title": "short threat title", 275 "description": "detailed threat description", 276 "attack_scenario": "how attacker would exploit", 277 "dread": { 278 "damage": 1-10, 279 "reproducibility": 1-10, 280 "exploitability": 1-10, 281 "affected_users": 1-10, 282 "discoverability": 1-10, 283 "total": sum, 284 "average": average 285 }, 286 "risk_level": "critical|high|medium|low", 287 "mitigation": "how to mitigate the threat", 288 "cwe_id": "CWE-XXX (if applicable)" 289 } 290 ], 291 "summary": "overall threat assessment", 292 "priority_threats": ["list of high/critical threats to fix immediately"] 293 }`; 294 295 const systemPrompt = `You are a security architect performing threat modeling. 296 Use STRIDE methodology systematically. 297 Calculate DREAD scores objectively: 298 - Damage: 10=complete compromise, 1=minimal impact 299 - Reproducibility: 10=always works, 1=difficult/unreliable 300 - Exploitability: 10=no skills needed, 1=expert only 301 - Affected users: 10=all users, 1=few users 302 - Discoverability: 10=obvious/documented, 1=obscure 303 304 Risk levels based on DREAD average: 305 - critical: 8.5-10 306 - high: 7.0-8.4 307 - medium: 4.0-6.9 308 - low: 1.0-3.9`; 309 310 const response = await callClaude({ 311 prompt, 312 systemPrompt, 313 temperature: 0.3, 314 maxTokens: 4000, 315 }); 316 317 try { 318 // Extract JSON from response 319 const jsonMatch = 320 response.match(/```json\s*([\s\S]*?)\s*```/) || response.match(/```\s*([\s\S]*?)\s*```/); 321 const jsonStr = jsonMatch ? jsonMatch[1] : response; 322 323 return JSON.parse(jsonStr); 324 } catch (error) { 325 logger.error('Failed to parse Claude threat model', { error: error.message, response }); 326 throw new Error(`Failed to perform threat modeling: ${error.message}`); 327 } 328 }