@@ -5,9 +5,27 @@ import { dump } from "js-yaml";
55import { prepareExternalApiQuery } from "./external-api-usage-queries" ;
66import { CodeQLCliServer } from "../codeql-cli/cli" ;
77import { showLlmGeneration } from "../config" ;
8+ import { Mode } from "./shared/mode" ;
9+ import { resolveQueriesFromPacks } from "../local-queries" ;
10+ import { modeTag } from "./mode-tag" ;
11+
12+ export const syntheticQueryPackName = "codeql/external-api-usage" ;
813
914/**
10- * setUpPack sets up a directory to use for the data extension editor queries.
15+ * setUpPack sets up a directory to use for the data extension editor queries if required.
16+ *
17+ * There are two cases (example language is Java):
18+ * - In case the queries are present in the codeql/java-queries, we don't need to write our own queries
19+ * to disk. We still need to create a synthetic query pack so we can pass the queryDir to the query
20+ * resolver without caring about whether the queries are present in the pack or not.
21+ * - In case the queries are not present in the codeql/java-queries, we need to write our own queries
22+ * to disk. We will create a synthetic query pack and install its dependencies so it is fully independent
23+ * and we can simply pass it through when resolving the queries.
24+ *
25+ * These steps together ensure that later steps of the process don't need to keep track of whether the queries
26+ * are present in codeql/java-queries or in our own query pack. They just need to resolve the query.
27+ *
28+ * @param cliServer The CodeQL CLI server to use.
1129 * @param queryDir The directory to set up.
1230 * @param language The language to use for the queries.
1331 * @returns true if the setup was successful, false otherwise.
@@ -17,34 +35,104 @@ export async function setUpPack(
1735 queryDir : string ,
1836 language : QueryLanguage ,
1937) : Promise < boolean > {
20- // Create the external API query
21- const externalApiQuerySuccess = await prepareExternalApiQuery (
22- queryDir ,
38+ // Download the required query packs
39+ await cliServer . packDownload ( [ `codeql/${ language } -queries` ] ) ;
40+
41+ // We'll only check if the application mode query exists in the pack and assume that if it does,
42+ // the framework mode query will also exist.
43+ const applicationModeQuery = await resolveEndpointsQuery (
44+ cliServer ,
2345 language ,
46+ Mode . Application ,
47+ [ ] ,
48+ [ ] ,
2449 ) ;
25- if ( ! externalApiQuerySuccess ) {
26- return false ;
27- }
2850
29- // Set up a synthetic pack so that the query can be resolved later.
30- const syntheticQueryPack = {
31- name : "codeql/external-api-usage" ,
32- version : "0.0.0" ,
33- dependencies : {
34- [ `codeql/ ${ language } -all` ] : "* " ,
35- } ,
36- } ;
51+ if ( applicationModeQuery ) {
52+ // Set up a synthetic pack so CodeQL doesn't crash later when we try
53+ // to resolve a query within this directory
54+ const syntheticQueryPack = {
55+ name : syntheticQueryPackName ,
56+ version : "0.0.0 " ,
57+ dependencies : { } ,
58+ } ;
3759
38- const qlpackFile = join ( queryDir , "codeql-pack.yml" ) ;
39- await writeFile ( qlpackFile , dump ( syntheticQueryPack ) , "utf8" ) ;
40- await cliServer . packInstall ( queryDir ) ;
60+ const qlpackFile = join ( queryDir , "codeql-pack.yml" ) ;
61+ await writeFile ( qlpackFile , dump ( syntheticQueryPack ) , "utf8" ) ;
62+ } else {
63+ // If we can't resolve the query, we need to write them to desk ourselves.
64+ const externalApiQuerySuccess = await prepareExternalApiQuery (
65+ queryDir ,
66+ language ,
67+ ) ;
68+ if ( ! externalApiQuerySuccess ) {
69+ return false ;
70+ }
4171
42- // Install the other needed query packs
43- await cliServer . packDownload ( [ `codeql/${ language } -queries` ] ) ;
72+ // Set up a synthetic pack so that the query can be resolved later.
73+ const syntheticQueryPack = {
74+ name : syntheticQueryPackName ,
75+ version : "0.0.0" ,
76+ dependencies : {
77+ [ `codeql/${ language } -all` ] : "*" ,
78+ } ,
79+ } ;
80+
81+ const qlpackFile = join ( queryDir , "codeql-pack.yml" ) ;
82+ await writeFile ( qlpackFile , dump ( syntheticQueryPack ) , "utf8" ) ;
83+ await cliServer . packInstall ( queryDir ) ;
84+ }
4485
86+ // Download any other required packs
4587 if ( language === "java" && showLlmGeneration ( ) ) {
4688 await cliServer . packDownload ( [ `codeql/${ language } -automodel-queries` ] ) ;
4789 }
4890
4991 return true ;
5092}
93+
94+ /**
95+ * Resolve the query path to the model editor endpoints query. All queries are tagged like this:
96+ * modeleditor endpoints <mode>
97+ * Example: modeleditor endpoints framework-mode
98+ *
99+ * @param cliServer The CodeQL CLI server to use.
100+ * @param language The language of the query pack to use.
101+ * @param mode The mode to resolve the query for.
102+ * @param additionalPackNames Additional pack names to search.
103+ * @param additionalPackPaths Additional pack paths to search.
104+ */
105+ export async function resolveEndpointsQuery (
106+ cliServer : CodeQLCliServer ,
107+ language : string ,
108+ mode : Mode ,
109+ additionalPackNames : string [ ] = [ ] ,
110+ additionalPackPaths : string [ ] = [ ] ,
111+ ) : Promise < string | undefined > {
112+ const packsToSearch = [ `codeql/${ language } -queries` , ...additionalPackNames ] ;
113+
114+ // First, resolve the query that we want to run.
115+ // All queries are tagged like this:
116+ // internal extract automodel <mode> <queryTag>
117+ // Example: internal extract automodel framework-mode candidates
118+ const queries = await resolveQueriesFromPacks (
119+ cliServer ,
120+ packsToSearch ,
121+ {
122+ kind : "table" ,
123+ "tags contain all" : [ "modeleditor" , "endpoints" , modeTag ( mode ) ] ,
124+ } ,
125+ additionalPackPaths ,
126+ ) ;
127+ if ( queries . length > 1 ) {
128+ throw new Error (
129+ `Found multiple endpoints queries for ${ mode } . Can't continue` ,
130+ ) ;
131+ }
132+
133+ if ( queries . length === 0 ) {
134+ return undefined ;
135+ }
136+
137+ return queries [ 0 ] ;
138+ }
0 commit comments