Source code for suricata_check.checkers.styleguide.whitespace

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