Use a single SARIF-compatible query instead of two separate queries

This commit is contained in:
Koen Vlaswinkel
2023-05-31 12:32:41 +02:00
parent 5c81671e67
commit a117e09796
10 changed files with 185 additions and 207 deletions

View File

@@ -37,7 +37,7 @@ export async function getAutoModelUsages({
const cancellationTokenSource = new CancellationTokenSource();
const queryResult = await runQuery("usagesQuery", {
const queryResult = await runQuery({
cliServer,
queryRunner,
queryStorageDir,

View File

@@ -7,9 +7,9 @@ export function decodeBqrsToExternalApiUsages(
const methodsByApiName = new Map<string, ExternalApiUsage>();
chunk?.tuples.forEach((tuple) => {
const signature = tuple[0] as string;
const supported = tuple[1] as boolean;
const usage = tuple[2] as Call;
const usage = tuple[0] as Call;
const signature = tuple[1] as string;
const supported = (tuple[2] as string) === "true";
const [packageWithType, methodDeclaration] = signature.split("#");

View File

@@ -243,7 +243,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
const cancellationTokenSource = new CancellationTokenSource();
try {
const queryResult = await runQuery("mainQuery", {
const queryResult = await runQuery({
cliServer: this.cliServer,
queryRunner: this.queryRunner,
databaseItem: this.databaseItem,

View File

@@ -16,7 +16,6 @@ import { QueryResultType } from "../pure/new-messages";
import { join } from "path";
import { redactableError } from "../pure/errors";
import { QueryLanguage } from "../common/query-language";
import { Query } from "./queries/query";
export type RunQueryOptions = {
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">;
@@ -28,17 +27,14 @@ export type RunQueryOptions = {
token: CancellationToken;
};
export async function runQuery(
queryName: keyof Omit<Query, "dependencies">,
{
cliServer,
queryRunner,
databaseItem,
queryStorageDir,
progress,
token,
}: RunQueryOptions,
): Promise<CoreCompletedQuery | undefined> {
export async function runQuery({
cliServer,
queryRunner,
databaseItem,
queryStorageDir,
progress,
token,
}: RunQueryOptions): Promise<CoreCompletedQuery | undefined> {
// The below code is temporary to allow for rapid prototyping of the queries. Once the queries are stabilized, we will
// move these queries into the `github/codeql` repository and use them like any other contextual (e.g. AST) queries.
// This is intentionally not pretty code, as it will be removed soon.
@@ -55,7 +51,7 @@ export async function runQuery(
const queryDir = (await dir({ unsafeCleanup: true })).path;
const queryFile = join(queryDir, "FetchExternalApis.ql");
await writeFile(queryFile, query[queryName], "utf8");
await writeFile(queryFile, query.mainQuery, "utf8");
if (query.dependencies) {
for (const [filename, contents] of Object.entries(query.dependencies)) {

View File

@@ -2,52 +2,31 @@ import { Query } from "./query";
export const fetchExternalApisQuery: Query = {
mainQuery: `/**
* @name Usage of APIs coming from external libraries
* @description A list of 3rd party APIs used in the codebase.
* @tags telemetry
* @id cs/telemetry/fetch-external-apis
*/
* @name Usage of APIs coming from external libraries
* @description A list of 3rd party APIs used in the codebase.
* @tags telemetry
* @kind problem
* @id cs/telemetry/fetch-external-apis
*/
import csharp
import ExternalApi
private Call aUsage(ExternalApi api) {
result.getTarget().getUnboundDeclaration() = api
}
private Call aUsage(ExternalApi api) { result.getTarget().getUnboundDeclaration() = api }
private boolean isSupported(ExternalApi api) {
api.isSupported() and result = true
or
not api.isSupported() and
result = false
api.isSupported() and result = true
or
not api.isSupported() and
result = false
}
from ExternalApi api, string apiName, boolean supported, Call usage
where
apiName = api.getApiName() and
supported = isSupported(api) and
usage = aUsage(api)
select apiName, supported, usage
`,
usagesQuery: `/**
* @name Usage of APIs coming from external libraries
* @description A list of 3rd party APIs used in the codebase.
* @kind problem
* @id cs/telemetry/fetch-external-api-usages
*/
import csharp
import ExternalApi
private Call aUsage(ExternalApi api) {
result.getTarget().getUnboundDeclaration() = api
}
from ExternalApi api, string apiName, Call usage
where
apiName = api.getApiName() and
usage = aUsage(api)
select usage, apiName
apiName = api.getApiName() and
supported = isSupported(api) and
usage = aUsage(api)
select usage, apiName, supported.toString(), "supported"
`,
dependencies: {
"ExternalApi.qll": `/** Provides classes and predicates related to handling APIs from external libraries. */

View File

@@ -5,6 +5,7 @@ export const fetchExternalApisQuery: Query = {
* @name Usage of APIs coming from external libraries
* @description A list of 3rd party APIs used in the codebase. Excludes test and generated code.
* @tags telemetry
* @kind problem
* @id java/telemetry/fetch-external-apis
*/
@@ -27,28 +28,7 @@ where
apiName = api.getApiName() and
supported = isSupported(api) and
usage = aUsage(api)
select apiName, supported, usage
`,
usagesQuery: `/**
* @name Usage of APIs coming from external libraries
* @description A list of 3rd party APIs used in the codebase. Excludes test and generated code.
* @kind problem
* @id java/telemetry/fetch-external-api-usages
*/
import java
import ExternalApi
private Call aUsage(ExternalApi api) {
result.getCallee().getSourceDeclaration() = api and
not result.getFile() instanceof GeneratedFile
}
from ExternalApi api, string apiName, Call usage
where
apiName = api.getApiName() and
usage = aUsage(api)
select usage, apiName
select usage, apiName, supported.toString(), "supported"
`,
dependencies: {
"ExternalApi.qll": `/** Provides classes and predicates related to handling APIs from external libraries. */

View File

@@ -1,6 +1,14 @@
export type Query = {
/**
* The main query.
*
* It should select all usages of external APIs, and return the following result pattern:
* - usage: the usage of the external API. This is an entity.
* - apiName: the name of the external API. This is a string.
* - supported: whether the external API is supported by the extension. This should be a string representation of a boolean to satify the result pattern for a problem query.
* - "supported": a string literal. This is required to make the query a valid problem query.
*/
mainQuery: string;
usagesQuery: string;
dependencies?: {
[filename: string]: string;
};

View File

@@ -115,7 +115,7 @@ export type BqrsKind =
| "Entity";
interface BqrsColumn {
name: string;
name?: string;
kind: BqrsKind;
}
export interface DecodedBqrsChunk {

View File

@@ -4,126 +4,13 @@ import { DecodedBqrsChunk } from "../../../src/pure/bqrs-cli-types";
describe("decodeBqrsToExternalApiUsages", () => {
const chunk: DecodedBqrsChunk = {
columns: [
{ name: "apiName", kind: "String" },
{ name: "supported", kind: "Boolean" },
{ name: "usage", kind: "Entity" },
{ name: "apiName", kind: "String" },
{ kind: "String" },
{ kind: "String" },
],
tuples: [
[
"java.io.PrintStream#println(String)",
true,
{
label: "println(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 29,
startColumn: 9,
endLine: 29,
endColumn: 49,
},
},
],
[
"org.springframework.boot.SpringApplication#run(Class,String[])",
false,
{
label: "run(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/Sql2oExampleApplication.java",
startLine: 9,
startColumn: 9,
endLine: 9,
endColumn: 66,
},
},
],
[
"org.sql2o.Connection#createQuery(String)",
true,
{
label: "createQuery(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 15,
startColumn: 13,
endLine: 15,
endColumn: 56,
},
},
],
[
"org.sql2o.Connection#createQuery(String)",
true,
{
label: "createQuery(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 26,
startColumn: 13,
endLine: 26,
endColumn: 39,
},
},
],
[
"org.sql2o.Query#executeScalar(Class)",
true,
{
label: "executeScalar(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 15,
startColumn: 13,
endLine: 15,
endColumn: 85,
},
},
],
[
"org.sql2o.Query#executeScalar(Class)",
true,
{
label: "executeScalar(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 26,
startColumn: 13,
endLine: 26,
endColumn: 68,
},
},
],
[
"org.sql2o.Sql2o#open()",
true,
{
label: "open(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 14,
startColumn: 24,
endLine: 14,
endColumn: 35,
},
},
],
[
"org.sql2o.Sql2o#open()",
true,
{
label: "open(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 25,
startColumn: 24,
endLine: 25,
endColumn: 35,
},
},
],
[
"org.sql2o.Sql2o#Sql2o(String,String,String)",
true,
{
label: "new Sql2o(...)",
url: {
@@ -134,10 +21,56 @@ describe("decodeBqrsToExternalApiUsages", () => {
endColumn: 88,
},
},
"org.sql2o.Sql2o#Sql2o(String,String,String)",
"false",
"supported",
],
[
{
label: "open(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 14,
startColumn: 24,
endLine: 14,
endColumn: 35,
},
},
"org.sql2o.Sql2o#open()",
"false",
"supported",
],
[
{
label: "executeScalar(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 15,
startColumn: 13,
endLine: 15,
endColumn: 85,
},
},
"org.sql2o.Query#executeScalar(Class)",
"false",
"supported",
],
[
{
label: "createQuery(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 15,
startColumn: 13,
endLine: 15,
endColumn: 56,
},
},
"org.sql2o.Connection#createQuery(String)",
"false",
"supported",
],
[
"org.sql2o.Sql2o#Sql2o(String)",
true,
{
label: "new Sql2o(...)",
url: {
@@ -148,6 +81,84 @@ describe("decodeBqrsToExternalApiUsages", () => {
endColumn: 36,
},
},
"org.sql2o.Sql2o#Sql2o(String)",
"false",
"supported",
],
[
{
label: "open(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 25,
startColumn: 24,
endLine: 25,
endColumn: 35,
},
},
"org.sql2o.Sql2o#open()",
"false",
"supported",
],
[
{
label: "executeScalar(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 26,
startColumn: 13,
endLine: 26,
endColumn: 68,
},
},
"org.sql2o.Query#executeScalar(Class)",
"false",
"supported",
],
[
{
label: "createQuery(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 26,
startColumn: 13,
endLine: 26,
endColumn: 39,
},
},
"org.sql2o.Connection#createQuery(String)",
"false",
"supported",
],
[
{
label: "println(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/HelloController.java",
startLine: 29,
startColumn: 9,
endLine: 29,
endColumn: 49,
},
},
"java.io.PrintStream#println(String)",
"true",
"supported",
],
[
{
label: "run(...)",
url: {
uri: "file:/home/runner/work/sql2o-example/sql2o-example/src/main/java/org/example/Sql2oExampleApplication.java",
startLine: 9,
startColumn: 9,
endLine: 9,
endColumn: 66,
},
},
"org.springframework.boot.SpringApplication#run(Class,String[])",
"false",
"supported",
],
],
};

View File

@@ -10,7 +10,7 @@ import { QueryResultType } from "../../../../src/pure/new-messages";
import { readdir, readFile } from "fs-extra";
import { load } from "js-yaml";
import { dirname, join } from "path";
import { fetchExternalApiQueries } from "../../../../src/data-extensions-editor/queries/index";
import { fetchExternalApiQueries } from "../../../../src/data-extensions-editor/queries";
import * as helpers from "../../../../src/helpers";
import { RedactableError } from "../../../../src/pure/errors";
@@ -66,7 +66,7 @@ describe("runQuery", () => {
onCancellationRequested: jest.fn(),
},
};
const result = await runQuery("mainQuery", options);
const result = await runQuery(options);
expect(result?.resultType).toEqual(QueryResultType.SUCCESS);
@@ -161,18 +161,20 @@ describe("readQueryResults", () => {
name: "#select",
rows: 10,
columns: [
{ name: "apiName", kind: "s" },
{ name: "supported", kind: "b" },
{ name: "usage", kind: "e" },
{ name: "apiName", kind: "s" },
{ kind: "s" },
{ kind: "s" },
],
},
{
name: "#select2",
rows: 10,
columns: [
{ name: "apiName", kind: "s" },
{ name: "supported", kind: "b" },
{ name: "usage", kind: "e" },
{ name: "apiName", kind: "s" },
{ kind: "s" },
{ kind: "s" },
],
},
],
@@ -191,9 +193,10 @@ describe("readQueryResults", () => {
name: "#select",
rows: 10,
columns: [
{ name: "apiName", kind: "s" },
{ name: "supported", kind: "b" },
{ name: "usage", kind: "e" },
{ name: "apiName", kind: "s" },
{ kind: "s" },
{ kind: "s" },
],
},
],
@@ -201,9 +204,10 @@ describe("readQueryResults", () => {
});
const decodedResultSet = {
columns: [
{ name: "apiName", kind: "String" },
{ name: "supported", kind: "Boolean" },
{ name: "usage", kind: "Entity" },
{ name: "usage", kind: "e" },
{ name: "apiName", kind: "s" },
{ kind: "s" },
{ kind: "s" },
],
tuples: [
[