{
  "meta": {
    "instanceId": "c2589fa234defe76e8a1321c3a7d0a73579d0120d64d927e88f5e3be584ae8d4"
  },
  "nodes": [
    {
      "id": "634f2fc5-0ba7-42ad-bdf5-ade3415dd288",
      "name": "Landing Page Url",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -200,
        580
      ],
      "webhookId": "afe067a5-4878-4c9d-b746-691f77190f54",
      "parameters": {
        "options": {},
        "formTitle": "Website Security Scanner",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Landing Page Url",
              "placeholder": "https://example.com",
              "requiredField": true
            }
          ]
        },
        "formDescription": "Check your website for security vulnerabilities and get a detailed report"
      },
      "typeVersion": 2.2
    },
    {
      "id": "6cee63ca-d0f6-444a-b882-22da1a9fd70c",
      "name": "Scrape Website",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        580
      ],
      "parameters": {
        "url": "={{ $json['Landing Page Url'] }}",
        "options": {
          "redirect": {
            "redirect": {
              "maxRedirects": 5
            }
          },
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "text"
            }
          }
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "0d5d1e76-e627-4565-a1ee-6a610f4b2028",
      "name": "OpenAI Headers Analysis",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        340,
        600
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "yZ0AIg9abV8HJadB",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "04427ef7-515d-4a1a-88d2-ade10aeefc87",
      "name": "OpenAI Content Analysis",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        340,
        980
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "yZ0AIg9abV8HJadB",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "d4ee4db8-aa04-4068-9b97-d16acf98c027",
      "name": "Security Vulnerabilities Audit",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        360,
        780
      ],
      "parameters": {
        "text": "=You are an elite cybersecurity expert specializing in web application security.nnIn this task, you will analyze the HTML and visible content of the webpage to identify potential security vulnerabilities.nnAudit StructurenYou will review all client-side security aspects of the page and present your findings in three sections:n- Critical Vulnerabilities u2013 Issues that could lead to immediate compromisen- Information Leakage u2013 Sensitive data exposed in page sourcen- Client-Side Weaknesses u2013 JavaScript vulnerabilities, XSS opportunities, etc.nnFor each issue found, provide:n1. A clear description of the vulnerabilityn2. The potential impactn3. A specific recommendation to fix itnnIf you find no issues in a particular section, explicitly state that no issues were found in that category.nnEnsure the output is properly formatted, clean, and highly readable. Focus only on issues that can be detected from the client-side code.nnHere is the content of the webpage: {{ $json.data }}",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "c9702f2b-845b-464d-9c32-3d5be308ef77",
      "name": "Security Configuration Audit",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        360,
        380
      ],
      "parameters": {
        "text": "=You are an elite web security expert specializing in secure configurations.nnIn this task, you will analyze the HTTP headers, cookies, and overall configuration of a webpage to identify security misconfigurations.nnAudit StructurenYou will begin by listing ALL security headers that ARE present and properly configured.nnBe very clear and explicit about which headers are present and which are missing. For each header, clearly state whether it is present or missing, and if present, what its value is.nnThen, present your findings in three sections:n- Header Security u2013 Missing or misconfigured security headersn- Cookie Security u2013 Insecure cookie configurationsn- Content Security u2013 CSP issues, mixed content, etc.nnFor each finding, provide:n1. A clear description of the misconfigurationn2. The security implicationsn3. The recommended secure configuration with example codennIf you find no issues in a particular section, explicitly state that no issues were found.nnUse proper formatting with code blocks for configuration examples. Only include issues that can be detected from client-side inspection.nHere are the response headers: {{ $json.formattedHeaders }}nnPlease Respond like thisnn### [any section heading that includes "Headers]nn1. **[Header Title]**n   - **Present?** Yes/Non   - **Value:** `actual-header-value`n",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 1.7
    },
    {
      "id": "3b43be75-c35c-44e4-8ecc-a29c48e3625c",
      "name": "Merge Security Results",
      "type": "n8n-nodes-base.merge",
      "position": [
        860,
        580
      ],
      "parameters": {},
      "typeVersion": 3,
      "alwaysOutputData": true
    },
    {
      "id": "da134256-d7fa-4a3f-ba24-acc320a944a2",
      "name": "Aggregate Audit Results",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1060,
        580
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "output"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "aef1da93-0b01-4a7f-9439-1f74c2af12d6",
      "name": "Process Audit Results",
      "type": "n8n-nodes-base.code",
      "position": [
        1240,
        580
      ],
      "parameters": {
        "jsCode": "// u2705 Updated extractSecurityHeaders and related logic remains unchangednnfunction extractSecurityHeaders(rawHeaders = {}, configOutput = '') {n  const securityHeaders = [n    'Content-Security-Policy',n    'Strict-Transport-Security',n    'X-Content-Type-Options',n    'X-Frame-Options',n    'Referrer-Policy',n    'Permissions-Policy',n    'X-XSS-Protection',n    'Cross-Origin-Embedder-Policy',n    'Cross-Origin-Opener-Policy',n    'X-Permitted-Cross-Domain-Policies'n  ];nn  const headerStatus = {};n  for (const header of securityHeaders) {n    headerStatus[header] = { present: false, value: '' };n  }nn  for (const header in rawHeaders) {n    const norm = header.trim().toLowerCase();n    for (const standard of securityHeaders) {n      if (norm === standard.toLowerCase()) {n        headerStatus[standard].present = true;n        headerStatus[standard].value = rawHeaders[header];n      }n    }n  }nn  const presentSection = configOutput.match(/(?:###|##|\*\*)[^\n]*?\bheaders?\b[\s\S]*?(?=###|##|\*\*|$)/i);n  if (presentSection) {n    const section = presentSection[0];n    for (const header of securityHeaders) {n      const title = header.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());n      const regex = new RegExp(`\\*\\*${title}\\*\\*[^\\n]*?\\*\\*Present\\?\\*\\*\\s*Yes[^\\n]*?\\*\\*Value:\\*\\*\\s*\`([^\\\`]+)\``, 'is');n      const match = section.match(regex);n      if (match && match[1]) {n        headerStatus[header].present = true;n        headerStatus[header].value = match[1].trim();n      }n    }n  }nn  return headerStatus;n}nnfunction hasUnsafeInline(value) {n  return value && value.includes('unsafe-inline');n}nnfunction determineGrade(headerStatus) {n  const critical = [n    'Content-Security-Policy',n    'Strict-Transport-Security',n    'X-Content-Type-Options',n    'X-Frame-Options'n  ];n  const important = ['Referrer-Policy', 'Permissions-Policy'];n  const additional = [n    'X-XSS-Protection',n    'Cross-Origin-Embedder-Policy',n    'Cross-Origin-Opener-Policy',n    'X-Permitted-Cross-Domain-Policies'n  ];nn  let criticalCount = 0;n  let importantCount = 0;n  let hasCSPIssue = false;nn  for (const h of critical) {n    if (headerStatus[h]?.present) {n      criticalCount++;n      if (h === 'Content-Security-Policy' && hasUnsafeInline(headerStatus[h].value)) {n        hasCSPIssue = true;n      }n    }n  }nn  for (const h of important) {n    if (headerStatus[h]?.present) importantCount++;n  }nn  if (criticalCount === critical.length) {n    if (importantCount === important.length) return hasCSPIssue ? 'A-' : 'A+';n    if (importantCount >= 1) return hasCSPIssue ? 'B+' : 'A-';n    return hasCSPIssue ? 'B' : 'B+';n  } else if (criticalCount >= critical.length - 1) {n    return importantCount >= 1 ? 'B' : 'C+';n  } else if (criticalCount >= 2) {n    return 'C';n  } else if (criticalCount >= 1) {n    return 'D';n  } else {n    return 'F';n  }n}nnfunction formatHeadersForDisplay(headerStatus) {n  const present = Object.keys(headerStatus).filter(h => headerStatus[h].present);n  return present.length > 0 ? present.join(', ') : 'No security headers detected';n}nnfunction processSecurityHeaders(items) {n  try {n    const json = items[0].json || items[0];nn    // u26cfufe0f Try to grab from originalHeaders if availablen    const rawHeaders =n      json?.originalHeaders ||n      $('Extract Headers for Debug')?.first()?.json?.originalHeaders ||n      json?.headers ||n      {};nn    const configOutput = json.configOutput || json.output?.[0] || '';n    const vulnOutput = json.vulnOutput || json.output?.[1] || '';nn    const headerStatus = extractSecurityHeaders(rawHeaders, configOutput);n    const presentHeaders = formatHeadersForDisplay(headerStatus);n    const grade = determineGrade(headerStatus);nn    const timestamp = new Date().toLocaleString('en-US', {n      year: 'numeric',n      month: 'long',n      day: 'numeric',n      hour: '2-digit',n      minute: '2-digit'n    });nn    const url =n      json?.formValues?.url ||n      json?.['Landing Page Url'] ||n      $('Landing Page Url')?.first()?.json?.['Landing Page Url'] ||n      json?.Landing_Page_Url ||n      json?.landingPageUrl ||n      json?.url ||n      'https://example.com';nn    return [n      {n        json: {n          ...json,n          auditData: {n            url,n            timestamp,n            grade,n            criticalCount:n              headerStatus['Content-Security-Policy'].present &&n              hasUnsafeInline(headerStatus['Content-Security-Policy'].value)n                ? 1n                : 0,n            warningCount: Object.keys(headerStatus).filter(n              h =>n                !headerStatus[h].present &&n                !['Strict-Transport-Security', 'Content-Security-Policy'].includes(h)n            ).length,n            presentHeaders,n            configOutput,n            vulnOutput,n            headerStatus,n            originalHeaders: rawHeadersn          }n        }n      }n    ];n  } catch (err) {n    return [{ json: { ...items[0].json, error: err.message } }];n  }n}nnreturn processSecurityHeaders(items);n"
      },
      "typeVersion": 2
    },
    {
      "id": "ced29b26-474c-4d62-808a-3284103c9d60",
      "name": "Send Security Report",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1580,
        580
      ],
      "webhookId": "2979e4dc-1689-447e-8cd4-eb907b4eedf4",
      "parameters": {
        "sendTo": "=example@here.com",
        "message": "={{ $json.emailHtml }}",
        "options": {},
        "subject": "=Website Security Audit - {{ $json.auditData.url }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "9CEpbF4jIWb2OETv",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "918c0fc4-2f02-4594-bfc9-e36035f2d802",
      "name": "Sticky Note - Setup Instructions",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -820,
        400
      ],
      "parameters": {
        "width": 500,
        "height": 440,
        "content": "## Quick Setup Guidenn1. **Add OpenAI API Credentials**n   - Go to Settings u2192 Credentials u2192 New u2192 OpenAI APIn   - Enter your API key from platform.openai.comnn2. **Add Gmail Credentials**n   - Go to Settings u2192 Credentials u2192 New u2192 Gmail OAuth2 APIn   - Complete the OAuth setup processnn3. **Update Email Configuration**n   - Open the 'Send Security Report' noden   - Change the recipient email address from the defaultnn4. **Activate and Deploy Workflow**n   - Click 'Active' toggle in the top rightn   - Copy the form URL to share with others or use yourself"
      },
      "typeVersion": 1
    },
    {
      "id": "6e31b9b8-ae02-4da4-a75e-5d784b210c64",
      "name": "Sticky Note - OpenAI Analysis",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        300,
        120
      ],
      "parameters": {
        "color": 3,
        "width": 420,
        "height": 240,
        "content": "## OpenAI Security Analysisnn- Add your OpenAI credentials (required)n- Using GPT-4o models provides more detailed security analysisn- Analyzes for XSS, information disclosure, CSRF, and moren- Each agent scans different aspects of website securityn- Consider upgrading to GPT-4o (not mini) for production use"
      },
      "typeVersion": 1
    },
    {
      "id": "590b1f1c-024d-4002-a8eb-d9dc81528f89",
      "name": "Sticky Note - Email Configuration",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1480,
        220
      ],
      "parameters": {
        "color": 3,
        "width": 360,
        "height": 200,
        "content": "## Send Security Reportnn- Connects securely to Gmail for sending detailed reportsn- Report is sent as HTML formatted emailn- Subject line includes the scanned URLn- Requires Gmail OAuth credentials to be set up"
      },
      "typeVersion": 1
    },
    {
      "id": "dc6223f8-a98c-497a-97c9-af39e80e6d66",
      "name": "Sticky Note - Audit Process",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -200,
        780
      ],
      "parameters": {
        "color": 2,
        "width": 420,
        "height": 300,
        "content": "## Security Audit Processnn- This workflow performs two parallel security analysesn- Top path: Checks headers, cookies, and security configurationsn- Bottom path: Analyzes HTML/JavaScript for client-side vulnerabilitiesn- Results are merged and formatted into a comprehensive reportn- Analysis is non-invasive and only examines client-side content"
      },
      "typeVersion": 1
    },
    {
      "id": "cbda16d4-f1f4-491c-b38c-43d7544e129b",
      "name": "Sticky Note - How To Use",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        240
      ],
      "parameters": {
        "color": 4,
        "width": 400,
        "height": 280,
        "content": "## How To Use This Workflownn1. **Deploy the workflow** and activate itn2. **Access the form** via the provided URLn3. **Enter any website URL** to scan (must include http:// or https://)n4. **Submit the form** to trigger the analysisn5. **Check your email** for the detailed security reportn6. **Share the results** with your development team to implement fixes"
      },
      "typeVersion": 1
    },
    {
      "id": "4859416f-4de3-43ea-9461-3ead8a38db6e",
      "name": "Sticky Note - Report Formatting",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1160,
        220
      ],
      "parameters": {
        "color": 5,
        "width": 300,
        "height": 280,
        "content": "## Report Formattingnn- Creates beautiful, professional HTML email reportn- Visual grade indicator (A-F) based on findingsn- Includes count of critical issues and warningsn- Color-coded sections for easy readabilityn- Mobile-friendly responsive design"
      },
      "typeVersion": 1
    },
    {
      "id": "a02db4c7-2cad-41ff-b5ad-e1b19604a699",
      "name": "Sticky Note - Results Processing",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        840,
        240
      ],
      "parameters": {
        "width": 300,
        "height": 240,
        "content": "## Results Processingnn- Analyzes AI output to determine security graden- Counts critical issues and warningsn- Extracts present security headersn- Prepares data for the email report templaten- Generates timestamp for the report"
      },
      "typeVersion": 1
    },
    {
      "id": "41b834c8-62f7-47e7-9d9d-e0e1244faecb",
      "name": "Extract Headers for Debug",
      "type": "n8n-nodes-base.code",
      "position": [
        200,
        460
      ],
      "parameters": {
        "jsCode": "// Format headers into a readable stringnlet formattedHeaders = '';nif (items[0].json.headers) {n  for (const key in items[0].json.headers) {n    formattedHeaders += `${key}: ${items[0].json.headers[key]}\n`;n  }n}nn// Return both the original data and the formatted headersnreturn [{n  json: {n    ...items[0].json,n    formattedHeaders: formattedHeaders,n    originalHeaders: items[0].json.headers // Keep the original headers toon  }n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "0b76b396-fc96-41fc-a095-30971dd88271",
      "name": "convert to HTML",
      "type": "n8n-nodes-base.code",
      "position": [
        1400,
        580
      ],
      "parameters": {
        "jsCode": "// Create a direct HTML template with improved stylingnconst auditData = items[0].json.auditData;nnfunction formatConfigurationIssues() {n  if (!auditData.configOutput || auditData.configOutput.trim() === '') {n    return '<p>No specific configuration issues detected.</p>';n  }nn  try {n    const config = auditData.configOutput.trim();n    let html = '';n    const renderedKeys = new Set();nn    const renderBlock = (title, description, impact, recommendation) =&gt; `n      <div style="border-left: 4px solid #3498DB;padding: 10px;margin-bottom: 15px">n        <div style="font-weight: bold;color: #3498DB">${title}</div>n        ${description ? `<div style="margin-top: 5px">${description}</div>` : ''}n        ${impact ? `<div style="margin-top: 5px;font-style: italic;color: #7F8C8D">Impact: ${impact}</div>` : ''}n        ${recommendation ? `<div style="margin-top: 5px"><strong>Recommendation:</strong></div>n          <pre style="background-color: #f8f9fa;padding: 10px;border-radius: 5px;font-family: monospace">${recommendation}</pre>` : ''}n      </div>`;nn    const sections = config.split(/(?=^###\s+)/gm).filter(Boolean);nn    for (const section of sections) {n      const sectionTitleMatch = section.match(/^###\s+(.*)/);n      const sectionTitle = sectionTitleMatch?.[1]?.trim() || 'Unnamed Section';n      const sectionKey = sectionTitle.toLowerCase();nn      // Skip "no issues found" sectionsn      if (/no issues? (found|were found)/i.test(section)) continue;nn      const lines = section.split(/\n+/).filter(line =&gt; line.trim() !== '');nn      let currentTitle = '';n      let description = '';n      let impact = '';n      let recommendation = '';nn      for (let i = 0; i &lt; lines.length; i++) {n        const line = lines[i].trim();nn        // Start of a new numbered or bolded issuen        const numberedTitle = line.match(/^\d+\.\s+\*\*(.*?)\*\*/);n        const bulletTitle = line.match(/^\*\*(.*?)\*\*/);nn        if (numberedTitle || (!currentTitle &amp;&amp; bulletTitle)) {n          // Flush last blockn          if (currentTitle &amp;&amp; !renderedKeys.has(`${sectionKey}::${currentTitle.toLowerCase()}`)) {n            html += renderBlock(currentTitle, description, impact, recommendation);n            renderedKeys.add(`${sectionKey}::${currentTitle.toLowerCase()}`);n          }nn          currentTitle = (numberedTitle || bulletTitle)[1].trim();n          description = &#039;&#039;;n          impact = &#039;&#039;;n          recommendation = &#039;&#039;;n          continue;n        }nn        const valueMatch = line.match(/- \*\*Value:\*\*\s*`?(.*?)`?$/i);n        const presentMatch = line.match(/- \*\*Present\?\*\*.*?(Yes|No)/i);n        const descMatch = line.match(/- \*\*Description:\*\*\s*(.*)/i);n        const impactMatch = line.match(/- \*\*(?:Impact|Security Implication|Potential Impact):\*\*\s*(.*)/i);n        const recMatch = line.match(/```(?:\w*)?\n([\s\S]*?)```/i);nn        if (descMatch) {n          description = descMatch[1].trim();n        } else if (valueMatch || presentMatch) {n          const present = presentMatch?.[1]?.trim() || &#039;Unknown&#039;;n          const value = valueMatch?.[1]?.trim() || &#039;[Not provided]&#039;;n          description = `This header is ${present.toLowerCase()}. Value: ${value}.`;n        }nn        if (impactMatch) {n          impact = impactMatch[1].trim();n        }nn        if (recMatch) {n          recommendation = recMatch[1].trim();n        }n      }nn      // Final block in sectionn      if (currentTitle &amp;&amp; !renderedKeys.has(`${sectionKey}::${currentTitle.toLowerCase()}`)) {n        html += renderBlock(currentTitle, description, impact, recommendation);n        renderedKeys.add(`${sectionKey}::${currentTitle.toLowerCase()}`);n      }n    }nn    return html || &#039;<p>No configuration issues detected.</p>';n  } catch (e) {n    console.error('Error in formatConfigurationIssues:', e);n    return `<p>Error processing configuration issues: ${e.message}</p>`;n  }n}nnnn// Create header badge HTMLnfunction createHeaderBadge(headerName, isWarning = false) {n  const isPresent = auditData.headerStatus &amp;&amp; n                   auditData.headerStatus[headerName] &amp;&amp; n                   auditData.headerStatus[headerName].present;n  n  const color = isWarning &amp;&amp; isPresent ? "#F39C12" : (isPresent ? "#27AE60" : "#E74C3C");n  const icon = isPresent ? "u2713" : "u2717";n  n  return `<span style="margin: 2px;padding: 4px 8px;color: white;border-radius: 4px;font-size: 12px">${icon} ${headerName}</span>`;n}nn// Format warnings sectionnfunction formatWarningsSection() {n  if (!auditData.warningCount || auditData.warningCount === 0 || !auditData.headerStatus) {n    return '<p>No warnings detected.</p>';n  }nn  const csp = Object.entries(auditData.headerStatus).find(([k]) =&gt; k.toLowerCase() === 'content-security-policy');n  const hsts = Object.entries(auditData.headerStatus).find(([k]) =&gt; k.toLowerCase() === 'strict-transport-security');n  const xss = Object.entries(auditData.headerStatus).find(([k]) =&gt; k.toLowerCase() === 'x-xss-protection');nn  let warnings = '';nn  if (csp &amp;&amp; csp[1].value &amp;&amp; csp[1].value.includes('unsafe-inline')) {n    warnings += `n      <div style="margin-top: 15px">n        <div style="border-left: 4px solid #F39C12;padding: 10px">n          <strong style="color: #F39C12">Content-Security-Policy: unsafe-inline</strong>n          <p>The use of 'unsafe-inline' allows potentially malicious scripts to execute.</p>n        </div>n      </div>`;n  }nn  if (hsts &amp;&amp; hsts[1].value) {n    const match = hsts[1].value.match(/max-age=(\d+)/);n    const age = match ? parseInt(match[1]) : 0;n    if (age &lt; 2592000) {n      warnings += `n        <div style="margin-top: 15px">n          <div style="border-left: 4px solid #F39C12;padding: 10px">n            <strong style="color: #F39C12">Strict-Transport-Security</strong>n            <p>max-age is too low (${age}). Should be at least 2592000 (30 days).</p>n          </div>n        </div>`;n    }n  }nn  if (xss &amp;&amp; !xss[1].present) {n    warnings += `n      <div style="margin-top: 15px">n        <div style="border-left: 4px solid #F39C12;padding: 10px">n          <strong style="color: #F39C12">Missing X-XSS-Protection</strong>n          <p>This header enables the browser's XSS filter. Lack of it increases XSS risks.</p>n        </div>n      </div>`;n  }nn  if (!warnings) {n    warnings = `n      <div style="margin-top: 15px">n        <div style="border-left: 4px solid #F39C12;padding: 10px">n          <strong style="color: #F39C12">${auditData.warningCount} warnings detected</strong>n          <p>See the Configuration Issues section below for more info.</p>n        </div>n      </div>`;n  }nn  return warnings;n}nnfunction formatLongValue(value) {n  if (!value || typeof value !== 'string') return '[empty]';nn  // Convert URLs into clickable linksn  value = value.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1" style="color: #3498DB;text-decoration: none" target="_blank">$1</a>');nn  // Add line breaks after commas or semicolons for readabilityn  if (value.length &gt; 100) {n    value = value.replace(/([,;])\s*/g, '$1<br>');n  }nn  return value;n}nnfunction formatDetailedRawHeaders() {n  const allHeaders = [];n  const seen = new Set();nn  const addHeader = (name, value) =&gt; {n    const key = name.toLowerCase();n    if (seen.has(key)) return;n    seen.add(key);nn    const status = Object.entries(auditData.headerStatus || {}).find(n      ([k]) =&gt; k.toLowerCase() === name.toLowerCase()n    );n    const present = status ? status[1].present : !!value;nn    allHeaders.push({n      name: name.trim(),n      present,n      value: value || '[empty]'n    });n  };nn  Object.entries(auditData.originalHeaders || {}).forEach(([key, value]) =&gt; {n    if (key) addHeader(key, value);n  });nn  const securityHeaders = [n    'content-security-policy',n    'strict-transport-security',n    'x-content-type-options',n    'x-frame-options',n    'referrer-policy',n    'permissions-policy',n    'x-xss-protection'n  ];nn  const isWarningHeader = (name, value) =&gt; {n    const lower = name.toLowerCase();n    if (lower === 'strict-transport-security') {n      const match = value.match(/max-age=(\d+)/);n      return match &amp;&amp; parseInt(match[1])  {n    const isSecurity = securityHeaders.includes(header.name.toLowerCase());n    const warning = isSecurity &amp;&amp; isWarningHeader(header.name, header.value);n    const missing = isSecurity &amp;&amp; !header.present;nn    let bgColor = '#F8F9FA';n    let textColor = '#333';nn    if (isSecurity) {n      if (missing) {n        bgColor = '#FFEBEE';n        textColor = '#C62828';n      } else if (warning) {n        bgColor = '#FFF9C4';n        textColor = '#F57F17';n      } else {n        bgColor = '#E8F5E9';n        textColor = '#2E7D32';n      }n    }nn    return `n      <tr>n        <td title="${isSecurity ? (missing ? 'Missing' : (warning ? 'Needs review' : 'Secure')) : 'Informational'}" style="padding: 8px;font-weight: bold">${header.name}</td>n        <td style="padding: 8px;text-align: center">${header.present ? 'present' : 'absent'}</td>n        <td style="padding: 8px;font-family: monospace">${formatLongValue(header.value)}</td>n      </tr>`;n  }).join('');nn  return `n    <table style="width: 100%;border-collapse: collapse;margin-top: 10px">n      <thead>n        <tr style="background-color: #E0E0E0">n          <th style="padding: 10px">Header</th>n          <th style="padding: 10px">Status</th>n          <th style="padding: 10px">Value</th>n        </tr>n      </thead>n      <tbody>n        ${tableRows}n      </tbody>n    </table>`;n}nn// Format additional information sectionnfunction formatAdditionalInfo() {n  const headers = [n    {n      name: 'access-control-allow-origin',n      description: 'This is a very lax CORS policy. Such a policy should only be used on a public CDN.'n    },n    {n      name: 'strict-transport-security',n      description: 'HTTP Strict Transport Security is an excellent feature to support on your site and strengthens your implementation of TLS by getting the User Agent to enforce the use of HTTPS.'n    },n    {n      name: 'content-security-policy',n      description: 'Content Security Policy is an effective measure to protect your site from XSS attacks. By whitelisting sources of approved content, you can prevent the browser from loading malicious assets. Analyse this policy in more detail. You can sign up for a free account on Report URI to collect reports about problems on your site.'n    },n    {n      name: 'permissions-policy',n      description: 'Permissions Policy is a new header that allows a site to control which features and APIs can be used in the browser.'n    },n    {n      name: 'referrer-policy',n      description: 'Referrer Policy is a new header that allows a site to control how much information the browser includes with navigations away from a document and should be set by all sites.'n    },n    {n      name: 'x-content-type-options',n      description: 'X-Content-Type-Options stops a browser from trying to MIME-sniff the content type and forces it to stick with the declared content-type. The only valid value for this header is "X-Content-Type-Options: nosniff".'n    },n    {n      name: 'x-frame-options',n      description: 'X-Frame-Options tells the browser whether you want to allow your site to be framed or not. By preventing a browser from framing your site you can defend against attacks like clickjacking.'n    },n    {n      name: 'report-to',n      description: 'Report-To enables the Reporting API. This allows a website to collect reports from the browser about various errors that may occur. You can sign up for a free account on Report URI to collect these reports.'n    },n    {n      name: 'nel',n      description: 'Network Error Logging is a new header that instructs the browser to send reports during various network or application errors. You can sign up for a free account on Report URI to collect these reports.'n    },n    {n      name: 'server',n      description: 'Server value has been changed. Typically you will see values like "Microsoft-IIS/8.0" or "nginx 1.7.2".'n    }n  ];n  n  let rows = '';n  n  for (const header of headers) {n    const isSecurityHeader = ['content-security-policy', 'strict-transport-security', 'x-content-type-options', 'x-frame-options', 'referrer-policy', 'permissions-policy'].includes(header.name);n    const headerColor = isSecurityHeader ? '#27AE60' : '#3498DB';n    n    rows += `n      <tr>n        <td style="padding: 8px;border-bottom: 1px solid #eee;font-weight: bold">${header.name}</td>n        <td style="padding: 8px;border-bottom: 1px solid #eee">${header.description}</td>n      </tr>n    `;n  }n  n  return `n    <table style="width: 100%;border-collapse: collapse;margin-top: 10px">n      <tbody>n        ${rows}n      </tbody>n    </table>n  `;n}nnfunction formatSecurityGrade() {n  const gradeColors = {n    'A+': '#27AE60',n    'A': '#27AE60',n    'A-': '#27AE60',n    'B+': '#3498DB',n    'B': '#3498DB',n    'B-': '#3498DB',n    'C+': '#F39C12',n    'C': '#F39C12',n    'C-': '#F39C12',n    'D+': '#E74C3C',n    'D': '#E74C3C',n    'D-': '#E74C3C',n    'F': '#E74C3C'n  };n  n  return `<div class="grade" style="font-size: 64px;font-weight: bold;width: 100px;height: 100px;line-height: 100px;text-align: center;color: white;border-radius: 5px;margin: 0 auto">${auditData.grade}</div>`;n}nnfunction formatCriticalVulnerabilities() {n  if (!auditData.vulnOutput || auditData.vulnOutput.trim() === '') {n    return '<p>No vulnerabilities detected.</p>';n  }nn  try {n    const vuln = auditData.vulnOutput.trim();n    let html = '';n    const renderedTitles = new Set();nn    // Match sections like ## Category (e.g., ## Critical Vulnerabilities)n    const categories = vuln.split(/(?=^##\s+)/gm).filter(Boolean);nn    for (const categoryBlock of categories) {n      const categoryMatch = categoryBlock.match(/^##\s+(.*)/);n      const categoryTitle = categoryMatch?.[1]?.trim() || 'Uncategorized';nn      // Find numbered items: 1. **Title**n      const vulns = categoryBlock.split(/(?=^\d+\.\s+\*\*)/gm).filter(Boolean);nn      for (const vulnBlock of vulns) {n        const titleMatch = vulnBlock.match(/^\d+\.\s+\*\*(.*?)\*\*/);n        const title = titleMatch?.[1]?.trim() || 'Unnamed Vulnerability';n        const key = `${categoryTitle}::${title}`.toLowerCase();n        if (renderedTitles.has(key)) continue;nn        const descriptionMatch = vulnBlock.match(/\*\*Description\*\*:?\s*([\s\S]*?)(?=\n\*\*|\n$)/i);n        const impactMatch = vulnBlock.match(/\*\*(?:Impact|Potential Impact)\*\*:?\s*([\s\S]*?)(?=\n\*\*|\n$)/i);n        const recommendationMatch = vulnBlock.match(/\*\*(?:Recommendation|Mitigation|Fix)\*\*:?\s*([\s\S]*?)(?=\n\*\*|\n$)/i);nn        const description = descriptionMatch?.[1]?.trim() || '';n        const impact = impactMatch?.[1]?.trim() || '';n        const recommendation = recommendationMatch?.[1]?.trim() || '';nn        if (description || impact || recommendation) {n          html += `n            <div style="border-left: 4px solid #E74C3C;padding: 10px;margin-bottom: 15px">n              <div style="font-weight: bold;color: #E74C3C">${title}</div>n              ${description ? `<div style="margin-top: 5px">${description}</div>` : ''}n              ${impact ? `<div style="margin-top: 5px;font-style: italic;color: #7F8C8D">Impact: ${impact}</div>` : ''}n              ${recommendation ? `<div style="margin-top: 5px"><strong>Recommendation:</strong> ${recommendation}</div>` : ''}n            </div>`;n          renderedTitles.add(key);n        }n      }n    }nn    return html || '<p>No vulnerabilities parsed from output.</p>';n  } catch (e) {n    console.error('Error in formatCriticalVulnerabilities:', e);n    return `<p>Error processing vulnerabilities: ${e.message}</p>`;n  }n}nnn// Generate all security header badgesnfunction generateAllHeaderBadges() {n  // Only include the necessary security headersn  const securityHeaders = [n    'Content-Security-Policy',n    'Strict-Transport-Security',n    'X-Content-Type-Options',n    'X-Frame-Options',n    'Referrer-Policy',n    'Permissions-Policy'n  ];n  n  let badges = '';n  securityHeaders.forEach(header =&gt; {n                      n    const isWarning = header === 'Strict-Transport-Security' &amp;&amp;n                  auditData.headerStatus?.[header]?.value &amp;&amp;n                  parseInt(auditData.headerStatus[header].value.match(/max-age=(\d+)/)?.[1] || 0) &lt; 2592000;n    n    badges += createHeaderBadge(header, isWarning);n  });n  n  return badges;n}nn<!-- Modify the HTML to directly access auditData.originalHeaders or allHeaders -->nconst html = `nnn    n    n    <title>Website Security Audit Report</title>n    n        body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f9f9f9; }n        .container { max-width: 950px; margin: 0 auto; }n        .header { background-color: #2c3e50; color: white; padding: 25px 20px; text-align: center; }n        .header h1 { color: white; font-size: 28px; margin: 0; text-shadow: 1px 1px 2px rgba(0,0,0,0.5); }n        .content { padding: 20px; }n        .summary-box { background-color: #EBF5FB; padding: 15px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }n        .warning-box { background-color: #FEF5E7; padding: 15px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }n        .headers-box { background-color: #F5F7FA; padding: 15px; margin-bottom: 20px; border-radius: 5px; }n        .findings-box { background-color: white; padding: 15px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }n        .raw-headers-box { background-color: #F5F7FA; padding: 15px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }n        .additional-info-box { background-color: #F5F7FA; padding: 15px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }n        .details-table { width: 100%; border-collapse: collapse; }n        .details-table th { text-align: left; padding: 8px; background-color: #f2f2f2; }n        .details-table td { padding: 8px; border-bottom: 1px solid #eee; }n        .header-badges { margin-top: 10px; }n        h1, h2, h3 { color: #2c3e50; }n        .critical-item { border-left: 4px solid #E74C3C; padding: 10px; margin-bottom: 15px; }n        .critical-title { font-weight: bold; color: #E74C3C; }n        .config-item { border-left: 4px solid #3498DB; padding: 10px; margin-bottom: 15px; }n        .config-title { font-weight: bold; color: #3498DB; }n        pre { background-color: #f8f9fa; padding: 10px; border-radius: 5px; overflow-x: auto; font-family: monospace; margin-top: 5px; }n    nnn    <div class="container">n        <!-- Report Header -->n        <div class="header">n            <h1 style="color: white">Website Security Audit Report</h1>n        </div>n        n        <div class="content">n            <!-- Security Report Summary -->n            <div class="summary-box">n                <h2>Security Report Summary</h2>n                <table style="width: 100%">n                    <tr>n                        <td style="width: 120px" valign="top">n                            ${formatSecurityGrade()}n                        </td>n                        <td valign="top">n                            <table style="width: 100%">n                                <tr>n                                    <td><strong>Site:</strong></td>n                                    <td><a href="${auditData.url}" style="color: #3498db">${auditData.url}</a></td>n                                </tr>n                                <tr>n                                    <td><strong>Report Time:</strong></td>n                                    <td>${auditData.timestamp}</td>n                                </tr>n                                <tr>n                                    <td valign="top"><strong>Headers:</strong></td>n                                    <td>n                                        <div class="header-badges">n                                            ${generateAllHeaderBadges()}n                                        </div>n                                    </td>n                                </tr>n                                <tr>n                                    <td><strong>Critical Issues:</strong></td>n                                    <td>${auditData.criticalCount || 0}</td>n                                </tr>n                                <tr>n                                    <td><strong>Warnings:</strong></td>n                                    <td>${auditData.warningCount || 0}</td>n                                </tr>n                            </table>n                        </td>n                    </tr>n                </table>n            </div>nn            <!-- Warnings Section -->n            <div class="warning-box">n                <h2>Warnings</h2>n                ${formatWarningsSection()}n            </div>nn            <!-- Raw Headers Section -->n            <div class="raw-headers-box">n                <h2>Raw Headers</h2>n                ${formatDetailedRawHeaders()}n            </div>nn            <!-- Security Findings -->n            <div class="findings-box">n                <h2>Security Findings</h2>n                n                <!-- Vulnerabilities -->n                <h3>Vulnerabilities</h3>n                ${formatCriticalVulnerabilities()}n                n                <!-- Configuration Issues -->n                <h3>Configuration Issues</h3>n                ${formatConfigurationIssues()}n            </div>n            n            <div class="additional-info-box">n              <h2>Additional Information</h2>n              ${formatAdditionalInfo()}n            </div>n            n            <!-- Implementation Guide -->n            <div class="findings-box">n                <h2>Implementation Guide</h2>n                <p>This report highlights security issues detected through client-side analysis. For a comprehensive security assessment, consider engaging a professional penetration tester.</p>n                n                <div style="background-color: #eafaf1;padding: 15px;margin-top: 15px;border-left: 4px solid #2ecc71;border-radius: 3px">n                    <p><strong>To implement the fixes above:</strong></p>n                    <ol style="padding-left: 20px;margin-top: 10px">n                        <li>Work with your development team to address each issue in order of criticality</li>n                        <li>Retest after implementing each fix</li>n                        <li>Consider implementing a web application firewall for additional protection</li>n                    </ol>n                </div>n            </div>n            n            <!-- Footer -->n            <div style="text-align: center;padding: 20px;font-size: 12px;color: #777">n                <p>This report was automatically generated and represents an automated assessment of publicly accessible aspects of your website. For a more comprehensive security assessment, consider engaging with a professional security consultant.</p>n                <p>&copy; 2025 Website Security Scanner | Generated on ${auditData.timestamp}</p>n            </div>n        </div>n    </div>nn`;nnreturn [{n  json: {n    ...items[0].json,n    emailHtml: htmln  }n}];"
      },
      "typeVersion": 2
    }
  ],
  "pinData": {},
  "connections": {
    "Scrape Website": {
      "main": [
        [
          {
            "node": "Security Vulnerabilities Audit",
            "type": "main",
            "index": 0
          },
          {
            "node": "Extract Headers for Debug",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "convert to HTML": {
      "main": [
        [
          {
            "node": "Send Security Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Landing Page Url": {
      "main": [
        [
          {
            "node": "Scrape Website",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Audit Results": {
      "main": [
        [
          {
            "node": "convert to HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Security Results": {
      "main": [
        [
          {
            "node": "Aggregate Audit Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Audit Results": {
      "main": [
        [
          {
            "node": "Process Audit Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Content Analysis": {
      "ai_languageModel": [
        [
          {
            "node": "Security Vulnerabilities Audit",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Headers Analysis": {
      "ai_languageModel": [
        [
          {
            "node": "Security Configuration Audit",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Extract Headers for Debug": {
      "main": [
        [
          {
            "node": "Security Configuration Audit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Security Configuration Audit": {
      "main": [
        [
          {
            "node": "Merge Security Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Security Vulnerabilities Audit": {
      "main": [
        [
          {
            "node": "Merge Security Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  }
}