Source code for suricata_check.checkers.styleguide.whitespace

  1"""`WhitespaceChecker`."""
  2
  3import logging
  4
  5import idstools.rule
  6
  7from suricata_check.checkers.interface import CheckerInterface
  8from suricata_check.utils.checker import (
  9    is_rule_option_equal_to_regex,
 10)
 11from suricata_check.utils.checker_typing import ISSUES_TYPE, Issue
 12from suricata_check.utils.regex import (
 13    HEADER_REGEX,
 14    get_regex_provider,
 15)
 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 = { 67 "S100": {"severity": logging.INFO}, 68 "S101": {"severity": logging.INFO}, 69 "S102": {"severity": logging.INFO}, 70 "S103": {"severity": logging.INFO}, 71 "S104": {"severity": logging.INFO}, 72 "S105": {"severity": logging.INFO}, 73 "S106": {"severity": logging.INFO}, 74 "S110": {"severity": logging.INFO}, 75 "S111": {"severity": logging.INFO}, 76 "S120": {"severity": logging.INFO}, 77 "S121": {"severity": logging.INFO}, 78 "S122": {"severity": logging.INFO}, 79 "S123": {"severity": logging.INFO}, 80 } 81 82 def _check_rule( # noqa: C901, PLR0912 83 self: "WhitespaceChecker", 84 rule: idstools.rule.Rule, 85 ) -> ISSUES_TYPE: 86 issues: ISSUES_TYPE = [] 87 88 if ( 89 REGEX_S100.match( 90 rule["raw"].strip(), 91 ) 92 is not None 93 ): 94 issues.append( 95 Issue( 96 code="S100", 97 message="""The rule contains unneccessary whitespace after opening the rule body with. 98Consider removing the unneccessary whitespace.""", 99 ), 100 ) 101 102 if ( 103 REGEX_S101.match( 104 rule["raw"].strip(), 105 ) 106 is not None 107 ): 108 issues.append( 109 Issue( 110 code="S101", 111 message="""The rule contains unneccessary whitespace before closing the rule body with. 112Consider removing the unneccessary whitespace.""", 113 ), 114 ) 115 116 if ( 117 REGEX_S102.match( 118 rule["raw"].strip(), 119 ) 120 is not None 121 ): 122 issues.append( 123 Issue( 124 code="S102", 125 message="""The rule contains unneccessary whitespace before the colon (:) after an option name. 126Consider removing the unneccessary whitespace.""", 127 ), 128 ) 129 130 if ( 131 REGEX_S103.match( 132 rule["raw"].strip(), 133 ) 134 is not None 135 ): 136 issues.append( 137 Issue( 138 code="S103", 139 message="""The rule contains unneccessary whitespace before the colon (:) after an option name. 140Consider removing the unneccessary whitespace.""", 141 ), 142 ) 143 144 if ( 145 REGEX_S104.match( 146 rule["raw"].strip(), 147 ) 148 is not None 149 ): 150 issues.append( 151 Issue( 152 code="S104", 153 message="""The rule contains unneccessary whitespace before the semicolon (;) after an option value. 154Consider removing the unneccessary whitespace.""", 155 ), 156 ) 157 158 if ( 159 REGEX_S105.match( 160 rule["raw"].strip(), 161 ) 162 is not None 163 ): 164 issues.append( 165 Issue( 166 code="S105", 167 message="""The rule contains more than one space between options after an option value. 168Consider replacing the unneccessary whitespace by a single space.""", 169 ), 170 ) 171 172 if is_rule_option_equal_to_regex( 173 rule, 174 "content", 175 REGEX_S106, 176 ): 177 issues.append( 178 Issue( 179 code="S106", 180 message="""The rule contains more than one space between bytes in content. 181Consider replacing the unneccessary whitespace by a single space.""", 182 ), 183 ) 184 185 if ( 186 REGEX_S110.match( 187 rule["raw"].strip(), 188 ) 189 is not None 190 ): 191 issues.append( 192 Issue( 193 code="S110", 194 message="""The rule does not contain a space between the end of after an option value. 195Consider adding a single space.""", 196 ), 197 ) 198 199 if is_rule_option_equal_to_regex( 200 rule, 201 "content", 202 REGEX_S111, 203 ): 204 issues.append( 205 Issue( 206 code="S111", 207 message="""The rule contains more than no spaces between bytes in content. 208Consider replacing adding a single space.""", 209 ), 210 ) 211 212 if is_rule_option_equal_to_regex( 213 rule, 214 "content", 215 REGEX_S120, 216 ): 217 issues.append( 218 Issue( 219 code="S120", 220 message="""The rule did not escape \ 221(\\x3a\\x3b\\x20\\x22\\x27\\x7b\\x7c\\x5c\\x2f\\x60\\x24\\x28\\x29) in a content field. 222Consider using hex encoding instead.""", 223 ), 224 ) 225 226 if is_rule_option_equal_to_regex( 227 rule, 228 "pcre", 229 REGEX_S121, 230 ): 231 issues.append( 232 Issue( 233 code="S121", 234 message="""The rule did escape \ 235(\\x3a\\x3b\\x20\\x22\\x27\\x7b\\x7c\\x5c\\x2f\\x60\\x24\\x28\\x29) in a pcre field. 236Consider using hex encoding instead.""", 237 ), 238 ) 239 240 if is_rule_option_equal_to_regex(rule, "content", REGEX_S122): 241 issues.append( 242 Issue( 243 code="S122", 244 message="""The rule escaped special characters using a blackslash (\\) in a content field. 245Consider using hex encoding instead.""", 246 ), 247 ) 248 249 if is_rule_option_equal_to_regex(rule, "pcre", REGEX_S123): 250 issues.append( 251 Issue( 252 code="S123", 253 message="""The rule escaped special characters using a blackslash (\\) in a pcre field. 254Consider using hex encoding instead.""", 255 ), 256 ) 257 258 return issues