Test for unsafe files in tarfile.extractall

In addition to that, mark bifrost unbuildable since it's
failing on EPEL 9 enablement.

Closes-Bug: #1990432

Change-Id: I650fcbc8f773fad8116338f6fb0cf7b4f4f17b33
This commit is contained in:
Michal Nasiadka 2023-03-16 10:04:47 +00:00
parent 12d431e399
commit 3d008b7f5e
3 changed files with 54 additions and 1 deletions

@ -264,6 +264,14 @@ class BuildTask(EngineTask):
def builder(self, image): def builder(self, image):
def _test_malicious_tarball(archive, path):
tar_file = tarfile.open(archive, 'r|gz')
for n in tar_file.getnames():
if not os.path.abspath(os.path.join(path, n)).startswith(path):
tar_file.close()
self.logger.error(f'Unsafe filenames in archive {archive}')
raise ArchivingError
def make_an_archive(items, arcname, item_child_path=None): def make_an_archive(items, arcname, item_child_path=None):
if not item_child_path: if not item_child_path:
item_child_path = arcname item_child_path = arcname
@ -277,8 +285,9 @@ class BuildTask(EngineTask):
archives.append(archive_path) archives.append(archive_path)
if archives: if archives:
for archive in archives: for archive in archives:
_test_malicious_tarball(archive, items_path)
with tarfile.open(archive, 'r') as archive_tar: with tarfile.open(archive, 'r') as archive_tar:
archive_tar.extractall(path=items_path) archive_tar.extractall(path=items_path) # nosec
else: else:
try: try:
os.mkdir(items_path) os.mkdir(items_path)

@ -24,6 +24,7 @@ UNBUILDABLE_IMAGES = {
# Issues for SHA1 keys: # Issues for SHA1 keys:
# https://github.com/grafana/grafana/issues/41036 # https://github.com/grafana/grafana/issues/41036
'centos': { 'centos': {
"bifrost-base", # EPEL-related breakage
"hacluster-pcs", # Missing crmsh package "hacluster-pcs", # Missing crmsh package
"nova-spicehtml5proxy", # Missing spicehtml5 package "nova-spicehtml5proxy", # Missing spicehtml5 package
"ovsdpdk", # Not supported on CentOS "ovsdpdk", # Not supported on CentOS
@ -34,6 +35,7 @@ UNBUILDABLE_IMAGES = {
}, },
'rocky': { 'rocky': {
"bifrost-base", # EPEL-related breakage
"hacluster-pcs", # Missing crmsh package "hacluster-pcs", # Missing crmsh package
"nova-spicehtml5proxy", # Missing spicehtml5 package "nova-spicehtml5proxy", # Missing spicehtml5 package
"ovsdpdk", # Not supported on CentOS "ovsdpdk", # Not supported on CentOS

@ -14,6 +14,8 @@ import fixtures
import os import os
import requests import requests
import sys import sys
import tarfile
import tempfile
from unittest import mock from unittest import mock
from kolla.cmd import build as build_cmd from kolla.cmd import build as build_cmd
@ -289,6 +291,46 @@ class TasksTest(base.TestCase):
else: else:
self.assertIsNotNone(get_result) self.assertIsNotNone(get_result)
@mock.patch.dict(os.environ, clear=True)
@mock.patch('docker.APIClient')
def test_malicious_tar(self, mock_client):
tmpdir = tempfile.mkdtemp()
file_name = 'test.txt'
archive_name = 'my_archive.tar.gz'
file_path = os.path.join(tmpdir, file_name)
archive_path = os.path.join(tmpdir, archive_name)
# Ensure the file is read/write by the creator only
saved_umask = os.umask(0o077)
try:
with open(file_path, 'w') as f:
f.write('Hello')
with tarfile.open(archive_path, 'w:gz') as tar:
tar.add(file_path, arcname='../test.txt')
self.dc = mock_client
self.image.plugins = [{
'name': 'fake-image-base-plugin-test',
'type': 'local',
'enabled': True,
'source': archive_path}
]
push_queue = mock.Mock()
builder = tasks.BuildTask(self.conf, self.image, push_queue)
builder.run()
self.assertFalse(builder.success)
except IOError:
print('IOError')
else:
os.remove(file_path)
os.remove(archive_path)
finally:
os.umask(saved_umask)
os.rmdir(tmpdir)
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('os.utime') @mock.patch('os.utime')
@mock.patch('shutil.rmtree') @mock.patch('shutil.rmtree')