@@ -70,6 +70,24 @@ const UpstreamProxyDropdown = styled(Select)`
7070 margin-right: 10px;
7171` ;
7272
73+ const HOST_PATTERN = '[A-Za-z0-9\\-.]+' ;
74+ const PORT_PATTERN = '\\d+' ;
75+ // Disallow "@", "/" and ":" in username/password
76+ const CRED_PATTERN = '[^@/:]+' ;
77+
78+ const PROXY_HOST_REGEXES = [
79+ // 0) host or host:port
80+ new RegExp ( `^(${ HOST_PATTERN } )(:${ PORT_PATTERN } )?$` ) ,
81+ // 1) user:pass@host:port
82+ new RegExp ( `^(${ CRED_PATTERN } ):(${ CRED_PATTERN } )@(${ HOST_PATTERN } ):(${ PORT_PATTERN } )$` ) ,
83+ // 2) host:port@user:pass
84+ new RegExp ( `^(${ HOST_PATTERN } ):(${ PORT_PATTERN } )@(${ CRED_PATTERN } ):(${ CRED_PATTERN } )$` ) ,
85+ // 3) host:port:user:pass
86+ new RegExp ( `^(${ HOST_PATTERN } ):(${ PORT_PATTERN } ):(${ CRED_PATTERN } ):(${ CRED_PATTERN } )$` ) ,
87+ // 4) user:pass:host:port
88+ new RegExp ( `^(${ CRED_PATTERN } ):(${ CRED_PATTERN } ):(${ HOST_PATTERN } ):(${ PORT_PATTERN } )$` ) ,
89+ ] ;
90+
7391const isValidClientCertHost = ( input : string ) : boolean =>
7492 isValidHost ( input ) || input === '*' ;
7593
@@ -82,11 +100,30 @@ const validateClientCertHost = inputValidation(isValidClientCertHost,
82100) ;
83101
84102const isValidProxyHost = ( host : string | undefined ) : boolean =>
85- ! ! host ?. match ( / ^ ( [ ^ / @ ] * @ ) ? [ A - Z a - z 0 - 9 \- . ] + ( : \d + ) ? $ / ) ;
103+ ! ! host && PROXY_HOST_REGEXES . some ( regex => regex . test ( host ) ) ;
86104const validateProxyHost = inputValidation ( isValidProxyHost ,
87105 "Should be a plain hostname, optionally with a specific port and/or username:password"
88106) ;
89107
108+ // Translates the proxy host input into standard form if needed
109+ const normalizeProxyHost = ( host : string ) : string => {
110+ const idx = PROXY_HOST_REGEXES . findIndex ( regex => regex . test ( host ) ) ;
111+ if ( idx === - 1 ) return host ; // Should not occur
112+
113+ const groups = host . match ( PROXY_HOST_REGEXES [ idx ] ) ! ;
114+
115+ switch ( idx ) {
116+ case 2 : // host:port@user :pass
117+ return `${ groups [ 3 ] } :${ groups [ 4 ] } @${ groups [ 1 ] } :${ groups [ 2 ] } ` ;
118+ case 3 : // host:port:user:pass
119+ return `${ groups [ 3 ] } :${ groups [ 4 ] } @${ groups [ 1 ] } :${ groups [ 2 ] } ` ;
120+ case 4 : // user:pass:host:port
121+ return `${ groups [ 1 ] } :${ groups [ 2 ] } @${ groups [ 3 ] } :${ groups [ 4 ] } ` ;
122+ default : // Does not include auth or already in standard format
123+ return host ;
124+ }
125+ } ;
126+
90127@observer
91128class UpstreamProxyConfig extends React . Component < { rulesStore : RulesStore } > {
92129
@@ -126,7 +163,7 @@ class UpstreamProxyConfig extends React.Component<{ rulesStore: RulesStore }> {
126163 // We update the rules store proxy type only at the point where we save the host:
127164 const rulesStore = this . props . rulesStore ;
128165 rulesStore . upstreamProxyType = this . proxyType ;
129- rulesStore . upstreamProxyHost = this . proxyHostInput ;
166+ rulesStore . upstreamProxyHost = normalizeProxyHost ( this . proxyHostInput ) ;
130167 }
131168
132169 @action . bound
@@ -216,7 +253,7 @@ class UpstreamProxyConfig extends React.Component<{ rulesStore: RulesStore }> {
216253 < SettingsButton
217254 disabled = {
218255 ! isValidProxyHost ( proxyHostInput ) ||
219- ( proxyHostInput === savedProxyHost && proxyType === savedProxyType )
256+ ( normalizeProxyHost ( proxyHostInput ) === savedProxyHost && proxyType === savedProxyType )
220257 }
221258 onClick = { saveProxyHost }
222259 >
0 commit comments