inbound-defaults.test.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import { describe, expect, it } from 'vitest';
  2. import {
  3. createDefaultHttpInboundSettings,
  4. createDefaultHysteriaClient,
  5. createDefaultHysteriaInboundSettings,
  6. createDefaultMixedInboundSettings,
  7. createDefaultShadowsocksClient,
  8. createDefaultShadowsocksInboundSettings,
  9. createDefaultTrojanClient,
  10. createDefaultTrojanInboundSettings,
  11. createDefaultTuicClient,
  12. createDefaultTuicInboundSettings,
  13. createDefaultTunnelInboundSettings,
  14. createDefaultVlessClient,
  15. createDefaultVlessInboundSettings,
  16. createDefaultVmessClient,
  17. createDefaultVmessInboundSettings,
  18. createDefaultWireguardInboundSettings,
  19. } from '@/lib/xray/inbound-defaults';
  20. import {
  21. createHysteriaTlsSettingsWithDefaultCert,
  22. createTlsSettingsWithDefaultCert,
  23. } from '@/lib/xray/inbound-tls-defaults';
  24. import { HttpInboundSettingsSchema } from '@/schemas/protocols/inbound/http';
  25. import {
  26. HysteriaClientSchema,
  27. HysteriaInboundSettingsSchema,
  28. } from '@/schemas/protocols/inbound/hysteria';
  29. import { TlsStreamSettingsSchema } from '@/schemas/protocols/security/tls';
  30. import { MixedInboundSettingsSchema } from '@/schemas/protocols/inbound/mixed';
  31. import {
  32. ShadowsocksClientSchema,
  33. ShadowsocksInboundSettingsSchema,
  34. } from '@/schemas/protocols/inbound/shadowsocks';
  35. import {
  36. TrojanClientSchema,
  37. TrojanInboundSettingsSchema,
  38. } from '@/schemas/protocols/inbound/trojan';
  39. import { TuicClientSchema, TuicInboundSettingsSchema } from '@/schemas/protocols/inbound/tuic';
  40. import { TunnelInboundSettingsSchema } from '@/schemas/protocols/inbound/tunnel';
  41. import { VlessClientSchema, VlessInboundSettingsSchema } from '@/schemas/protocols/inbound/vless';
  42. import { VmessClientSchema, VmessInboundSettingsSchema } from '@/schemas/protocols/inbound/vmess';
  43. import { WireguardInboundSettingsSchema } from '@/schemas/protocols/inbound/wireguard';
  44. // Tests pass explicit seeds for every random field so the assertions don't
  45. // depend on window.crypto (the node test env has no crypto.randomUUID).
  46. // Each factory is verified two ways:
  47. // 1. snapshot — locks the exact shape
  48. // 2. Zod parse round-trip — confirms the factory output is a valid
  49. // member of the protocol's client schema (no missing defaults, no
  50. // stray fields)
  51. const seed = {
  52. email: '[email protected]',
  53. subId: 'fixed-sub-id-1234',
  54. };
  55. describe('createDefaultVlessClient', () => {
  56. it('produces a Zod-valid client', () => {
  57. const c = createDefaultVlessClient({ ...seed, id: '11111111-2222-4333-8444-555555555555' });
  58. expect(c).toMatchSnapshot();
  59. expect(VlessClientSchema.parse(c)).toEqual(c);
  60. });
  61. });
  62. describe('createDefaultVmessClient', () => {
  63. it('produces a Zod-valid client', () => {
  64. const c = createDefaultVmessClient({ ...seed, id: 'aaaaaaaa-bbbb-4ccc-9ddd-eeeeeeeeeeee' });
  65. expect(c).toMatchSnapshot();
  66. expect(VmessClientSchema.parse(c)).toEqual(c);
  67. });
  68. });
  69. describe('createDefaultTrojanClient', () => {
  70. it('produces a Zod-valid client', () => {
  71. const c = createDefaultTrojanClient({ ...seed, password: 'fixed-trojan-pw' });
  72. expect(c).toMatchSnapshot();
  73. expect(TrojanClientSchema.parse(c)).toEqual(c);
  74. });
  75. });
  76. describe('createDefaultShadowsocksClient', () => {
  77. it('produces a Zod-valid client', () => {
  78. const c = createDefaultShadowsocksClient({ ...seed, password: 'ZmFrZS1zcy1wYXNzd29yZA==' });
  79. expect(c).toMatchSnapshot();
  80. expect(ShadowsocksClientSchema.parse(c)).toEqual(c);
  81. });
  82. });
  83. describe('createDefaultHysteriaClient', () => {
  84. it('produces a Zod-valid client', () => {
  85. const c = createDefaultHysteriaClient({ ...seed, auth: 'fixed-hyst-auth' });
  86. expect(c).toMatchSnapshot();
  87. expect(HysteriaClientSchema.parse(c)).toEqual(c);
  88. });
  89. });
  90. describe('createDefaultTuicClient', () => {
  91. it('produces a Zod-valid client', () => {
  92. const c = createDefaultTuicClient({
  93. ...seed,
  94. uuid: '11111111-2222-3333-4444-555555555555',
  95. password: 'fixed-tuic-pw',
  96. });
  97. expect(TuicClientSchema.parse(c)).toEqual(c);
  98. });
  99. });
  100. describe('createDefault*InboundSettings factories', () => {
  101. it('vless', () => {
  102. const s = createDefaultVlessInboundSettings();
  103. expect(s).toMatchSnapshot();
  104. expect(VlessInboundSettingsSchema.parse(s)).toEqual(s);
  105. });
  106. it('vmess', () => {
  107. const s = createDefaultVmessInboundSettings();
  108. expect(s).toMatchSnapshot();
  109. expect(VmessInboundSettingsSchema.parse(s)).toEqual(s);
  110. });
  111. it('trojan', () => {
  112. const s = createDefaultTrojanInboundSettings();
  113. expect(s).toMatchSnapshot();
  114. expect(TrojanInboundSettingsSchema.parse(s)).toEqual(s);
  115. });
  116. it('shadowsocks', () => {
  117. const s = createDefaultShadowsocksInboundSettings({ password: 'ZmFrZS1zcy1zZWVk' });
  118. expect(s).toMatchSnapshot();
  119. expect(ShadowsocksInboundSettingsSchema.parse(s)).toEqual(s);
  120. });
  121. it('hysteria (v1, defaults to v2 wire version)', () => {
  122. const s = createDefaultHysteriaInboundSettings();
  123. expect(s).toMatchSnapshot();
  124. expect(HysteriaInboundSettingsSchema.parse(s)).toEqual(s);
  125. });
  126. it('http', () => {
  127. const s = createDefaultHttpInboundSettings();
  128. expect(s.allowTransparent).toBe(false);
  129. const accounts = s.accounts ?? [];
  130. expect(accounts).toHaveLength(1);
  131. expect(accounts[0].user.length).toBe(8);
  132. expect(accounts[0].pass.length).toBe(12);
  133. expect(HttpInboundSettingsSchema.parse(s)).toEqual(s);
  134. });
  135. it('mixed', () => {
  136. const s = createDefaultMixedInboundSettings();
  137. expect(s.auth).toBe('password');
  138. expect(s.udp).toBe(false);
  139. expect(s.ip).toBe('127.0.0.1');
  140. const accounts = s.accounts ?? [];
  141. expect(accounts).toHaveLength(1);
  142. expect(accounts[0].user.length).toBe(8);
  143. expect(accounts[0].pass.length).toBe(12);
  144. expect(MixedInboundSettingsSchema.parse(s)).toEqual(s);
  145. });
  146. it('tunnel', () => {
  147. const s = createDefaultTunnelInboundSettings();
  148. expect(s).toMatchSnapshot();
  149. expect(TunnelInboundSettingsSchema.parse(s)).toEqual(s);
  150. });
  151. it('wireguard', () => {
  152. const s = createDefaultWireguardInboundSettings({
  153. secretKey: 'QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=',
  154. });
  155. expect(s).toMatchSnapshot();
  156. expect(WireguardInboundSettingsSchema.parse(s)).toEqual(s);
  157. expect(s.peers).toEqual([]);
  158. expect(s.clients).toEqual([]);
  159. });
  160. it('tuic', () => {
  161. const s = createDefaultTuicInboundSettings();
  162. expect(TuicInboundSettingsSchema.parse(s)).toEqual(s);
  163. });
  164. });
  165. describe('TuicInboundSettingsSchema', () => {
  166. it('canonicalizes congestion-controller aliases and casing', () => {
  167. expect(
  168. TuicInboundSettingsSchema.parse({ server: { congestion_control: 'RENO' } }).server
  169. ?.congestion_control,
  170. ).toBe('new_reno');
  171. expect(
  172. TuicInboundSettingsSchema.parse({ server: { congestion_control: ' CuBiC ' } }).server
  173. ?.congestion_control,
  174. ).toBe('cubic');
  175. expect(
  176. TuicInboundSettingsSchema.parse({ server: { congestion_control: '' } }).server
  177. ?.congestion_control,
  178. ).toBe('bbr');
  179. expect(
  180. TuicInboundSettingsSchema.parse({ server: { congestion_control: ' ' } }).server
  181. ?.congestion_control,
  182. ).toBe('bbr');
  183. expect(
  184. TuicInboundSettingsSchema.parse({ congestion_control: ' CuBiC ' }).congestion_control,
  185. ).toBe('cubic');
  186. expect(
  187. TuicInboundSettingsSchema.parse({ congestion_control: 'invalid' }).congestion_control,
  188. ).toBe('new_reno');
  189. });
  190. it('clamps legacy packet-size values to the SOCKS-safe UDP payload maximum', () => {
  191. expect(
  192. TuicInboundSettingsSchema.parse({ server: { max_udp_relay_packet_size: 65507 } }).server
  193. ?.max_udp_relay_packet_size,
  194. ).toBe(65245);
  195. expect(
  196. TuicInboundSettingsSchema.parse({ max_udp_relay_packet_size: 65500 })
  197. .max_udp_relay_packet_size,
  198. ).toBe(65245);
  199. expect(() =>
  200. TuicInboundSettingsSchema.parse({ server: { max_udp_relay_packet_size: 65508 } }),
  201. ).toThrow();
  202. expect(() => TuicInboundSettingsSchema.parse({ max_udp_relay_packet_size: 65508 })).toThrow();
  203. });
  204. });
  205. describe('createHysteriaTlsSettingsWithDefaultCert', () => {
  206. it('defaults Hysteria TLS to uTLS None and h3 ALPN', () => {
  207. const tls = createHysteriaTlsSettingsWithDefaultCert();
  208. expect(tls.alpn).toEqual(['h3']);
  209. expect((tls.settings as Record<string, unknown>).fingerprint).toBe('');
  210. expect(tls.certificates).toEqual([
  211. expect.objectContaining({
  212. useFile: true,
  213. certificateFile: '',
  214. keyFile: '',
  215. }),
  216. ]);
  217. });
  218. });
  219. describe('createTlsSettingsWithDefaultCert', () => {
  220. it('keeps the schema ALPN default for non-WebSocket transports', () => {
  221. for (const network of [undefined, '', 'tcp', 'kcp', 'grpc', 'httpupgrade', 'xhttp']) {
  222. const tls = createTlsSettingsWithDefaultCert(network);
  223. expect(tls.alpn).toEqual(['h2', 'http/1.1']);
  224. }
  225. });
  226. it('defaults ALPN to http/1.1 for WebSocket (issue #6782)', () => {
  227. const tls = createTlsSettingsWithDefaultCert('ws');
  228. expect(tls.alpn).toEqual(['http/1.1']);
  229. // The overridden value must still satisfy the TLS settings schema.
  230. expect(TlsStreamSettingsSchema.parse(tls).alpn).toEqual(['http/1.1']);
  231. });
  232. });