clamav/unit_tests/clamscan/fp_check_test.py

222 lines
9 KiB
Python
Raw Normal View History

2025-02-14 10:24:30 -05:00
# Copyright (C) 2020-2025 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
"""
Run {valgrind} {valgrind_args} {clamscan} tests.
"""
import unittest
import hashlib
from zipfile import ZIP_DEFLATED, ZipFile
import sys
sys.path.append('../unit_tests')
import testcase
class TC(testcase.TestCase):
@classmethod
def setUpClass(cls):
super(TC, cls).setUpClass()
TC.test_file = TC.path_tmp / "test_file"
2023-04-14 19:15:19 -07:00
with TC.test_file.open('wb') as testfile:
testfile.write(
b"""<?php
IGNORE_user_abort(asdf) scandir(asdfasdfasf]);
foreach(asdfasfs) strpos(asdfasfsfasf) sdfasdfasdf .php.suspected
aasdfasdfsf explode asdasdfasfsf
rename()
<script>sfasfasf</script>
?>
""")
TC.normalized_match_sig = TC.path_tmp / "normalized.ndb"
TC.normalized_match_sig.write_text(r"Malicious.PHP.normalized:0:*:69676e6f72655f757365725f61626f7274286173646629")
TC.original_hash_fp = TC.path_tmp / "original_hash.fp"
TC.original_hash_fp.write_text(r"a4b3c39134fa424beb9f84ffe5f175a3:190:original_hash")
TC.original_hash_wild_fp = TC.path_tmp / "original_hash.wild.fp"
TC.original_hash_wild_fp.write_text(r"a4b3c39134fa424beb9f84ffe5f175a3:*:original_hash.wild:73")
# The normalized hash is this for now. Changes to clamav normalization logic may require
# changes to this hash.
TC.normalized_hash_fp = TC.path_tmp / "normalized_hash.fp"
TC.normalized_hash_fp.write_text(r"0e32a3ab501afb50daedc04764f8dc16:188:normalized_hash")
TC.normalized_hash_wild_fp = TC.path_tmp / "normalized_hash.wild.fp"
TC.normalized_hash_wild_fp.write_text(r"0e32a3ab501afb50daedc04764f8dc16:*:normalized_hash.wild:73")
TC.test_file_zipped = TC.path_tmp / 'test_file.zip'
with ZipFile(str(TC.test_file_zipped), 'w', ZIP_DEFLATED) as zf:
2024-01-19 09:08:36 -08:00
# Add truncated PNG file that will alert with --alert-broken-media
2023-04-14 19:15:19 -07:00
with (TC.path_source / 'logo.png').open('br') as logo_png:
zf.writestr('test_file', b"""<?php
IGNORE_user_abort(asdf) scandir(asdfasdfasf]);
foreach(asdfasfs) strpos(asdfasfsfasf) sdfasdfasdf .php.suspected
aasdfasdfsf explode asdasdfasfsf
rename()
<script>sfasfasf</script>
?>
""")
# Generate hash of the zipped file.
# Since we generated the zip in python, we don't know the hash in advance.
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
hash_sha256 = hashlib.sha256()
with TC.test_file_zipped.open("rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
hash_sha256.update(chunk)
hash_sha256 = hash_sha256.hexdigest()
TC.test_file_zipped_hash_fp = TC.path_tmp / 'test_file.zip.hash.fp'
TC.test_file_zipped_hash_fp.write_text('{hash}:{size}:test_file.zip'.format(
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
hash=hash_sha256,
size=TC.test_file_zipped.stat().st_size))
TC.test_file_zipped_hash_wild_fp = TC.path_tmp / 'test_file.zip.hash.wild.fp'
TC.test_file_zipped_hash_wild_fp.write_text('{hash}:*:test_file.zip.wild:73'.format(
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
hash=hash_sha256))
@classmethod
def tearDownClass(cls):
super(TC, cls).tearDownClass()
def setUp(self):
super(TC, self).setUp()
def tearDown(self):
super(TC, self).tearDown()
self.verify_valgrind_log()
def test_alerts_on_normalized(self):
"""
This test expects that the normalized pattern match sig without the .fp sig will in fact alert.
"""
self.step_name("Test file detection with pattern from normalized HTML")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1}".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file,
db1=TC.normalized_match_sig,
)
)
self.verify_output(output.out, expected=["Malicious.PHP.normalized.UNOFFICIAL FOUND"], unexpected=[])
def test_alerts_on_zip(self):
"""
This test expects that the OG sig without the .fp sig will in fact alert.
"""
self.step_name("Test file detection with pattern from normalized HTML inside a ZIP file")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1}".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file_zipped,
db1=TC.normalized_match_sig,
)
)
self.verify_output(output.out, expected=["Malicious.PHP.normalized.UNOFFICIAL FOUND"], unexpected=[])
def test_fp_for_normalized(self):
"""
This test expects that FP sigs for normalized HTML hashes will work,
because hashes are now created when an fmap is created and all embedded
file content to be scanned now gets its own fmap.
"""
self.step_name("Test file trusted with fixed-size hash of the normalized HTML")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2} ".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file,
db1=TC.normalized_match_sig,
db2=TC.normalized_hash_fp,
)
)
self.verify_output(output.out, expected=["OK"], unexpected=[])
def test_fp_for_normalized_wild(self):
"""
This test expects that wildcard FP sigs for normalized HTML hashes will work,
because hashes are now created when an fmap is created and all embedded
file content to be scanned now gets its own fmap.
"""
self.step_name("Test file trusted with wild-card hash of the normalized HTML")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2} ".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file,
db1=TC.normalized_match_sig,
db2=TC.normalized_hash_wild_fp,
)
)
self.verify_output(output.out, expected=["OK"], unexpected=[])
def test_fp_for_nonnormalized(self):
"""
This test expects that FP sigs for non-normalized HTML hashes will work,
because we now check each hash in the fmap recursion list.
"""
self.step_name("Test file trusted with the original non-normalized fixed-size hash")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file,
db1=TC.normalized_match_sig,
db2=TC.original_hash_fp,
)
)
self.verify_output(output.out, expected=["OK"], unexpected=[])
def test_fp_for_nonnormalized_wild(self):
"""
This test expects that FP sigs for non-normalized HTML hashes will work,
because we now check each hash in the fmap recursion list.
"""
self.step_name("Test file trusted with the original non-normalized wild-card hash")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file,
db1=TC.normalized_match_sig,
db2=TC.original_hash_wild_fp,
)
)
self.verify_output(output.out, expected=["OK"], unexpected=[])
def test_fp_for_zipped_file(self):
"""
This test expects that FP sigs for a zip containing the test file will work.
"""
self.step_name("Test file trusted with fixed-size hash of zip containing test file")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file_zipped,
db1=TC.normalized_match_sig,
db2=TC.test_file_zipped_hash_fp,
)
)
self.verify_output(output.out, expected=["OK"], unexpected=[])
def test_fp_for_zipped_file_wild(self):
"""
This test expects that FP sigs for a zip containing the test file will work.
"""
self.step_name("Test file trusted with wildcard hash of zip containing test file")
output = self.execute_command(
"{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
testfiles=TC.test_file_zipped,
db1=TC.normalized_match_sig,
db2=TC.test_file_zipped_hash_wild_fp,
)
)
self.verify_output(output.out, expected=["OK"], unexpected=[])