reality-mldsa-cert-chain.test.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import { describe, it, expect } from 'vitest';
  2. import { Form } from 'antd';
  3. import type { ReactNode } from 'react';
  4. import { FormProvider, useForm } from 'react-hook-form';
  5. import { RealityForm } from '@/pages/inbounds/form/security';
  6. import RealityTargetScannerModal from '@/pages/inbounds/form/security/RealityTargetScannerModal';
  7. import type { InboundFormValues } from '@/schemas/forms/inbound-form';
  8. import type { RealityScanResult } from '@/generated/types';
  9. import { renderWithProviders } from './test-utils';
  10. const smallChain: RealityScanResult = {
  11. alpn: 'h2',
  12. certChainBytes: 3427,
  13. certChainValid: true,
  14. certIssuer: 'Google Trust Services',
  15. certSubject: 'cloudflare.com',
  16. certValid: true,
  17. curveID: 'X25519',
  18. feasible: true,
  19. h2: true,
  20. host: 'www.cloudflare.com',
  21. ip: '104.16.124.96',
  22. latencyMs: 180,
  23. notAfter: '2026-08-01T00:00:00Z',
  24. port: 443,
  25. privateTarget: false,
  26. reason: '',
  27. serverNames: ['www.cloudflare.com'],
  28. target: 'www.cloudflare.com:443',
  29. tls13: true,
  30. tlsVersion: '1.3',
  31. x25519: true,
  32. };
  33. function FormHarness({
  34. children,
  35. defaultValues,
  36. }: {
  37. children: ReactNode;
  38. defaultValues?: Record<string, unknown>;
  39. }) {
  40. const methods = useForm<InboundFormValues>({ defaultValues: defaultValues as never });
  41. return (
  42. <FormProvider {...methods}>
  43. <Form>{children}</Form>
  44. </FormProvider>
  45. );
  46. }
  47. const noop = () => {};
  48. function renderRealityForm(
  49. scanResult: RealityScanResult | null,
  50. defaultValues?: Record<string, unknown>,
  51. ) {
  52. return renderWithProviders(
  53. <FormHarness defaultValues={defaultValues}>
  54. <RealityForm
  55. saving={false}
  56. scanning={false}
  57. scanResult={scanResult}
  58. scanRealityTarget={noop}
  59. scanRealityCandidates={async () => []}
  60. applyRealityScanResult={noop}
  61. randomizeShortIds={noop}
  62. randomizeSpiderX={noop}
  63. genRealityKeypair={noop}
  64. clearRealityKeypair={noop}
  65. genMldsa65={noop}
  66. clearMldsa65={noop}
  67. />
  68. </FormHarness>,
  69. );
  70. }
  71. describe('ML-DSA-65 cert chain warning', () => {
  72. it('warns on the inbound form when ML-DSA-65 is on and the scanned chain is under 3500 bytes', () => {
  73. const { getByText } = renderRealityForm(smallChain, {
  74. streamSettings: { realitySettings: { mldsa65Seed: 'seed' } },
  75. });
  76. expect(getByText(/below the 3500-byte minimum required for ML-DSA-65/)).toBeTruthy();
  77. });
  78. it('does not warn on the inbound form when ML-DSA-65 is off', () => {
  79. const { queryByText } = renderRealityForm(smallChain);
  80. expect(queryByText(/below the 3500-byte minimum required for ML-DSA-65/)).toBeNull();
  81. });
  82. it('tags scanner rows whose cert chain is too small for ML-DSA-65', async () => {
  83. const { findByText } = renderWithProviders(
  84. <RealityTargetScannerModal
  85. open
  86. onClose={noop}
  87. scanRealityCandidates={async () => [smallChain]}
  88. onPick={noop}
  89. mldsa65Enabled
  90. />,
  91. );
  92. expect(await findByText('3427 B')).toBeTruthy();
  93. });
  94. });