Skip to main content
Glama

recon_tls_sans

Extract Subject Alternative Names from TLS certificates to identify all domains secured by a certificate. Perform read-only TLS handshake analysis to discover certificate details including common name, issuer, validity, and SAN count.

Instructions

Extract Subject Alternative Names from the TLS certificate. Returns common_name, subject_alternative_names, issuer, validity, and san_count. Read-only TLS handshake.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
targetYesTarget domain or IP:port, e.g. example.com or 1.2.3.4:443

Implementation Reference

  • Implementation of the "recon_tls_sans" tool in src/tools/recon.ts. It uses openssl to extract and parse Subject Alternative Names (SANs) and other certificate details from a target domain.
    server.tool(
      "recon_tls_sans",
      "Extract Subject Alternative Names from the TLS certificate. Returns common_name, subject_alternative_names, issuer, validity, and san_count. Read-only TLS handshake.",
      {
        target: z
          .string()
          .describe("Target domain or IP:port, e.g. example.com or 1.2.3.4:443"),
      },
      async ({ target }) => {
        const host = target.includes(":") ? target.split(":")[0] : target;
        const port = target.includes(":") ? target.split(":")[1] : "443";
    
        const certInfo = await runShell(
          `echo | openssl s_client -servername ${host} -connect ${host}:${port} 2>/dev/null | openssl x509 -noout -text 2>/dev/null`
        );
    
        // Parse SANs
        const sans: string[] = [];
        let cn = "";
        let issuer = "";
        let validity = "";
    
        for (const rawLine of certInfo.stdout.split("\n")) {
          const line = rawLine.trim();
          if (line.includes("DNS:")) {
            for (const part of line.split(",")) {
              const p = part.trim();
              if (p.startsWith("DNS:")) {
                sans.push(p.slice(4));
              }
            }
          }
          if (line.includes("Subject:") && line.includes("CN")) {
            const cnStart = line.indexOf("CN");
            if (cnStart !== -1) {
              cn = line.slice(cnStart).split(",")[0].replace("CN = ", "").replace("CN=", "");
            }
          }
          if (line.includes("Issuer:")) {
            issuer = line.replace("Issuer:", "").trim();
          }
          if (line.includes("Not After")) {
            validity = line.trim();
          }
        }
    
        const result = {
          common_name: cn,
          subject_alternative_names: sans,
          issuer: issuer.slice(0, 200),
          validity,
          san_count: sans.length,
        };
    
        return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
      }
    );
  • The implementation of the `recon_tls_sans` tool handler in `src/tools/recon.ts`. It uses OpenSSL to retrieve certificate info and parses the Subject Alternative Names.
    server.tool(
      "recon_tls_sans",
      "Extract Subject Alternative Names from the TLS certificate. Returns common_name, subject_alternative_names, issuer, validity, and san_count. Read-only TLS handshake.",
      {
        target: z
          .string()
          .describe("Target domain or IP:port, e.g. example.com or 1.2.3.4:443"),
      },
      async ({ target }) => {
        const host = target.includes(":") ? target.split(":")[0] : target;
        const port = target.includes(":") ? target.split(":")[1] : "443";
    
        const certInfo = await runShell(
          `echo | openssl s_client -servername ${host} -connect ${host}:${port} 2>/dev/null | openssl x509 -noout -text 2>/dev/null`
        );
    
        // Parse SANs
        const sans: string[] = [];
        let cn = "";
        let issuer = "";
        let validity = "";
    
        for (const rawLine of certInfo.stdout.split("\n")) {
          const line = rawLine.trim();
          if (line.includes("DNS:")) {
            for (const part of line.split(",")) {
              const p = part.trim();
              if (p.startsWith("DNS:")) {
                sans.push(p.slice(4));
              }
            }
          }
          if (line.includes("Subject:") && line.includes("CN")) {
            const cnStart = line.indexOf("CN");
            if (cnStart !== -1) {
              cn = line.slice(cnStart).split(",")[0].replace("CN = ", "").replace("CN=", "");
            }
          }
          if (line.includes("Issuer:")) {
            issuer = line.replace("Issuer:", "").trim();
          }
          if (line.includes("Not After")) {
            validity = line.trim();
          }
        }
    
        const result = {
          common_name: cn,
          subject_alternative_names: sans,
          issuer: issuer.slice(0, 200),
          validity,
          san_count: sans.length,
        };
    
        return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
      }
    );

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/operantlabs/operant-mcp'

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