Skip to content

Commit

Permalink
malware-detection feature: handle different yara versions (#3428)
Browse files Browse the repository at this point in the history
* Download versioned signatures file corresponding to installed version of yara
* Also fixes permission denied bug from fuse mounted filesystems

Signed-off-by: Mark Huth <[email protected]>
  • Loading branch information
mhuth authored Jun 2, 2022
1 parent 613cec2 commit ffdc458
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 31 deletions.
37 changes: 30 additions & 7 deletions insights/client/apps/malware_detection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,8 @@ def yara_version_ok(yara):
# Check the installed yara version >= MIN_YARA_VERSION
installed_yara_version = call([[yara, '--version']]).strip()
try:
if float(installed_yara_version[:3]) < float(MIN_YARA_VERSION[:3]):
self.yara_version = '.'.join(installed_yara_version.split('.')[:2]) # Eg 4.2.0 becomes 4.2
if float(self.yara_version) < float(MIN_YARA_VERSION[:3]):
raise RuntimeError("Found %s with version %s, but malware-detection requires version >= %s\n"
"Please install a later version of yara."
% (yara, installed_yara_version, MIN_YARA_VERSION))
Expand Down Expand Up @@ -619,11 +620,14 @@ def _get_rules(self):
logger.error("Couldn't access the insights-client configuration")
exit(constants.sig_kill_bad)

signatures_file = default_signatures_file = 'signatures.yar'
if hasattr(self, 'yara_version'):
signatures_file = 'signatures-%s.yar' % self.yara_version
if not self.rules_location:
self.rules_location = "https://console.redhat.com/api/malware-detection/v1/signatures.yar"
self.rules_location = "https://console.redhat.com/api/malware-detection/v1/" + signatures_file
if '/redhat_access/' in self.insights_config.base_url:
# Satellite URLs have '/redhat_access/' in the base_url config option
self.rules_location = self.insights_config.base_url + '/malware-detection/v1/signatures.yar'
self.rules_location = self.insights_config.base_url + '/malware-detection/v1/' + signatures_file

# Make sure the rules_location starts with https://
if not re.match('^https?://', self.rules_location):
Expand Down Expand Up @@ -651,6 +655,11 @@ def _get_rules(self):
self.insights_config.cert_verify = True
conn = InsightsConnection(self.insights_config)
response = conn.get(self.rules_location, log_response_text=log_rule_contents)
if response.status_code == 404 and not (self.test_scan or signatures_file == default_signatures_file):
# Got a 404 with the 'signatures-X.Y.yar' file, so try with just 'signatures.yar' instead
logger.debug("Couldn't find %s file, trying %s instead", signatures_file, default_signatures_file)
self.rules_location = self.rules_location.replace(signatures_file, default_signatures_file)
response = conn.get(self.rules_location, log_response_text=log_rule_contents)
if response.status_code != 200:
logger.error("%s %s: %s", response.status_code, response.reason, response.text)
exit(constants.sig_kill_bad)
Expand Down Expand Up @@ -688,7 +697,8 @@ def _build_yara_command(self):
try:
call([cmd])
except CalledProcessError as err:
logger.error("Unable to use rules file %s: %s", self.rules_file, err.output.strip())
err_str = str(err.output.strip().decode())
logger.error("Unable to use rules file %s: %s", self.rules_file, err_str)
exit(constants.sig_kill_bad)

# Limit the number of threads used by yara to limit the CPU load of the scans
Expand Down Expand Up @@ -1161,13 +1171,26 @@ def get_toplevel_dirs():
return toplevel_dirs


def is_same_file_or_root(file1, file2):
# Catch possible permission denied error with fuse mounted filesystems. Yes, even for root!
try:
if os.path.samefile(file1, file2) or os.path.samefile(file1, '/'):
return True
except Exception as err:
logger.debug("Encountered exception running os.path.samefile('%s', '%s'): %s. Trying string comparison ...",
file1, file2, str(err))
if file1 == file2 or file1 == '/':
return True
return False


def get_parent_dirs(item, parent_dir_list, base_case='/'):
"""
Get a list of parent directories of a particular filesystem item, stopping at base_case (root by default)
Eg for get_parent_dirs('/path/to/some/item', parent_dir_list) ->
parent_dir_list = ['/path', '/path/to', '/path/to/some', '/path/to/some/item']
"""
if os.path.samefile(item, base_case) or os.path.samefile(item, '/'):
if is_same_file_or_root(item, base_case):
return
get_parent_dirs(os.path.dirname(item), parent_dir_list, base_case)
parent_dir_list.append(item)
Expand Down Expand Up @@ -1196,7 +1219,7 @@ def process_include_items(include_items=[]):
elif os.path.islink(include_item):
logger.debug("Skipping link '%s' ...", include_item)
continue
elif os.path.samefile(include_item, '/'):
elif include_item == '/':
# Found / in include item list. No need to get the other items because / trumps all
logger.debug("Found root directory in list of items to scan. Ignoring the other items ...")
parsed_list = default_values
Expand Down Expand Up @@ -1231,7 +1254,7 @@ def process_exclude_items(exclude_items=[]):
exclude_item = os.path.normpath(item).replace('//', '/')
if os.path.exists(exclude_item):
# ignore the exclude_item if its not a full directory path
if os.path.samefile(exclude_item, '/'):
if exclude_item == '/':
# Found / in exclude list. No need to get the other items because / trumps all
logger.debug("Found root directory in the exclude list. Expanding it to all toplevel directories ...")
parsed_list = get_toplevel_dirs()
Expand Down
Loading

0 comments on commit ffdc458

Please sign in to comment.