Skip to content

Commit 35077a9

Browse files
authored
Merge pull request #251 from contentstack/fix/snyk
fix: update version to 1.12.1 and add hostname validation to prevent SSRF
2 parents 3facdd8 + 8b506b5 commit 35077a9

3 files changed

Lines changed: 37 additions & 3 deletions

File tree

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v1.12.1
4+
5+
### Jun 29, 2026
6+
7+
- Snyk fix
8+
39
## v1.12.0
410

511
### Jun 15, 2026

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<artifactId>cms</artifactId>
88
<packaging>jar</packaging>
99
<name>contentstack-management-java</name>
10-
<version>1.12.0</version>
10+
<version>1.12.1</version>
1111
<description>Contentstack Java Management SDK for Content Management API, Contentstack is a headless CMS with an
1212
API-first approach
1313
</description>

src/main/java/com/contentstack/cms/Contentstack.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)