Source code for suricata_check.checkers.styleguide._whitespace

  1"""`WhitespaceChecker`."""
  2
  3import logging
  4from types import MappingProxyType
  5
  6from suricata_check.checkers.interface import CheckerInterface
  7from suricata_check.utils.checker import (
  8    is_rule_option_equal_to_regex,
  9)
 10from suricata_check.utils.checker_typing import ISSUES_TYPE, Issue
 11from suricata_check.utils.regex import (
 12    HEADER_REGEX,
 13)
 14from suricata_check.utils.regex_provider import get_regex_provider
 15from suricata_check.utils.rule import Rule
 16
 17_regex_provider = get_regex_provider()
 18
 19# Regular expressions are placed here such that they are compiled only once.
 20# This has a significant impact on the performance.
 21REGEX_S100 = _regex_provider.compile(
 22    rf"^(\s*#)?\s*{HEADER_REGEX.pattern}\s*\( .*\)\s*(#.*)?$",
 23)
 24REGEX_S101 = _regex_provider.compile(
 25    rf"^(\s*#)?\s*{HEADER_REGEX.pattern}\s*\(.* \)\s*(#.*)?$",
 26)
 27REGEX_S102 = _regex_provider.compile(
 28    rf"^(\s*#)?\s*{HEADER_REGEX.pattern}\s*\(.+ :.+\)\s*(#.*)?$",
 29)
 30REGEX_S103 = _regex_provider.compile(
 31    rf"^(\s*#)?\s*{HEADER_REGEX.pattern}\s*\(.+: .+\)\s*(#.*)?$",
 32)
 33REGEX_S104 = _regex_provider.compile(
 34    rf"^(\s*#)?\s*{HEADER_REGEX.pattern}\s*\(.+ ;.+\)\s*(#.*)?$",
 35)
 36REGEX_S105 = _regex_provider.compile(
 37    rf"^(\s*#)?\s*{HEADER_REGEX.pattern}\s*\(.+; \s+.+\)\s*(#.*)?$",
 38)
 39REGEX_S106 = _regex_provider.compile(r'^".*\|.*  .*\|.*"$')
 40REGEX_S110 = _regex_provider.compile(
 41    rf"^(\s*#)?\s*{HEADER_REGEX.pattern}\s*\(.+;(?! ).+\)\s*(#.*)?$",
 42)
 43REGEX_S111 = _regex_provider.compile(r'^".*\|.*[a-fA-F0-9]{4}.*\|.*"$')
 44REGEX_S120 = _regex_provider.compile(
 45    r'^"([^\|]*|(\|[\sa-zA-Z0-9]*\|))*(\\?[\x3a\x3b\x20\x27\x7b\x5c\x2f\x60\x24\x28\x29]+|\\[\x22\x7c]+)([^\|]*|(\|[\sa-zA-Z0-9]*\|))*"$',
 46)
 47REGEX_S121 = _regex_provider.compile(
 48    r"^\"/.*(\\?[\x3a\x3b\x20\x22\x27\x2f\x60]+|\\[\x7b\x5c\x7c\x24\x28\x29]+).*/[ism]*\"$",
 49)
 50REGEX_S122 = _regex_provider.compile(r'^".*\\.*"$')
 51REGEX_S123 = _regex_provider.compile(
 52    r'^".*(?!\\(a|c[0-127]|e|f|n|r|t|0[0-9]{2}|[0-9]{3}|0\{[0-9]{3}\}|x[0-9a-f]{2}|x[0-9a-f]{3}|u[0-9a-f]{4}|d|D|h|H|s|S|v|V|w|W))(\\.).*"$',
 53)
 54
 55
[docs] 56class WhitespaceChecker(CheckerInterface): 57 """The `WhitespaceChecker` contains several checks based on the Suricata Style guide relating to formatting rules. 58 59 Codes S100-S109 report on unneccessary whitespace that should be removed. 60 61 Codes S110-S119 report on missing whitespace that should be added. 62 63 Codes S120-S129 report on non-standard escaping of special characters. 64 """ 65 66 codes = MappingProxyType( 67 { 68 "S100": {"severity": logging.INFO}, 69 "S101": {"severity": logging.INFO}, 70 "S102": {"severity": logging.INFO}, 71 "S103": {"severity": logging.INFO}, 72 "S104": {"severity": logging.INFO}, 73 "S105": {"severity": logging.INFO}, 74 "S106": {"severity": logging.INFO}, 75 "S110": {"severity": logging.INFO}, 76 "S111": {"severity": logging.INFO}, 77 "S120": {"severity": logging.INFO}, 78 "S121": {"severity": logging.INFO}, 79 "S122": {"severity": logging.INFO}, 80 "S123": {"severity": logging.INFO}, 81 }, 82 ) 83 84 def _check_rule( # noqa: C901, PLR0912 85 self: "WhitespaceChecker", 86 rule: Rule, 87 ) -> ISSUES_TYPE: 88 issues: ISSUES_TYPE = [] 89 90 if ( 91 REGEX_S100.match( 92 rule.raw.strip(), 93 ) 94 is not None 95 ): 96 issues.append( 97 Issue( 98 code="S100", 99 message="""The rule contains unneccessary whitespace after opening the rule body with. 100Consider removing the unneccessary whitespace.""", 101 ), 102 ) 103 104 if ( 105 REGEX_S101.match( 106 rule.raw.strip(), 107 ) 108 is not None 109 ): 110 issues.append( 111 Issue( 112 code="S101", 113 message="""The rule contains unneccessary whitespace before closing the rule body with. 114Consider removing the unneccessary whitespace.""", 115 ), 116 ) 117 118 if ( 119 REGEX_S102.match( 120 rule.raw.strip(), 121 ) 122 is not None 123 ): 124 issues.append( 125 Issue( 126 code="S102", 127 message="""The rule contains unneccessary whitespace before the colon (:) after an option name. 128Consider removing the unneccessary whitespace.""", 129 ), 130 ) 131 132 if ( 133 REGEX_S103.match( 134 rule.raw.strip(), 135 ) 136 is not None 137 ): 138 issues.append( 139 Issue( 140 code="S103", 141 message="""The rule contains unneccessary whitespace before the colon (:) after an option name. 142Consider removing the unneccessary whitespace.""", 143 ), 144 ) 145 146 if ( 147 REGEX_S104.match( 148 rule.raw.strip(), 149 ) 150 is not None 151 ): 152 issues.append( 153 Issue( 154 code="S104", 155 message="""The rule contains unneccessary whitespace before the semicolon (;) after an option value. 156Consider removing the unneccessary whitespace.""", 157 ), 158 ) 159 160 if ( 161 REGEX_S105.match( 162 rule.raw.strip(), 163 ) 164 is not None 165 ): 166 issues.append( 167 Issue( 168 code="S105", 169 message="""The rule contains more than one space between options after an option value. 170Consider replacing the unneccessary whitespace by a single space.""", 171 ), 172 ) 173 174 if is_rule_option_equal_to_regex( 175 rule, 176 "content", 177 REGEX_S106, 178 ): 179 issues.append( 180 Issue( 181 code="S106", 182 message="""The rule contains more than one space between bytes in content. 183Consider replacing the unneccessary whitespace by a single space.""", 184 ), 185 ) 186 187 if ( 188 REGEX_S110.match( 189 rule.raw.strip(), 190 ) 191 is not None 192 ): 193 issues.append( 194 Issue( 195 code="S110", 196 message="""The rule does not contain a space between the end of after an option value. 197Consider adding a single space.""", 198 ), 199 ) 200 201 if is_rule_option_equal_to_regex( 202 rule, 203 "content", 204 REGEX_S111, 205 ): 206 issues.append( 207 Issue( 208 code="S111", 209 message="""The rule contains more than no spaces between bytes in content. 210Consider replacing adding a single space.""", 211 ), 212 ) 213 214 if is_rule_option_equal_to_regex( 215 rule, 216 "content", 217 REGEX_S120, 218 ): 219 issues.append( 220 Issue( 221 code="S120", 222 message="""The rule did not escape \ 223(\\x3a\\x3b\\x20\\x22\\x27\\x7b\\x7c\\x5c\\x2f\\x60\\x24\\x28\\x29) in a content field. 224Consider using hex encoding instead.""", 225 ), 226 ) 227 228 if is_rule_option_equal_to_regex( 229 rule, 230 "pcre", 231 REGEX_S121, 232 ): 233 issues.append( 234 Issue( 235 code="S121", 236 message="""The rule did escape \ 237(\\x3a\\x3b\\x20\\x22\\x27\\x7b\\x7c\\x5c\\x2f\\x60\\x24\\x28\\x29) in a pcre field. 238Consider using hex encoding instead.""", 239 ), 240 ) 241 242 if is_rule_option_equal_to_regex(rule, "content", REGEX_S122): 243 issues.append( 244 Issue( 245 code="S122", 246 message="""The rule escaped special characters using a blackslash (\\) in a content field. 247Consider using hex encoding instead.""", 248 ), 249 ) 250 251 if is_rule_option_equal_to_regex(rule, "pcre", REGEX_S123): 252 issues.append( 253 Issue( 254 code="S123", 255 message="""The rule escaped special characters using a blackslash (\\) in a pcre field. 256Consider using hex encoding instead.""", 257 ), 258 ) 259 260 return issues