UNPKG

vulnmatter-extension

Version:

VS Code extension for CVE vulnerability analysis using the VulnMatter API with X-API-Key. See CHANGELOG.md for release notes.

321 lines (282 loc) 10.4 kB
// The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below import * as vscode from 'vscode'; // This method is called when your extension is activated // Your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) { // Use the console to output diagnostic information (console.log) and errors (console.error) // This line of code will only be executed once when your extension is activated console.log('Congratulations, your extension "vulnmatter-cli" is now active!'); // The command has been defined in the package.json file // Now provide the implementation of the command with registerCommand // The commandId parameter must match the command field in package.json const disposable = vscode.commands.registerCommand('vulnmatter-cli.helloWorld', () => { // The code you place here will be executed every time your command is executed // Display a message box to the user vscode.window.showInformationMessage('Hello World from Mi Primera Extension!'); }); // Command to configure API Key const configureApiKeyDisposable = vscode.commands.registerCommand('vulnmatter-cli.configureApiKey', () => { createApiKeyConfigPanel(context); }); // Command to test MCP connection (example usage of stored API key) const testMcpConnectionDisposable = vscode.commands.registerCommand('vulnmatter-cli.testMcpConnection', async () => { const apiKey = await getStoredApiKey(context); if (apiKey) { vscode.window.showInformationMessage(`MCP Connection Test: API Key found (${apiKey.substring(0, 8)}...)`); // Here you would implement the actual MCP connection logic console.log('API Key available for MCP connection:', apiKey.substring(0, 8) + '...'); } else { const result = await vscode.window.showWarningMessage( 'No API Key configured. Would you like to configure it now?', 'Configure API Key' ); if (result === 'Configure API Key') { vscode.commands.executeCommand('vulnmatter-cli.configureApiKey'); } } }); context.subscriptions.push(disposable); context.subscriptions.push(configureApiKeyDisposable); context.subscriptions.push(testMcpConnectionDisposable); } function createApiKeyConfigPanel(context: vscode.ExtensionContext) { // Create and show a new webview panel const panel = vscode.window.createWebviewPanel( 'apiKeyConfig', // Identifies the type of the webview 'Configure X-API-Key', // Title of the panel displayed to the user vscode.ViewColumn.One, // Editor column to show the new webview panel in { // Enable scripts in the webview enableScripts: true, // Restrict the webview to only load content from our extension's directory localResourceRoots: [context.extensionUri] } ); // Set the HTML content for the webview with strict CSP and nonce panel.webview.html = getWebviewContent(panel.webview); // Handle messages from the webview panel.webview.onDidReceiveMessage( async message => { switch (message.command) { case 'saveApiKey': if (message.apiKey && message.apiKey.trim()) { // Store the API key securely await context.secrets.store('vulnmatter.x-api-key', message.apiKey.trim()); vscode.window.showInformationMessage('X-API-Key saved successfully!'); panel.dispose(); } else { vscode.window.showErrorMessage('Please enter a valid API key'); } break; case 'loadApiKey': // Load existing API key (if any) and send it to the webview const existingKey = await context.secrets.get('vulnmatter.x-api-key'); panel.webview.postMessage({ command: 'displayApiKey', apiKey: existingKey || '' }); break; case 'clearApiKey': await context.secrets.delete('vulnmatter.x-api-key'); vscode.window.showInformationMessage('X-API-Key cleared successfully!'); panel.webview.postMessage({ command: 'displayApiKey', apiKey: '' }); break; case 'ready': // Send existing API key to webview when it's ready const readyKey = await context.secrets.get('vulnmatter.x-api-key'); panel.webview.postMessage({ command: 'displayApiKey', apiKey: readyKey || '' }); break; } }, undefined, context.subscriptions ); } function getWebviewContent(webview: vscode.Webview): string { const nonce = getNonce(); const cspSource = webview.cspSource; return ` <!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${cspSource} https:; style-src ${cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}';"> <title>Configure X-API-Key</title> <style> body { font-family: var(--vscode-font-family); color: var(--vscode-foreground); background-color: var(--vscode-editor-background); padding: 20px; max-width: 600px; } h1 { color: var(--vscode-titleBar-activeForeground); margin-bottom: 20px; } .form-group { margin-bottom: 20px; } label { display: block; margin-bottom: 8px; font-weight: bold; } input[type="password"], input[type="text"] { width: 100%; padding: 8px 12px; border: 1px solid var(--vscode-input-border); background-color: var(--vscode-input-background); color: var(--vscode-input-foreground); border-radius: 3px; font-size: 14px; box-sizing: border-box; } input[type="password"]:focus, input[type="text"]:focus { outline: none; border-color: var(--vscode-focusBorder); } .button-group { display: flex; gap: 10px; margin-top: 20px; } button { padding: 8px 16px; border: none; border-radius: 3px; cursor: pointer; font-size: 14px; background-color: var(--vscode-button-background); color: var(--vscode-button-foreground); } button:hover { background-color: var(--vscode-button-hoverBackground); } .secondary-button { background-color: var(--vscode-button-secondaryBackground) !important; color: var(--vscode-button-secondaryForeground) !important; } .secondary-button:hover { background-color: var(--vscode-button-secondaryHoverBackground) !important; } .danger-button { background-color: var(--vscode-inputValidation-errorBackground) !important; color: var(--vscode-inputValidation-errorForeground) !important; } .info { background-color: var(--vscode-editorWidget-background); border: 1px solid var(--vscode-editorWidget-border); padding: 15px; margin: 20px 0; border-radius: 3px; font-size: 13px; } .toggle-container { display: flex; align-items: center; gap: 10px; margin-top: 10px; } .toggle-container input[type="checkbox"] { width: auto; } </style> </head> <body> <h1>🔐 Configure X-API-Key</h1> <div class="info"> <strong>ℹ️ Información:</strong><br> Esta clave API se almacenará de forma segura en tu sistema y será utilizada para conectar con servicios MCP (Model Context Protocol). </div> <div class="form-group"> <label for="apiKeyInput">X-API-Key:</label> <input type="password" id="apiKeyInput" placeholder="Ingresa tu clave API..." /> <div class="toggle-container"> <input type="checkbox" id="showPassword" /> <label for="showPassword">Mostrar clave</label> </div> </div> <div class="button-group"> <button id="saveButton">💾 Guardar Clave</button> <button id="testButton" class="secondary-button">🧪 Probar Conexión</button> <button id="clearButton" class="danger-button">🗑️ Limpiar</button> </div> <script nonce="${nonce}"> const vscode = acquireVsCodeApi(); const apiKeyInput = document.getElementById('apiKeyInput'); const showPasswordCheckbox = document.getElementById('showPassword'); const saveButton = document.getElementById('saveButton'); const testButton = document.getElementById('testButton'); const clearButton = document.getElementById('clearButton'); // Toggle password visibility showPasswordCheckbox.addEventListener('change', function() { apiKeyInput.type = this.checked ? 'text' : 'password'; }); // Save API key saveButton.addEventListener('click', () => { const apiKey = apiKeyInput.value; if (apiKey.trim()) { vscode.postMessage({ command: 'saveApiKey', apiKey }); } else { alert('Por favor, ingresa una clave API válida.'); } }); // Test connection (placeholder for future implementation) testButton.addEventListener('click', () => { const apiKey = apiKeyInput.value.trim(); if (apiKey) { alert('Funcionalidad de prueba: Próximamente se implementará la verificación de conectividad MCP.'); } else { alert('Por favor, ingresa una clave API para probar.'); } }); // Clear API key clearButton.addEventListener('click', () => { if (confirm('¿Estás seguro de que deseas eliminar la clave API almacenada?')) { vscode.postMessage({ command: 'clearApiKey' }); } }); // Listen for messages from the extension window.addEventListener('message', event => { const message = event.data; if (message?.command === 'displayApiKey') { apiKeyInput.value = message.apiKey || ''; } }); // Notify extension that webview is ready vscode.postMessage({ command: 'ready' }); // Allow Enter key to save apiKeyInput.addEventListener('keypress', function(event) { if (event.key === 'Enter') { saveButton.click(); } }); </script> </body> </html> `; } // Helper function to get stored API key (for use in other parts of the extension) export async function getStoredApiKey(context: vscode.ExtensionContext): Promise<string | undefined> { return await context.secrets.get('vulnmatter.x-api-key'); } // This method is called when your extension is deactivated export function deactivate() {} // Generate a nonce for Content Security Policy function getNonce() { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < 32; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }