9
9
import re
10
10
import argparse
11
11
12
+
12
13
def quote_if_needed (row ):
13
14
if row != "true" and row != "false" :
14
- return " \" " + row + " \" "
15
+ return '"' + row + '"'
15
16
# subtypes column
16
17
return row [0 ].upper () + row [1 :]
17
18
19
+
18
20
def parseData (data ):
19
- rows = { }
21
+ rows = {}
20
22
21
23
for row in data :
22
- d = row [0 ].split (';' )
24
+ d = row [0 ].split (";" )
23
25
namespace = d [0 ]
24
26
d = map (quote_if_needed , d )
25
- helpers .insert_update (rows , namespace , " - [" + ', ' .join (d ) + ' ]\n ' )
27
+ helpers .insert_update (rows , namespace , " - [" + ", " .join (d ) + " ]\n " )
26
28
27
29
return rows
28
30
@@ -39,6 +41,7 @@ def parseData(data):
39
41
40
42
Requirements: `codeql` should appear on your path."""
41
43
44
+
42
45
class Generator :
43
46
generateSinks = False
44
47
generateSources = False
@@ -55,42 +58,97 @@ def __init__(self, language=None):
55
58
self .language = language
56
59
57
60
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
+ )
59
66
self .database = database or self .database
60
67
self .folder = folder or self .folder
61
68
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
+ )
63
71
self .workDir = tempfile .mkdtemp ()
64
72
if self .ram is None :
65
73
threads = self .threads if self .threads > 0 else os .cpu_count ()
66
74
self .ram = 2048 * threads
67
75
os .makedirs (self .generatedFrameworks , exist_ok = True )
68
76
69
-
70
77
@staticmethod
71
78
def make ():
72
79
p = argparse .ArgumentParser (
73
80
description = description ,
74
81
formatter_class = argparse .RawTextHelpFormatter ,
75
- epilog = epilog )
82
+ epilog = epilog ,
83
+ )
76
84
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
+ )
87
143
generator = p .parse_args (namespace = Generator ())
88
144
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
+ ):
94
152
generator .generateSinks = True
95
153
generator .generateSources = True
96
154
generator .generateSummaries = True
@@ -99,22 +157,37 @@ def make():
99
157
generator .setenvironment ()
100
158
return generator
101
159
102
-
103
160
def runQuery (self , query ):
104
161
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
+ )
106
165
resultBqrs = os .path .join (self .workDir , "out.bqrs" )
107
166
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
+ ]
109
181
helpers .run_cmd (cmd , "Failed to generate " + query )
110
182
111
183
return helpers .readData (self .workDir , resultBqrs )
112
184
113
-
114
185
def asAddsTo (self , rows , predicate ):
115
- extensions = { }
186
+ extensions = {}
116
187
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
+ )
118
191
return extensions
119
192
120
193
def getAddsTo (self , query , predicate ):
@@ -125,27 +198,37 @@ def getAddsTo(self, query, predicate):
125
198
def makeContent (self ):
126
199
summaryAddsTo = {}
127
200
if self .generateSummaries :
128
- summaryAddsTo = self .getAddsTo ("CaptureSummaryModels.ql" , helpers .summaryModelPredicate )
201
+ summaryAddsTo = self .getAddsTo (
202
+ "CaptureSummaryModels.ql" , helpers .summaryModelPredicate
203
+ )
129
204
130
205
sinkAddsTo = {}
131
206
if self .generateSinks :
132
- sinkAddsTo = self .getAddsTo ("CaptureSinkModels.ql" , helpers .sinkModelPredicate )
207
+ sinkAddsTo = self .getAddsTo (
208
+ "CaptureSinkModels.ql" , helpers .sinkModelPredicate
209
+ )
133
210
134
211
sourceAddsTo = {}
135
212
if self .generateSources :
136
- sourceAddsTo = self .getAddsTo ("CaptureSourceModels.ql" , helpers .sourceModelPredicate )
213
+ sourceAddsTo = self .getAddsTo (
214
+ "CaptureSourceModels.ql" , helpers .sourceModelPredicate
215
+ )
137
216
138
217
neutralAddsTo = {}
139
218
if self .generateNeutrals :
140
- neutralAddsTo = self .getAddsTo ("CaptureNeutralModels.ql" , helpers .neutralModelPredicate )
219
+ neutralAddsTo = self .getAddsTo (
220
+ "CaptureNeutralModels.ql" , helpers .neutralModelPredicate
221
+ )
141
222
142
223
return helpers .merge (summaryAddsTo , sinkAddsTo , sourceAddsTo , neutralAddsTo )
143
224
144
225
def makeTypeBasedContent (self ):
145
226
if self .generateTypeBasedSummaries :
146
- typeBasedSummaryAddsTo = self .getAddsTo ("CaptureTypeBasedSummaryModels.ql" , helpers .summaryModelPredicate )
227
+ typeBasedSummaryAddsTo = self .getAddsTo (
228
+ "CaptureTypeBasedSummaryModels.ql" , helpers .summaryModelPredicate
229
+ )
147
230
else :
148
- typeBasedSummaryAddsTo = { }
231
+ typeBasedSummaryAddsTo = {}
149
232
150
233
return typeBasedSummaryAddsTo
151
234
@@ -156,13 +239,14 @@ def save(self, extensions, extension):
156
239
{0}"""
157
240
for entry in extensions :
158
241
# 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
+ )
160
245
target = os .path .join (self .generatedFrameworks , sanitizedEntry + extension )
161
246
with open (target , "w" ) as f :
162
247
f .write (extensionTemplate .format (extensions [entry ]))
163
248
print ("Models as data extensions written to " + target )
164
249
165
-
166
250
def run (self ):
167
251
content = self .makeContent ()
168
252
typeBasedContent = self .makeTypeBasedContent ()
@@ -171,14 +255,17 @@ def run(self):
171
255
print ("Models as data extensions generated, but not written to file." )
172
256
sys .exit (0 )
173
257
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
+ ):
178
264
self .save (content , ".model.yml" )
179
265
180
266
if self .generateTypeBasedSummaries :
181
267
self .save (typeBasedContent , ".typebased.model.yml" )
182
268
183
- if __name__ == '__main__' :
269
+
270
+ if __name__ == "__main__" :
184
271
Generator .make ().run ()
0 commit comments