1+ from datetime import datetime , timezone
12from io import BytesIO
23from pathlib import Path
4+ from textwrap import dedent
5+
6+ import pytest
37
48from dissect .target .filesystem import VirtualFilesystem
59from dissect .target .plugins .os .unix .shadow import ShadowPlugin
@@ -25,12 +29,12 @@ def test_unix_shadow(target_unix_users: Target, fs_unix: VirtualFilesystem) -> N
2529 ) # noqa E501
2630 assert results [0 ].algorithm == "sha512"
2731 assert results [0 ].crypt_param is None
28- assert results [0 ].last_change == " 18963"
29- assert results [0 ].min_age == 0
30- assert results [0 ].max_age == 99999
32+ assert results [0 ].last_change == datetime ( 2021 , 12 , 2 , 0 , 0 , 0 , tzinfo = timezone . utc ) # 18963
33+ assert results [0 ].min_age is None
34+ assert results [0 ].max_age == datetime ( 2295 , 9 , 16 , 0 , 0 , 0 , tzinfo = timezone . utc ) # 99999
3135 assert results [0 ].warning_period == 7
32- assert results [0 ].inactivity_period == ""
33- assert results [0 ].expiration_date == ""
36+ assert results [0 ].inactivity_period is None
37+ assert results [0 ].expiration_date is None
3438 assert results [0 ].unused_field == ""
3539
3640
@@ -49,3 +53,66 @@ def test_unix_shadow_backup_file(target_unix_users: Target, fs_unix: VirtualFile
4953 assert results [0 ].name == "test"
5054 assert results [1 ].name == "other-user"
5155 assert results [0 ].hash == results [1 ].hash
56+
57+
58+ def test_unix_shadow_invalid_shent (
59+ caplog : pytest .LogCaptureFixture , target_unix_users : Target , fs_unix : VirtualFilesystem
60+ ) -> None :
61+ """test if we can parse invalid day values in shents."""
62+
63+ shadow_invalid = """
64+ no_last_change:$6$salt$hash1::0:99999:7::123456:
65+ no_max_age:$6$salt$hash2:18963:0::7:::
66+ only_last_change:$6$salt$hash3:18963::::::
67+ no_int_fields:$6$salt$hash4:string::::::
68+ daemon:*:18474:0:99999:7:::
69+ bin:*:18474:0:99999:7:::
70+ nobody:*:18474:0:99999:7:::
71+ regular:$6$salt$hash5:1337:0:99999:7::123456:
72+ """
73+ fs_unix .map_file_fh ("/etc/shadow" , BytesIO (dedent (shadow_invalid ).encode ()))
74+
75+ results = list (target_unix_users .passwords ())
76+ assert len (results ) == 5
77+
78+ assert [r .name for r in results ] == [
79+ "no_last_change" ,
80+ "no_max_age" ,
81+ "only_last_change" ,
82+ "no_int_fields" ,
83+ "regular" ,
84+ ]
85+
86+ assert results [0 ].name == "no_last_change"
87+ assert results [0 ].last_change is None
88+ assert results [0 ].min_age is None
89+ assert results [0 ].max_age is None
90+ assert results [0 ].warning_period == 7
91+ assert results [0 ].inactivity_period is None
92+ assert results [0 ].expiration_date == datetime (2308 , 1 , 6 , tzinfo = timezone .utc )
93+
94+ assert results [1 ].name == "no_max_age"
95+ assert results [1 ].last_change == datetime (2021 , 12 , 2 , tzinfo = timezone .utc )
96+ assert results [1 ].max_age is None
97+
98+ assert results [2 ].name == "only_last_change"
99+ assert results [2 ].last_change == datetime (2021 , 12 , 2 , tzinfo = timezone .utc )
100+
101+ assert results [3 ].name == "no_int_fields"
102+ assert results [3 ].last_change is None
103+ assert (
104+ "Unable to parse last_change shadow value in /etc/shadow: invalid literal for int() with base 10: 'string' ('string')" # noqa:E501
105+ in caplog .text
106+ )
107+
108+ # make sure we parsed the last entry even though the other entries are 'broken'
109+ assert results [- 1 ].name == "regular"
110+ assert results [- 1 ].salt == "salt"
111+ assert results [- 1 ].hash == "hash5"
112+ assert results [- 1 ].algorithm == "sha512"
113+ assert results [- 1 ].last_change == datetime (1973 , 8 , 30 , tzinfo = timezone .utc )
114+ assert results [- 1 ].min_age is None
115+ assert results [- 1 ].max_age == datetime (2247 , 6 , 14 , tzinfo = timezone .utc )
116+ assert results [- 1 ].warning_period == 7
117+ assert results [- 1 ].inactivity_period is None
118+ assert results [- 1 ].expiration_date == datetime (2308 , 1 , 6 , tzinfo = timezone .utc )
0 commit comments