1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from pathlib
import Path
21 from tarfile
import ExtractError
, TarFile
as UnsafeTarFile
24 class TarFile(UnsafeTarFile
):
25 """This TarFile implementation is trying to ameliorate CVE-2007-4559,
26 where tarfile.TarFiles can step outside of the target directory
31 # New in version 3.11.4 (also has been backported)
32 # https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extraction_filter
33 # https://peps.python.org/pep-0706/
34 extraction_filter
= staticmethod(tarfile
.tar_filter
)
35 except AttributeError:
36 def extract(self
, member
, path
="", set_attrs
=True, *,
38 self
._safetarfile
_check
()
39 super().extract(member
, path
, set_attrs
=set_attrs
,
40 numeric_owner
=numeric_owner
)
42 def extractall(self
, path
, members
=None, *, numeric_owner
=False):
43 self
._safetarfile
_check
()
44 super().extractall(path
, members
,
45 numeric_owner
=numeric_owner
)
47 def _safetarfile_check(self
):
48 for tarinfo
in self
.__iter
__():
49 if self
._is
_traversal
_attempt
(tarinfo
=tarinfo
):
51 "Attempted directory traversal for "
52 f
"member: {tarinfo.name}")
53 if self
._is
_unsafe
_symlink
(tarinfo
=tarinfo
):
55 "Attempted directory traversal via symlink for "
56 f
"member: {tarinfo.linkname}")
57 if self
._is
_unsafe
_link
(tarinfo
=tarinfo
):
59 "Attempted directory traversal via link for "
60 f
"member: {tarinfo.linkname}")
62 def _resolve_path(self
, path
):
63 return os
.path
.realpath(os
.path
.abspath(path
))
65 def _is_path_in_dir(self
, path
, basedir
):
66 return self
._resolve
_path
(os
.path
.join(basedir
,
67 path
)).startswith(basedir
)
69 def _is_traversal_attempt(self
, tarinfo
):
70 if (tarinfo
.name
.startswith(os
.sep
)
71 or ".." + os
.sep
in tarinfo
.name
):
75 def _is_unsafe_symlink(self
, tarinfo
):
78 os
.path
.normpath(os
.path
.join(os
.getcwd(),
80 if not self
._is
_path
_in
_dir
(symlink_file
, os
.getcwd()):
84 def _is_unsafe_link(self
, tarinfo
):
87 os
.path
.normpath(os
.path
.join(os
.getcwd(),
89 if not self
._is
_path
_in
_dir
(link_file
, os
.getcwd()):