fix(frontend): tolerate a malformed happyEyeballs value in the Xray Basics tab

BasicsTab derived directHappyEyeballs by calling HappyEyeballsSchema.parse
during render, guarding only against null/non-object. A wrong-typed field
(e.g. happyEyeballs.tryDelayMs as a string) or any other shape mismatch —
reachable via the Complete Template JSON editor or an imported config — threw
straight out of render, white-screening the default Xray landing tab.

Use safeParse and fall back to null so a bad value degrades to "no override"
instead of crashing the page.
This commit is contained in:
MHSanaei
2026-07-15 05:54:01 +02:00
parent ebfaad1499
commit 980c023287
2 changed files with 38 additions and 1 deletions
+2 -1
View File
@@ -115,7 +115,8 @@ export default function BasicsTab({
?.sockopt;
const raw = sockopt?.happyEyeballs;
if (raw == null || typeof raw !== 'object') return null;
return HappyEyeballsSchema.parse(raw);
const parsed = HappyEyeballsSchema.safeParse(raw);
return parsed.success ? parsed.data : null;
})();
const setDirectHappyEyeballs = useCallback(
@@ -0,0 +1,36 @@
import { describe, it, expect, vi } from 'vitest';
import BasicsTab from '@/pages/xray/basics/BasicsTab';
import type { XraySettingsValue } from '@/hooks/useXraySetting';
import { renderWithProviders } from './test-utils';
function settingsWithMalformedHappyEyeballs(): XraySettingsValue {
return {
outbounds: [
{
protocol: 'freedom',
tag: 'direct',
streamSettings: { sockopt: { happyEyeballs: { tryDelayMs: 'fast' } } },
},
],
} as unknown as XraySettingsValue;
}
describe('BasicsTab malformed happyEyeballs', () => {
it('renders instead of white-screening on a wrong-typed happyEyeballs value', () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
expect(() =>
renderWithProviders(
<BasicsTab
templateSettings={settingsWithMalformedHappyEyeballs()}
setTemplateSettings={vi.fn()}
outboundTestUrl=""
onChangeOutboundTestUrl={vi.fn()}
onResetDefault={vi.fn()}
/>,
),
).not.toThrow();
errorSpy.mockRestore();
});
});