From 38745a86e4425e83377f9edc08c7668cc196ff13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Luiz?= Date: Thu, 4 Sep 2025 10:51:54 +0100 Subject: [PATCH 1/3] flag as a VPN only if the service name is known --- .../mmdb/ipinfo/IpInfoAnonymousResult.java | 7 +++- .../building/ipinfo/IpInfoMmdbTestIT.java | 37 +++++++++++++++++-- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/dice-where/src/main/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResult.java b/dice-where/src/main/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResult.java index 8367e8b..17b98ca 100644 --- a/dice-where/src/main/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResult.java +++ b/dice-where/src/main/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResult.java @@ -11,6 +11,7 @@ public class IpInfoAnonymousResult implements AnonymousResult { private final boolean hostingProvider; private final boolean proxy; private final boolean relay; + private final String service; @MaxMindDbConstructor public IpInfoAnonymousResult( @@ -18,12 +19,14 @@ public IpInfoAnonymousResult( @MaxMindDbParameter(name = "tor") String torExitNode, @MaxMindDbParameter(name = "relay") String relay, @MaxMindDbParameter(name = "proxy") String proxy, - @MaxMindDbParameter(name = "hosting") String hostingProvider) { + @MaxMindDbParameter(name = "hosting") String hostingProvider, + @MaxMindDbParameter(name = "service") String service) { this.vpn = Boolean.parseBoolean(vpn); this.torExitNode = Boolean.parseBoolean(torExitNode); this.relay = Boolean.parseBoolean(relay); this.proxy = Boolean.parseBoolean(proxy); this.hostingProvider = Boolean.parseBoolean(hostingProvider); + this.service = service; } @Override @@ -33,7 +36,7 @@ public boolean hostingProvider() { @Override public boolean vpn() { - return vpn; + return vpn && (service != null && !service.isBlank()); } @Override diff --git a/dice-where/src/test/java/technology/dice/dicewhere/building/ipinfo/IpInfoMmdbTestIT.java b/dice-where/src/test/java/technology/dice/dicewhere/building/ipinfo/IpInfoMmdbTestIT.java index e8e9636..189f2d1 100644 --- a/dice-where/src/test/java/technology/dice/dicewhere/building/ipinfo/IpInfoMmdbTestIT.java +++ b/dice-where/src/test/java/technology/dice/dicewhere/building/ipinfo/IpInfoMmdbTestIT.java @@ -28,9 +28,9 @@ public class IpInfoMmdbTestIT { private static final Path cityDatabase = Paths.get( - "/Users/gluiz/Downloads/ipinfo/standard_location/mmdb/standard_location-20230210.mmdb"); + "/Users/gluiz/Downloads/ipinfo_static/standard_location/mmdb/standard_location-20230210.mmdb"); private static final Path anonymousDatabase = - Paths.get("/Users/gluiz/Downloads/ipinfo/privacy/mmdb/privacy_detection_sample.mmdb"); + Paths.get("/Users/gluiz/Downloads/ipinfo_static/privacy/mmdb/privacy-20230209.mmdb"); @Test public void lookupWithCityDatabase() throws IOException { @@ -58,7 +58,7 @@ public void lookupWithCityDatabase() throws IOException { } @Test - public void lookupWithCityDatabaseAndAnonymous() throws IOException { + public void lookupWithCityDatabaseAndAnonymousEmptyServiceName() throws IOException { final IP ipToLookup = new IP(InetAddress.getByName("110.142.177.68")); final IPResolver build = new Builder() @@ -75,7 +75,7 @@ public void lookupWithCityDatabaseAndAnonymous() throws IOException { final IpInformation expected = IpInformation.builder() .withCountryCodeAlpha2("AU") - .isVpn(true) + .isVpn(false) .withPostcode("3061") .withCityGeonameId("2158177") .withCity("Melbourne") @@ -86,4 +86,33 @@ public void lookupWithCityDatabaseAndAnonymous() throws IOException { .build(); assertEquals(Optional.of(expected), resolve.get(new ProviderKey("custom") {})); } + + @Test + public void lookupWithCityDatabaseAndAnonymousWithServiceName() throws IOException { + final IP ipToLookup = new IP(InetAddress.getByName("31.171.154.78")); + final IPResolver build = + new Builder() + .withProvider( + new MmdbDatabase( + "custom", + new IpInfoLocationSource(cityDatabase), + new IpInfoAnonymousSource(anonymousDatabase))) + .build(); + + final Map> resolve = build.resolve(ipToLookup); + assertEquals(1, resolve.size()); + assertTrue(resolve.containsKey(new ProviderKey("custom") {})); + final IpInformation expected = + IpInformation.builder() + .withCountryCodeAlpha2("AL") + .isVpn(true) + .withCityGeonameId("3183875") + .withCity("Tirana") + .withLeastSpecificDivision("Tirana") + .withMostSpecificDivision("Tirana") + .withStartOfRange(ipToLookup) + .withEndOfRange(ipToLookup) + .build(); + assertEquals(Optional.of(expected), resolve.get(new ProviderKey("custom") {})); + } } From 3a07c426a604c2c0c5431e3799e0678de951caef Mon Sep 17 00:00:00 2001 From: Miguel Pontes Date: Thu, 4 Sep 2025 11:24:20 +0100 Subject: [PATCH 2/3] Update mapdb mapdb < 3.0.10 has a non-deterministic dependency on Eclipse Collections --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f4ae67a..dcacff1 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ UTF-8 21.0 4.2.0 - 3.0.7 + 3.0.10 3.21.12 5.9.2 5.1.1 From 8448f2aaad35149cbc90940fdbbb2ffa33e5dd30 Mon Sep 17 00:00:00 2001 From: Miguel Pontes Date: Fri, 5 Sep 2025 08:33:27 +0100 Subject: [PATCH 3/3] Add test coverage for IPInfo result parser --- .../ipinfo/IpInfoAnonymousResultTest.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 dice-where/src/test/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResultTest.java diff --git a/dice-where/src/test/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResultTest.java b/dice-where/src/test/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResultTest.java new file mode 100644 index 0000000..796ccaa --- /dev/null +++ b/dice-where/src/test/java/technology/dice/dicewhere/building/mmdb/ipinfo/IpInfoAnonymousResultTest.java @@ -0,0 +1,128 @@ +package technology.dice.dicewhere.building.mmdb.ipinfo; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class IpInfoAnonymousResultTest { + + @Test + public void testVpnWithValidServiceName() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("true", "false", "false", "false", "false", "ExpressVPN"); + + assertTrue( + result.vpn(), "Should be detected as VPN when vpn=true and service name is provided"); + } + + @Test + public void testVpnWithEmptyServiceName() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("true", "false", "false", "false", "false", ""); + + assertFalse(result.vpn(), "Should not be detected as VPN when service name is empty"); + } + + @Test + public void testVpnWithBlankServiceName() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("true", "false", "false", "false", "false", " "); + + assertFalse(result.vpn(), "Should not be detected as VPN when service name is blank"); + } + + @Test + public void testVpnWithNullServiceName() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("true", "false", "false", "false", "false", null); + + assertFalse(result.vpn(), "Should not be detected as VPN when service name is null"); + } + + @Test + public void testVpnFalseWithServiceName() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("false", "false", "false", "false", "false", "ExpressVPN"); + + assertFalse( + result.vpn(), "Should not be detected as VPN when vpn=false regardless of service name"); + } + + @Test + public void testVpnFalseWithoutServiceName() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("false", "false", "false", "false", "false", ""); + + assertFalse(result.vpn(), "Should not be detected as VPN when vpn=false and no service name"); + } + + @Test + public void testHostingProvider() { + IpInfoAnonymousResult resultTrue = + new IpInfoAnonymousResult("false", "false", "false", "false", "true", ""); + IpInfoAnonymousResult resultFalse = + new IpInfoAnonymousResult("false", "false", "false", "false", "false", ""); + + assertTrue(resultTrue.hostingProvider(), "Should detect hosting provider when true"); + assertFalse(resultFalse.hostingProvider(), "Should not detect hosting provider when false"); + } + + @Test + public void testTorExitNode() { + IpInfoAnonymousResult resultTrue = + new IpInfoAnonymousResult("false", "true", "false", "false", "false", ""); + IpInfoAnonymousResult resultFalse = + new IpInfoAnonymousResult("false", "false", "false", "false", "false", ""); + + assertTrue(resultTrue.torExitNode(), "Should detect Tor exit node when true"); + assertFalse(resultFalse.torExitNode(), "Should not detect Tor exit node when false"); + } + + @Test + public void testResidentialProxy() { + IpInfoAnonymousResult resultTrue = + new IpInfoAnonymousResult("false", "false", "true", "false", "false", ""); + IpInfoAnonymousResult resultFalse = + new IpInfoAnonymousResult("false", "false", "false", "false", "false", ""); + + assertTrue(resultTrue.residentialProxy(), "Should detect residential proxy when relay=true"); + assertFalse( + resultFalse.residentialProxy(), "Should not detect residential proxy when relay=false"); + } + + @Test + public void testPublicProxy() { + IpInfoAnonymousResult resultTrue = + new IpInfoAnonymousResult("false", "false", "false", "true", "false", ""); + IpInfoAnonymousResult resultFalse = + new IpInfoAnonymousResult("false", "false", "false", "false", "false", ""); + + assertTrue(resultTrue.publicProxy(), "Should detect public proxy when true"); + assertFalse(resultFalse.publicProxy(), "Should not detect public proxy when false"); + } + + @Test + public void testAllFieldsTrue() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("true", "true", "true", "true", "true", "TestService"); + + assertTrue(result.vpn(), "Should be detected as VPN with service name"); + assertTrue(result.torExitNode(), "Should detect Tor exit node"); + assertTrue(result.residentialProxy(), "Should detect residential proxy"); + assertTrue(result.publicProxy(), "Should detect public proxy"); + assertTrue(result.hostingProvider(), "Should detect hosting provider"); + } + + @Test + public void testAllFieldsFalse() { + IpInfoAnonymousResult result = + new IpInfoAnonymousResult("false", "false", "false", "false", "false", ""); + + assertFalse(result.vpn(), "Should not be detected as VPN"); + assertFalse(result.torExitNode(), "Should not detect Tor exit node"); + assertFalse(result.residentialProxy(), "Should not detect residential proxy"); + assertFalse(result.publicProxy(), "Should not detect public proxy"); + assertFalse(result.hostingProvider(), "Should not detect hosting provider"); + } +}