Skip to content

Commit 2080523

Browse files
[3.13] gh-80198: Improve test_pwd and test_grp (GH-150380) (GH-150398) (GH-150423)
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 4bdff2c) (cherry picked from commit 46e8f7a) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent ab4df90 commit 2080523

2 files changed

Lines changed: 88 additions & 96 deletions

File tree

Lib/test/test_grp.py

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

3+
import random
4+
import string
35
import unittest
46
from test.support import import_helper
57

@@ -50,61 +52,51 @@ def test_values_extended(self):
5052
def test_errors(self):
5153
self.assertRaises(TypeError, grp.getgrgid)
5254
self.assertRaises(TypeError, grp.getgrgid, 3.14)
55+
self.assertRaises(TypeError, grp.getgrgid, 0.0)
56+
self.assertRaises(TypeError, grp.getgrgid, 0, 0)
57+
# should be out of gid_t range
58+
self.assertRaises(OverflowError, grp.getgrgid, 2**128)
59+
self.assertRaises(OverflowError, grp.getgrgid, -2**128)
5360
self.assertRaises(TypeError, grp.getgrnam)
5461
self.assertRaises(TypeError, grp.getgrnam, 42)
55-
self.assertRaises(TypeError, grp.getgrall, 42)
62+
self.assertRaises(TypeError, grp.getgrnam, b'root')
63+
self.assertRaises(TypeError, grp.getgrnam, 'root', 0)
5664
# embedded null character
5765
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'a\x00b')
66+
self.assertRaisesRegex(ValueError, 'null', grp.getgrnam, 'root\x00')
67+
self.assertRaises(UnicodeEncodeError, grp.getgrnam, 'roo\udc74')
68+
self.assertRaises(KeyError, grp.getgrnam, '')
69+
self.assertRaises(TypeError, grp.getgrall, 42)
5870

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
71+
# Find a non-existent group name.
72+
# getgrall() will not necessarily report all existing groups
73+
# (typical for LDAP based directories in big organizations).
74+
for _ in range(30):
75+
fakename = ''.join(random.choices(string.ascii_lowercase, k=6))
76+
try:
77+
grp.getgrnam(fakename)
78+
except KeyError:
79+
break
80+
else:
81+
self.fail('Cannot find non-existent group name')
82+
83+
# Find a non-existent gid.
84+
maxgid = 2**31
85+
for _ in range(30):
86+
fakegid = random.randrange(maxgid)
87+
try:
88+
grp.getgrgid(fakegid)
89+
except KeyError:
90+
break
91+
except OverflowError:
92+
if maxgid == 2**31:
93+
maxgid = 2**16-1
94+
elif maxgid == 2**16-1:
95+
maxgid = 2**15
7996
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))
97+
raise
98+
else:
99+
self.fail('Cannot find non-existent gid')
108100

109101

110102
if __name__ == "__main__":

Lib/test/test_pwd.py

Lines changed: 47 additions & 47 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,59 +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')
105+
106+
# On Cygwin, getpwuid(-1) returns 'Unknown+User' user
107+
if sys.platform != 'cygwin':
108+
# -1 shouldn't be a valid uid because it has a special meaning in many
109+
# uid-related functions
110+
self.assertRaises(KeyError, pwd.getpwuid, -1)
105111

106-
# -1 shouldn't be a valid uid because it has a special meaning in many
107-
# uid-related functions
108-
self.assertRaises(KeyError, pwd.getpwuid, -1)
109-
# should be out of uid_t range
110-
self.assertRaises(KeyError, pwd.getpwuid, 2**128)
111-
self.assertRaises(KeyError, pwd.getpwuid, -2**128)
112112

113113
if __name__ == "__main__":
114114
unittest.main()

0 commit comments

Comments
 (0)