Coverage for /usr/local/lib/python3.10/site-packages/opal_common-0.0.0-py3.10.egg/opal_common/security/tarsafe.py: 24%

50 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 11:54 +0000

1# This file is a copy of tarsafe by Andrew Scott MIT license https://github.com/beatsbears/tarsafe 

2import os 

3import pathlib 

4import tarfile 

5 

6 

7class TarSafe(tarfile.TarFile): 

8 """A safe subclass of the TarFile class for interacting with tar files.""" 

9 

10 def __init__(self, *args, **kwargs): 

11 super().__init__(*args, **kwargs) 

12 self.directory = os.getcwd() 

13 

14 @classmethod 

15 def open( 

16 cls, name=None, mode="r", fileobj=None, bufsize=tarfile.RECORDSIZE, **kwargs 

17 ): 

18 return super().open(name, mode, fileobj, bufsize, **kwargs) 

19 

20 def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): 

21 """Override the parent extract method and add safety checks.""" 

22 self._safetar_check() 

23 super().extract(member, path, set_attrs=set_attrs, numeric_owner=numeric_owner) 

24 

25 def extractall(self, path=".", members=None, numeric_owner=False): 

26 """Override the parent extractall method and add safety checks.""" 

27 self._safetar_check() 

28 super().extractall(path, members, numeric_owner=numeric_owner) 

29 

30 def _safetar_check(self): 

31 """Runs all necessary checks for the safety of a tarfile.""" 

32 try: 

33 for tarinfo in self.__iter__(): 

34 if self._is_traversal_attempt(tarinfo=tarinfo): 

35 raise TarSafeException( 

36 f"Attempted directory traversal for member: {tarinfo.name}" 

37 ) 

38 if self._is_unsafe_symlink(tarinfo=tarinfo): 

39 raise TarSafeException( 

40 f"Attempted directory traversal via symlink for member: {tarinfo.linkname}" 

41 ) 

42 if self._is_unsafe_link(tarinfo=tarinfo): 

43 raise TarSafeException( 

44 f"Attempted directory traversal via link for member: {tarinfo.linkname}" 

45 ) 

46 if self._is_device(tarinfo=tarinfo): 

47 raise TarSafeException( 

48 f"tarfile returns true for isblk() or ischr()" 

49 ) 

50 except Exception as err: 

51 raise 

52 

53 def _is_traversal_attempt(self, tarinfo): 

54 if not os.path.abspath(os.path.join(self.directory, tarinfo.name)).startswith( 

55 self.directory 

56 ): 

57 return True 

58 return False 

59 

60 def _is_unsafe_symlink(self, tarinfo): 

61 if tarinfo.issym(): 

62 symlink_file = pathlib.Path( 

63 os.path.normpath(os.path.join(self.directory, tarinfo.linkname)) 

64 ) 

65 if not os.path.abspath( 

66 os.path.join(self.directory, symlink_file) 

67 ).startswith(self.directory): 

68 return True 

69 return False 

70 

71 def _is_unsafe_link(self, tarinfo): 

72 if tarinfo.islnk(): 

73 link_file = pathlib.Path( 

74 os.path.normpath(os.path.join(self.directory, tarinfo.linkname)) 

75 ) 

76 if not os.path.abspath(os.path.join(self.directory, link_file)).startswith( 

77 self.directory 

78 ): 

79 return True 

80 return False 

81 

82 def _is_device(self, tarinfo): 

83 return tarinfo.ischr() or tarinfo.isblk() 

84 

85 

86class TarSafeException(Exception): 

87 pass 

88 

89 

90open = TarSafe.open