Skip to content

Commit 6eddc40

Browse files
gh-80198: Improve test_pwd and test_grp
Fix tests for non-existing names and ids when getpwall()/getgrall() don't return all users/groups. Add tests for out-of-range uids, gid=-1, integer float ids, bytes names, null-terminated names, names with surrogates, empty names, excessive arguments.
1 parent 7e5d1d8 commit 6eddc40

2 files changed

Lines changed: 89 additions & 93 deletions

File tree

Lib/test/test_grp.py

Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""Test script for the grp module."""
22

3+
import random
4+
import string
5+
import sys
36
import unittest
47
from test.support import import_helper
58

@@ -50,61 +53,56 @@ def test_values_extended(self):
5053
def test_errors(self):
5154
self.assertRaises(TypeError, grp.getgrgid)
5255
self.assertRaises(TypeError, grp.getgrgid, 3.14)
56+
self.assertRaises(TypeError, grp.getgrgid, 0.0)
57+
self.assertRaises(TypeError, grp.getgrgid, 0, 0)
58+
# should be out of gid_t range
59+
self.assertRaises(OverflowError, grp.getgrgid, 2**128)
60+
self.assertRaises(OverflowError, grp.getgrgid, -2**128)
5361
self.assertRaises(TypeError, grp.getgrnam)
5462
self.assertRaises(TypeError, grp.getgrnam, 42)
55-
self.assertRaises(TypeError, grp.getgrall, 42)
63+
self.assertRaises(TypeError, grp.getgrnam, b'root')
64+
self.assertRaises(TypeError, grp.getgrnam, 'root', 0)
5665
# embedded null character
5766
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'a\x00b')
67+
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'root\x00')
68+
self.assertRaises(UnicodeEncodeError, grp.getgrnam, 'roo\udc74')
69+
self.assertRaises(KeyError, grp.getgrnam, '')
70+
self.assertRaises(TypeError, grp.getgrall, 42)
5871

59-
# try to get some errors
60-
bynames = {}
61-
bygids = {}
62-
for (n, p, g, mem) in grp.getgrall():
63-
if not n or n == '+':
64-
continue # skip NIS entries etc.
65-
bynames[n] = g
66-
bygids[g] = n
67-
68-
allnames = list(bynames.keys())
69-
namei = 0
70-
fakename = allnames[namei]
71-
while fakename in bynames:
72-
chars = list(fakename)
73-
for i in range(len(chars)):
74-
if chars[i] == 'z':
75-
chars[i] = 'A'
76-
break
77-
elif chars[i] == 'Z':
78-
continue
72+
# Find a non-existent group name.
73+
# getgrall() will not necessarily report all existing groups
74+
# (typical for LDAP based directories in big organizations).
75+
for _ in range(30):
76+
fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
77+
try:
78+
grp.getgrnam(fakename)
79+
except KeyError:
80+
break
81+
else:
82+
self.fail('Cannot find non-existent group name')
83+
84+
# Find a non-existent gid.
85+
maxgid = 2**31
86+
for _ in range(30):
87+
fakegid = random.randrange(maxgid)
88+
try:
89+
grp.getgrgid(fakegid)
90+
except KeyError:
91+
break
92+
except OverflowError:
93+
if maxgid == 2**31:
94+
maxgid = 2**16-1
95+
elif maxgid == 2**16-1:
96+
maxgid = 2**15
7997
else:
80-
chars[i] = chr(ord(chars[i]) + 1)
81-
break
82-
else:
83-
namei = namei + 1
84-
try:
85-
fakename = allnames[namei]
86-
except IndexError:
87-
# should never happen... if so, just forget it
88-
break
89-
fakename = ''.join(chars)
90-
91-
self.assertRaises(KeyError, grp.getgrnam, fakename)
92-
93-
# Choose a non-existent gid.
94-
fakegid = 4127
95-
while fakegid in bygids:
96-
fakegid = (fakegid * 3) % 0x10000
97-
98-
self.assertRaises(KeyError, grp.getgrgid, fakegid)
99-
100-
def test_noninteger_gid(self):
101-
entries = grp.getgrall()
102-
if not entries:
103-
self.skipTest('no groups')
104-
# Choose an existent gid.
105-
gid = entries[0][2]
106-
self.assertRaises(TypeError, grp.getgrgid, float(gid))
107-
self.assertRaises(TypeError, grp.getgrgid, str(gid))
98+
raise
99+
else:
100+
self.fail('Cannot find non-existent gid')
101+
102+
if sys.platform != 'cygwin':
103+
# -1 shouldn't be a valid gid because it has a special meaning in many
104+
# uid-related functions
105+
self.assertRaises(KeyError, grp.getgrgid, -1)
108106

109107

110108
if __name__ == "__main__":

Lib/test/test_pwd.py

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import random
2+
import string
13
import sys
24
import unittest
35
from test.support import import_helper
@@ -56,61 +58,57 @@ def test_values_extended(self):
5658
def test_errors(self):
5759
self.assertRaises(TypeError, pwd.getpwuid)
5860
self.assertRaises(TypeError, pwd.getpwuid, 3.14)
61+
self.assertRaises(TypeError, pwd.getpwuid, 0.0)
62+
self.assertRaises(TypeError, pwd.getpwuid, 0, 0)
63+
# should be out of uid_t range
64+
self.assertRaises(KeyError, pwd.getpwuid, 2**128)
65+
self.assertRaises(KeyError, pwd.getpwuid, -2**128)
5966
self.assertRaises(TypeError, pwd.getpwnam)
6067
self.assertRaises(TypeError, pwd.getpwnam, 42)
61-
self.assertRaises(TypeError, pwd.getpwall, 42)
68+
self.assertRaises(TypeError, pwd.getpwnam, b'root')
69+
self.assertRaises(TypeError, pwd.getpwnam, 'root', 0)
6270
# embedded null character
6371
self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'a\x00b')
72+
self.assertRaisesRegex(ValueError, 'null', pwd.getpwnam, 'root\x00')
73+
self.assertRaises(UnicodeEncodeError, pwd.getpwnam, 'roo\udc74')
74+
self.assertRaises(KeyError, pwd.getpwnam, '')
75+
self.assertRaises(TypeError, pwd.getpwall, 42)
6476

65-
# try to get some errors
66-
bynames = {}
67-
byuids = {}
68-
for (n, p, u, g, gecos, d, s) in pwd.getpwall():
69-
bynames[n] = u
70-
byuids[u] = n
71-
72-
allnames = list(bynames.keys())
73-
namei = 0
74-
fakename = allnames[namei] if allnames else "invaliduser"
75-
while fakename in bynames:
76-
chars = list(fakename)
77-
for i in range(len(chars)):
78-
if chars[i] == 'z':
79-
chars[i] = 'A'
80-
break
81-
elif chars[i] == 'Z':
82-
continue
83-
else:
84-
chars[i] = chr(ord(chars[i]) + 1)
85-
break
86-
else:
87-
namei = namei + 1
88-
try:
89-
fakename = allnames[namei]
90-
except IndexError:
91-
# should never happen... if so, just forget it
92-
break
93-
fakename = ''.join(chars)
94-
95-
self.assertRaises(KeyError, pwd.getpwnam, fakename)
96-
97-
# In some cases, byuids isn't a complete list of all users in the
98-
# system, so if we try to pick a value not in byuids (via a perturbing
99-
# loop, say), pwd.getpwuid() might still be able to find data for that
100-
# uid. Using sys.maxint may provoke the same problems, but hopefully
101-
# it will be a more repeatable failure.
102-
fakeuid = sys.maxsize
103-
self.assertNotIn(fakeuid, byuids)
104-
self.assertRaises(KeyError, pwd.getpwuid, fakeuid)
77+
# Find a non-existent user name.
78+
# getpwall() will not necessarily report all existing users
79+
# (typical for LDAP based directories in big organizations).
80+
for _ in range(30):
81+
fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
82+
try:
83+
pwd.getpwnam(fakename)
84+
except KeyError:
85+
break
86+
else:
87+
self.fail('Cannot find non-existent user name')
88+
89+
# Find a non-existent uid.
90+
maxuid = max(e.pw_uid for e in pwd.getpwall())
91+
if maxuid < 2**15:
92+
maxuid = 2**15
93+
elif maxuid < 2**16:
94+
maxuid = 2**16-1
95+
else:
96+
maxuid = 2**31
97+
for _ in range(30):
98+
fakeuid = random.randrange(maxuid)
99+
try:
100+
pwd.getpwuid(fakeuid)
101+
except KeyError:
102+
break
103+
else:
104+
self.fail('Cannot find non-existent uid')
105105

106106
# On Cygwin, getpwuid(-1) returns 'Unknown+User' user
107107
if sys.platform != 'cygwin':
108108
# -1 shouldn't be a valid uid because it has a special meaning in many
109109
# uid-related functions
110110
self.assertRaises(KeyError, pwd.getpwuid, -1)
111-
# should be out of uid_t range
112-
self.assertRaises(KeyError, pwd.getpwuid, 2**128)
113-
self.assertRaises(KeyError, pwd.getpwuid, -2**128)
111+
114112

115113
if __name__ == "__main__":
116114
unittest.main()

0 commit comments

Comments
 (0)