Skip to main content
Glama

Solana Agent Kit MCP Server

security.ts6.48 kB
import { Connection, PublicKey } from "@solana/web3.js"; import * as bs58 from "bs58"; // Interface for security.txt content export interface SecurityTxtInfo { contact?: string; expires?: string; encryption?: string; acknowledgments?: string; preferredLanguages?: string; canonical?: string; policy?: string; hiring?: string; // Any additional fields that might be in the security.txt [key: string]: string | undefined; } /** * Extracts and parses security.txt information from a Solana program * * @param programId - The PublicKey of the Solana program to inspect * @param connection - The Solana connection object * @returns The parsed security.txt information */ export async function getSecurityTxtInfo(programId: PublicKey, connection: Connection): Promise<SecurityTxtInfo> { try { // Get the program account data const programAccount = await connection.getAccountInfo(programId); if (!programAccount) { throw new Error("Program account not found"); } // Check if this is an upgradeable program const BPF_UPGRADEABLE_LOADER_ID = new PublicKey("BPFLoaderUpgradeab1e11111111111111111111111"); let programData: Buffer; if (programAccount.owner.equals(BPF_UPGRADEABLE_LOADER_ID)) { // This is an upgradeable program, we need to find the program data account // The first 4 bytes indicate the account type const accountType = programAccount.data.slice(0, 4); // Check if this is a Program account (type 2) if (accountType[0] === 2) { // Extract the program data address (next 32 bytes) const programDataAddress = new PublicKey(programAccount.data.slice(4, 36)); // Get the program data account const programDataAccount = await connection.getAccountInfo(programDataAddress); if (!programDataAccount) { throw new Error("Program data account not found"); } // The program data starts after the header (first 8 bytes) // First byte is account type, next 7 bytes are the slot programData = programDataAccount.data.slice(8); } else { throw new Error("Not a valid upgradeable program account"); } } else { // For non-upgradeable programs, use the account data directly programData = programAccount.data; } // Now search for security.txt in the program data return findAndParseSecurityTxt(programData); } catch (error) { throw error; } } /** * Finds and parses security.txt content in program data * * @param programData - The program binary data * @returns Parsed security.txt information */ function findAndParseSecurityTxt(programData: Buffer): SecurityTxtInfo { try { // Convert the binary data to a string // Note: This is a simplification, as the security.txt might be encoded in various ways const dataString = new TextDecoder().decode(programData); // Look for security.txt pattern // Pattern 1: Standard security.txt format const securityTxtMatch = dataString.match(/(?:^|\n)# ?security\.txt(?:\n|$)([\s\S]*?)(?:\n\n|\n#|$)/i); if (securityTxtMatch && securityTxtMatch[1]) { return parseSecurityTxt(securityTxtMatch[1]); } // Pattern 2: Look for BEGIN/END SECURITY.TXT markers const securityTxtBlockMatch = dataString.match(/-----BEGIN SECURITY\.TXT-----([\s\S]*?)-----END SECURITY\.TXT-----/i); if (securityTxtBlockMatch && securityTxtBlockMatch[1]) { return parseSecurityTxt(securityTxtBlockMatch[1]); } // Pattern 3: Look for common security.txt fields const fieldMatch = dataString.match(/Contact: ([^\n]+)/i) || dataString.match(/Security-Contact: ([^\n]+)/i) || dataString.match(/mailto:([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i); if (fieldMatch) { const result: SecurityTxtInfo = {}; result.contact = fieldMatch[1].trim(); return result; } // If we couldn't find any specific security.txt format, try to extract any security-related info return extractSecurityInfoFromText(dataString); } catch (error) { // If we encounter any error during parsing, return an empty object console.error("Error parsing security.txt:", error); return {}; } } /** * Parses security.txt content into a structured object * * @param content - The raw security.txt content * @returns Structured security.txt information */ function parseSecurityTxt(content: string): SecurityTxtInfo { const result: SecurityTxtInfo = {}; // Split by lines and process each line const lines = content.split('\n'); for (const line of lines) { // Skip empty lines and comments if (!line.trim() || line.trim().startsWith('#')) { continue; } // Extract field and value const match = line.match(/^([^:]+):\s*(.*)$/); if (match) { const [_, field, value] = match; const fieldName = field.trim().toLowerCase(); // Convert kebab-case to camelCase const camelCaseField = fieldName.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()); result[camelCaseField] = value.trim(); } } return result; } /** * Attempts to extract security information from program text when no explicit security.txt is found * * @param text - The program text to analyze * @returns Any security information found */ function extractSecurityInfoFromText(text: string): SecurityTxtInfo { const result: SecurityTxtInfo = {}; // Look for common patterns that might indicate security contact info const contactMatches = [ // Email patterns ...text.match(/security@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g) || [], ...text.match(/mailto:[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g) || [], // URL patterns ...text.match(/https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\/security/g) || [], ...text.match(/https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\/responsible-disclosure/g) || [], ...text.match(/https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\/vulnerability/g) || [], ]; if (contactMatches.length > 0) { result.contact = contactMatches[0]; } // Look for PGP key patterns const pgpMatches = text.match(/-----BEGIN PGP PUBLIC KEY BLOCK-----([\s\S]*?)-----END PGP PUBLIC KEY BLOCK-----/g); if (pgpMatches) { result.encryption = "PGP key found in program data"; } return result; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cryptoleek-team/awesome-solana-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server