Skip to content

Commit 1dd8166

Browse files
author
John Andersen
authored
extractor: Native Python support for rpm files (#995)
* setup: Reference requirements.txt for install_requires setup.py and requirements.txt had slightly differing lists of dependencies. By reading from requirements.txt in setup.py, we ensure that they aways have the same list. Signed-off-by: John Andersen <[email protected]> * extractor: Native Python support for rpm files Using rpmfile PyPi library we can extract rpm files if rpm2cpio and cpio are not present. Fixes: #657 Signed-off-by: John Andersen <[email protected]>
1 parent 563cdd7 commit 1dd8166

File tree

5 files changed

+34
-22
lines changed

5 files changed

+34
-22
lines changed

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
include requirements.txt
12
include *.md
23
include test/binaries/*.c
34
include test/csv/*.csv

cve_bin_tool/extractor.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import sys
1010
import tempfile
1111

12+
from rpmfile.cli import main as rpmextract
13+
1214
from .async_utils import (
1315
aio_unpack_archive,
1416
aio_run_command,
@@ -20,11 +22,16 @@
2022
ChangeDirContext,
2123
FileIO,
2224
run_coroutine,
25+
async_wrap,
2326
)
2427
from .error_handler import ExtractionFailed, UnknownArchiveType, ErrorHandler, ErrorMode
2528
from .log import LOGGER
2629

2730

31+
# Run rpmfile in a thread
32+
rpmextract = async_wrap(rpmextract)
33+
34+
2835
class BaseExtractor:
2936
"""Extracts tar, rpm, etc. files"""
3037

@@ -66,10 +73,7 @@ async def extract_file_rpm(self, filename, extraction_path):
6673
""" Extract rpm packages """
6774
if sys.platform.startswith("linux"):
6875
if not await aio_inpath("rpm2cpio") or not await aio_inpath("cpio"):
69-
with ErrorHandler(mode=self.error_mode, logger=self.logger):
70-
raise Exception(
71-
"'rpm2cpio' and 'cpio' are required to extract rpm files"
72-
)
76+
await rpmextract("-xC", extraction_path, filename)
7377
else:
7478
stdout, stderr = await aio_run_command(["rpm2cpio", filename])
7579
if stderr or not stdout:

requirements.txt

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
jsonschema
21
rich
32
plotly
43
jinja2
54
beautifulsoup4
65
aiohttp[speedups]
76
toml
87
pyyaml
8+
jsonschema>=3.0.2
99
pytest
1010
pytest-xdist
1111
pytest-cov
12-
pytest-asyncio
12+
pytest-asyncio
13+
rpmfile>=1.0.6
14+
zstandard; python_version >= "3.4"

setup.py

+4-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
with open("README.md", "r", encoding="utf-8") as f:
88
readme = f.read()
99

10+
with open("requirements.txt", "r", encoding="utf-8") as f:
11+
requirements = f.read().split("\n")
12+
1013
with open(os.path.join("cve_bin_tool", "version.py"), "r") as f:
1114
for line in f:
1215
if line.startswith("VERSION"):
@@ -38,20 +41,7 @@
3841
"Programming Language :: Python :: Implementation :: CPython",
3942
"Programming Language :: Python :: Implementation :: PyPy",
4043
],
41-
install_requires=[
42-
"rich",
43-
"plotly",
44-
"jinja2",
45-
"beautifulsoup4",
46-
"aiohttp[speedups]",
47-
"toml",
48-
"pyyaml",
49-
"jsonschema>=3.0.2",
50-
"pytest",
51-
"pytest-xdist",
52-
"pytest-cov",
53-
"pytest-asyncio",
54-
],
44+
install_requires=requirements,
5545
packages=find_packages(),
5646
package_data={
5747
"cve_bin_tool.output_engine": [

test/test_extractor.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import tarfile
66
import tempfile
77
import unittest
8+
import unittest.mock
89
from io import BytesIO
910
from zipfile import ZipFile, ZipInfo
1011

@@ -102,8 +103,10 @@ async def test_extract_cleanup(self):
102103
class TestExtractFileRpm(TestExtractorBase):
103104
""" Tests for the rpm file extractor """
104105

105-
def setup_method(self):
106-
download_file(CURL_7_20_0_URL, os.path.join(self.tempdir, "test.rpm"))
106+
@classmethod
107+
def setup_class(cls):
108+
super(TestExtractFileRpm, cls).setup_class()
109+
download_file(CURL_7_20_0_URL, os.path.join(cls.tempdir, "test.rpm"))
107110

108111
@pytest.mark.asyncio
109112
async def test_extract_file_rpm(self):
@@ -116,6 +119,18 @@ async def test_extract_file_rpm(self):
116119
):
117120
assert os.path.isfile(os.path.join(extracted_path, "usr", "bin", "curl"))
118121

122+
@pytest.mark.asyncio
123+
async def test_extract_file_rpm_no_rpm2cipo(self):
124+
""" Test rpm extraction using rpmfile """
125+
with unittest.mock.patch(
126+
"cve_bin_tool.async_utils.aio_inpath",
127+
return_value=False,
128+
), unittest.mock.patch(
129+
"cve_bin_tool.async_utils.aio_run_command",
130+
) as mock_aio_run_command:
131+
await self.test_extract_file_rpm()
132+
mock_aio_run_command.assert_not_called()
133+
119134

120135
class TestExtractFileDeb(TestExtractorBase):
121136
""" Tests for deb file extractor """

0 commit comments

Comments
 (0)