diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f4cd3f..f068cedb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ CHANGELOG ========= +5.0.2 (unreleased) +------------------ + +* Fixed an issue where decoding `IpRiskResponse` from the IP Risk database would + fail when the `ip_risk` field was not present in the database record. The + `ipRisk` field now defaults to 0.0 when not present. A value of 0.0 indicates + that the risk score was not set in the database. In a future major release, + this field may be changed to a nullable `Double` to better distinguish between + "no data" and "zero risk". Reported by Fabrice Bacchella. GitHub #644. + 5.0.1 (2025-12-02) ------------------ diff --git a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java index 4795ae18..b137e4e2 100644 --- a/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java +++ b/src/main/java/com/maxmind/geoip2/model/IpRiskResponse.java @@ -28,8 +28,14 @@ * @param isTorExitNode Whether the IP address is a Tor exit node. * @param network The network associated with the record. In particular, this is the largest * network where all the fields besides IP address have the same value. - * @param ipRisk The IP risk of a model. + * @param ipRisk The IP risk score for the IP address. A value of 0.0 indicates that the + * risk score was not set in the database. This is a limitation of primitive + * types in Java - the value cannot be null. In a future major version, this + * field may be changed to a nullable {@code Double} to distinguish between + * "no data" and "zero risk". */ +// TODO: In the next major version (6.0.0), consider changing ipRisk to Double +// to allow null values, distinguishing "no data" from "zero risk". public record IpRiskResponse( @JsonProperty("ip_address") @MaxMindDbIpAddress @@ -65,7 +71,7 @@ public record IpRiskResponse( Network network, @JsonProperty("ip_risk") - @MaxMindDbParameter(name = "ip_risk") + @MaxMindDbParameter(name = "ip_risk", useDefault = true) double ipRisk ) implements JsonSerializable { diff --git a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java index b92e1a1c..21b1e6e6 100644 --- a/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java +++ b/src/test/java/com/maxmind/geoip2/DatabaseReaderTest.java @@ -19,6 +19,7 @@ import com.maxmind.geoip2.model.CountryResponse; import com.maxmind.geoip2.model.DomainResponse; import com.maxmind.geoip2.model.EnterpriseResponse; +import com.maxmind.geoip2.model.IpRiskResponse; import com.maxmind.geoip2.model.IspResponse; import java.io.File; import java.io.IOException; @@ -427,6 +428,48 @@ public void testIsp() throws Exception { } } + @Test + public void testIpRisk() throws Exception { + try (DatabaseReader reader = new DatabaseReader.Builder( + this.getFile("GeoIP2-IP-Risk-Test.mmdb")).build() + ) { + InetAddress ipAddress = InetAddress.getByName("214.2.3.0"); + IpRiskResponse response = reader.ipRisk(ipAddress); + assertEquals(25.0, response.ipRisk()); + assertTrue(response.isAnonymous()); + assertTrue(response.isAnonymousVpn()); + assertFalse(response.isHostingProvider()); + assertFalse(response.isPublicProxy()); + assertFalse(response.isResidentialProxy()); + assertFalse(response.isTorExitNode()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); + assertEquals("214.2.3.0/30", response.network().toString()); + + IpRiskResponse tryResponse = reader.tryIpRisk(ipAddress).get(); + assertEquals(response.toJson(), tryResponse.toJson()); + } + } + + @Test + public void testIpRiskWithoutIpRiskField() throws Exception { + // Test that records without ip_risk field default to 0.0. + // A value of 0.0 indicates that the risk score was not set in the database. + try (DatabaseReader reader = new DatabaseReader.Builder( + this.getFile("GeoIP2-IP-Risk-Test.mmdb")).build() + ) { + InetAddress ipAddress = InetAddress.getByName("11.1.2.3"); + IpRiskResponse response = reader.ipRisk(ipAddress); + assertEquals(0.0, response.ipRisk()); + assertTrue(response.isAnonymous()); + assertFalse(response.isAnonymousVpn()); + assertFalse(response.isHostingProvider()); + assertTrue(response.isPublicProxy()); + assertFalse(response.isResidentialProxy()); + assertFalse(response.isTorExitNode()); + assertEquals(ipAddress.getHostAddress(), response.ipAddress().getHostAddress()); + } + } + private File getFile(String filename) throws URISyntaxException { URL resource = DatabaseReaderTest.class .getResource("/maxmind-db/test-data/" + filename); diff --git a/src/test/resources/maxmind-db b/src/test/resources/maxmind-db index f387805e..b6eb21df 160000 --- a/src/test/resources/maxmind-db +++ b/src/test/resources/maxmind-db @@ -1 +1 @@ -Subproject commit f387805e4af30036ec4245c9069ac1f2fdae4953 +Subproject commit b6eb21df882cc939d6b31bc05811a101f17fe148