Page MenuHomePhabricator

isBlocked cannot detect range blocks
Closed, ResolvedPublicBUG REPORT

Description

List of steps to reproduce (step by step, including full links if applicable):

  • Block a range on the wiki (say 8.0.0.0/24)
  • Use pywikibot to load this range as a 'user' and check if it is blocked:
import pywikibot

site = pywikibot.Site('test') # or whatever site you are using
ip_range = '8.0.0.0/24' # or whatever range you decided to block

target = pywikibot.User(site, ip_range)
target.isBlocked() # returns False

What happens?:
isBlocked() returns False

What should have happened instead?:
isBloced() should return True

Software version (if not a Wikimedia wiki), browser information, screenshots, other information, etc.: I discovered it in 90740c9d3e16e97e47d81e4b5db2f1769683cb09 but it seems to be in issue in latest master HEAD too.

Event Timeline

@Xqt in a cursory look, I couldn't quickly piece together the API call that is used for isBlocked() but I am guessing the issue is we are using an API call that works for named users and individual IPs, but should use a different one for IP ranges. Can you help me figure it out?

@Xqt in a cursory look, I couldn't quickly piece together the API call that is used for isBlocked() but I am guessing the issue is we are using an API call that works for named users and individual IPs, but should use a different one for IP ranges. Can you help me figure it out?

is_blocked() calls Page.getprops() to get the blocking status. getprops() calls APISite.users() to retrieve user information an additionaly APISite.blocks() for IP user.
https://doc.wikimedia.org/pywikibot/master/api_ref/pywikibot.site.html?highlight=blocks#pywikibot.site._generators.GeneratorsMixin.blocks
Maybe using iprange parameter instead of users parameter for site.blocks works as expected? I think so: https://www.mediawiki.org/wiki/API:Blocks

Xqt triaged this task as High priority.Feb 9 2022, 4:33 AM

Change 761111 had a related patch set uploaded (by Xqt; author: Xqt):

[pywikibot/core@master] [bugfix] detect range blocks with Page.is_blocked()

https://gerrit.wikimedia.org/r/761111

Change 761111 merged by Huji:

[pywikibot/core@master] [bugfix] detect range blocks with Page.is_blocked()

https://gerrit.wikimedia.org/r/761111

@Xqt the change we applied above doesn't exactly work (anymore?)

Take as an example the range 107.167.241.0/24 which was recently blocked on enwiki. If I check for the blocked status of an IP from that range, this method returns True correctly. But when I pass the entire range, it returns False.

pywikibot.User(pywikibot.Site('en', 'wikipedia'), '107.167.241.0').is_blocked()    # returns True
pywikibot.User(pywikibot.Site('en', 'wikipedia'), '107.167.241.1').is_blocked()    # returns True
pywikibot.User(pywikibot.Site('en', 'wikipedia'), '107.167.241.0/24').is_blocked() # returns False

I tried to review the API response, but as discussed in T354672 I don't seem to know how to use the API directly for checking IP blocks.

If the MediaWIki API in fact doesn't return block information for the range, then we either have to fix it there, or we have to use a workaround here in Pywikibot whereby when a CIDR is passed to is_blocked() we would just check the status of any IP in the range (the easiest choice being the first IP in that range, which is easy to get by just trimming at /). If MediaWiki API does return the block information correctly for CIDR ranges, then we have to figure out why Pywikibot is not parsing that correctly.

And we should add some unit tests for this code too.

Found the problem. To get the blocks for registered users, we can use API:Users, but for anonymous users, we cannot use that IP and we fetch the last block using API:Blocks instead. This relies on correctly determining that the user is an anonymous user.

This line is meant to achieve that. The problem: isAnonymous() (which essentially calls pywikibot.tools.is_ip_address) returns true for single IP addresses, but not for CIDR ranges.

Logical solution would be to add a is_ip_range method to pywikibot.tools and either extend isAnonymous() to check for both of these, or modify the line linked above to check for both isAnonymous() and a new isCIDR() method yet to be created.

pywikibot.tools.is_ip_address essentially imports the ipaddress library from Python, then passes the input string to it; if that library throws an exception, it returns false, otherwise it returns true. We can use the ip_network() method from that same library to conveniently validate CIDR ranges (see documentation).

With all that in mind, creating a patch is straightforward.

Since other code (such as this in patrol.py) relies on isAnonymous() to indicate if the user is an anonymous *user* (as opposed to CIDR), we cannot extend isAnonymous() to also return True for CIDR ranges. Therefore, the correct solution is to create a new isCIDR() method.

Change 990725 had a related patch set uploaded (by Huji; author: Huji):

[pywikibot/core@master] Detect range blocks with Page.is_blocked()

https://gerrit.wikimedia.org/r/990725

Change 990725 merged by jenkins-bot:

[pywikibot/core@master] Detect range blocks with Page.is_blocked()

https://gerrit.wikimedia.org/r/990725