Skip to content

Commit 15f205e

Browse files
committed
fix: allow extractraction on all files to fail (#1285)
* fixes #1281 * Related to #1181 Changes in #1181 to check return codes made it so that cve-bin-tool failed if any file did not extract correctly. This changes the default so that failures during extraction are logged but do not halt the scan. Error messages are not logged when the file extension is .exe because users found those confusing (cve-bin-tool tries to unzip all .exe files in case they are self-extracting zipfiles, but many are not) Signed-off-by: Terri Oda <[email protected]>
1 parent 75dbefc commit 15f205e

File tree

7 files changed

+49
-5
lines changed

7 files changed

+49
-5
lines changed

cve_bin_tool/async_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def run_coroutine(coro):
7373
return result
7474

7575

76-
async def aio_run_command(args, process_can_fail=False):
76+
async def aio_run_command(args, process_can_fail=True):
7777
process = await asyncio.create_subprocess_exec(
7878
*args,
7979
stdout=asyncio.subprocess.PIPE,

cve_bin_tool/extractor.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,25 @@ async def extract_file_cab(filename, extraction_path):
178178
return 0
179179

180180
@staticmethod
181-
async def extract_file_zip(filename, extraction_path, process_can_fail=False):
181+
async def extract_file_zip(filename, extraction_path, process_can_fail=True):
182182
"""Extract zip files"""
183+
184+
is_exe = filename.endswith(".exe")
183185
if await aio_inpath("unzip"):
184186
stdout, stderr, _ = await aio_run_command(
185-
["unzip", "-n", "-d", extraction_path, filename]
187+
["unzip", "-n", "-d", extraction_path, filename], process_can_fail
186188
)
187189
if stderr or not stdout:
190+
if is_exe:
191+
return 0 # not all .exe files are zipfiles, no need for error
188192
return 1
189193
elif await aio_inpath("7z"):
190-
stdout, stderr, _ = await aio_run_command(["7z", "x", filename])
194+
stdout, stderr, _ = await aio_run_command(
195+
["7z", "x", filename], process_can_fail
196+
)
191197
if stderr or not stdout:
198+
if is_exe:
199+
return 0 # not all .exe files are zipfiles, no need for error
192200
return 1
193201
else:
194202
with ErrorHandler(mode=ErrorMode.Ignore) as e:

test/assets/empty-file.exe

Whitespace-only changes.

test/assets/empty-file.zip

Whitespace-only changes.

test/test_async_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ async def test_aio_run_command_success():
3939
async def test_aio_run_command_returncode_non_zero():
4040
with unittest.mock.patch("asyncio.create_subprocess_exec", new=mkexec(1)):
4141
with pytest.raises(subprocess.CalledProcessError):
42-
await aio_run_command(("echo", "hello"))
42+
await aio_run_command(("echo", "hello"), process_can_fail=False)

test/test_cli.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,25 @@ def test_no_extraction(self):
6060
"""Test scanner against curl-7.20.0 rpm with extraction turned off"""
6161
assert main(["cve-bin-tool", os.path.join(self.tempdir, CURL_7_20_0_RPM)]) != 0
6262

63+
def test_extract_bad_zip_messages(self, caplog):
64+
"""Test that bad zip files are logged as extraction failed, but
65+
bad exe files produce no such message"""
66+
BAD_EXE_FILE = os.path.join(
67+
os.path.join(os.path.abspath(os.path.dirname(__file__)), "assets"),
68+
"empty-file.exe",
69+
)
70+
with caplog.at_level(logging.WARNING):
71+
main(["cve-bin-tool", BAD_EXE_FILE])
72+
assert "Failure extracting" not in caplog.text
73+
74+
BAD_ZIP_FILE = os.path.join(
75+
os.path.join(os.path.abspath(os.path.dirname(__file__)), "assets"),
76+
"empty-file.zip",
77+
)
78+
with caplog.at_level(logging.WARNING):
79+
main(["cve-bin-tool", BAD_ZIP_FILE])
80+
assert "Failure extracting" in caplog.text
81+
6382
def test_exclude(self, caplog):
6483
"""Test that the exclude paths are not scanned"""
6584
test_path = os.path.abspath(os.path.dirname(__file__))

test/test_extractor.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
os.path.join(os.path.abspath(os.path.dirname(__file__)), "assets"),
2727
"cab-test-python3.8.cab",
2828
)
29+
BAD_EXE_FILE = os.path.join(
30+
os.path.join(os.path.abspath(os.path.dirname(__file__)), "assets"),
31+
"empty-file.exe",
32+
)
33+
BAD_ZIP_FILE = os.path.join(
34+
os.path.join(os.path.abspath(os.path.dirname(__file__)), "assets"),
35+
"empty-file.zip",
36+
)
2937

3038

3139
class TestExtractorBase:
@@ -216,3 +224,12 @@ async def test_extract_file_zip(self):
216224
]
217225
):
218226
assert os.path.isdir(path)
227+
228+
@pytest.mark.asyncio
229+
async def test_bad_zip(self):
230+
"""Test handling of invalid zip files. No errors should be raised.
231+
Log messages differ for .exe and .zip and are tested in test_cli.py
232+
"""
233+
234+
self.extract_files(BAD_EXE_FILE)
235+
self.extract_files(BAD_ZIP_FILE)

0 commit comments

Comments
 (0)