ci(docker): use step outputs for REGISTRY_PASSWORD in docker-publish workflow
This commit is contained in:
@@ -408,6 +408,9 @@ export function AccessListForm({ initialData, onSubmit, onCancel, onDelete, isLo
|
||||
|
||||
{!formData.local_network_only && (
|
||||
<>
|
||||
<div className="mb-2 text-xs text-gray-500">
|
||||
Note: IP-based blocklists (botnets, cloud scanners, VPN ranges) are better handled by CrowdSec, WAF, or rate limiting. Use IP-based ACLs sparingly for static or known ranges.
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<label className="block text-sm font-medium text-gray-300">IP Addresses / CIDR Ranges</label>
|
||||
|
||||
@@ -48,16 +48,10 @@ describe('securityPresets', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('blacklist presets have ipRanges', () => {
|
||||
it('no IP-based blacklist presets are included (CrowdSec handles dynamic IP threats)', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
// IP-based blacklists are deprecated and should be handled by CrowdSec / WAF / rate limiting
|
||||
expect(ipPresets.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,9 +78,9 @@ describe('securityPresets', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('returns advanced category presets', () => {
|
||||
it('returns advanced category presets (may be empty)', () => {
|
||||
const advancedPresets = getPresetsByCategory('advanced');
|
||||
expect(advancedPresets.length).toBeGreaterThan(0);
|
||||
expect(Array.isArray(advancedPresets)).toBe(true);
|
||||
advancedPresets.forEach((preset) => {
|
||||
expect(preset.category).toBe('advanced');
|
||||
});
|
||||
|
||||
@@ -77,127 +77,6 @@ export const SECURITY_PRESETS: SecurityPreset[] = [
|
||||
dataSourceUrl: 'https://isc.sans.edu/',
|
||||
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 (Shodan, Censys, etc.)',
|
||||
category: 'security',
|
||||
type: 'blacklist',
|
||||
ipRanges: [
|
||||
// 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: '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: '~4,000',
|
||||
dataSource: 'Shodan/Censys official scanner lists',
|
||||
dataSourceUrl: 'https://help.shodan.io/the-basics/what-is-shodan',
|
||||
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 to prevent anonymous access',
|
||||
category: 'advanced',
|
||||
type: 'blacklist',
|
||||
ipRanges: [
|
||||
// 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: '~70,000',
|
||||
dataSource: 'Tor Project Exit Node List',
|
||||
dataSourceUrl: 'https://check.torproject.org/exit-addresses',
|
||||
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.',
|
||||
},
|
||||
];
|
||||
|
||||
export const getPresetById = (id: string): SecurityPreset | undefined => {
|
||||
|
||||
@@ -510,6 +510,11 @@ export default function ProxyHosts() {
|
||||
WS
|
||||
</span>
|
||||
)}
|
||||
{host.access_list_id && (
|
||||
<span className="px-2 py-1 text-xs bg-purple-900/30 text-purple-400 rounded">
|
||||
ACL
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{host.certificate && host.certificate.provider === 'custom' && (
|
||||
<div className="text-xs text-gray-400">
|
||||
@@ -609,6 +614,9 @@ export default function ProxyHosts() {
|
||||
<p className="text-sm text-gray-400">
|
||||
Applying to <span className="text-blue-400 font-medium">{selectedHosts.size}</span> selected host(s)
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Note: Each proxy host can have a single Access Control List applied. Selecting multiple lists will apply them sequentially and the last applied list will be the effective one for each host.
|
||||
</p>
|
||||
|
||||
{/* Action Toggle */}
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -76,6 +76,23 @@ describe('Security page', () => {
|
||||
expect(screen.queryByTestId('enable-all-btn')).toBeNull()
|
||||
})
|
||||
|
||||
it('calls updateSetting when toggling ACL', async () => {
|
||||
const status: SecurityStatus = {
|
||||
cerberus: { enabled: true },
|
||||
crowdsec: { enabled: false, mode: 'disabled' as const, api_url: '' },
|
||||
waf: { enabled: false, mode: 'disabled' as const },
|
||||
rate_limit: { enabled: false },
|
||||
acl: { enabled: false },
|
||||
}
|
||||
vi.mocked(api.getSecurityStatus).mockResolvedValue(status as SecurityStatus)
|
||||
const updateSpy = vi.mocked(settingsApi.updateSetting)
|
||||
renderWithProviders(<Security />)
|
||||
await waitFor(() => expect(screen.getByText('Security Dashboard')).toBeInTheDocument())
|
||||
const aclToggle = screen.getByTestId('toggle-acl')
|
||||
await userEvent.click(aclToggle)
|
||||
await waitFor(() => expect(updateSpy).toHaveBeenCalledWith('security.acl.enabled', 'true', 'security', 'bool'))
|
||||
})
|
||||
|
||||
it('calls export endpoint when clicking Export', async () => {
|
||||
const status: SecurityStatus = {
|
||||
cerberus: { enabled: true },
|
||||
|
||||
5
frontend/src/types/testing-library-user-event.d.ts
vendored
Normal file
5
frontend/src/types/testing-library-user-event.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// ambient module declaration to satisfy typescript in editor environment
|
||||
declare module '@testing-library/user-event' {
|
||||
const userEvent: any;
|
||||
export default userEvent;
|
||||
}
|
||||
Reference in New Issue
Block a user