warp-change-ip.test.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { describe, expect, it } from 'vitest';
  2. import { buildWarpOutbound, mergeWarpRotation } from '@/pages/xray/overrides/WarpModal';
  3. const clientId = btoa(String.fromCharCode(1, 2, 3));
  4. function rotatedConfig(
  5. overrides: { public_key?: string; host?: string; v4?: string; v6?: string } = {},
  6. ) {
  7. return {
  8. config: {
  9. client_id: clientId,
  10. interface: {
  11. addresses: { v4: overrides.v4 ?? '172.16.0.2', v6: overrides.v6 ?? '2606:4700::2' },
  12. },
  13. peers: [
  14. {
  15. public_key: overrides.public_key ?? 'newPub',
  16. endpoint: { host: overrides.host ?? 'engage.cloudflareclient.com:2408' },
  17. },
  18. ],
  19. },
  20. };
  21. }
  22. describe('mergeWarpRotation', () => {
  23. it('patches only rotated fields and preserves user customizations', () => {
  24. const existing = {
  25. tag: 'warp',
  26. protocol: 'wireguard',
  27. settings: {
  28. mtu: 1280,
  29. secretKey: 'oldSecret',
  30. address: ['172.16.0.2/32'],
  31. reserved: [9, 9, 9],
  32. domainStrategy: 'ForceIPv4',
  33. noKernelTun: false,
  34. peers: [
  35. {
  36. publicKey: 'oldPub',
  37. endpoint: 'engage.cloudflareclient.com:2408',
  38. keepAlive: 25,
  39. allowedIPs: ['10.0.0.0/24'],
  40. preSharedKey: 'psk',
  41. },
  42. { publicKey: 'extraPeer', endpoint: 'extra.test:51820' },
  43. ],
  44. },
  45. };
  46. const merged = mergeWarpRotation(
  47. existing,
  48. { private_key: 'newSecret' },
  49. rotatedConfig({
  50. public_key: 'newPub',
  51. host: 'engage.cloudflareclient.com:2408',
  52. v4: '172.16.0.9',
  53. }),
  54. );
  55. expect(merged).not.toBeNull();
  56. const settings = (merged as { settings: Record<string, unknown> }).settings;
  57. expect(settings.secretKey).toBe('newSecret');
  58. expect(settings.address).toEqual(['172.16.0.9/32', '2606:4700::2/128']);
  59. expect(settings.reserved).toEqual([1, 2, 3]);
  60. const peers = settings.peers as Array<Record<string, unknown>>;
  61. expect(peers[0].publicKey).toBe('newPub');
  62. expect(peers[0].endpoint).toBe('engage.cloudflareclient.com:2408');
  63. expect(peers[0].keepAlive).toBe(25);
  64. expect(peers[0].allowedIPs).toEqual(['10.0.0.0/24']);
  65. expect(peers[0].preSharedKey).toBe('psk');
  66. expect(peers[1]).toEqual({ publicKey: 'extraPeer', endpoint: 'extra.test:51820' });
  67. expect(settings.mtu).toBe(1280);
  68. expect(settings.domainStrategy).toBe('ForceIPv4');
  69. expect(settings.noKernelTun).toBe(false);
  70. });
  71. it('does not mutate the existing outbound object', () => {
  72. const existing = {
  73. tag: 'warp',
  74. protocol: 'wireguard',
  75. settings: {
  76. secretKey: 'oldSecret',
  77. address: ['172.16.0.2/32'],
  78. reserved: [9, 9, 9],
  79. peers: [{ publicKey: 'oldPub', endpoint: 'old:1', keepAlive: 25 }],
  80. },
  81. };
  82. const snapshot = JSON.parse(JSON.stringify(existing));
  83. mergeWarpRotation(existing, { private_key: 'newSecret' }, rotatedConfig());
  84. expect(existing).toEqual(snapshot);
  85. });
  86. it('returns null when the rotation response has no peer', () => {
  87. expect(mergeWarpRotation(undefined, null, { config: { peers: [] } })).toBeNull();
  88. expect(mergeWarpRotation(undefined, null, null)).toBeNull();
  89. });
  90. it('seeds a default warp outbound when none existed yet', () => {
  91. const merged = mergeWarpRotation(undefined, { private_key: 'newSecret' }, rotatedConfig());
  92. expect(merged).not.toBeNull();
  93. expect((merged as Record<string, unknown>).tag).toBe('warp');
  94. expect((merged as Record<string, unknown>).protocol).toBe('wireguard');
  95. const settings = (merged as { settings: Record<string, unknown> }).settings;
  96. expect(settings.secretKey).toBe('newSecret');
  97. expect(settings.peers).toEqual([
  98. { publicKey: 'newPub', endpoint: 'engage.cloudflareclient.com:2408' },
  99. ]);
  100. });
  101. });
  102. describe('buildWarpOutbound', () => {
  103. // xray-core 26.9.30 ignores wireguard's settings.domainStrategy, so the IPv4-first
  104. // endpoint lookup #5205 depends on has to travel in sockopt, where the core reads it.
  105. it('places the IPv4-first strategy where xray-core reads it', () => {
  106. const outbound = buildWarpOutbound({ private_key: 'secret' }, rotatedConfig()) ?? {};
  107. expect(outbound.streamSettings).toEqual({ sockopt: { domainStrategy: 'ForceIPv4v6' } });
  108. expect(outbound.targetStrategy).toBe('ForceIPv4v6');
  109. expect(outbound.settings).not.toHaveProperty('domainStrategy');
  110. });
  111. });