Copy more files into the synthetic variant analysis pack
Before this change and starting with CLI v2.14.3, if you wanted to run a variant analysis query and the pack it is contained in has at least one query that contains an extensible predicate, this would be an error. The reason is that v2.14.3 introduced deep validation for data extensions. We are not copying the query that contains an extensible predicate to the synthetic pack we are generating. This means that deep validation will fail because there will be extensions that target the missing extensible predicate. This change avoids the problem by copying any query files that contain extensible predicates to the synthetic pack. It uses the internal `generate extensible-predicate-metadata` command to discover which query files contain extensible predicates and copies them.
This commit is contained in:
@@ -123,6 +123,15 @@ export type ResolveExtensionsResult = {
|
||||
};
|
||||
};
|
||||
|
||||
export type GenerateExtensiblePredicateMetadataResult = {
|
||||
// There are other properties in this object, but they are
|
||||
// not relevant for its use in the extension, so we omit them.
|
||||
extensible_predicates: Array<{
|
||||
// pack relative path
|
||||
path: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The expected output of `codeql resolve qlref`.
|
||||
*/
|
||||
@@ -1458,6 +1467,17 @@ export class CodeQLCliServer implements Disposable {
|
||||
);
|
||||
}
|
||||
|
||||
async generateExtensiblePredicateMetadata(
|
||||
packRoot: string,
|
||||
): Promise<GenerateExtensiblePredicateMetadataResult> {
|
||||
return await this.runJsonCodeQlCliCommand(
|
||||
["generate", "extensible-predicate-metadata"],
|
||||
[packRoot],
|
||||
"Generating extensible predicate metadata",
|
||||
{ addFormat: false },
|
||||
);
|
||||
}
|
||||
|
||||
public async getVersion() {
|
||||
if (!this._version) {
|
||||
try {
|
||||
@@ -1830,6 +1850,14 @@ export class CliVersionConstraint {
|
||||
*/
|
||||
public static CLI_VERSION_WITH_QUICK_EVAL_COUNT = new SemVer("2.13.3");
|
||||
|
||||
/**
|
||||
* CLI version where the `generate extensible-predicate-metadata`
|
||||
* command was implemented.
|
||||
*/
|
||||
public static CLI_VERSION_WITH_EXTENSIBLE_PREDICATE_METADATA = new SemVer(
|
||||
"2.14.3",
|
||||
);
|
||||
|
||||
/**
|
||||
* CLI version where the langauge server supports visisbility change notifications.
|
||||
*/
|
||||
@@ -1916,4 +1944,10 @@ export class CliVersionConstraint {
|
||||
CliVersionConstraint.CLI_VERSION_WITH_QUICK_EVAL_COUNT,
|
||||
);
|
||||
}
|
||||
|
||||
async supportsGenerateExtensiblePredicateMetadata() {
|
||||
return this.isVersionAtLeast(
|
||||
CliVersionConstraint.CLI_VERSION_WITH_EXTENSIBLE_PREDICATE_METADATA,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,22 @@ async function copyExistingQueryPack(
|
||||
) {
|
||||
const toCopy = await cliServer.packPacklist(originalPackRoot, false);
|
||||
|
||||
// Also include query files that contain extensible predicates. These query files are not
|
||||
// needed for the query to run, but they are needed for the query pack to pass deep validation
|
||||
// of data extensions.
|
||||
if (
|
||||
await cliServer.cliConstraints.supportsGenerateExtensiblePredicateMetadata()
|
||||
) {
|
||||
const metadata = await cliServer.generateExtensiblePredicateMetadata(
|
||||
originalPackRoot,
|
||||
);
|
||||
metadata.extensible_predicates.forEach((predicate) => {
|
||||
if (predicate.path.endsWith(".ql")) {
|
||||
toCopy.push(join(originalPackRoot, predicate.path));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[
|
||||
// also copy the lock file (either new name or old name) and the query file itself. These are not included in the packlist.
|
||||
...QLPACK_LOCK_FILENAMES.map((f) => join(originalPackRoot, f)),
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CancellationTokenSource, commands, Uri, window } from "vscode";
|
||||
import { extLogger } from "../../../../src/common/logging/vscode";
|
||||
import { setRemoteControllerRepo } from "../../../../src/config";
|
||||
import * as ghApiClient from "../../../../src/variant-analysis/gh-api/gh-api-client";
|
||||
import { join } from "path";
|
||||
import { isAbsolute, join } from "path";
|
||||
|
||||
import { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager";
|
||||
import {
|
||||
@@ -275,13 +275,52 @@ describe("Variant Analysis Manager", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Test running core java queries to ensure that we can compile queries in packs
|
||||
// that contain queries with extensible predicates
|
||||
it("should run a remote query that is part of the java pack", async () => {
|
||||
if (
|
||||
!(await cli.cliConstraints.supportsGenerateExtensiblePredicateMetadata())
|
||||
) {
|
||||
console.log(
|
||||
`Skipping test because generating extensible predicate metadata was only introduced in CLI version ${CliVersionConstraint.CLI_VERSION_WITH_EXTENSIBLE_PREDICATE_METADATA}.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env.TEST_CODEQL_PATH) {
|
||||
fail(
|
||||
"TEST_CODEQL_PATH environment variable not set. It should point to the absolute path to a checkout of the codeql repository.",
|
||||
);
|
||||
}
|
||||
|
||||
const queryToRun =
|
||||
"Security/CWE/CWE-020/ExternalAPIsUsedWithUntrustedData.ql";
|
||||
const extraQuery = "Telemetry/ExtractorInformation.ql";
|
||||
|
||||
await doVariantAnalysisTest({
|
||||
queryPath: join(
|
||||
process.env.TEST_CODEQL_PATH,
|
||||
"java/ql/src",
|
||||
queryToRun,
|
||||
),
|
||||
expectedPackName: "codeql/java-queries",
|
||||
filesThatExist: [queryToRun, extraQuery],
|
||||
filesThatDoNotExist: [],
|
||||
qlxFilesThatExist: [],
|
||||
dependenciesToCheck: ["codeql/java-all"],
|
||||
// Don't check the version since it will be the same version
|
||||
checkVersion: false,
|
||||
});
|
||||
});
|
||||
|
||||
async function doVariantAnalysisTest({
|
||||
queryPath,
|
||||
expectedPackName,
|
||||
filesThatExist,
|
||||
qlxFilesThatExist,
|
||||
filesThatDoNotExist,
|
||||
dependenciesToCheck = ["codeql/javascript-all"],
|
||||
dependenciesToCheck = ["codeql/java-all"],
|
||||
checkVersion = true,
|
||||
}: {
|
||||
queryPath: string;
|
||||
expectedPackName: string;
|
||||
@@ -289,6 +328,7 @@ describe("Variant Analysis Manager", () => {
|
||||
qlxFilesThatExist: string[];
|
||||
filesThatDoNotExist: string[];
|
||||
dependenciesToCheck?: string[];
|
||||
checkVersion?: boolean;
|
||||
}) {
|
||||
const fileUri = getFile(queryPath);
|
||||
await variantAnalysisManager.runVariantAnalysis(
|
||||
@@ -339,7 +379,9 @@ describe("Variant Analysis Manager", () => {
|
||||
packFS.fileContents(packFileName).toString("utf-8"),
|
||||
);
|
||||
expect(qlpackContents.name).toEqual(expectedPackName);
|
||||
expect(qlpackContents.version).toEqual("0.0.0");
|
||||
if (checkVersion) {
|
||||
expect(qlpackContents.version).toEqual("0.0.0");
|
||||
}
|
||||
expect(qlpackContents.dependencies?.["codeql/javascript-all"]).toEqual(
|
||||
"*",
|
||||
);
|
||||
@@ -357,7 +399,11 @@ describe("Variant Analysis Manager", () => {
|
||||
}
|
||||
|
||||
function getFile(file: string): Uri {
|
||||
return Uri.file(join(baseDir, file));
|
||||
if (isAbsolute(file)) {
|
||||
return Uri.file(file);
|
||||
} else {
|
||||
return Uri.file(join(baseDir, file));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user