11import os
22import glob
3- import shutil
3+ import json
4+ import logging
45import subprocess
56
7+ __HERE__ = os .path .dirname (os .path .abspath (__file__ ))
8+ __ROOT__ = os .path .abspath (os .path .join (__HERE__ , ".." ))
9+
610CODEQL_LOCATIONS = [
711 "codeql" ,
812 # gh cli
913 "gh codeql" ,
14+ ]
15+ # Actions
16+ CODEQL_LOCATIONS .extend (glob .glob ("/opt/hostedtoolcache/CodeQL/*/x64/codeql/codeql" ))
17+ # VSCode install
18+ CODEQL_LOCATIONS .extend (glob .glob (
19+ "/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/*/codeql/codeql"
20+ )),
21+ print (CODEQL_LOCATIONS )
22+
23+ CODEQL_DATABASE_LOCATIONS = [
24+ # local db
25+ ".codeql/db" ,
1026 # Actions
11- "/opt/hostedtoolcache/CodeQL/*/x64/codeql/codeql" ,
12- # VSCode install
13- "/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/*/codeql/codeql" ,
27+ "/home/runner/work/_temp/codeql_databases/" ,
1428]
1529
16- CODEQL_DATABASE_LOCATIONS = [".codeql/db" , "/home/runner/work/_temp/codeql_databases/" ]
30+ CODEQL_TEMP = os .path .join ("/tmp" , "codeqldepgraph" )
31+
32+ logger = logging .getLogger ("codeql" )
1733
1834
1935def find_codeql () -> str :
2036 """Find the CodeQL executable"""
2137 for codeql in CODEQL_LOCATIONS :
22- codeql = glob .glob (codeql )
23- # test if the glob found anything
24- if codeql :
25- # test command works
26- try :
27- subprocess .run ([codeql [0 ], "--version" ], stdout = subprocess .PIPE )
28- return codeql [0 ]
29- except Exception as err :
30- pass
38+ try :
39+ with open (os .devnull ) as null :
40+ subprocess .run ([codeql , "--version" ], stdout = null , stderr = null )
41+ return codeql
42+ except Exception as err :
43+ pass
44+ print (f" >> { codeql } " )
3145
3246 raise Exception ("Could not find CodeQL executable" )
3347
@@ -43,35 +57,94 @@ def find_codeql_databases() -> list:
4357
4458
4559class CodeQL :
46- def __init__ (self , database : str , language : str , codeql_path : str = None ):
60+ def __init__ (self , database : str , codeql_path : str = None ):
4761 self .database = database
48- self .language = language
4962
5063 self .codeql_path = codeql_path or find_codeql ()
51- self .databases = []
64+ self .language = self .find_language ()
65+
66+ self .pack_name = f"codeql-depgraph-{ self .language } "
67+
68+ if not os .path .exists (CODEQL_TEMP ):
69+ os .makedirs (CODEQL_TEMP )
5270
5371 def find_language (self ) -> str :
5472 """Find the language of the CodeQL database"""
55- # find db folder
56- db = glob . glob ( os . path . join ( self . database ), "db-*" )
73+ db = glob . glob ( os . path . join ( self . database , "db-*" ))
74+
5775 if db :
58- return db [0 ].split ("-" )[1 ]
76+ return db [0 ].split ("-" )[- 1 ]
5977
6078 raise Exception ("Could not find CodeQL database language" )
6179
62- def run_query (self , query ):
80+ def run (self , query ):
6381 """Run a CodeQL query"""
64- return subprocess . run (
65- [
66- self . codeql_path ,
67- " query" ,
68- "run" ,
69- query ,
70- "--database" ,
71- self . database ,
72- "--language" ,
73- self .language ,
74- ] ,
75- stdout = subprocess . PIPE ,
76- stderr = subprocess . PIPE ,
82+ local_query = os . path . join ( __ROOT__ , "ql" , self . language , query )
83+ if os . path . exists ( local_query ):
84+ full_query = local_query
85+ elif os . path . exists ( query ):
86+ full_query = query
87+ else :
88+ full_query = f" { self . pack_name } : { query } "
89+
90+ resultBqrs = os . path . join (
91+ self .database ,
92+ "results" ,
93+ self . pack_name ,
94+ query . replace ( ":" , "/" ). replace ( ".ql" , ".bqrs" ) ,
7795 )
96+
97+ cmd = [
98+ self .codeql_path ,
99+ "database" ,
100+ "run-queries" ,
101+ # use all the threads on system
102+ "--threads" ,
103+ "0" ,
104+ self .database ,
105+ full_query ,
106+ ]
107+ logger .debug (f"Running: { ' ' .join (cmd )} " )
108+
109+ output_std = os .path .join (CODEQL_TEMP , "runquery.txt" )
110+ with open (output_std , "wb" ) as std :
111+ subprocess .run (cmd , stdout = std , stderr = std )
112+
113+ return self .readRows (resultBqrs )
114+
115+ def readRows (self , bqrsFile : str ) -> list :
116+ generatedJson = os .path .join (CODEQL_TEMP , "out.json" )
117+ output_std = os .path .join (CODEQL_TEMP , "rows.txt" )
118+
119+ with open (output_std , "wb" ) as std :
120+ subprocess .run (
121+ [
122+ self .codeql_path ,
123+ "bqrs" ,
124+ "decode" ,
125+ "--format" ,
126+ "json" ,
127+ "--output" ,
128+ generatedJson ,
129+ bqrsFile ,
130+ ],
131+ stdout = std ,
132+ stderr = std ,
133+ )
134+
135+ with open (generatedJson ) as f :
136+ results = json .load (f )
137+
138+ try :
139+ results ["#select" ]["tuples" ]
140+ except KeyError :
141+ raise Exception ("Unexpected JSON output - no tuples found" )
142+
143+ rows = []
144+ for tup in results ["#select" ]["tuples" ]:
145+ rows .extend (tup )
146+
147+ return rows
148+
149+ def __str__ (self ) -> str :
150+ return f"CodeQL(language='{ self .language } ', path='{ self .database } ')"
0 commit comments