| 1 | import { describe, expect, it } from 'vitest' |
| 2 | import { |
| 3 | containsNoProxyWildcard, |
| 4 | matchesNoProxy, |
| 5 | parseNoProxyEntries |
| 6 | } from '../../../../src/main/utils/no-proxy' |
| 7 | |
| 8 | const match = (noProxy: string, hostname: string, port = 443): boolean => |
| 9 | matchesNoProxy(parseNoProxyEntries(noProxy), hostname, port) |
| 10 | |
| 11 | describe('parseNoProxyEntries', () => { |
| 12 | it('按逗号和空白切分,去掉空项并统一小写', () => { |
| 13 | expect(parseNoProxyEntries('localhost, 127.0.0.1,, corp.internal')).toEqual([ |
| 14 | { hostname: 'localhost', port: null, cidr: null }, |
| 15 | { hostname: '127.0.0.1', port: null, cidr: null }, |
| 16 | { hostname: 'corp.internal', port: null, cidr: null } |
| 17 | ]) |
| 18 | }) |
| 19 | |
| 20 | it('支持 *.domain 与 .domain 前缀,剥离后按域名匹配', () => { |
| 21 | expect(match('*.corp.internal', 'api.corp.internal')).toBe(true) |
| 22 | expect(match('.corp.internal', 'api.corp.internal')).toBe(true) |
| 23 | }) |
| 24 | |
| 25 | it('识别带端口的条目', () => { |
| 26 | const entries = parseNoProxyEntries('gw.corp.cn:8080') |
| 27 | expect(entries).toEqual([{ hostname: 'gw.corp.cn', port: 8080, cidr: null }]) |
| 28 | }) |
| 29 | |
| 30 | it('识别 IPv4 CIDR 条目', () => { |
| 31 | const entries = parseNoProxyEntries('10.0.0.0/8') |
| 32 | expect(entries[0].cidr).toEqual({ address: 10 << 24, bits: 8 }) |
| 33 | }) |
| 34 | |
| 35 | it('忽略非法 CIDR 位数', () => { |
| 36 | expect(parseNoProxyEntries('10.0.0.0/40')[0].cidr).toBeNull() |
| 37 | }) |
| 38 | |
| 39 | it('空值与非法输入返回空列表', () => { |
| 40 | expect(parseNoProxyEntries(undefined)).toEqual([]) |
| 41 | expect(parseNoProxyEntries('')).toEqual([]) |
| 42 | expect(parseNoProxyEntries('*')).toEqual([]) |
| 43 | }) |
| 44 | }) |
| 45 | |
| 46 | describe('matchesNoProxy', () => { |
| 47 | it('精确匹配主机名(大小写不敏感)', () => { |
| 48 | expect(match('corp.internal', 'CORP.INTERNAL')).toBe(true) |
| 49 | expect(match('corp.internal', 'other.internal')).toBe(false) |
| 50 | }) |
| 51 | |
| 52 | it('域名条目自动覆盖子域名,但不误伤后缀相同的前缀', () => { |
| 53 | expect(match('corp.internal', 'api.corp.internal')).toBe(true) |
| 54 | expect(match('corp.internal', 'deep.api.corp.internal')).toBe(true) |
| 55 | expect(match('corp.internal', 'xcorp.internal')).toBe(false) |
| 56 | }) |
| 57 | |
| 58 | it('localhost 只匹配 localhost 本身', () => { |
| 59 | expect(match('localhost,127.0.0.1', 'localhost', 80)).toBe(true) |
| 60 | expect(match('localhost,127.0.0.1', 'xlocalhost')).toBe(false) |
| 61 | }) |
| 62 | |
| 63 | it('带端口的条目仅在主机与端口都一致时命中', () => { |
| 64 | expect(match('gw.corp.cn:8080', 'gw.corp.cn', 8080)).toBe(true) |
| 65 | expect(match('gw.corp.cn:8080', 'gw.corp.cn', 443)).toBe(false) |
| 66 | expect(match('gw.corp.cn:8080', 'api.corp.cn', 8080)).toBe(false) |
| 67 | }) |
| 68 | |
| 69 | it('IPv4 精确与 CIDR 网段匹配', () => { |
| 70 | expect(match('10.0.0.0/8', '10.1.2.3')).toBe(true) |
| 71 | expect(match('10.0.0.0/8', '11.1.2.3')).toBe(false) |
| 72 | expect(match('192.168.0.0/16', '192.168.5.4')).toBe(true) |
| 73 | expect(match('192.168.0.0/16', '192.169.0.1')).toBe(false) |
| 74 | expect(match('10.20.30.40/32', '10.20.30.40')).toBe(true) |
| 75 | expect(match('10.20.30.40/32', '10.20.30.41')).toBe(false) |
| 76 | }) |
| 77 | |
| 78 | it('CIDR 条目不会匹配域名,域名条目不会误匹配 IP', () => { |
| 79 | expect(match('10.0.0.0/8', 'app.10.0.0.0')).toBe(false) |
| 80 | expect(match('0.0.0.0/0', 'app.corp.cn')).toBe(false) |
| 81 | }) |
| 82 | |
| 83 | it('多条件之间任一命中即绕过', () => { |
| 84 | expect(match('localhost,10.0.0.0/8,*.corp.internal', 'api.corp.internal')).toBe(true) |
| 85 | expect(match('localhost,10.0.0.0/8,*.corp.internal', '9.9.9.9')).toBe(false) |
| 86 | }) |
| 87 | }) |
| 88 | |
| 89 | describe('containsNoProxyWildcard', () => { |
| 90 | it('仅当存在独立的 * 条目时视为全部直连', () => { |
| 91 | expect(containsNoProxyWildcard('*')).toBe(true) |
| 92 | expect(containsNoProxyWildcard('localhost, *')).toBe(true) |
| 93 | expect(containsNoProxyWildcard('*.corp.internal')).toBe(false) |
| 94 | expect(containsNoProxyWildcard('')).toBe(false) |
| 95 | }) |
| 96 | }) |
| 97 |