Source code for suricata_check.checkers.interface.checker

  1"""The `suricata_check.checkers.interface.checker` module contains the `CheckerInterface`.
  2
  3Implementation of the `CheckerInterface` is neccessary for checker auto-discovery.
  4"""
  5
  6import abc
  7import logging
  8from collections.abc import Iterable, Mapping
  9from typing import Optional
 10
 11import idstools.rule
 12
 13from suricata_check.utils.checker import get_rule_option, is_rule_option_set
 14from suricata_check.utils.checker_typing import ISSUES_TYPE
 15
 16_logger = logging.getLogger(__name__)
 17
 18
[docs] 19class CheckerInterface: 20 """Interface for rule checkers returning a list of issues. 21 22 These checkers are automatically discovered through `suricata_check.suricata_check.get_checkers()`. 23 24 Each code should start with an upper case letter (may be multiple), followed by three digits. 25 In other words, each code should follow the following regex `[A-Z]{1,}[0-9]{3}` 26 27 We recommend using a letter to indicate the category of the issue, such as described in `README.md`. 28 Moreover, we suggest to reserve certain ranges of numbers for each checker. 29 30 """ 31 32 codes: Mapping[str, Mapping[str, int]] 33 """A Mapping of issue codes emitted by the checker to metadata for those issue types. 34 The metadata is structured in the form of a Mapping from attribute name to attribute value. 35 The one mandatory metadata attribute is severity, which must be one of the levels provided by the `logging` module""" 36 37 enabled_by_default: bool = True 38 """A boolean indicating if the checker is enabled by default when discovered automatically.""" 39 40 def __init__( 41 self: "CheckerInterface", include: Optional[Iterable[str]] = None 42 ) -> None: 43 """Initializes the checker given a list of issue codes to emit.""" 44 if include is None: 45 include = self.codes 46 self.include = include 47 48 super().__init__() 49
[docs] 50 def check_rule( 51 self: "CheckerInterface", 52 rule: idstools.rule.Rule, 53 ) -> ISSUES_TYPE: 54 """Checks a rule and returns a list of issues found.""" 55 self.__log_rule_processing(rule) 56 return self.__add_checker_metadata( 57 self.__add_issue_metadata(self.__filter_issues(self._check_rule(rule))) 58 )
59 60 @abc.abstractmethod 61 def _check_rule( 62 self: "CheckerInterface", 63 rule: idstools.rule.Rule, 64 ) -> ISSUES_TYPE: 65 """Checks a rule and returns a list of issues found.""" 66 67 def __log_rule_processing( 68 self: "CheckerInterface", 69 rule: idstools.rule.Rule, 70 ) -> None: 71 sid: Optional[int] = None 72 if is_rule_option_set(rule, "sid"): 73 sid_str = get_rule_option(rule, "sid") 74 assert sid_str is not None 75 sid = int(sid_str) 76 77 _logger.debug("Running %s on rule %s", self.__class__.__name__, sid) 78 79 def __add_issue_metadata( 80 self: "CheckerInterface", 81 issues: ISSUES_TYPE, 82 ) -> ISSUES_TYPE: 83 """Given a list of issues, return the same list with metadata from the issue types.""" 84 for issue in issues: 85 metadata = self.codes[issue.code] 86 if "severity" in metadata: 87 issue.severity = metadata["severity"] 88 89 return issues 90 91 def __add_checker_metadata( 92 self: "CheckerInterface", 93 issues: ISSUES_TYPE, 94 ) -> ISSUES_TYPE: 95 """Given a list of issues, return the same list with metadata from the checker.""" 96 name = self.__class__.__name__ 97 98 for issue in issues: 99 issue.checker = name 100 101 return issues 102 103 def __filter_issues( 104 self: "CheckerInterface", 105 issues: ISSUES_TYPE, 106 ) -> ISSUES_TYPE: 107 """Given a list of issues, return the same list having filtered out disabled issue types.""" 108 filtered_issues = [] 109 110 for issue in issues: 111 if issue.code in self.include: 112 filtered_issues.append(issue) 113 elif issue.code not in self.codes: 114 _logger.warning( 115 "Issue with filtered code %s not found in checker %s", 116 issue.code, 117 self.__class__.__name__, 118 ) 119 120 return filtered_issues