@@ -747,10 +747,35 @@ public Builder setRetry(@NotNull Boolean retry) {
747747 * @return Client host
748748 */
749749 public Builder setHost (@ NotNull String hostname ) {
750- this .hostname = hostname ;
750+ this .hostname = validateHostname ( hostname ) ;
751751 return this ;
752752 }
753753
754+ /**
755+ * Validates that the supplied hostname is a bare host (optionally with a
756+ * port) and does not smuggle a scheme, credentials, path, query, or other
757+ * characters that could redirect outbound requests to an unintended
758+ * destination. This guards against Server-Side Request Forgery (SSRF)
759+ * when the host is sourced from untrusted input.
760+ *
761+ * @param hostname the candidate host
762+ * @return the validated host, unchanged
763+ * @throws IllegalArgumentException if the host is null, blank, or malformed
764+ */
765+ private static String validateHostname (String hostname ) {
766+ if (hostname == null || hostname .trim ().isEmpty ()) {
767+ throw new IllegalArgumentException ("Hostname must not be null or empty" );
768+ }
769+ String host = hostname .trim ();
770+ // Reject embedded scheme, credentials, path/query/fragment, whitespace,
771+ // and other characters that would change the request target.
772+ if (!host .matches ("^[A-Za-z0-9.-]+(:\\ d{1,5})?$" )) {
773+ throw new IllegalArgumentException (
774+ "Invalid hostname: '" + hostname + "'. Expected a bare host name with an optional port." );
775+ }
776+ return host ;
777+ }
778+
754779 /**
755780 * Configures the client to target a specific Contentstack region by resolving
756781 * the correct Content Management API host from the bundled regions registry.
@@ -974,7 +999,10 @@ public Contentstack build() {
974999 }
9751000
9761001 private void validateClient (Contentstack contentstack ) {
977- String baseUrl = Util .PROTOCOL + "://" + this .hostname + "/" + version + "/" ;
1002+ // Re-validate the host at build time so SSRF protection applies regardless
1003+ // of how the hostname was set (setHost, region resolution, or default).
1004+ String validatedHost = validateHostname (this .hostname );
1005+ String baseUrl = Util .PROTOCOL + "://" + validatedHost + "/" + version + "/" ;
9781006 this .instance = new Retrofit .Builder ().baseUrl (baseUrl )
9791007 .addConverterFactory (GsonConverterFactory .create ())
9801008 .client (httpClient (contentstack , this .retry )).build ();
0 commit comments