fix(outbound): fill encryption and pqv when importing VLESS link

The link-to-JSON importer dropped two VLESS Reality fields:

- pqv (post-quantum ML-DSA-65 verify key) was never parsed; map it back
  to realitySettings.mldsa65Verify, matching the inbound link generator.
- encryption was force-reset to 'none' in the form adapter regardless of
  the parsed value, discarding post-quantum encryption strings.

Add regression tests for both paths.
This commit is contained in:
MHSanaei 2026-06-01 17:37:54 +02:00
parent 28330e60d8
commit 13c04bb982
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
4 changed files with 38 additions and 1 deletions

View file

@ -123,7 +123,7 @@ function vlessFromWire(raw: Raw): VlessOutboundFormSettings {
port,
id,
flow,
encryption: (encryption === 'none' ? 'none' : 'none') as 'none',
encryption: encryption || 'none',
reverseTag,
reverseSniffing,
testpre: asNumber(raw.testpre, 0),

View file

@ -210,6 +210,7 @@ function applySecurityParams(stream: Raw, params: URLSearchParams): void {
reality.publicKey = params.get('pbk') ?? '';
reality.shortId = params.get('sid') ?? '';
reality.spiderX = params.get('spx') ?? '';
reality.mldsa65Verify = params.get('pqv') ?? '';
}
}

View file

@ -74,6 +74,25 @@ describe('outbound-form-adapter: round-trip', () => {
});
});
it('vless preserves a non-none encryption value (post-quantum)', () => {
const enc = 'mlkem768x25519plus.native.0rtt.G3cdPSd1-NnlpTbWNSM5vHsT5VNzWfFzYSKwbUMnV1Y';
const wire = {
protocol: 'vless',
settings: {
address: 'srv',
port: 443,
id: '11111111-2222-4333-8444-555555555555',
flow: '',
encryption: enc,
},
};
const form = rawOutboundToFormValues(wire);
if (form.protocol === 'vless') {
expect(form.settings.encryption).toBe(enc);
}
expect((formValuesToWirePayload(form).settings as Record<string, unknown>).encryption).toBe(enc);
});
it('vless emits reverse + sniffing when reverseTag is set', () => {
const wire = {
protocol: 'vless',

View file

@ -173,6 +173,23 @@ describe('parseVlessLink', () => {
expect(reality.shortId).toBe('abcd');
expect(reality.serverName).toBe('cloudflare.com');
});
it('parses encryption + pqv (post-quantum) into settings and mldsa65Verify', () => {
const enc = 'mlkem768x25519plus.native.0rtt.G3cdPSd1-NnlpTbWNSM5vHsT5VNzWfFzYSKwbUMnV1Y';
const pqv = 'GIsemxbGPjDRH1ONfmoGlVkJ4etNuLmYDvzpjmFFreDLd8WjoJxJ4Fmt_NQJaC6';
const link
= 'vless://9406c224-8ac6-4675-ae0b-f93785959418@localhost:1121'
+ `?encryption=${enc}&pqv=${pqv}`
+ '&security=reality&sid=29cf418813d5bac7&sni=aws.amazon.com'
+ '&pbk=aQaGBOT2hMfXWebYtjADoOVUrP8qZRdwXVap7nrId0I&fp=chrome&spx=%2FOUTjB7xHRiP4zBP&type=tcp'
+ '#giqssbgmo9';
const out = parseVlessLink(link);
const settings = out?.settings as { encryption: string };
expect(settings.encryption).toBe(enc);
const reality = (out?.streamSettings as Record<string, unknown>).realitySettings as Record<string, unknown>;
expect(reality.mldsa65Verify).toBe(pqv);
expect(reality.publicKey).toBe('aQaGBOT2hMfXWebYtjADoOVUrP8qZRdwXVap7nrId0I');
});
});
describe('parseTrojanLink', () => {