12
12
13
13
from abc import ABCMeta
14
14
from enum import Enum
15
- from string import Template
16
15
import collections
17
16
import platform
18
17
import sys
@@ -86,15 +85,16 @@ def add_checker(self, checker_name, description='',
86
85
"""
87
86
self .__available_checkers [checker_name ] = (state , description )
88
87
89
- def set_checker_enabled (self , checker_name , enabled = True ):
88
+ def set_checker_enabled (self , checker_name , enabled = True , is_strict = False ):
90
89
"""
91
90
Explicitly handle checker state, keep description if already set.
92
91
"""
93
92
changed_states = []
94
93
regex = "^" + re .escape (str (checker_name )) + "\\ b.*$"
95
94
96
95
for ch_name , values in self .__available_checkers .items ():
97
- if re .match (regex , ch_name ):
96
+ if (is_strict and ch_name == checker_name ) \
97
+ or (not is_strict and re .match (regex , ch_name )):
98
98
_ , description = values
99
99
state = CheckerState .ENABLED if enabled \
100
100
else CheckerState .DISABLED
@@ -118,7 +118,7 @@ def checks(self):
118
118
"""
119
119
return self .__available_checkers
120
120
121
- def __gen_name_variations (self ):
121
+ def __gen_name_variations (self , only_prefix = False ):
122
122
"""
123
123
Generate all applicable name variations from the given checker list.
124
124
"""
@@ -134,9 +134,9 @@ def __gen_name_variations(self):
134
134
# ['misc', 'misc-dangling', 'misc-dangling-handle']
135
135
# from 'misc-dangling-handle'.
136
136
v = [delim .join (parts [:(i + 1 )]) for i in range (len (parts ))]
137
- reserved_names += v
137
+ reserved_names += v [: - 1 ] if only_prefix else v
138
138
139
- return reserved_names
139
+ return list ( set ( reserved_names ))
140
140
141
141
def initialize_checkers (self ,
142
142
checkers ,
@@ -184,7 +184,7 @@ def initialize_checkers(self,
184
184
else :
185
185
# Turn default checkers on.
186
186
for checker in default_profile_checkers :
187
- self .set_checker_enabled (checker )
187
+ self .set_checker_enabled (checker , is_strict = True )
188
188
189
189
self .enable_all = enable_all
190
190
# If enable_all is given, almost all checkers should be enabled.
@@ -207,48 +207,82 @@ def initialize_checkers(self,
207
207
self .set_checker_enabled (checker_name )
208
208
209
209
# Set user defined enabled or disabled checkers from the command line.
210
+ for identifier , enabled in cmdline_enable :
211
+ labels = checker_labels .labels () \
212
+ if callable (getattr (checker_labels , 'labels' , None )) \
213
+ else ["guideline" , "profile" , "severity" , "sei-cert" ]
210
214
211
- # Construct a list of reserved checker names.
212
- # (It is used to check if a profile name is valid.)
213
- reserved_names = self .__gen_name_variations ()
214
- profiles = checker_labels .get_description ('profile' )
215
- guidelines = checker_labels .occurring_values ('guideline' )
215
+ all_namespaces = ["checker" , "prefix" ] + labels
216
216
217
- templ = Template ("The ${entity} name '${identifier}' conflicts with a "
218
- "checker name prefix '${identifier}'. Please use -e "
219
- "${entity}:${identifier} to enable checkers of the "
220
- "${identifier} ${entity} or use -e "
221
- "prefix:${identifier} to select checkers which have "
222
- "a name starting with '${identifier}'." )
217
+ all_options = dict (zip (labels , map (
218
+ checker_labels .occurring_values , labels )))
223
219
224
- for identifier , enabled in cmdline_enable :
225
- if "prefix:" in identifier :
226
- identifier = identifier . replace ( "prefix:" , "" )
227
- self .set_checker_enabled ( identifier , enabled )
228
-
229
- elif ':' in identifier :
230
- for checker in checker_labels . checkers_by_labels ([ identifier ]):
231
- self . set_checker_enabled ( checker , enabled )
232
-
233
- elif identifier in profiles :
234
- if identifier in reserved_names :
235
- LOG . error ( templ . substitute ( entity = "profile " ,
236
- identifier = identifier ))
220
+ all_options [ "prefix" ] = list ( set ( self . __gen_name_variations (
221
+ only_prefix = True )))
222
+
223
+ all_options [ "checker" ] = self .__available_checkers
224
+
225
+ if ":" in identifier :
226
+ identifier_namespace = identifier . split ( ":" )[ 0 ]
227
+ identifier = identifier . split ( ":" , 1 )[ 1 ]
228
+
229
+ if identifier_namespace not in all_namespaces :
230
+ LOG . error ( "The %s namespace is not known. Please select"
231
+ "one of these existing namespace options: %s. " ,
232
+ identifier_namespace , ", " . join ( all_namespaces ))
237
233
sys .exit (1 )
238
- else :
239
- for checker in checker_labels .checkers_by_labels (
240
- [f'profile:{ identifier } ' ]):
241
- self .set_checker_enabled (checker , enabled )
242
-
243
- elif identifier in guidelines :
244
- if identifier in reserved_names :
245
- LOG .error (templ .substitute (entity = "guideline" ,
246
- identifier = identifier ))
234
+
235
+ # TODO: Each analyzer has its own config handler and is unaware
236
+ # of other checkers. To avoid not reliable error, we pass
237
+ # checker:'any_options' and prefix:'any_options'. This ensures
238
+ # enabling a checker doesn't inadvertently cause an error in a
239
+ # different analyzer. Ideally, there should be a centralized
240
+ # main configuration accessible to all analyzers.
241
+ if identifier not in all_options [identifier_namespace ] \
242
+ and identifier_namespace not in ("checker" , "prefix" ):
243
+ LOG .error ("The %s identifier does not exist in the %s "
244
+ "namespace. Please select one of these "
245
+ "existing options: %s." , identifier ,
246
+ identifier_namespace , ", " .join (
247
+ all_options [identifier_namespace ]))
247
248
sys .exit (1 )
248
- else :
249
- for checker in checker_labels .checkers_by_labels (
250
- [f'guideline:{ identifier } ' ]):
251
- self .set_checker_enabled (checker , enabled )
249
+
250
+ self .initialize_checkers_by_namespace (
251
+ identifier_namespace , identifier , enabled )
252
252
253
253
else :
254
- self .set_checker_enabled (identifier , enabled )
254
+ possible_options = {}
255
+ for label , options in all_options .items ():
256
+ if identifier in options :
257
+ possible_options [label ] = identifier
258
+
259
+ if len (possible_options ) == 1 :
260
+ self .initialize_checkers_by_namespace (
261
+ * list (possible_options .items ())[0 ], enabled )
262
+ elif len (possible_options ) > 1 :
263
+ error_options = ", " .join (f"{ label } :{ option } "
264
+ for label , option
265
+ in possible_options .items ())
266
+
267
+ LOG .error ("The %s is ambigous. Please select one of these"
268
+ " options to clarify the checker list: %s." ,
269
+ identifier , error_options )
270
+ sys .exit (1 )
271
+ else :
272
+ # The identifier is not known but we just pass it
273
+ # and handle it in a different section.
274
+ continue
275
+
276
+ def initialize_checkers_by_namespace (self ,
277
+ identifier_namespace ,
278
+ identifier ,
279
+ enabled ):
280
+ if identifier_namespace == "checker" :
281
+ self .set_checker_enabled (identifier , enabled , is_strict = True )
282
+ elif identifier_namespace == "prefix" :
283
+ self .set_checker_enabled (identifier , enabled )
284
+ else :
285
+ checker_labels = analyzer_context .get_context ().checker_labels
286
+ for checker in checker_labels .checkers_by_labels (
287
+ [f"{ identifier_namespace } :{ identifier } " ]):
288
+ self .set_checker_enabled (checker , enabled , is_strict = True )
0 commit comments