99import re
1010import argparse
1111
12+
1213def quote_if_needed (row ):
1314 if row != "true" and row != "false" :
14- return " \" " + row + " \" "
15+ return '"' + row + '"'
1516 # subtypes column
1617 return row [0 ].upper () + row [1 :]
1718
19+
1820def parseData (data ):
19- rows = { }
21+ rows = {}
2022
2123 for row in data :
22- d = row [0 ].split (';' )
24+ d = row [0 ].split (";" )
2325 namespace = d [0 ]
2426 d = map (quote_if_needed , d )
25- helpers .insert_update (rows , namespace , " - [" + ', ' .join (d ) + ' ]\n ' )
27+ helpers .insert_update (rows , namespace , " - [" + ", " .join (d ) + " ]\n " )
2628
2729 return rows
2830
@@ -39,6 +41,7 @@ def parseData(data):
3941
4042Requirements: `codeql` should appear on your path."""
4143
44+
4245class Generator :
4346 generateSinks = False
4447 generateSources = False
@@ -55,42 +58,97 @@ def __init__(self, language=None):
5558 self .language = language
5659
5760 def setenvironment (self , database = None , folder = None ):
58- self .codeQlRoot = subprocess .check_output (["git" , "rev-parse" , "--show-toplevel" ]).decode ("utf-8" ).strip ()
61+ self .codeQlRoot = (
62+ subprocess .check_output (["git" , "rev-parse" , "--show-toplevel" ])
63+ .decode ("utf-8" )
64+ .strip ()
65+ )
5966 self .database = database or self .database
6067 self .folder = folder or self .folder
6168 self .generatedFrameworks = os .path .join (
62- self .codeQlRoot , f"{ self .language } /ql/lib/ext/generated/{ self .folder } " )
69+ self .codeQlRoot , f"{ self .language } /ql/lib/ext/generated/{ self .folder } "
70+ )
6371 self .workDir = tempfile .mkdtemp ()
6472 if self .ram is None :
6573 threads = self .threads if self .threads > 0 else os .cpu_count ()
6674 self .ram = 2048 * threads
6775 os .makedirs (self .generatedFrameworks , exist_ok = True )
6876
69-
7077 @staticmethod
7178 def make ():
7279 p = argparse .ArgumentParser (
7380 description = description ,
7481 formatter_class = argparse .RawTextHelpFormatter ,
75- epilog = epilog )
82+ epilog = epilog ,
83+ )
7684 p .add_argument ("database" , help = "Path to the CodeQL database" )
77- p .add_argument ("folder" , nargs = "?" , default = "" , help = "Optional folder to place the generated files in" )
78- p .add_argument ("--language" , required = True , help = "The language for which to generate models" )
79- p .add_argument ("--with-sinks" , action = "store_true" , help = "Generate sink models" , dest = "generateSinks" )
80- p .add_argument ("--with-sources" , action = "store_true" , help = "Generate source models" , dest = "generateSources" )
81- p .add_argument ("--with-summaries" , action = "store_true" , help = "Generate summary models" , dest = "generateSummaries" )
82- p .add_argument ("--with-neutrals" , action = "store_true" , help = "Generate neutral models" , dest = "generateNeutrals" )
83- p .add_argument ("--with-typebased-summaries" , action = "store_true" , help = "Generate type-based summary models (experimental)" , dest = "generateTypeBasedSummaries" )
84- p .add_argument ("--dry-run" , action = "store_true" , help = "Do not write the generated files, just print them to stdout" , dest = "dryRun" )
85- p .add_argument ("--threads" , type = int , default = Generator .threads , help = "Number of threads to use for CodeQL queries (default %(default)s). `0` means use all available threads." )
86- p .add_argument ("--ram" , type = int , help = "Amount of RAM to use for CodeQL queries in MB. Default is to use 2048 MB per thread." )
85+ p .add_argument (
86+ "folder" ,
87+ nargs = "?" ,
88+ default = "" ,
89+ help = "Optional folder to place the generated files in" ,
90+ )
91+ p .add_argument (
92+ "--language" ,
93+ required = True ,
94+ help = "The language for which to generate models" ,
95+ )
96+ p .add_argument (
97+ "--with-sinks" ,
98+ action = "store_true" ,
99+ help = "Generate sink models" ,
100+ dest = "generateSinks" ,
101+ )
102+ p .add_argument (
103+ "--with-sources" ,
104+ action = "store_true" ,
105+ help = "Generate source models" ,
106+ dest = "generateSources" ,
107+ )
108+ p .add_argument (
109+ "--with-summaries" ,
110+ action = "store_true" ,
111+ help = "Generate summary models" ,
112+ dest = "generateSummaries" ,
113+ )
114+ p .add_argument (
115+ "--with-neutrals" ,
116+ action = "store_true" ,
117+ help = "Generate neutral models" ,
118+ dest = "generateNeutrals" ,
119+ )
120+ p .add_argument (
121+ "--with-typebased-summaries" ,
122+ action = "store_true" ,
123+ help = "Generate type-based summary models (experimental)" ,
124+ dest = "generateTypeBasedSummaries" ,
125+ )
126+ p .add_argument (
127+ "--dry-run" ,
128+ action = "store_true" ,
129+ help = "Do not write the generated files, just print them to stdout" ,
130+ dest = "dryRun" ,
131+ )
132+ p .add_argument (
133+ "--threads" ,
134+ type = int ,
135+ default = Generator .threads ,
136+ help = "Number of threads to use for CodeQL queries (default %(default)s). `0` means use all available threads." ,
137+ )
138+ p .add_argument (
139+ "--ram" ,
140+ type = int ,
141+ help = "Amount of RAM to use for CodeQL queries in MB. Default is to use 2048 MB per thread." ,
142+ )
87143 generator = p .parse_args (namespace = Generator ())
88144
89- if (not generator .generateSinks and
90- not generator .generateSources and
91- not generator .generateSummaries and
92- not generator .generateNeutrals and
93- not generator .generateTypeBasedSummaries ):
145+ if (
146+ not generator .generateSinks
147+ and not generator .generateSources
148+ and not generator .generateSummaries
149+ and not generator .generateNeutrals
150+ and not generator .generateTypeBasedSummaries
151+ ):
94152 generator .generateSinks = True
95153 generator .generateSources = True
96154 generator .generateSummaries = True
@@ -99,22 +157,37 @@ def make():
99157 generator .setenvironment ()
100158 return generator
101159
102-
103160 def runQuery (self , query ):
104161 print ("########## Querying " + query + "..." )
105- queryFile = os .path .join (self .codeQlRoot , f"{ self .language } /ql/src/utils/{ self .dirname } " , query )
162+ queryFile = os .path .join (
163+ self .codeQlRoot , f"{ self .language } /ql/src/utils/{ self .dirname } " , query
164+ )
106165 resultBqrs = os .path .join (self .workDir , "out.bqrs" )
107166
108- cmd = ['codeql' , 'query' , 'run' , queryFile , '--database' , self .database , '--output' , resultBqrs , "--threads" , str (self .threads ), "--ram" , str (self .ram )]
167+ cmd = [
168+ "codeql" ,
169+ "query" ,
170+ "run" ,
171+ queryFile ,
172+ "--database" ,
173+ self .database ,
174+ "--output" ,
175+ resultBqrs ,
176+ "--threads" ,
177+ str (self .threads ),
178+ "--ram" ,
179+ str (self .ram ),
180+ ]
109181 helpers .run_cmd (cmd , "Failed to generate " + query )
110182
111183 return helpers .readData (self .workDir , resultBqrs )
112184
113-
114185 def asAddsTo (self , rows , predicate ):
115- extensions = { }
186+ extensions = {}
116187 for key in rows :
117- extensions [key ] = helpers .addsToTemplate .format (f"codeql/{ self .language } -all" , predicate , rows [key ])
188+ extensions [key ] = helpers .addsToTemplate .format (
189+ f"codeql/{ self .language } -all" , predicate , rows [key ]
190+ )
118191 return extensions
119192
120193 def getAddsTo (self , query , predicate ):
@@ -125,27 +198,37 @@ def getAddsTo(self, query, predicate):
125198 def makeContent (self ):
126199 summaryAddsTo = {}
127200 if self .generateSummaries :
128- summaryAddsTo = self .getAddsTo ("CaptureSummaryModels.ql" , helpers .summaryModelPredicate )
201+ summaryAddsTo = self .getAddsTo (
202+ "CaptureSummaryModels.ql" , helpers .summaryModelPredicate
203+ )
129204
130205 sinkAddsTo = {}
131206 if self .generateSinks :
132- sinkAddsTo = self .getAddsTo ("CaptureSinkModels.ql" , helpers .sinkModelPredicate )
207+ sinkAddsTo = self .getAddsTo (
208+ "CaptureSinkModels.ql" , helpers .sinkModelPredicate
209+ )
133210
134211 sourceAddsTo = {}
135212 if self .generateSources :
136- sourceAddsTo = self .getAddsTo ("CaptureSourceModels.ql" , helpers .sourceModelPredicate )
213+ sourceAddsTo = self .getAddsTo (
214+ "CaptureSourceModels.ql" , helpers .sourceModelPredicate
215+ )
137216
138217 neutralAddsTo = {}
139218 if self .generateNeutrals :
140- neutralAddsTo = self .getAddsTo ("CaptureNeutralModels.ql" , helpers .neutralModelPredicate )
219+ neutralAddsTo = self .getAddsTo (
220+ "CaptureNeutralModels.ql" , helpers .neutralModelPredicate
221+ )
141222
142223 return helpers .merge (summaryAddsTo , sinkAddsTo , sourceAddsTo , neutralAddsTo )
143224
144225 def makeTypeBasedContent (self ):
145226 if self .generateTypeBasedSummaries :
146- typeBasedSummaryAddsTo = self .getAddsTo ("CaptureTypeBasedSummaryModels.ql" , helpers .summaryModelPredicate )
227+ typeBasedSummaryAddsTo = self .getAddsTo (
228+ "CaptureTypeBasedSummaryModels.ql" , helpers .summaryModelPredicate
229+ )
147230 else :
148- typeBasedSummaryAddsTo = { }
231+ typeBasedSummaryAddsTo = {}
149232
150233 return typeBasedSummaryAddsTo
151234
@@ -156,13 +239,14 @@ def save(self, extensions, extension):
156239{0}"""
157240 for entry in extensions :
158241 # Replace problematic characters with dashes, and collapse multiple dashes.
159- sanitizedEntry = re .sub (r'-+' , '-' , entry .replace ('/' , '-' ).replace (':' , '-' ))
242+ sanitizedEntry = re .sub (
243+ r"-+" , "-" , entry .replace ("/" , "-" ).replace (":" , "-" )
244+ )
160245 target = os .path .join (self .generatedFrameworks , sanitizedEntry + extension )
161246 with open (target , "w" ) as f :
162247 f .write (extensionTemplate .format (extensions [entry ]))
163248 print ("Models as data extensions written to " + target )
164249
165-
166250 def run (self ):
167251 content = self .makeContent ()
168252 typeBasedContent = self .makeTypeBasedContent ()
@@ -171,14 +255,17 @@ def run(self):
171255 print ("Models as data extensions generated, but not written to file." )
172256 sys .exit (0 )
173257
174- if (self .generateSinks or
175- self .generateSources or
176- self .generateSummaries or
177- self .generateNeutrals ):
258+ if (
259+ self .generateSinks
260+ or self .generateSources
261+ or self .generateSummaries
262+ or self .generateNeutrals
263+ ):
178264 self .save (content , ".model.yml" )
179265
180266 if self .generateTypeBasedSummaries :
181267 self .save (typeBasedContent , ".typebased.model.yml" )
182268
183- if __name__ == '__main__' :
269+
270+ if __name__ == "__main__" :
184271 Generator .make ().run ()
0 commit comments