[3.15] gh-80198: Improve test_pwd and test_grp (GH-150380) (GH-150398)

Fix tests for non-existing names and ids when getpwall()/getgrall()
don't return all users/groups.

Add tests for out-of-range uids, integer float ids, bytes names,
null-terminated names, names with surrogates, empty names, excessive
arguments.
(cherry picked from commit 46e8f7a9e7)
This commit is contained in:
Serhiy Storchaka 2026-05-25 21:43:23 +03:00 committed by GitHub
parent e2362aac34
commit 4bdff2cc89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 90 additions and 98 deletions

View file

@ -1,5 +1,7 @@
"""Test script for the grp module."""
import random
import string
import unittest
from test.support import import_helper
@ -50,61 +52,51 @@ def test_values_extended(self):
def test_errors(self):
self.assertRaises(TypeError, grp.getgrgid)
self.assertRaises(TypeError, grp.getgrgid, 3.14)
self.assertRaises(TypeError, grp.getgrgid, 0.0)
self.assertRaises(TypeError, grp.getgrgid, 0, 0)
# should be out of gid_t range
self.assertRaises(OverflowError, grp.getgrgid, 2**128)
self.assertRaises(OverflowError, grp.getgrgid, -2**128)
self.assertRaises(TypeError, grp.getgrnam)
self.assertRaises(TypeError, grp.getgrnam, 42)
self.assertRaises(TypeError, grp.getgrall, 42)
self.assertRaises(TypeError, grp.getgrnam, b'root')
self.assertRaises(TypeError, grp.getgrnam, 'root', 0)
# embedded null character
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'a\x00b')
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'root\x00')
self.assertRaises(UnicodeEncodeError, grp.getgrnam, 'roo\udc74')
self.assertRaises(KeyError, grp.getgrnam, '')
self.assertRaises(TypeError, grp.getgrall, 42)
# try to get some errors
bynames = {}
bygids = {}
for (n, p, g, mem) in grp.getgrall():
if not n or n == '+':
continue # skip NIS entries etc.
bynames[n] = g
bygids[g] = n
# Find a non-existent group name.
# getgrall() will not necessarily report all existing groups
# (typical for LDAP based directories in big organizations).
for _ in range(30):
fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
try:
grp.getgrnam(fakename)
except KeyError:
break
else:
self.fail('Cannot find non-existent group name')
allnames = list(bynames.keys())
namei = 0
fakename = allnames[namei]
while fakename in bynames:
chars = list(fakename)
for i in range(len(chars)):
if chars[i] == 'z':
chars[i] = 'A'
break
elif chars[i] == 'Z':
continue
# Find a non-existent gid.
maxgid = 2**31
for _ in range(30):
fakegid = random.randrange(maxgid)
try:
grp.getgrgid(fakegid)
except KeyError:
break
except OverflowError:
if maxgid == 2**31:
maxgid = 2**16-1
elif maxgid == 2**16-1:
maxgid = 2**15
else:
chars[i] = chr(ord(chars[i]) + 1)
break
else:
namei = namei + 1
try:
fakename = allnames[namei]
except IndexError:
# should never happen... if so, just forget it
break
fakename = ''.join(chars)
self.assertRaises(KeyError, grp.getgrnam, fakename)
# Choose a non-existent gid.
fakegid = 4127
while fakegid in bygids:
fakegid = (fakegid * 3) % 0x10000
self.assertRaises(KeyError, grp.getgrgid, fakegid)
def test_noninteger_gid(self):
entries = grp.getgrall()
if not entries:
self.skipTest('no groups')
# Choose an existent gid.
gid = entries[0][2]
self.assertRaises(TypeError, grp.getgrgid, float(gid))
self.assertRaises(TypeError, grp.getgrgid, str(gid))
raise
else:
self.fail('Cannot find non-existent gid')
if __name__ == "__main__":

View file

@ -1,3 +1,5 @@
import random
import string
import sys
import unittest
from test.support import import_helper
@ -56,59 +58,57 @@ def test_values_extended(self):
def test_errors(self):
self.assertRaises(TypeError, pwd.getpwuid)
self.assertRaises(TypeError, pwd.getpwuid, 3.14)
self.assertRaises(TypeError, pwd.getpwnam)
self.assertRaises(TypeError, pwd.getpwnam, 42)
self.assertRaises(TypeError, pwd.getpwall, 42)
# embedded null character
self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'a\x00b')
# try to get some errors
bynames = {}
byuids = {}
for (n, p, u, g, gecos, d, s) in pwd.getpwall():
bynames[n] = u
byuids[u] = n
allnames = list(bynames.keys())
namei = 0
fakename = allnames[namei] if allnames else "invaliduser"
while fakename in bynames:
chars = list(fakename)
for i in range(len(chars)):
if chars[i] == 'z':
chars[i] = 'A'
break
elif chars[i] == 'Z':
continue
else:
chars[i] = chr(ord(chars[i]) + 1)
break
else:
namei = namei + 1
try:
fakename = allnames[namei]
except IndexError:
# should never happen... if so, just forget it
break
fakename = ''.join(chars)
self.assertRaises(KeyError, pwd.getpwnam, fakename)
# In some cases, byuids isn't a complete list of all users in the
# system, so if we try to pick a value not in byuids (via a perturbing
# loop, say), pwd.getpwuid() might still be able to find data for that
# uid. Using sys.maxint may provoke the same problems, but hopefully
# it will be a more repeatable failure.
fakeuid = sys.maxsize
self.assertNotIn(fakeuid, byuids)
self.assertRaises(KeyError, pwd.getpwuid, fakeuid)
# -1 shouldn't be a valid uid because it has a special meaning in many
# uid-related functions
self.assertRaises(KeyError, pwd.getpwuid, -1)
self.assertRaises(TypeError, pwd.getpwuid, 0.0)
self.assertRaises(TypeError, pwd.getpwuid, 0, 0)
# should be out of uid_t range
self.assertRaises(KeyError, pwd.getpwuid, 2**128)
self.assertRaises(KeyError, pwd.getpwuid, -2**128)
self.assertRaises(TypeError, pwd.getpwnam)
self.assertRaises(TypeError, pwd.getpwnam, 42)
self.assertRaises(TypeError, pwd.getpwnam, b'root')
self.assertRaises(TypeError, pwd.getpwnam, 'root', 0)
# embedded null character
self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'a\x00b')
self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'root\x00')
self.assertRaises(UnicodeEncodeError, pwd.getpwnam, 'roo\udc74')
self.assertRaises(KeyError, pwd.getpwnam, '')
self.assertRaises(TypeError, pwd.getpwall, 42)
# Find a non-existent user name.
# getpwall() will not necessarily report all existing users
# (typical for LDAP based directories in big organizations).
for _ in range(30):
fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
try:
pwd.getpwnam(fakename)
except KeyError:
break
else:
self.fail('Cannot find non-existent user name')
# Find a non-existent uid.
maxuid = max(e.pw_uid for e in pwd.getpwall())
if maxuid < 2**15:
maxuid = 2**15
elif maxuid < 2**16:
maxuid = 2**16-1
else:
maxuid = 2**31
for _ in range(30):
fakeuid = random.randrange(maxuid)
try:
pwd.getpwuid(fakeuid)
except KeyError:
break
else:
self.fail('Cannot find non-existent uid')
# On Cygwin, getpwuid(-1) returns 'Unknown+User' user
if sys.platform != 'cygwin':
# -1 shouldn't be a valid uid because it has a special meaning in many
# uid-related functions
self.assertRaises(KeyError, pwd.getpwuid, -1)
if __name__ == "__main__":
unittest.main()