@@ -19,8 +19,8 @@ public UrlValidateUtil(OpenAIConfig config) {
1919
2020
2121 public void validateFinalUrl (String finalUrl ) {
22- if (finalUrl == null || finalUrl .isBlank ()) {
23- throw new ServiceException ("400" , "Invalid baseUrl: empty url " );
22+ if (finalUrl == null || finalUrl .isEmpty ()) {
23+ throw new ServiceException ("400" , "baseUrl cannot be null or empty " );
2424 }
2525
2626 URI uri ;
@@ -44,6 +44,7 @@ public void validateFinalUrl(String finalUrl) {
4444 throw new ServiceException ("500" , "No AI allowed hosts configured" );
4545 }
4646
47+ enforceHttpsAndIpCheck (uri , host );
4748 return ;
4849 }
4950
@@ -55,14 +56,97 @@ public void validateFinalUrl(String finalUrl) {
5556 }
5657
5758 if (isLoopback ) {
58- throw new ServiceException ("400" , "Loopback addresses are not allowed for baseUrl" );
59+ throw new ServiceException ("400" , "Loopback addresses are not allowed for custom baseUrl" );
5960 }
6061
62+ enforceHttpsAndIpCheck (uri , host );
6163 }
6264
65+ void enforceHttpsAndIpCheck (URI uri , String host ) {
66+ String scheme = uri .getScheme ();
67+ if (scheme == null || !"https" .equalsIgnoreCase (scheme )) {
68+ throw new ServiceException ("400" , "Only HTTPS protocol is allowed for custom baseUrl" );
69+ }
70+
71+ try {
72+ InetAddress [] addresses = resolveHostAddresses (host );
73+ boolean hasBlockedAddress = Arrays .stream (addresses ).anyMatch (this ::isBlockedAddress );
74+ if (hasBlockedAddress ) {
75+ throw new ServiceException ("400" , "Internal network addresses are not allowed" );
76+ }
77+ } catch (UnknownHostException e ) {
78+ throw new ServiceException ("400" , "Unable to resolve host: " + host );
79+ }
80+ }
81+
82+ InetAddress [] resolveHostAddresses (String host ) throws UnknownHostException {
83+ return InetAddress .getAllByName (host );
84+ }
85+
86+ boolean isBlockedAddress (InetAddress address ) {
87+ if (address .isLoopbackAddress ()
88+ || address .isSiteLocalAddress ()
89+ || address .isLinkLocalAddress ()
90+ || address .isAnyLocalAddress ()
91+ || address .isMulticastAddress ()) {
92+ return true ;
93+ }
94+
95+ if (address instanceof Inet4Address ) {
96+ return isBlockedIpv4 ((Inet4Address ) address );
97+ }
98+ if (address instanceof Inet6Address ) {
99+ return isBlockedIpv6 ((Inet6Address ) address );
100+ }
101+ return false ;
102+ }
63103
104+ private boolean isBlockedIpv4 (Inet4Address address ) {
105+ byte [] octets = address .getAddress ();
106+ int first = octets [0 ] & 0xFF ;
107+ int second = octets [1 ] & 0xFF ;
108+ int third = octets [2 ] & 0xFF ;
64109
110+ if (first == 0 ) {
111+ return true ;
112+ }
113+ if (first == 100 && second >= 64 && second <= 127 ) {
114+ return true ;
115+ }
116+ if (first == 192 && second == 0 && third == 0 ) {
117+ return true ;
118+ }
119+ if (first == 192 && second == 0 && third == 2 ) {
120+ return true ;
121+ }
122+ if (first == 198 && (second == 18 || second == 19 )) {
123+ return true ;
124+ }
125+ if (first == 198 && second == 51 && third == 100 ) {
126+ return true ;
127+ }
128+ if (first == 203 && second == 0 && third == 113 ) {
129+ return true ;
130+ }
131+ return first >= 240 ;
132+ }
65133
134+ private boolean isBlockedIpv6 (Inet6Address address ) {
135+ byte [] octets = address .getAddress ();
136+ int first = octets [0 ] & 0xFF ;
137+ int second = octets [1 ] & 0xFF ;
66138
139+ if ((first & 0xFE ) == 0xFC ) {
140+ return true ;
141+ }
142+ if (first == 0x20 && second == 0x01 ) {
143+ int third = octets [2 ] & 0xFF ;
144+ int fourth = octets [3 ] & 0xFF ;
145+ if (third == 0x0D && fourth == 0xB8 ) {
146+ return true ;
147+ }
148+ }
149+ return first == 0xFF ;
150+ }
67151
68152}
0 commit comments