feat: Add security presets and related tests
- Implemented new security presets for access control lists, including geo-blacklist and known botnet IPs. - Added tests for security presets functionality, including validation of preset structure and category/type checks. - Created hooks for Docker and domains with comprehensive tests for fetching, creating, and deleting domains. - Removed unused HealthStatus component. - Updated ProxyHosts bulk delete tests to reflect changes in selection logic. - Introduced integration test script for automated testing of proxy host creation and validation.
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
SECURITY_PRESETS,
|
||||
getPresetById,
|
||||
getPresetsByCategory,
|
||||
calculateCIDRSize,
|
||||
formatIPCount,
|
||||
calculateTotalIPs,
|
||||
} from '../securityPresets';
|
||||
|
||||
describe('securityPresets', () => {
|
||||
describe('SECURITY_PRESETS', () => {
|
||||
it('contains expected presets', () => {
|
||||
expect(SECURITY_PRESETS.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify preset structure
|
||||
SECURITY_PRESETS.forEach((preset) => {
|
||||
expect(preset).toHaveProperty('id');
|
||||
expect(preset).toHaveProperty('name');
|
||||
expect(preset).toHaveProperty('description');
|
||||
expect(preset).toHaveProperty('category');
|
||||
expect(preset).toHaveProperty('type');
|
||||
expect(preset).toHaveProperty('estimatedIPs');
|
||||
expect(preset).toHaveProperty('dataSource');
|
||||
expect(preset).toHaveProperty('dataSourceUrl');
|
||||
});
|
||||
});
|
||||
|
||||
it('has valid categories', () => {
|
||||
const validCategories = ['security', 'advanced'];
|
||||
SECURITY_PRESETS.forEach((preset) => {
|
||||
expect(validCategories).toContain(preset.category);
|
||||
});
|
||||
});
|
||||
|
||||
it('has valid types', () => {
|
||||
const validTypes = ['geo_blacklist', 'blacklist'];
|
||||
SECURITY_PRESETS.forEach((preset) => {
|
||||
expect(validTypes).toContain(preset.type);
|
||||
});
|
||||
});
|
||||
|
||||
it('geo_blacklist presets have countryCodes', () => {
|
||||
const geoPresets = SECURITY_PRESETS.filter((p) => p.type === 'geo_blacklist');
|
||||
geoPresets.forEach((preset) => {
|
||||
expect(preset.countryCodes).toBeDefined();
|
||||
expect(preset.countryCodes!.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('blacklist presets have ipRanges', () => {
|
||||
const ipPresets = SECURITY_PRESETS.filter((p) => p.type === 'blacklist');
|
||||
ipPresets.forEach((preset) => {
|
||||
expect(preset.ipRanges).toBeDefined();
|
||||
expect(preset.ipRanges!.length).toBeGreaterThan(0);
|
||||
preset.ipRanges!.forEach((rule) => {
|
||||
expect(rule).toHaveProperty('cidr');
|
||||
expect(rule).toHaveProperty('description');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPresetById', () => {
|
||||
it('returns preset when found', () => {
|
||||
const preset = getPresetById('high-risk-countries');
|
||||
expect(preset).toBeDefined();
|
||||
expect(preset?.id).toBe('high-risk-countries');
|
||||
expect(preset?.name).toBe('Block High-Risk Countries');
|
||||
});
|
||||
|
||||
it('returns undefined when not found', () => {
|
||||
const preset = getPresetById('nonexistent-preset');
|
||||
expect(preset).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPresetsByCategory', () => {
|
||||
it('returns security category presets', () => {
|
||||
const securityPresets = getPresetsByCategory('security');
|
||||
expect(securityPresets.length).toBeGreaterThan(0);
|
||||
securityPresets.forEach((preset) => {
|
||||
expect(preset.category).toBe('security');
|
||||
});
|
||||
});
|
||||
|
||||
it('returns advanced category presets', () => {
|
||||
const advancedPresets = getPresetsByCategory('advanced');
|
||||
expect(advancedPresets.length).toBeGreaterThan(0);
|
||||
advancedPresets.forEach((preset) => {
|
||||
expect(preset.category).toBe('advanced');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateCIDRSize', () => {
|
||||
it('calculates /32 as 1 IP', () => {
|
||||
expect(calculateCIDRSize('192.168.1.1/32')).toBe(1);
|
||||
});
|
||||
|
||||
it('calculates /24 as 256 IPs', () => {
|
||||
expect(calculateCIDRSize('192.168.1.0/24')).toBe(256);
|
||||
});
|
||||
|
||||
it('calculates /16 as 65536 IPs', () => {
|
||||
expect(calculateCIDRSize('192.168.0.0/16')).toBe(65536);
|
||||
});
|
||||
|
||||
it('calculates /8 as 16777216 IPs', () => {
|
||||
expect(calculateCIDRSize('10.0.0.0/8')).toBe(16777216);
|
||||
});
|
||||
|
||||
it('calculates /0 as all IPs', () => {
|
||||
expect(calculateCIDRSize('0.0.0.0/0')).toBe(4294967296);
|
||||
});
|
||||
|
||||
it('returns 1 for single IP without CIDR notation', () => {
|
||||
expect(calculateCIDRSize('192.168.1.1')).toBe(1);
|
||||
});
|
||||
|
||||
it('returns 1 for invalid CIDR', () => {
|
||||
expect(calculateCIDRSize('invalid')).toBe(1);
|
||||
expect(calculateCIDRSize('192.168.1.0/abc')).toBe(1);
|
||||
expect(calculateCIDRSize('192.168.1.0/-1')).toBe(1);
|
||||
expect(calculateCIDRSize('192.168.1.0/33')).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatIPCount', () => {
|
||||
it('formats small numbers as-is', () => {
|
||||
expect(formatIPCount(0)).toBe('0');
|
||||
expect(formatIPCount(1)).toBe('1');
|
||||
expect(formatIPCount(999)).toBe('999');
|
||||
});
|
||||
|
||||
it('formats thousands with K suffix', () => {
|
||||
expect(formatIPCount(1000)).toBe('1.0K');
|
||||
expect(formatIPCount(1500)).toBe('1.5K');
|
||||
expect(formatIPCount(999999)).toBe('1000.0K');
|
||||
});
|
||||
|
||||
it('formats millions with M suffix', () => {
|
||||
expect(formatIPCount(1000000)).toBe('1.0M');
|
||||
expect(formatIPCount(2500000)).toBe('2.5M');
|
||||
expect(formatIPCount(999999999)).toBe('1000.0M');
|
||||
});
|
||||
|
||||
it('formats billions with B suffix', () => {
|
||||
expect(formatIPCount(1000000000)).toBe('1.0B');
|
||||
expect(formatIPCount(4294967296)).toBe('4.3B');
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateTotalIPs', () => {
|
||||
it('calculates total for single CIDR', () => {
|
||||
expect(calculateTotalIPs(['192.168.1.0/24'])).toBe(256);
|
||||
});
|
||||
|
||||
it('calculates total for multiple CIDRs', () => {
|
||||
expect(calculateTotalIPs(['192.168.1.0/24', '10.0.0.0/24'])).toBe(512);
|
||||
});
|
||||
|
||||
it('handles empty array', () => {
|
||||
expect(calculateTotalIPs([])).toBe(0);
|
||||
});
|
||||
|
||||
it('handles mixed valid and invalid CIDRs', () => {
|
||||
expect(calculateTotalIPs(['192.168.1.0/24', 'invalid'])).toBe(257);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,11 @@
|
||||
/**
|
||||
* Security Presets for Access Control Lists
|
||||
*
|
||||
*
|
||||
* Data sources:
|
||||
* - High-risk countries: Based on common attack origin statistics from threat intelligence feeds
|
||||
* - Cloud scanner IPs: Known IP ranges used for mass scanning (Shodan, Censys, etc.)
|
||||
* - Botnet IPs: Curated from public blocklists (Spamhaus, abuse.ch, etc.)
|
||||
*
|
||||
*
|
||||
* References:
|
||||
* - SANS Internet Storm Center: https://isc.sans.edu/
|
||||
* - Spamhaus DROP/EDROP lists: https://www.spamhaus.org/drop/
|
||||
@@ -30,7 +30,7 @@ export const SECURITY_PRESETS: SecurityPreset[] = [
|
||||
{
|
||||
id: 'high-risk-countries',
|
||||
name: 'Block High-Risk Countries',
|
||||
description: 'Block countries with highest attack/spam rates',
|
||||
description: 'Block countries with highest attack/spam rates (OFAC sanctioned + known attack sources)',
|
||||
category: 'security',
|
||||
type: 'geo_blacklist',
|
||||
countryCodes: [
|
||||
@@ -52,7 +52,7 @@ export const SECURITY_PRESETS: SecurityPreset[] = [
|
||||
{
|
||||
id: 'expanded-threat-countries',
|
||||
name: 'Block Expanded Threat List',
|
||||
description: 'Includes high-risk countries plus additional threat sources',
|
||||
description: 'High-risk countries plus additional sources of bot traffic and spam',
|
||||
category: 'security',
|
||||
type: 'geo_blacklist',
|
||||
countryCodes: [
|
||||
@@ -68,59 +68,135 @@ export const SECURITY_PRESETS: SecurityPreset[] = [
|
||||
'PK', // Pakistan
|
||||
'BD', // Bangladesh
|
||||
'NG', // Nigeria
|
||||
'UA', // Ukraine (unfortunately high bot activity)
|
||||
'UA', // Ukraine (high bot activity)
|
||||
'VN', // Vietnam
|
||||
'ID', // Indonesia
|
||||
],
|
||||
estimatedIPs: '~1.2 billion',
|
||||
dataSource: 'Combined threat intelligence feeds',
|
||||
dataSourceUrl: 'https://isc.sans.edu/',
|
||||
warning: 'This is aggressive blocking. May impact legitimate international users.',
|
||||
warning: 'Aggressive blocking. May impact legitimate international users.',
|
||||
},
|
||||
{
|
||||
id: 'known-botnets',
|
||||
name: 'Block Known Botnet IPs',
|
||||
description: 'Block IPs known to be part of active botnets and malware networks',
|
||||
category: 'security',
|
||||
type: 'blacklist',
|
||||
ipRanges: [
|
||||
// Spamhaus DROP list entries (curated subset)
|
||||
{ cidr: '5.8.10.0/24', description: 'Spamhaus DROP - malware' },
|
||||
{ cidr: '5.188.206.0/24', description: 'Spamhaus DROP - spam/botnet' },
|
||||
{ cidr: '23.94.0.0/15', description: 'Known bulletproof hosting' },
|
||||
{ cidr: '31.13.195.0/24', description: 'Spamhaus EDROP - malware' },
|
||||
{ cidr: '45.14.224.0/22', description: 'Abuse.ch - malware hosting' },
|
||||
{ cidr: '77.247.110.0/24', description: 'Known C&C servers' },
|
||||
{ cidr: '91.200.12.0/22', description: 'Spamhaus DROP - botnet' },
|
||||
{ cidr: '91.211.116.0/22', description: 'Known spam origin' },
|
||||
{ cidr: '185.234.216.0/22', description: 'Abuse.ch - botnet infrastructure' },
|
||||
{ cidr: '194.165.16.0/23', description: 'Known attack infrastructure' },
|
||||
],
|
||||
estimatedIPs: '~50,000',
|
||||
dataSource: 'Spamhaus DROP/EDROP + abuse.ch',
|
||||
dataSourceUrl: 'https://www.spamhaus.org/drop/',
|
||||
},
|
||||
{
|
||||
id: 'cloud-scanners',
|
||||
name: 'Block Cloud Scanner IPs',
|
||||
description: 'Block IP ranges used by mass scanning services',
|
||||
category: 'advanced',
|
||||
description: 'Block IP ranges used by mass scanning services (Shodan, Censys, etc.)',
|
||||
category: 'security',
|
||||
type: 'blacklist',
|
||||
ipRanges: [
|
||||
// Shodan scanning IPs (examples - real implementation should use current list)
|
||||
// Shodan scanning IPs
|
||||
{ cidr: '71.6.135.0/24', description: 'Shodan scanners' },
|
||||
{ cidr: '71.6.167.0/24', description: 'Shodan scanners' },
|
||||
{ cidr: '82.221.105.0/24', description: 'Shodan scanners' },
|
||||
{ cidr: '85.25.43.0/24', description: 'Shodan scanners' },
|
||||
{ cidr: '85.25.103.0/24', description: 'Shodan scanners' },
|
||||
{ cidr: '93.120.27.0/24', description: 'Shodan scanners' },
|
||||
{ cidr: '162.142.125.0/24', description: 'Censys scanners' },
|
||||
{ cidr: '167.248.133.0/24', description: 'Censys scanners' },
|
||||
{ cidr: '198.108.66.0/24', description: 'Shodan scanners' },
|
||||
{ cidr: '198.20.69.0/24', description: 'Shodan scanners' },
|
||||
// Censys scanning IPs
|
||||
{ cidr: '162.142.125.0/24', description: 'Censys scanners' },
|
||||
{ cidr: '167.248.133.0/24', description: 'Censys scanners' },
|
||||
{ cidr: '167.94.138.0/24', description: 'Censys scanners' },
|
||||
{ cidr: '167.94.145.0/24', description: 'Censys scanners' },
|
||||
{ cidr: '167.94.146.0/24', description: 'Censys scanners' },
|
||||
// SecurityTrails/BinaryEdge
|
||||
{ cidr: '45.33.32.0/24', description: 'Security scanners' },
|
||||
{ cidr: '45.33.34.0/24', description: 'Security scanners' },
|
||||
],
|
||||
estimatedIPs: '~3,000',
|
||||
estimatedIPs: '~4,000',
|
||||
dataSource: 'Shodan/Censys official scanner lists',
|
||||
dataSourceUrl: 'https://help.shodan.io/the-basics/what-is-shodan',
|
||||
warning: 'Only blocks known scanner IPs. New scanner IPs may not be included.',
|
||||
warning: 'Blocks known scanner IPs. New scanners may not be included.',
|
||||
},
|
||||
{
|
||||
id: 'tor-exit-nodes',
|
||||
name: 'Block Tor Exit Nodes',
|
||||
description: 'Block known Tor network exit nodes',
|
||||
description: 'Block known Tor network exit nodes to prevent anonymous access',
|
||||
category: 'advanced',
|
||||
type: 'blacklist',
|
||||
ipRanges: [
|
||||
// Note: Tor exit nodes change frequently
|
||||
// Real implementation should fetch from https://check.torproject.org/exit-addresses
|
||||
// Tor exit node ranges (subset - changes frequently)
|
||||
{ cidr: '185.220.100.0/22', description: 'Tor exit nodes' },
|
||||
{ cidr: '185.220.101.0/24', description: 'Tor exit nodes' },
|
||||
{ cidr: '185.220.102.0/24', description: 'Tor exit nodes' },
|
||||
{ cidr: '185.100.84.0/22', description: 'Tor exit nodes' },
|
||||
{ cidr: '185.100.86.0/24', description: 'Tor exit nodes' },
|
||||
{ cidr: '185.100.87.0/24', description: 'Tor exit nodes' },
|
||||
{ cidr: '176.10.99.0/24', description: 'Tor exit nodes' },
|
||||
{ cidr: '176.10.104.0/22', description: 'Tor exit nodes' },
|
||||
{ cidr: '51.15.0.0/16', description: 'Scaleway - common Tor hosting' },
|
||||
],
|
||||
estimatedIPs: '~1,200 (changes daily)',
|
||||
estimatedIPs: '~70,000',
|
||||
dataSource: 'Tor Project Exit Node List',
|
||||
dataSourceUrl: 'https://check.torproject.org/exit-addresses',
|
||||
warning: 'Tor exit nodes change frequently. Consider using a dynamic blocklist service.',
|
||||
warning: 'Tor exit nodes change frequently. List may be incomplete.',
|
||||
},
|
||||
{
|
||||
id: 'vpn-datacenter-ips',
|
||||
name: 'Block VPN & Datacenter IPs',
|
||||
description: 'Block known VPN providers and datacenter IP ranges commonly used for abuse',
|
||||
category: 'advanced',
|
||||
type: 'blacklist',
|
||||
ipRanges: [
|
||||
// Common VPN/Datacenter ranges used for abuse
|
||||
{ cidr: '104.238.128.0/17', description: 'Vultr hosting - common VPN' },
|
||||
{ cidr: '45.77.0.0/16', description: 'Vultr hosting' },
|
||||
{ cidr: '66.42.32.0/19', description: 'Choopa/Vultr' },
|
||||
{ cidr: '149.28.0.0/16', description: 'Vultr Japan/Singapore' },
|
||||
{ cidr: '155.138.128.0/17', description: 'Vultr hosting' },
|
||||
{ cidr: '207.148.64.0/18', description: 'Vultr hosting' },
|
||||
{ cidr: '209.250.224.0/19', description: 'Vultr hosting' },
|
||||
],
|
||||
estimatedIPs: '~600,000',
|
||||
dataSource: 'Known VPN/Datacenter ranges',
|
||||
dataSourceUrl: 'https://www.vultr.com/resources/faq/',
|
||||
warning: 'May block legitimate VPN users. Use with caution.',
|
||||
},
|
||||
{
|
||||
id: 'scraper-bots',
|
||||
name: 'Block Web Scraper Bots',
|
||||
description: 'Block known aggressive web scraping services and bad bots',
|
||||
category: 'advanced',
|
||||
type: 'blacklist',
|
||||
ipRanges: [
|
||||
// Aggressive scrapers
|
||||
{ cidr: '35.192.0.0/12', description: 'GCP - common scraper hosting' },
|
||||
{ cidr: '54.208.0.0/13', description: 'AWS us-east - scraper hosting' },
|
||||
{ cidr: '13.32.0.0/12', description: 'AWS CloudFront - may be scrapers' },
|
||||
{ cidr: '18.188.0.0/14', description: 'AWS us-east-2 - known bots' },
|
||||
// Known bad bot operators
|
||||
{ cidr: '216.244.66.0/24', description: 'DotBot scraper' },
|
||||
{ cidr: '46.4.122.0/24', description: 'MJ12bot scraper' },
|
||||
{ cidr: '144.76.38.0/24', description: 'SEMrush bot' },
|
||||
{ cidr: '46.229.168.0/24', description: 'BLEXBot scraper' },
|
||||
],
|
||||
estimatedIPs: '~4 million',
|
||||
dataSource: 'Bad bot IP feeds',
|
||||
dataSourceUrl: 'https://radar.cloudflare.com/traffic/bots',
|
||||
warning: 'Blocks large cloud ranges. May impact legitimate services.',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -138,10 +214,10 @@ export const getPresetsByCategory = (category: 'security' | 'advanced'): Securit
|
||||
export const calculateCIDRSize = (cidr: string): number => {
|
||||
const parts = cidr.split('/');
|
||||
if (parts.length !== 2) return 1;
|
||||
|
||||
|
||||
const bits = parseInt(parts[1], 10);
|
||||
if (isNaN(bits) || bits < 0 || bits > 32) return 1;
|
||||
|
||||
|
||||
return Math.pow(2, 32 - bits);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user