11// trojan-parser.js
22
3+ import {
4+ extractPathQueryParam ,
5+ getPathQueryParam ,
6+ parseSafeIntegerValue ,
7+ } from '../../transport-path' ;
8+ import { isIPv6 } from '@/utils' ;
9+
10+ const unsafePathSegments = new Set ( [ "__proto__" , "constructor" , "prototype" ] ) ;
11+
312let parser ;
413
514function $set ( obj , path , value ) {
615 if ( Object ( obj ) !== obj ) return obj ;
716 if ( ! Array . isArray ( path ) ) path = path . toString ( ) . match ( / [ ^ . [ \] ] + / g) || [ ] ;
17+ if ( path . some ( ( segment ) => unsafePathSegments . has ( segment ) ) ) {
18+ throw new Error ( "Unsafe property path" ) ;
19+ }
820
921 path
1022 . slice ( 0 , - 1 )
1123 . reduce ( ( a , c , i ) => (
12- Object ( a [ c ] ) === a [ c ]
24+ Object . prototype . hasOwnProperty . call ( a , c ) && Object ( a [ c ] ) === a [ c ]
1325 ? a [ c ]
1426 : ( a [ c ] = Math . abs ( path [ i + 1 ] ) >> 0 === + path [ i + 1 ] ? [ ] : { } )
1527 ) , obj ) [ path [ path . length - 1 ] ] = value ;
@@ -22,14 +34,8 @@ function toBool(str) {
2234 return / ( T R U E ) | 1 / i. test ( str ) ;
2335}
2436
25- function parseEarlyDataSize ( value ) {
26- if ( value == null || ! / ^ \d + $ / . test ( String ( value ) ) ) return null ;
27- const n = parseInt ( value , 10 ) ;
28- return Number . isSafeInteger ( n ) ? n : null ;
29- }
30-
3137function isNumericEarlyData ( value ) {
32- return parseEarlyDataSize ( value ) != null ;
38+ return parseSafeIntegerValue ( value ) != null ;
3339}
3440
3541function decode ( value ) {
@@ -45,7 +51,7 @@ function parseTrojan(url) {
4551 const proxy = { } ;
4652
4753 const match = url . match (
48- / ^ t r o j a n : \/ \/ ( [ ^ @ ] + ) @ ( [ ^ : / ? # ] + ) : ( \d + ) (?: \/ ) ? (?: \? ( [ ^ # ] * ) ) ? (?: # ( .* ) ) ? $ /
54+ / ^ t r o j a n : \/ \/ ( [ ^ @ ] + ) @ ( \[ [ ^ \] ] + \] | [ ^ / ? # ] + ) : ( \d + ) (?: \/ ) ? (?: \? ( [ ^ # ] * ) ) ? (?: # ( .* ) ) ? $ /
4955 ) ;
5056
5157 if ( ! match ) {
@@ -61,11 +67,19 @@ function parseTrojan(url) {
6167 name
6268 ] = match ;
6369
70+ const normalizedServer = server . replace ( / ^ \[ | \] $ / g, "" ) ;
71+ if ( ( server . startsWith ( "[" ) || server . includes ( ":" ) ) && ! isIPv6 ( normalizedServer ) ) {
72+ throw new Error ( `Invalid server: ${ server } ` ) ;
73+ }
74+
6475
6576 proxy . type = "trojan" ;
6677 proxy . password = decode ( password ) ;
6778 proxy . server = server ;
6879 proxy . port = Number ( port ) ;
80+ if ( ! Number . isSafeInteger ( proxy . port ) || proxy . port < 1 || proxy . port > 65535 ) {
81+ throw new Error ( `Invalid port: ${ port } ` ) ;
82+ }
6983
7084 proxy . name = name
7185 ? decode ( name )
@@ -76,8 +90,11 @@ function parseTrojan(url) {
7690
7791 if ( query ) {
7892 for ( const item of query . split ( "&" ) ) {
79- const [ k , v = "" ] = item . split ( "=" ) ;
80- params [ k ] = decode ( v ) ;
93+ const separatorIndex = item . indexOf ( "=" ) ;
94+ const key = separatorIndex === - 1 ? item : item . slice ( 0 , separatorIndex ) ;
95+ params [ key ] = separatorIndex === - 1
96+ ? true
97+ : decode ( item . slice ( separatorIndex + 1 ) ) ;
8198 }
8299 }
83100
@@ -144,18 +161,12 @@ function parseTrojan(url) {
144161
145162 if ( proxy . network === "ws" ) {
146163
147- const ed = new URL (
148- "http://a" + path
149- ) . searchParams . get ( "ed" ) ;
164+ const ed = getPathQueryParam ( path , "ed" ) ;
150165
151166
152167 if ( isNumericEarlyData ( ed ) ) {
153168
154- path =
155- path . replace (
156- / [ ? & ] e d = \d + / ,
157- ""
158- ) ;
169+ path = extractPathQueryParam ( path , "ed" ) . path ;
159170
160171
161172 if ( httpupgrade )
@@ -227,7 +238,7 @@ function parseTrojan(url) {
227238 $set (
228239 proxy ,
229240 "ws-opts.max-early-data" ,
230- parseEarlyDataSize ( pathEarlyData )
241+ parseSafeIntegerValue ( pathEarlyData )
231242 ) ;
232243
233244
@@ -253,6 +264,12 @@ function parseTrojan(url) {
253264 if ( params . spx )
254265 opts [ "_spider-x" ] = params . spx ;
255266
267+ if ( params . mode )
268+ proxy . _mode = params . mode ;
269+
270+ if ( params . extra )
271+ proxy . _extra = params . extra ;
272+
256273
257274 if ( Object . keys ( opts ) . length ) {
258275 $set (
@@ -281,4 +298,4 @@ export default function getParser() {
281298 }
282299
283300 return parser ;
284- }
301+ }
0 commit comments