Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code donation: A different HTML output that would use the JSON report #264

Open
paul-hammant opened this issue Nov 21, 2024 · 0 comments
Open

Comments

@paul-hammant
Copy link

paul-hammant commented Nov 21, 2024

Report as you'd initially see it

image

Report after clicking to expand the summary

image

HTML + JS + DataTables.js:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Report: GoTestWAF testing Web Application Firewall</title>
    <link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">

    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
        h2 { margin-top: 40px; }

        .tooltip {
            position: relative;
            display: inline-block;
            cursor: pointer;
        }

        .tooltip .tooltiptext {
            visibility: hidden;
            width: 600px;
            background-color: #f9f9f9;
            color: #333;
            text-align: left;
            border-radius: 6px;
            padding: 10px;
            position: absolute;
            z-index: 1;
            bottom: 125%; /* Position above the text */
            left: 50%;
            margin-left: -150px;
            opacity: 0;
            transition: opacity 0.3s;
            box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1);
        }

        .tooltip:hover .tooltiptext {
            visibility: visible;
            opacity: 1;
        }
    </style>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
</head>
<body>
    <h1>Report: GoTestWAF testing Web Application Firewall</h1>
    <div id="summary"></div>
    <div id="tables"></div>

    <script>
        function toggleSummary() {
            const summaryContent = document.getElementById('summary-content');
            const toggleButton = document.getElementById('toggle-summary');
            if (summaryContent.style.display === 'none') {
                summaryContent.style.display = 'block';
                toggleButton.innerHTML = '[V] Hide Summary';
            } else {
                summaryContent.style.display = 'none';
                toggleButton.innerHTML = '[&gt;] Show Summary';
            }
        }
        function escapeHtml(unsafe) {
            return unsafe
                .replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;")
                .replace(/"/g, "&quot;")
                .replace(/'/g, "&#039;");
        }
        function formatDate(dateString) {
            const date = new Date(dateString);
            const year = date.getFullYear();
            const month = String(date.getMonth() + 1).padStart(2, '0');
            const day = String(date.getDate()).padStart(2, '0');
            const hours = String(date.getHours()).padStart(2, '0');
            const minutes = String(date.getMinutes()).padStart(2, '0');
            const seconds = String(date.getSeconds()).padStart(2, '0');
            return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
        }

        function generateSummaryTables(summary) {
            const descriptions = {
                "true_positive_tests": "Malicious requests that were correctly identified and blocked by the WAF, preventing them from reaching the application.",
                "true_negative_tests": "Legitimate requests that were correctly allowed through by the WAF without being flagged or blocked."
            };
            let html = '';
            for (const [key, value] of Object.entries(summary)) {
                const description = descriptions[key] || key.replace(/_/g, ' ');
                html += `<h3>${description}</h3>`;
                html += '<table><thead><tr>';
                for (const header in value) {
                    if (header !== 'test_sets') {
                        html += `<th>${header.replace(/_/g, ' ')}</th>`;
                    }
                }
                html += '</tr></thead><tbody><tr>';
                for (const cell in value) {
                    if (cell !== 'test_sets') {
                        const cellValue = value[cell];
                        if (typeof cellValue === 'object') {
                            html += '<td><table><tbody>';
                            for (const [subKey, subValue] of Object.entries(cellValue)) {
                                html += `<tr><th>${subKey.replace(/_/g, ' ')}</th><td>${subValue}</td></tr>`;
                            }
                            html += '</tbody></table></td>';
                        } else {
                            html += `<td>${cellValue}</td>`;
                        }
                    }
                }
                html += '</tr></tbody></table>';

                if (value.test_sets) {
                    const tableId = `table-${key.replace(/\s+/g, '-')}-test-sets`;
                    html += `<table id="${tableId}" class="display"><thead><tr><th>Set</th><th>Test</th><th>Percentage</th><th>Sent</th><th>Blocked</th><th>Bypassed</th><th>Unresolved</th><th>Failed</th></tr></thead><tbody>`;
                    for (const [set, tests] of Object.entries(summary[key].test_sets)) {
                        for (const [test, results] of Object.entries(tests)) {
                            html += `<tr><td>${set}</td><td>${test}</td>`;
                            html += `<td>${results.percentage !== undefined ? results.percentage : 'N/A'}</td>`;
                            html += `<td>${results.sent !== undefined ? results.sent : 'N/A'}</td>`;
                            html += `<td>${results.blocked !== undefined ? results.blocked : 'N/A'}</td>`;
                            html += `<td>${results.bypassed !== undefined ? results.bypassed : 'N/A'}</td>`;
                            html += `<td>${results.unresolved !== undefined ? results.unresolved : 'N/A'}</td>`;
                            html += `<td>${results.failed !== undefined ? results.failed : 'N/A'}</td></tr>`;
                        }
                    }
                    html += '</tbody></table>';
                }
            }
            return html;
        }

        fetch('output.json')
            .then(response => response.json())
            .then(data => {
                const summaryDiv = document.getElementById('summary');
                const tablesDiv = document.getElementById('tables');

                // Display summary
                const summaryHtml = `
                    <p><strong>Date:</strong> ${formatDate(data.date)}</p>
                    <p><strong>Project Name:</strong> ${data.project_name}</p>
                    <p><strong>URL:</strong> ${data.url}</p>
                    <p><strong>Score:</strong> ${data.score}</p>
                        <div id="summary-section">
                            <button id="toggle-summary" onclick="toggleSummary()">[&gt;] Show Summary</button>
                            <div id="summary-content" style="display: none;">
                                ${generateSummaryTables(data.summary)}
                            </div>
                        </div>
                `;

                summaryDiv.innerHTML = summaryHtml;

                // Function to create tables
                function createTable(title, testData) {
                    const tableId = `table-${title.replace(/\s+/g, '-')}`;
                    const tooltipText = {
                        "true negative tests payloads - allowed": `Legitimate requests that were correctly allowed through by the WAF without being flagged or blocked.`,
                        "true negative tests payloads - blocked": `Legitimate requests that should have been allowed through by the WAF, but were incorrectly blocked as if they were malicious.`,
                        "true negative tests payloads - unresolved": `Legitimate requests where the WAF's response was unclear or the result could not be determined.`,
                        "true positive tests payloads - blocked": `Malicious requests that were correctly identified and blocked by the WAF, preventing them from reaching the application.`,
                        "true positive tests payloads - bypassed": `Malicious requests that should have been detected and blocked by the WAF, but instead incorrectly bypassed the WAF as it failed to identify them.`,
                        "true positive tests payloads - unresolved": `Malicious requests that should have been detected and blocked by the WAF, but the outcome is unclear or indeterminate due to an ambiguous response or incomplete analysis by the WAF.`
                    };
                    let tableHtml = `<h2 class="tooltip">${tooltipText[title]}<span class="tooltiptext">${title}</span></h2><table id="${tableId}" class="display"><thead><tr>`;
                    const headers = Object.keys(testData[0]);
                    // Move 'payload' to the end
                    const reorderedHeaders = headers.filter(header => header !== 'payload').concat('payload');
                    reorderedHeaders.forEach(header => {
                        tableHtml += `<th>${header}</th>`;
                    });
                    tableHtml += `</tr></thead><tbody>`;
                    testData.forEach(row => {
                        tableHtml += `<tr>`;
                        reorderedHeaders.forEach(header => {
                            if (header === 'payload') {
                                let payload = row[header];
                                if (payload.length > 256) {
                                    payload = payload.substring(0, 256) + '...';
                                }
                                tableHtml += `<td><pre>${escapeHtml(payload)}</pre></td>`;
                            } else if (header === 'test_result') {
                                let icon = '';
                                if (row[header] === 'passed') {
                                    icon = '✅';
                                } else if (row[header] === 'failed') {
                                    icon = '❌';
                                } else if (row[header] === 'unknown') {
                                    icon = '❓';
                                }
                                tableHtml += `<td>${icon}</td>`;
                            } else {
                                tableHtml += `<td>${row[header]}</td>`;
                            }
                        });
                        tableHtml += `</tr>`;
                    });
                    tableHtml += `</tbody></table>`;
                    return tableHtml;
                }

                // Create tables for each test type
                const testTypes = ['true_positive_tests_payloads', 'true_negative_tests_payloads'];
                testTypes.forEach(type => {
                    for (const [key, value] of Object.entries(data[type])) {
                        tablesDiv.innerHTML += createTable(`${type.replace(/_/g, ' ')} - ${key}`, value);
                    }
                });
                // Initialize DataTables for each table
                $(document).ready(function() {
                    $('table.display').DataTable();
                });
            })
            .catch(error => console.error('Error loading JSON:', error));
    </script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant