1- import sys
21import msc_pyparser
3- import difflib
4- import argparse
52import re
6- import subprocess
7- import logging
8- import os .path
93
10- def parse_config (text ):
11- try :
12- mparser = msc_pyparser .MSCParser ()
13- mparser .parser .parse (text )
14- return mparser .configlines
4+ class LintProblem :
5+ """Represents a linting problem found by crs-linter."""
6+ def __init__ (self , line , end_line , column = None , desc = '<no description>' , rule = None ):
7+ #: Line on which the problem was found (starting at 1)
8+ self .line = line
9+ #: Line on which the problem ends
10+ self .end_line = end_line
11+ #: Column on which the problem was found (starting at 1)
12+ self .column = column
13+ #: Human-readable description of the problem
14+ self .desc = desc
15+ #: Identifier of the rule that detected the problem
16+ self .rule = rule
17+ self .level = None
1518
16- except Exception as e :
17- print (e )
19+ @property
20+ def message (self ):
21+ if self .rule is not None :
22+ return f'{ self .desc } ({ self .rule } )'
23+ return self .desc
1824
25+ def __eq__ (self , other ):
26+ return (self .line == other .line and
27+ self .column == other .column and
28+ self .rule == other .rule )
1929
20- def parse_file (filename ):
21- try :
22- mparser = msc_pyparser .MSCParser ()
23- with open (filename , "r" ) as f :
24- mparser .parser .parse (f .read ())
25- return mparser .configlines
30+ def __lt__ (self , other ):
31+ return (self .line < other .line or
32+ (self .line == other .line and self .column < other .column ))
2633
27- except Exception as e :
28- print ( e )
34+ def __repr__ ( self ) :
35+ return f' { self . line } : { self . column } : { self . message } '
2936
3037
31- class Check ():
32- def __init__ (self , data , filename = None , txvars = {}):
38+ class Linter :
39+ ids = {} # list of rule id's and their location in files
40+ vars = {} # list of TX variables and their location in files
3341
42+ def __init__ (self , data , filename = None , txvars = None ):
3443 # txvars is a global used hash table, but processing of rules is a sequential flow
3544 # all rules need this global table
36- self .globtxvars = txvars
45+ self .globtxvars = txvars or {}
3746 # list available operators, actions, transformations and ctl args
3847 self .operators = "beginsWith|containsWord|contains|detectSQLi|detectXSS|endsWith|eq|fuzzyHash|geoLookup|ge|gsbLookup|gt|inspectFile|ipMatch|ipMatchF|ipMatchFromFile|le|lt|noMatch|pmFromFile|pmf|pm|rbl|rsub|rx|streq|strmatch|unconditionalMatch|validateByteRange|validateDTD|validateHash|validateSchema|validateUrlEncoding|validateUtf8Encoding|verifyCC|verifyCPF|verifySSN|within" .split (
3948 "|"
@@ -101,7 +110,6 @@ def __init__(self, data, filename=None, txvars={}):
101110 self .ids = {} # list of rule id's and their location in files
102111
103112 # Any of these variables below are used to store the errors
104-
105113 self .error_case_mistmatch = [] # list of case mismatch errors
106114 self .error_action_order = [] # list of ordered action errors
107115 self .error_wrong_ctl_auditlogparts = [] # list of wrong ctl:auditLogParts
@@ -120,10 +128,11 @@ def __init__(self, data, filename=None, txvars={}):
120128 self .error_tx_N_without_capture_action = (
121129 []
122130 ) # list of rules which uses TX.N without previous 'capture'
123- self .error_rule_hasnotest = (
131+ self .error_rule_hasnotest = (
124132 []
125133 ) # list of rules which don't have any tests
126134 # regex to produce tag from filename:
135+ import os .path
127136 self .re_fname = re .compile (r"(REQUEST|RESPONSE)\-\d{3}\-" )
128137 self .filename_tag_exclusions = []
129138
@@ -231,6 +240,7 @@ def check_ignore_case(self):
231240 e ["message" ] += f" (rule: { self .current_ruleid } )"
232241
233242 def check_action_order (self ):
243+ import sys
234244 for d in self .data :
235245 if "actions" in d :
236246 max_order = 0 # maximum position of read actions
@@ -771,6 +781,7 @@ def gen_crs_file_tag(self, fname=None):
771781 """
772782 generate tag from filename
773783 """
784+ import os .path
774785 filename_for_tag = fname if fname is not None else self .filename
775786 filename = self .re_fname .sub ("" , os .path .basename (os .path .splitext (filename_for_tag )[0 ]))
776787 filename = filename .replace ("APPLICATION-" , "" )
@@ -993,3 +1004,32 @@ def find_ids_without_tests(self, test_cases, exclusion_list):
9931004 'message' : f"rule does not have any tests; rule id: { rid } '"
9941005 })
9951006 return True
1007+
1008+
1009+ def parse_config (text ):
1010+ try :
1011+ mparser = msc_pyparser .MSCParser ()
1012+ mparser .parser .parse (text )
1013+ return mparser .configlines
1014+
1015+ except Exception as e :
1016+ print (e )
1017+
1018+
1019+ def parse_file (filename ):
1020+ try :
1021+ mparser = msc_pyparser .MSCParser ()
1022+ with open (filename , "r" ) as f :
1023+ mparser .parser .parse (f .read ())
1024+ return mparser .configlines
1025+
1026+ except Exception as e :
1027+ print (e )
1028+
1029+
1030+ def get_id (actions ):
1031+ """ Return the ID from actions """
1032+ for a in actions :
1033+ if a ["act_name" ] == "id" :
1034+ return int (a ["act_arg" ])
1035+ return 0
0 commit comments