Files
gh-christianlouis-dmarq/docs/XSS_FIXES.md
T
copilot-swe-agent[bot] fd99cee758 Complete code quality audit with comprehensive documentation
- Created detailed audit report (docs/CODE_QUALITY_AUDIT_2026-02.md)
- Created XSS fix guide (docs/XSS_FIXES.md)
- Identified 4 critical XSS vulnerabilities
- Documented CSP improvements needed
- Verified Python code with CodeQL (0 alerts)
- Overall grade: B+ (83/100)

Key findings:
 Excellent Python code quality (A-, 92/100)
 Strong security infrastructure (A, 95/100)
 Good infrastructure & config (A, 95/100)
⚠️  Frontend needs XSS fixes (B-, 72/100)
⚠️  Test suite needs attention (B, 80/100)

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-02-09 12:13:37 +00:00

7.5 KiB

XSS Vulnerability Fixes - Quick Reference

This document provides specific code fixes for the XSS vulnerabilities identified in the code quality audit.

Issue 1: dashboard.js - Line 234

VULNERABLE CODE

row.innerHTML = `
    <td>${domainName}</td>
    <td>${formattedDate}</td>
    <td>${report.is_compliant ? 
        '<span style="color: green;">Compliant</span>' : 
        '<span style="color: red;">Non-compliant</span>'
    }</td>
`;

SECURE FIX

// Create row
const row = document.createElement('tr');

// Domain cell - safe text content
const domainCell = document.createElement('td');
domainCell.textContent = domainName;
row.appendChild(domainCell);

// Date cell - safe text content
const dateCell = document.createElement('td');
dateCell.textContent = formattedDate;
row.appendChild(dateCell);

// Compliance status cell
const statusCell = document.createElement('td');
const statusSpan = document.createElement('span');
statusSpan.textContent = report.is_compliant ? 'Compliant' : 'Non-compliant';
// Use CSS classes instead of inline styles
statusSpan.className = report.is_compliant ? 'text-success' : 'text-error';
row.appendChild(statusCell.appendChild(statusSpan));

Alternative using DaisyUI classes

statusSpan.className = report.is_compliant ? 'badge badge-success' : 'badge badge-error';

Issue 2: dashboard.js - Line 15

VULNERABLE CODE

alertDiv.innerHTML = message;

SECURE FIX

alertDiv.textContent = message;

Issue 3: login.js - Line 9

VULNERABLE CODE

errorDiv.innerHTML = `<div class="alert alert-error">${message}</div>`;

SECURE FIX

// Clear existing content
errorDiv.innerHTML = '';

// Create alert div
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-error';
alertDiv.textContent = message;

// Append to error div
errorDiv.appendChild(alertDiv);

Even Safer Alternative

function showError(message) {
    const errorDiv = document.getElementById('error-div');
    errorDiv.innerHTML = ''; // Clear previous errors
    
    const alertDiv = document.createElement('div');
    alertDiv.className = 'alert alert-error';
    
    const icon = document.createElement('svg');
    icon.innerHTML = '<path d="..."/>'; // Safe - controlled SVG path
    icon.className = 'stroke-current shrink-0 h-6 w-6';
    
    const span = document.createElement('span');
    span.textContent = message; // User input here - safe
    
    alertDiv.appendChild(icon);
    alertDiv.appendChild(span);
    errorDiv.appendChild(alertDiv);
}

Issue 4: setup.js - Line 9

VULNERABLE CODE

alertDiv.innerHTML = message;

SECURE FIX

alertDiv.textContent = message;

Issue 5: Credentials in localStorage (setup.js Lines 188-189)

INSECURE CODE

localStorage.setItem('setup_cloudflare_token', cloudflareToken);
localStorage.setItem('setup_cloudflare_zone', cloudflareZone);

SECURE FIX

Step 1: Remove client-side storage

// DELETE these lines completely
// localStorage.setItem('setup_cloudflare_token', cloudflareToken);
// localStorage.setItem('setup_cloudflare_zone', cloudflareZone);

Step 2: Send to backend immediately

async function saveCloudflareCredentials(token, zone) {
    const response = await fetch('/api/v1/settings/cloudflare', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${getAuthToken()}`,
        },
        body: JSON.stringify({
            cloudflare_api_token: token,
            cloudflare_zone_id: zone
        })
    });
    
    if (!response.ok) {
        throw new Error('Failed to save Cloudflare credentials');
    }
    
    // Don't store the actual credentials - just a success flag
    sessionStorage.setItem('cloudflare_configured', 'true');
}

Step 3: Backend endpoint (Python)

from app.core.security import encrypt_credential

@router.post("/api/v1/settings/cloudflare")
async def save_cloudflare_credentials(
    credentials: CloudflareCredentials,
    current_user: User = Depends(get_current_user)
):
    """Save Cloudflare credentials securely"""
    # Encrypt before storing
    encrypted_token = encrypt_credential(credentials.cloudflare_api_token)
    encrypted_zone = encrypt_credential(credentials.cloudflare_zone_id)
    
    # Store in database with encryption
    db.store_setting("cloudflare_token", encrypted_token, user_id=current_user.id)
    db.store_setting("cloudflare_zone", encrypted_zone, user_id=current_user.id)
    
    return {"success": True, "message": "Credentials saved securely"}

Testing Your Fixes

Manual XSS Test Cases

Test 1: Malicious Domain Name

// Try injecting this as a domain name
const maliciousDomain = '<img src=x onerror=alert("XSS")>';

// With vulnerable code: XSS executes
// With fixed code: Displays as text (safe)

Test 2: Script Injection

// Try injecting this as a message
const maliciousMessage = '<script>alert("XSS")</script>';

// With vulnerable code: Script executes
// With fixed code: Displays as text (safe)

Test 3: Event Handler Injection

// Try injecting this
const maliciousData = '<div onmouseover="alert(\'XSS\')">Hover me</div>';

// With vulnerable code: XSS on hover
// With fixed code: Displays as text (safe)

Automated Testing

// Add to your test suite
describe('XSS Prevention Tests', () => {
    const xssPayloads = [
        '<script>alert("XSS")</script>',
        '<img src=x onerror=alert("XSS")>',
        '<svg onload=alert("XSS")>',
        '"><script>alert("XSS")</script>',
        'javascript:alert("XSS")',
    ];
    
    xssPayloads.forEach(payload => {
        it(`should safely handle XSS payload: ${payload}`, () => {
            const element = renderDomainRow(payload, new Date(), {is_compliant: true});
            
            // Check that payload is not executed
            expect(element.innerHTML).not.toContain('<script>');
            expect(element.innerHTML).not.toContain('onerror=');
            
            // Check that it's displayed as text
            expect(element.textContent).toContain(payload);
        });
    });
});

CSP Header Update

After fixing the XSS issues, update your CSP header in backend/app/middleware/security.py:

Current (Insecure)

"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",

Target (Secure)

"script-src 'self'",  # No unsafe-inline needed
"style-src 'self' https://fonts.googleapis.com",  # No unsafe-inline needed

Verification Checklist

  • All innerHTML usage replaced with safe DOM methods
  • All user input uses textContent not innerHTML
  • No credentials stored in localStorage
  • Inline styles replaced with CSS classes
  • CSP headers updated to remove 'unsafe-inline'
  • Manual XSS testing completed
  • Automated tests added
  • Code review completed

Resources