mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
fix(outbound): keep imported links to remote-server settings
A share link describes a remote server, but extra=, fm= and the vmess JSON copy provider objects verbatim, so a link could carry keys that only make sense on the importing host. Both link parsers now drop dial-redirect and host file keys (matched case-folded, as the core binds them). Outbound subscriptions also refuse outbounds that dial this host or its network without allowPrivate, any that target the core's own API or metrics port, and any that take their server from DNS records. Rows stored earlier are filtered the same way when the config is built.
This commit is contained in:
@@ -859,12 +859,31 @@ export function parseWireguardLink(link: string): Raw | null {
|
||||
export function parseOutboundLink(link: string): Raw | null {
|
||||
const trimmed = link.trim();
|
||||
if (!trimmed) return null;
|
||||
return (
|
||||
const outbound =
|
||||
parseVmessLink(trimmed) ??
|
||||
parseVlessLink(trimmed) ??
|
||||
parseTrojanLink(trimmed) ??
|
||||
parseShadowsocksLink(trimmed) ??
|
||||
parseHysteria2Link(trimmed) ??
|
||||
parseWireguardLink(trimmed)
|
||||
);
|
||||
parseWireguardLink(trimmed);
|
||||
if (outbound) dropHostLocalKeys(outbound);
|
||||
return outbound;
|
||||
}
|
||||
|
||||
// Keys that redirect the core's dial or name files on the importing host; extra=, fm= and
|
||||
// vmess JSON copy provider objects verbatim, and the core binds keys case-folded.
|
||||
const HOST_LOCAL_KEYS = new Set(['dialerproxy', 'certificatefile', 'keyfile', 'masterkeylog']);
|
||||
|
||||
function dropHostLocalKeys(value: unknown): void {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(dropHostLocalKeys);
|
||||
return;
|
||||
}
|
||||
if (!value || typeof value !== 'object') return;
|
||||
const obj = value as Raw;
|
||||
for (const key of Object.keys(obj)) {
|
||||
// Upper-casing first folds the long s and the Kelvin sign the way Go's decoder does.
|
||||
if (HOST_LOCAL_KEYS.has(key.toUpperCase().toLowerCase())) delete obj[key];
|
||||
else dropHostLocalKeys(obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,3 +1124,32 @@ describe('obfs=gecko packetSize validation', () => {
|
||||
expect(packetSizeOf(link)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseOutboundLink — host-local keys', () => {
|
||||
it('drops keys that redirect the dial or name host files, however they are cased', () => {
|
||||
const extra = encodeURIComponent(
|
||||
JSON.stringify({
|
||||
downloadSettings: {
|
||||
address: '8.8.8.8',
|
||||
port: 443,
|
||||
sockopt: { dialerProxy: 'api', tcpFastOpen: true },
|
||||
tlsSettings: {
|
||||
serverName: 'cdn.example.com',
|
||||
certificates: [{ CERTIFICATEFILE: '/dev/zero', keyFile: '/etc/shadow' }],
|
||||
maſterKeyLog: '/root/keys',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
const fm = encodeURIComponent(
|
||||
JSON.stringify({ tcp: [{ type: 'header-custom', settings: { DialerProxy: 'api' } }] }),
|
||||
);
|
||||
const out = parseOutboundLink(
|
||||
`vless://uuid@8.8.8.8:443?type=xhttp&security=tls&extra=${extra}&fm=${fm}#node`,
|
||||
);
|
||||
const json = JSON.stringify(out);
|
||||
expect(json).not.toMatch(/dialerproxy|certificatefile|keyfile|maſterkeylog/i);
|
||||
expect(json).toContain('"tcpFastOpen":true');
|
||||
expect(json).toContain('"serverName":"cdn.example.com"');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user