Skip to content

Commit dd0f6ea

Browse files
oschwaldclaude
andcommitted
Add anonymizer property documentation and tests for IPAddress model
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1d8bed8 commit dd0f6ea

File tree

5 files changed

+69
-0
lines changed

5 files changed

+69
-0
lines changed

HISTORY.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ History
1111
* The version is now retrieved from package metadata at runtime using
1212
``importlib.metadata``. This reduces the chance of version inconsistencies
1313
during releases.
14+
* Added documentation for the ``anonymizer`` attribute on the
15+
``minfraud.models.IPAddress`` class. This attribute is inherited from
16+
``geoip2.models.Insights`` and provides data about anonymizers associated
17+
with the IP address, including confidence score, anonymizer type flags
18+
(VPN, proxy, Tor, etc.), network last seen date, and provider name.
1419

1520
3.2.0 (2025-11-20)
1621
++++++++++++++++++

src/minfraud/models.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,29 @@ class IPAddress(geoip2.models.Insights):
113113
to the attributes provided by that class, it provides the ``risk`` and
114114
``risk_reasons`` attributes. It also overrides the ``location`` attribute
115115
to use :py:class:`GeoIP2Location`.
116+
117+
.. attribute:: anonymizer
118+
119+
A :py:class:`geoip2.records.Anonymizer` object containing data about
120+
the anonymizer associated with the IP address. This object has the
121+
following attributes:
122+
123+
* ``confidence`` - A number from 0 to 100 indicating our confidence that
124+
the IP address is an anonymizer based on analysis of our data.
125+
* ``is_anonymous`` - ``True`` if the IP address belongs to any sort of
126+
anonymous network.
127+
* ``is_anonymous_vpn`` - ``True`` if the IP address is registered to an
128+
anonymous VPN provider.
129+
* ``is_hosting_provider`` - ``True`` if the IP address belongs to a
130+
hosting or VPN provider (see description of ``is_anonymous_vpn``).
131+
* ``is_public_proxy`` - ``True`` if the IP address belongs to a public
132+
proxy.
133+
* ``is_residential_proxy`` - ``True`` if the IP address is on a suspected
134+
anonymizing network and belongs to a residential ISP.
135+
* ``is_tor_exit_node`` - ``True`` if the IP address is a Tor exit node.
136+
* ``network_last_seen`` - A ``datetime.date`` object indicating
137+
when the network was last seen as an anonymizer.
138+
* ``provider_name`` - The name of the anonymizer provider, if known.
116139
"""
117140

118141
location: GeoIP2Location

tests/data/factors-response.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919
"reason": "Suspicious activity has been seen on this IP address across minFraud customers."
2020
}
2121
],
22+
"anonymizer": {
23+
"confidence": 99,
24+
"is_anonymous": true,
25+
"is_anonymous_vpn": true,
26+
"is_hosting_provider": true,
27+
"is_public_proxy": true,
28+
"is_tor_exit_node": true,
29+
"network_last_seen": "2025-01-15",
30+
"provider_name": "TestVPN"
31+
},
2232
"city": {
2333
"confidence": 42,
2434
"geoname_id": 2643743,

tests/data/insights-response.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919
"reason": "Suspicious activity has been seen on this IP address across minFraud customers."
2020
}
2121
],
22+
"anonymizer": {
23+
"confidence": 99,
24+
"is_anonymous": true,
25+
"is_anonymous_vpn": true,
26+
"is_hosting_provider": true,
27+
"is_public_proxy": true,
28+
"is_tor_exit_node": true,
29+
"network_last_seen": "2025-01-15",
30+
"provider_name": "TestVPN"
31+
},
2232
"city": {
2333
"confidence": 42,
2434
"geoname_id": 2643743,

tests/test_models.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import datetime
34
import unittest
45
from typing import Any
56

@@ -187,6 +188,16 @@ def test_ip_address(self) -> None:
187188
"across minFraud customers.",
188189
},
189190
],
191+
anonymizer={
192+
"confidence": 99,
193+
"is_anonymous": True,
194+
"is_anonymous_vpn": True,
195+
"is_hosting_provider": True,
196+
"is_public_proxy": True,
197+
"is_tor_exit_node": True,
198+
"network_last_seen": "2025-01-15",
199+
"provider_name": "TestVPN",
200+
},
190201
traits={
191202
"is_anonymous": True,
192203
"is_anonymous_proxy": True,
@@ -215,6 +226,16 @@ def test_ip_address(self) -> None:
215226
self.assertEqual("310", address.traits.mobile_country_code)
216227
self.assertEqual("004", address.traits.mobile_network_code)
217228

229+
# Test anonymizer attribute
230+
self.assertEqual(99, address.anonymizer.confidence)
231+
self.assertEqual(True, address.anonymizer.is_anonymous)
232+
self.assertEqual(True, address.anonymizer.is_anonymous_vpn)
233+
self.assertEqual(True, address.anonymizer.is_hosting_provider)
234+
self.assertEqual(True, address.anonymizer.is_public_proxy)
235+
self.assertEqual(True, address.anonymizer.is_tor_exit_node)
236+
self.assertEqual(datetime.date(2025, 1, 15), address.anonymizer.network_last_seen)
237+
self.assertEqual("TestVPN", address.anonymizer.provider_name)
238+
218239
self.assertEqual("ANONYMOUS_IP", address.risk_reasons[0].code)
219240
self.assertEqual(
220241
"The IP address belongs to an anonymous network. "

0 commit comments

Comments
 (0)