← back to Wine Finder Next

__tests__/security/security-vulnerabilities.test.ts

369 lines

// Security Vulnerability Tests
import { NextRequest } from 'next/server';
import { sanitizeString, validateEmail, validatePrice } from '@/lib/inputValidation';
import { maliciousPayloads } from '../fixtures/testData';

describe('Security Vulnerability Tests', () => {
  describe('SQL Injection Prevention', () => {
    it('should reject SQL injection in bottle ID', () => {
      const sqlInjectionAttempts = [
        "bottle_123'; DROP TABLE bottles; --",
        "bottle_123' OR '1'='1",
        "bottle_123' UNION SELECT * FROM users--",
      ];

      sqlInjectionAttempts.forEach(attempt => {
        const sanitized = sanitizeString(attempt);
        expect(sanitized).not.toContain("'");
        expect(sanitized).not.toContain('--');
        expect(sanitized).not.toContain('DROP TABLE');
      });
    });

    it('should prevent SQL injection in search parameters', () => {
      maliciousPayloads.sqlInjection.forEach(payload => {
        const sanitized = sanitizeString(payload);
        expect(sanitized).not.toContain("'");
        expect(sanitized).not.toMatch(/DROP\s+TABLE/i);
        expect(sanitized).not.toMatch(/UNION\s+SELECT/i);
      });
    });

    it('should sanitize user input before database queries', () => {
      const userInput = "test'; DELETE FROM users WHERE 'a'='a";
      const sanitized = sanitizeString(userInput);

      expect(sanitized).not.toContain("'");
      expect(sanitized).not.toContain('DELETE');
    });
  });

  describe('XSS (Cross-Site Scripting) Prevention', () => {
    it('should strip script tags from user input', () => {
      const xssAttempts = [
        '<script>alert("XSS")</script>',
        '<script src="malicious.js"></script>',
        '<img src=x onerror=alert(1)>',
        '<iframe src="javascript:alert(1)">',
      ];

      xssAttempts.forEach(attempt => {
        const sanitized = sanitizeString(attempt);
        expect(sanitized).not.toContain('<script');
        expect(sanitized).not.toContain('</script>');
        expect(sanitized).not.toContain('<iframe');
      });
    });

    it('should remove event handlers from HTML', () => {
      const eventHandlers = [
        'onclick=alert(1)',
        'onload=malicious()',
        'onerror=steal()',
        'onmouseover=hack()',
      ];

      eventHandlers.forEach(handler => {
        const sanitized = sanitizeString(handler);
        expect(sanitized).not.toMatch(/on\w+=/i);
      });
    });

    it('should remove javascript: protocol', () => {
      const jsProtocols = [
        'javascript:alert(1)',
        'JAVASCRIPT:void(0)',
        'javascript:document.cookie',
      ];

      jsProtocols.forEach(protocol => {
        const sanitized = sanitizeString(protocol);
        expect(sanitized).not.toMatch(/javascript:/i);
      });
    });

    it('should handle all XSS payloads from fixtures', () => {
      maliciousPayloads.xss.forEach(payload => {
        const sanitized = sanitizeString(payload);
        expect(sanitized).not.toContain('<script');
        expect(sanitized).not.toMatch(/javascript:/i);
        expect(sanitized).not.toMatch(/on\w+=/i);
      });
    });
  });

  describe('Command Injection Prevention', () => {
    it('should prevent command injection attempts', () => {
      const commandInjections = [
        '; ls -la',
        '| cat /etc/passwd',
        '`whoami`',
        '$(rm -rf /)',
      ];

      commandInjections.forEach(injection => {
        const sanitized = sanitizeString(injection);
        // These should be escaped or removed
        expect(sanitized).not.toMatch(/;\s*ls/);
        expect(sanitized).not.toMatch(/\|\s*cat/);
        expect(sanitized).not.toContain('`');
        expect(sanitized).not.toContain('$(');
      });
    });

    it('should handle command injection from fixtures', () => {
      maliciousPayloads.commandInjection.forEach(payload => {
        const sanitized = sanitizeString(payload);
        expect(sanitized).not.toContain('`');
        expect(sanitized).not.toContain('$(');
      });
    });
  });

  describe('Path Traversal Prevention', () => {
    it('should prevent path traversal attempts', () => {
      const pathTraversals = [
        '../../../etc/passwd',
        '..\\..\\..\\windows\\system32',
        '/etc/shadow',
        '....//....//etc/passwd',
      ];

      pathTraversals.forEach(path => {
        const sanitized = sanitizeString(path);
        // Should not allow directory traversal
        expect(sanitized).not.toMatch(/\.\.\//);
        expect(sanitized).not.toMatch(/\.\.\\/);
      });
    });
  });

  describe('Input Length Validation', () => {
    it('should enforce maximum input length', () => {
      const longInput = 'a'.repeat(10000);
      const sanitized = sanitizeString(longInput, 1000);

      expect(sanitized.length).toBeLessThanOrEqual(1000);
    });

    it('should handle different max lengths', () => {
      const input = 'test input';

      expect(sanitizeString(input, 5)).toHaveLength(5);
      expect(sanitizeString(input, 100)).toHaveLength(10); // original length
    });
  });

  describe('Email Validation Security', () => {
    it('should reject malicious email formats', () => {
      const maliciousEmails = [
        'test@<script>alert(1)</script>.com',
        'admin\'--@example.com',
        'user@domain.com<script>',
        'javascript:alert(1)@example.com',
      ];

      maliciousEmails.forEach(email => {
        expect(validateEmail(email)).toBe(false);
      });
    });

    it('should prevent excessively long emails', () => {
      const longEmail = 'a'.repeat(250) + '@example.com';
      expect(validateEmail(longEmail)).toBe(false);
    });
  });

  describe('Price Validation Security', () => {
    it('should reject negative prices', () => {
      expect(validatePrice(-1)).toBe(false);
      expect(validatePrice(-100.50)).toBe(false);
    });

    it('should reject infinite or NaN prices', () => {
      expect(validatePrice(Infinity)).toBe(false);
      expect(validatePrice(-Infinity)).toBe(false);
      expect(validatePrice(NaN)).toBe(false);
    });

    it('should reject excessively large prices', () => {
      expect(validatePrice(10000000)).toBe(false);
      expect(validatePrice(Number.MAX_VALUE)).toBe(false);
    });
  });

  describe('CSRF Token Validation', () => {
    it('should validate CSRF tokens exist in POST requests', () => {
      // This would be implemented with actual CSRF middleware
      // For now, test the concept
      const validRequest = {
        headers: {
          'x-csrf-token': 'valid-token-12345',
        },
      };

      const invalidRequest = {
        headers: {},
      };

      expect(validRequest.headers['x-csrf-token']).toBeDefined();
      expect(invalidRequest.headers['x-csrf-token']).toBeUndefined();
    });
  });

  describe('HTTP Header Injection', () => {
    it('should prevent header injection in user-agent', () => {
      const maliciousUserAgent = 'Mozilla/5.0\r\nX-Injected: header';
      const sanitized = sanitizeString(maliciousUserAgent);

      expect(sanitized).not.toContain('\r\n');
      expect(sanitized).not.toContain('X-Injected');
    });

    it('should prevent header injection in referer', () => {
      const maliciousReferer = 'http://example.com\r\nSet-Cookie: session=hacked';
      const sanitized = sanitizeString(maliciousReferer);

      expect(sanitized).not.toContain('\r\n');
      expect(sanitized).not.toContain('Set-Cookie');
    });
  });

  describe('NoSQL Injection Prevention', () => {
    it('should prevent NoSQL injection in JSON payloads', () => {
      const maliciousPayloads = [
        { $gt: '' },
        { $ne: null },
        { $regex: '.*' },
        { $where: 'this.password.length > 0' },
      ];

      maliciousPayloads.forEach(payload => {
        const serialized = JSON.stringify(payload);
        const sanitized = sanitizeString(serialized);

        // Should remove or escape MongoDB operators
        expect(sanitized).not.toContain('$gt');
        expect(sanitized).not.toContain('$ne');
        expect(sanitized).not.toContain('$where');
      });
    });
  });

  describe('Rate Limiting Bypass Prevention', () => {
    it('should not allow rate limit bypass via different IPs', () => {
      // This is tested in rate limit implementation
      // Ensure X-Forwarded-For headers are validated
      const spoofedHeaders = [
        'X-Forwarded-For: 1.2.3.4, 5.6.7.8',
        'X-Real-IP: 1.2.3.4',
      ];

      spoofedHeaders.forEach(header => {
        // Should use most reliable IP source
        expect(header).toBeDefined();
      });
    });
  });

  describe('Authentication Bypass Prevention', () => {
    it('should validate JWT tokens properly', () => {
      const invalidTokens = [
        'eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiYWRtaW4ifQ.', // alg: none
        'malformed.token.structure',
        '',
        'null',
      ];

      invalidTokens.forEach(token => {
        // Token validation would reject these
        expect(token).toBeDefined();
        expect(token.split('.').length <= 3).toBe(true);
      });
    });
  });

  describe('File Upload Security', () => {
    it('should validate file extensions', () => {
      const dangerousExtensions = [
        'file.exe',
        'script.php',
        'malware.sh',
        'virus.bat',
      ];

      const allowedExtensions = ['.jpg', '.png', '.pdf'];

      dangerousExtensions.forEach(filename => {
        const ext = filename.substring(filename.lastIndexOf('.'));
        expect(allowedExtensions.includes(ext)).toBe(false);
      });
    });

    it('should validate MIME types', () => {
      const dangerousMimeTypes = [
        'application/x-executable',
        'application/x-php',
        'text/x-shellscript',
      ];

      const allowedMimeTypes = [
        'image/jpeg',
        'image/png',
        'application/pdf',
      ];

      dangerousMimeTypes.forEach(mimeType => {
        expect(allowedMimeTypes.includes(mimeType)).toBe(false);
      });
    });
  });

  describe('Session Fixation Prevention', () => {
    it('should regenerate session after login', () => {
      // Conceptual test - actual implementation would be in auth middleware
      const sessionBefore = 'session_123';
      const sessionAfter = 'session_456'; // Should be different

      expect(sessionBefore).not.toBe(sessionAfter);
    });
  });

  describe('XML External Entity (XXE) Prevention', () => {
    it('should prevent XXE attacks in XML parsing', () => {
      const xxePayload = `<?xml version="1.0"?>
        <!DOCTYPE foo [
          <!ENTITY xxe SYSTEM "file:///etc/passwd">
        ]>
        <data>&xxe;</data>`;

      const sanitized = sanitizeString(xxePayload);

      expect(sanitized).not.toContain('<!ENTITY');
      expect(sanitized).not.toContain('SYSTEM');
    });
  });

  describe('Server-Side Request Forgery (SSRF) Prevention', () => {
    it('should validate URLs to prevent SSRF', () => {
      const maliciousUrls = [
        'http://localhost/admin',
        'http://169.254.169.254/latest/meta-data/',
        'file:///etc/passwd',
        'http://192.168.1.1/internal',
      ];

      maliciousUrls.forEach(url => {
        // URL validation should reject internal IPs
        const isInternal = url.includes('localhost') ||
                          url.includes('127.0.0.1') ||
                          url.includes('192.168.') ||
                          url.includes('169.254.') ||
                          url.startsWith('file://');

        expect(isInternal).toBe(true);
      });
    });
  });
});