1313class Generator :
1414 def __init__ (self , language ):
1515 self .language = language
16+ self .generateSinks = False
17+ self .generateSources = False
18+ self .generateSummaries = False
19+ self .dryRun = False
1620
1721 def printHelp (self ):
1822 print (f"""Usage:
@@ -38,99 +42,97 @@ def printHelp(self):
3842Requirements: `codeql` should both appear on your path.
3943 """ )
4044
41- generator = Generator (language )
4245
43- if any (s == "--help" for s in sys .argv ):
44- generator .printHelp ()
45- sys .exit (0 )
46-
47- generateSinks = False
48- generateSources = False
49- generateSummaries = False
50- dryRun = False
51-
52- if "--with-sinks" in sys .argv :
53- sys .argv .remove ("--with-sinks" )
54- generateSinks = True
55-
56- if "--with-sources" in sys .argv :
57- sys .argv .remove ("--with-sources" )
58- generateSources = True
59-
60- if "--with-summaries" in sys .argv :
61- sys .argv .remove ("--with-summaries" )
62- generateSummaries = True
63-
64- if "--dry-run" in sys .argv :
65- sys .argv .remove ("--dry-run" )
66- dryRun = True
67-
68- if not generateSinks and not generateSources and not generateSummaries :
69- generateSinks = generateSources = generateSummaries = True
70-
71- if len (sys .argv ) != 3 :
72- generator .printHelp ()
73- sys .exit (1 )
74-
75- codeQlRoot = subprocess .check_output (
76- ["git" , "rev-parse" , "--show-toplevel" ]).decode ("utf-8" ).strip ()
77- targetQll = sys .argv [2 ]
78- if not targetQll .endswith (".qll" ):
79- targetQll += ".qll"
80- filename = os .path .basename (targetQll )
81- shortname = filename [:- 4 ]
82- generatedFrameworks = os .path .join (
83- codeQlRoot , f"{ language } /ql/lib/semmle/code/{ language } /frameworks/" )
84- frameworkTarget = os .path .join (generatedFrameworks , targetQll )
85-
86- workDir = tempfile .mkdtemp ()
87- os .makedirs (generatedFrameworks , exist_ok = True )
88-
89-
90- def runQuery (infoMessage , query ):
91- print ("########## Querying " + infoMessage + "..." )
92- database = sys .argv [1 ]
93- queryFile = os .path .join (os .path .dirname (
94- __file__ ), query )
95- resultBqrs = os .path .join (workDir , "out.bqrs" )
96- cmd = ['codeql' , 'query' , 'run' , queryFile , '--database' ,
97- database , '--output' , resultBqrs , '--threads' , '8' ]
98-
99- ret = subprocess .call (cmd )
100- if ret != 0 :
101- print ("Failed to generate " + infoMessage +
102- ". Failed command was: " + shlex .join (cmd ))
103- sys .exit (1 )
104- return readRows (resultBqrs )
105-
106-
107- def readRows (bqrsFile ):
108- generatedJson = os .path .join (workDir , "out.json" )
109- cmd = ['codeql' , 'bqrs' , 'decode' , bqrsFile ,
110- '--format=json' , '--output' , generatedJson ]
111- ret = subprocess .call (cmd )
112- if ret != 0 :
113- print ("Failed to decode BQRS. Failed command was: " + shlex .join (cmd ))
114- sys .exit (1 )
115-
116- with open (generatedJson ) as f :
117- results = json .load (f )
118-
119- try :
120- results ['#select' ]['tuples' ]
121- except KeyError :
122- print ('Unexpected JSON output - no tuples found' )
123- exit (1 )
124-
125- rows = ""
126- for (row ) in results ['#select' ]['tuples' ]:
127- rows += " \" " + row [0 ] + "\" ,\n "
128-
129- return rows [:- 2 ]
130-
131-
132- def asCsvModel (superclass , kind , rows ):
133- classTemplate = """
46+ def setenvironment (self , target , database ):
47+ self .codeQlRoot = subprocess .check_output (["git" , "rev-parse" , "--show-toplevel" ]).decode ("utf-8" ).strip ()
48+ if not target .endswith (".qll" ):
49+ target += ".qll"
50+ self .filename = os .path .basename (target )
51+ self .shortname = self .filename [:- 4 ]
52+ self .database = database
53+ self .generatedFrameworks = os .path .join (
54+ self .codeQlRoot , f"{ self .language } /ql/lib/semmle/code/{ self .language } /frameworks/" )
55+ self .frameworkTarget = os .path .join (self .generatedFrameworks , target )
56+
57+ self .workDir = tempfile .mkdtemp ()
58+ os .makedirs (self .generatedFrameworks , exist_ok = True )
59+
60+ @staticmethod
61+ def make (language ):
62+ generator = Generator (language )
63+ if any (s == "--help" for s in sys .argv ):
64+ generator .printHelp ()
65+ sys .exit (0 )
66+
67+ if "--with-sinks" in sys .argv :
68+ sys .argv .remove ("--with-sinks" )
69+ generator .generateSinks = True
70+
71+ if "--with-sources" in sys .argv :
72+ sys .argv .remove ("--with-sources" )
73+ generator .generateSources = True
74+
75+ if "--with-summaries" in sys .argv :
76+ sys .argv .remove ("--with-summaries" )
77+ generator .generateSummaries = True
78+
79+ if "--dry-run" in sys .argv :
80+ sys .argv .remove ("--dry-run" )
81+ generator .dryRun = True
82+
83+ if not generator .generateSinks and not generator .generateSources and not generator .generateSummaries :
84+ generator .generateSinks = generator .generateSources = generator .generateSummaries = True
85+
86+ if len (sys .argv ) != 3 :
87+ generator .printHelp ()
88+ sys .exit (1 )
89+
90+ generator .setenvironment (sys .argv [2 ], sys .argv [1 ])
91+ return generator
92+
93+ def runQuery (self , infoMessage , query ):
94+ print ("########## Querying " + infoMessage + "..." )
95+ queryFile = os .path .join (os .path .dirname (
96+ __file__ ), query )
97+ resultBqrs = os .path .join (self .workDir , "out.bqrs" )
98+ cmd = ['codeql' , 'query' , 'run' , queryFile , '--database' ,
99+ self .database , '--output' , resultBqrs , '--threads' , '8' ]
100+
101+ ret = subprocess .call (cmd )
102+ if ret != 0 :
103+ print ("Failed to generate " + infoMessage +
104+ ". Failed command was: " + shlex .join (cmd ))
105+ sys .exit (1 )
106+ return self .readRows (resultBqrs )
107+
108+
109+ def readRows (self , bqrsFile ):
110+ generatedJson = os .path .join (self .workDir , "out.json" )
111+ cmd = ['codeql' , 'bqrs' , 'decode' , bqrsFile ,
112+ '--format=json' , '--output' , generatedJson ]
113+ ret = subprocess .call (cmd )
114+ if ret != 0 :
115+ print ("Failed to decode BQRS. Failed command was: " + shlex .join (cmd ))
116+ sys .exit (1 )
117+
118+ with open (generatedJson ) as f :
119+ results = json .load (f )
120+
121+ try :
122+ results ['#select' ]['tuples' ]
123+ except KeyError :
124+ print ('Unexpected JSON output - no tuples found' )
125+ exit (1 )
126+
127+ rows = ""
128+ for (row ) in results ['#select' ]['tuples' ]:
129+ rows += " \" " + row [0 ] + "\" ,\n "
130+
131+ return rows [:- 2 ]
132+
133+
134+ def asCsvModel (self , superclass , kind , rows ):
135+ classTemplate = """
134136private class {0}{1}Csv extends {2} {{
135137 override predicate row(string row) {{
136138 row =
@@ -139,54 +141,56 @@ def asCsvModel(superclass, kind, rows):
139141 ]
140142 }}
141143}}
142- """
143- if rows .strip () == "" :
144- return ""
145- return classTemplate .format (shortname [0 ].upper () + shortname [1 :], kind .capitalize (), superclass , rows )
144+ """
145+ if rows .strip () == "" :
146+ return ""
147+ return classTemplate .format (self .shortname [0 ].upper () + self .shortname [1 :], kind .capitalize (), superclass , rows )
148+
146149
150+ generator = Generator .make (language )
147151
148- if generateSummaries :
149- summaryRows = runQuery ("summary models" , "CaptureSummaryModels.ql" )
150- summaryCsv = asCsvModel ("SummaryModelCsv" , "summary" , summaryRows )
152+ if generator . generateSummaries :
153+ summaryRows = generator . runQuery ("summary models" , "CaptureSummaryModels.ql" )
154+ summaryCsv = generator . asCsvModel ("SummaryModelCsv" , "summary" , summaryRows )
151155else :
152156 summaryCsv = ""
153157
154- if generateSinks :
155- sinkRows = runQuery ("sink models" , "CaptureSinkModels.ql" )
156- sinkCsv = asCsvModel ("SinkModelCsv" , "sinks" , sinkRows )
158+ if generator . generateSinks :
159+ sinkRows = generator . runQuery ("sink models" , "CaptureSinkModels.ql" )
160+ sinkCsv = generator . asCsvModel ("SinkModelCsv" , "sinks" , sinkRows )
157161else :
158162 sinkCsv = ""
159163
160- if generateSources :
161- sourceRows = runQuery ("source models" , "CaptureSourceModels.ql" )
162- sourceCsv = asCsvModel ("SourceModelCsv" , "sources" , sourceRows )
164+ if generator . generateSources :
165+ sourceRows = generator . runQuery ("source models" , "CaptureSourceModels.ql" )
166+ sourceCsv = generator . asCsvModel ("SourceModelCsv" , "sources" , sourceRows )
163167else :
164168 sourceCsv = ""
165169
166170qllContents = f"""
167- /** Definitions of taint steps in the { shortname } framework */
171+ /** Definitions of taint steps in the { generator . shortname } framework */
168172
169- import { language }
170- private import semmle.code.{ language } .dataflow.ExternalFlow
173+ import { generator . language }
174+ private import semmle.code.{ generator . language } .dataflow.ExternalFlow
171175
172176{ sinkCsv }
173177{ sourceCsv }
174178{ summaryCsv }
175179
176180"""
177181
178- if dryRun :
182+ if generator . dryRun :
179183 print ("CSV Models generated, but not written to file." )
180184 sys .exit (0 )
181185
182- with open (frameworkTarget , "w" ) as frameworkQll :
186+ with open (generator . frameworkTarget , "w" ) as frameworkQll :
183187 frameworkQll .write (qllContents )
184188
185- cmd = ['codeql' , 'query' , 'format' , '--in-place' , frameworkTarget ]
189+ cmd = ['codeql' , 'query' , 'format' , '--in-place' , generator . frameworkTarget ]
186190ret = subprocess .call (cmd )
187191if ret != 0 :
188192 print ("Failed to format query. Failed command was: " + shlex .join (cmd ))
189193 sys .exit (1 )
190194
191195print ("" )
192- print ("CSV model written to " + frameworkTarget )
196+ print ("CSV model written to " + generator . frameworkTarget )
0 commit comments