Skip to content

Commit 98c14d2

Browse files
committed
change the default behavior of archive extraction
Pass the `SECURE_NODOTDOT`, `SECURE_NOABSOLUTEPATHS` and `SECURE_SYMLINKS` flags to libarchive by default, unless the current directory is the root.
1 parent 8256dba commit 98c14d2

2 files changed

Lines changed: 34 additions & 21 deletions

File tree

libarchive/extract.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from contextlib import contextmanager
22
from ctypes import byref, c_longlong, c_size_t, c_void_p
3+
import os
34

45
from .ffi import (
56
write_disk_new, write_disk_set_options, write_free, write_header,
@@ -27,6 +28,12 @@
2728
EXTRACT_SECURE_NOABSOLUTEPATHS = 0x10000
2829
EXTRACT_CLEAR_NOCHANGE_FFLAGS = 0x20000
2930

31+
PREVENT_ESCAPE = (
32+
EXTRACT_SECURE_NOABSOLUTEPATHS |
33+
EXTRACT_SECURE_NODOTDOT |
34+
EXTRACT_SECURE_SYMLINKS
35+
)
36+
3037

3138
@contextmanager
3239
def new_archive_write_disk(flags):
@@ -38,9 +45,16 @@ def new_archive_write_disk(flags):
3845
write_free(archive_p)
3946

4047

41-
def extract_entries(entries, flags=0):
48+
def extract_entries(entries, flags=None):
4249
"""Extracts the given archive entries into the current directory.
4350
"""
51+
if flags is None:
52+
if os.getcwd() == '/':
53+
# If the current directory is the root, then trying to prevent
54+
# escaping is probably undesirable.
55+
flags = 0
56+
else:
57+
flags = PREVENT_ESCAPE
4458
buff, size, offset = c_void_p(), c_size_t(), c_longlong()
4559
buff_p, size_p, offset_p = byref(buff), byref(size), byref(offset)
4660
with new_archive_write_disk(flags) as write_p:
@@ -55,20 +69,20 @@ def extract_entries(entries, flags=0):
5569
write_finish_entry(write_p)
5670

5771

58-
def extract_fd(fd, flags=0):
72+
def extract_fd(fd, flags=None):
5973
"""Extracts an archive from a file descriptor into the current directory.
6074
"""
6175
with fd_reader(fd) as archive:
6276
extract_entries(archive, flags)
6377

6478

65-
def extract_file(filepath, flags=0):
79+
def extract_file(filepath, flags=None):
6680
"""Extracts an archive from a file into the current directory."""
6781
with file_reader(filepath) as archive:
6882
extract_entries(archive, flags)
6983

7084

71-
def extract_memory(buffer_, flags=0):
85+
def extract_memory(buffer_, flags=None):
7286
"""Extracts an archive from memory into the current directory."""
7387
with memory_reader(buffer_) as archive:
7488
extract_entries(archive, flags)

tests/test_security_flags.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,34 @@
33
import pytest
44
import os
55

6-
from libarchive import extract_file
7-
from libarchive.ffi import version_number
6+
from libarchive import extract_file, file_reader
87
from libarchive.extract import (
98
EXTRACT_SECURE_NOABSOLUTEPATHS, EXTRACT_SECURE_NODOTDOT,
109
)
1110
from libarchive.exception import ArchiveError
1211
from . import data_dir
1312

1413

15-
def run_test(flag, filename):
14+
def run_test(flags):
1615
archive_path = os.path.join(data_dir, 'flags.tar')
1716
try:
18-
extract_file(archive_path)
17+
extract_file(archive_path, 0)
1918
with pytest.raises(ArchiveError):
20-
extract_file(archive_path, flag)
19+
extract_file(archive_path, flags)
2120
finally:
22-
if os.path.exists(filename):
23-
os.remove(filename)
21+
with file_reader(archive_path) as archive:
22+
for entry in archive:
23+
if os.path.exists(entry.pathname):
24+
os.remove(entry.pathname)
2425

2526

26-
def test_no_dot_dot():
27-
run_test(EXTRACT_SECURE_NODOTDOT, '../python-libarchive-c-test-dot-dot-file')
27+
def test_extraction_is_secure_by_default():
28+
run_test(None)
2829

2930

30-
def test_absolute():
31-
# EXTRACT_SECURE_NOABSOLUTEPATHS was only added in 3.1.900
32-
# 3.1.900 -> 3001009
33-
if version_number() >= 3001009:
34-
run_test(
35-
EXTRACT_SECURE_NOABSOLUTEPATHS,
36-
'/tmp/python-libarchive-c-test-absolute-file'
37-
)
31+
def test_explicit_no_dot_dot():
32+
run_test(EXTRACT_SECURE_NODOTDOT)
33+
34+
35+
def test_explicit_no_absolute_paths():
36+
run_test(EXTRACT_SECURE_NOABSOLUTEPATHS)

0 commit comments

Comments
 (0)