Skip to content

Commit 03be414

Browse files
committed
feat: Stash VLESS REALITY 支持各种 flow(未限制 一般常见的截断后能使用), 不支持非 TCP 以外的传输层
1 parent dea8852 commit 03be414

File tree

3 files changed

+234
-5
lines changed

3 files changed

+234
-5
lines changed

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sub-store",
3-
"version": "2.21.68",
3+
"version": "2.21.69",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"scripts": {

backend/src/core/proxy-utils/producers/stash.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,14 @@ export default function Stash_Producer() {
4545
'2022-blake3-aes-128-gcm',
4646
'2022-blake3-aes-256-gcm',
4747
].includes(proxy.cipher)) ||
48-
(proxy.type === 'snell' && proxy.version >= 4) ||
49-
(proxy.type === 'vless' &&
50-
proxy['reality-opts'] &&
51-
!['xtls-rprx-vision'].includes(proxy.flow))
48+
(proxy.type === 'snell' && proxy.version >= 4)
49+
) {
50+
return false;
51+
} else if (
52+
['vless'].includes(proxy.type) &&
53+
proxy['reality-opts'] &&
54+
proxy.network &&
55+
!['tcp'].includes(proxy.network)
5256
) {
5357
return false;
5458
} else if (

backend/src/test/proxy-producers/structured.spec.js

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { expect } from 'chai';
22
import { describe, it } from 'mocha';
33

4+
import { ProxyUtils } from '@/core/proxy-utils';
45
import {
56
UUID,
67
expectSubset,
@@ -226,6 +227,230 @@ describe('Proxy structured producers', function () {
226227
});
227228
});
228229

230+
it('emits Stash VLESS TCP REALITY proxies without validating flow values', function () {
231+
const proxy = {
232+
type: 'vless',
233+
name: 'Stash Reality Custom Flow',
234+
server: 'vless.example.com',
235+
port: 443,
236+
uuid: UUID,
237+
tls: true,
238+
network: 'tcp',
239+
flow: 'xtls-rprx-unknown',
240+
sni: 'sni.example.com',
241+
'reality-opts': {
242+
'public-key': 'pubkey',
243+
'short-id': '08',
244+
},
245+
};
246+
247+
const internal = produceInternal('Stash', proxy);
248+
const external = loadProducedYaml('Stash', proxy);
249+
250+
expect(internal).to.have.length(1);
251+
expect(external.proxies).to.have.length(1);
252+
expectSubset(internal[0], {
253+
type: 'vless',
254+
name: 'Stash Reality Custom Flow',
255+
network: 'tcp',
256+
flow: 'xtls-rprx-unknown',
257+
servername: 'sni.example.com',
258+
'reality-opts': {
259+
'public-key': 'pubkey',
260+
'short-id': '08',
261+
},
262+
});
263+
expectSubset(external.proxies[0], {
264+
type: 'vless',
265+
name: 'Stash Reality Custom Flow',
266+
network: 'tcp',
267+
flow: 'xtls-rprx-unknown',
268+
servername: 'sni.example.com',
269+
'reality-opts': {
270+
'public-key': 'pubkey',
271+
'short-id': '08',
272+
},
273+
});
274+
});
275+
276+
it('keeps default-tcp Stash VLESS REALITY proxies when network is omitted', function () {
277+
const proxy = {
278+
type: 'vless',
279+
name: 'Implicit TCP Reality',
280+
server: 'vless.example.com',
281+
port: 443,
282+
uuid: UUID,
283+
tls: true,
284+
sni: 'sni.example.com',
285+
'reality-opts': {
286+
'public-key': 'pubkey',
287+
'short-id': '08',
288+
},
289+
};
290+
291+
const internal = produceInternal('Stash', proxy);
292+
const external = loadProducedYaml('Stash', proxy);
293+
294+
expect(internal).to.have.length(1);
295+
expect(external.proxies).to.have.length(1);
296+
expectSubset(internal[0], {
297+
type: 'vless',
298+
name: 'Implicit TCP Reality',
299+
servername: 'sni.example.com',
300+
'reality-opts': {
301+
'public-key': 'pubkey',
302+
'short-id': '08',
303+
},
304+
});
305+
expect(external.proxies[0]).to.not.have.property('network');
306+
});
307+
308+
it('keeps Stash VLESS TCP REALITY proxies when URI input omits flow', function () {
309+
const proxies = ProxyUtils.parse(
310+
`vless://${UUID}@vless.example.com:443?type=tcp&security=reality&sni=sni.example.com&pbk=pubkey&sid=08#No%20Flow`,
311+
);
312+
313+
const internal = produceInternal('Stash', proxies);
314+
const external = loadProducedYaml('Stash', proxies);
315+
316+
expect(internal).to.have.length(1);
317+
expect(external.proxies).to.have.length(1);
318+
expect(internal[0]).to.not.have.property('flow');
319+
expectSubset(internal[0], {
320+
type: 'vless',
321+
name: 'No Flow',
322+
network: 'tcp',
323+
servername: 'sni.example.com',
324+
'reality-opts': {
325+
'public-key': 'pubkey',
326+
'short-id': '08',
327+
},
328+
});
329+
expectSubset(external.proxies[0], {
330+
type: 'vless',
331+
name: 'No Flow',
332+
network: 'tcp',
333+
servername: 'sni.example.com',
334+
'reality-opts': {
335+
'public-key': 'pubkey',
336+
'short-id': '08',
337+
},
338+
});
339+
});
340+
341+
it('keeps Stash VLESS TCP REALITY nodes while still filtering non-tcp and unsupported variants', function () {
342+
const proxies = [
343+
{
344+
type: 'vless',
345+
name: 'Supported Reality',
346+
server: 'vless.example.com',
347+
port: 443,
348+
uuid: UUID,
349+
tls: true,
350+
network: 'tcp',
351+
'reality-opts': {
352+
'public-key': 'pubkey',
353+
'short-id': '08',
354+
},
355+
},
356+
{
357+
type: 'vless',
358+
name: 'Custom Flow',
359+
server: 'unsupported-flow.example.com',
360+
port: 443,
361+
uuid: UUID,
362+
tls: true,
363+
network: 'tcp',
364+
flow: 'xtls-rprx-unknown',
365+
'reality-opts': {
366+
'public-key': 'pubkey',
367+
'short-id': '08',
368+
},
369+
},
370+
{
371+
type: 'vless',
372+
name: 'Reality WS',
373+
server: 'vless-ws.example.com',
374+
port: 443,
375+
uuid: UUID,
376+
tls: true,
377+
network: 'ws',
378+
'ws-opts': {
379+
path: '/ws',
380+
headers: {
381+
Host: 'cdn.example.com',
382+
},
383+
},
384+
'reality-opts': {
385+
'public-key': 'pubkey',
386+
'short-id': '08',
387+
},
388+
},
389+
{
390+
type: 'vless',
391+
name: 'XHTTP',
392+
server: 'vless-xhttp.example.com',
393+
port: 443,
394+
uuid: UUID,
395+
tls: true,
396+
network: 'xhttp',
397+
'xhttp-opts': {
398+
path: '/xhttp',
399+
},
400+
'reality-opts': {
401+
'public-key': 'pubkey',
402+
'short-id': '08',
403+
},
404+
},
405+
{
406+
type: 'vless',
407+
name: 'Encrypted VLESS',
408+
server: 'encrypted.example.com',
409+
port: 443,
410+
uuid: UUID,
411+
tls: true,
412+
network: 'tcp',
413+
encryption: 'aes-128-gcm',
414+
'reality-opts': {
415+
'public-key': 'pubkey',
416+
'short-id': '08',
417+
},
418+
},
419+
];
420+
421+
const internal = produceInternal('Stash', proxies);
422+
const external = loadProducedYaml('Stash', proxies);
423+
424+
expect(internal).to.have.length(2);
425+
expect(external.proxies).to.have.length(2);
426+
expect(internal.map((proxy) => proxy.name)).to.deep.equal([
427+
'Supported Reality',
428+
'Custom Flow',
429+
]);
430+
expect(external.proxies.map((proxy) => proxy.name)).to.deep.equal([
431+
'Supported Reality',
432+
'Custom Flow',
433+
]);
434+
expectSubset(internal[0], {
435+
type: 'vless',
436+
name: 'Supported Reality',
437+
});
438+
expectSubset(internal[1], {
439+
type: 'vless',
440+
name: 'Custom Flow',
441+
flow: 'xtls-rprx-unknown',
442+
});
443+
expectSubset(external.proxies[0], {
444+
type: 'vless',
445+
name: 'Supported Reality',
446+
});
447+
expectSubset(external.proxies[1], {
448+
type: 'vless',
449+
name: 'Custom Flow',
450+
flow: 'xtls-rprx-unknown',
451+
});
452+
});
453+
229454
it('promotes shadow-tls fields for Shadowrocket', function () {
230455
const proxy = {
231456
type: 'ss',

0 commit comments

Comments
 (0)