Merge branch 'main' into henrymercer/diagnostics-code-scanning-config

This commit is contained in:
Henry Mercer 2023-03-09 18:47:43 +00:00
commit 53f80edaf6
10 changed files with 53 additions and 41 deletions

View file

@ -7,6 +7,7 @@ queries:
# we include both even though one is a superset of the # we include both even though one is a superset of the
# other, because we're testing the parsing logic and # other, because we're testing the parsing logic and
# that the suites exist in the codeql bundle. # that the suites exist in the codeql bundle.
- uses: security-experimental
- uses: security-extended - uses: security-extended
- uses: security-and-quality - uses: security-and-quality
paths-ignore: paths-ignore:

View file

@ -2,7 +2,7 @@
## [UNRELEASED] ## [UNRELEASED]
No user facing changes. - Update default CodeQL bundle version to 2.12.4.
## 2.2.5 - 24 Feb 2023 ## 2.2.5 - 24 Feb 2023

View file

@ -1,6 +1,6 @@
{ {
"bundleVersion": "codeql-bundle-20230217", "bundleVersion": "codeql-bundle-20230304",
"cliVersion": "2.12.3", "cliVersion": "2.12.4",
"priorBundleVersion": "codeql-bundle-20230207", "priorBundleVersion": "codeql-bundle-20230217",
"priorCliVersion": "2.12.2" "priorCliVersion": "2.12.3"
} }

16
lib/feature-flags.js generated
View file

@ -45,22 +45,27 @@ exports.featureConfig = {
[Feature.DisableKotlinAnalysisEnabled]: { [Feature.DisableKotlinAnalysisEnabled]: {
envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS", envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS",
minimumVersion: undefined, minimumVersion: undefined,
defaultValue: false,
}, },
[Feature.CliConfigFileEnabled]: { [Feature.CliConfigFileEnabled]: {
envVar: "CODEQL_PASS_CONFIG_TO_CLI", envVar: "CODEQL_PASS_CONFIG_TO_CLI",
minimumVersion: "2.11.6", minimumVersion: "2.11.6",
defaultValue: true,
}, },
[Feature.ExportCodeScanningConfigEnabled]: { [Feature.ExportCodeScanningConfigEnabled]: {
envVar: "CODEQL_ACTION_EXPORT_CODE_SCANNING_CONFIG", envVar: "CODEQL_ACTION_EXPORT_CODE_SCANNING_CONFIG",
minimumVersion: codeql_1.CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG, minimumVersion: codeql_1.CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG,
defaultValue: false,
}, },
[Feature.MlPoweredQueriesEnabled]: { [Feature.MlPoweredQueriesEnabled]: {
envVar: "CODEQL_ML_POWERED_QUERIES", envVar: "CODEQL_ML_POWERED_QUERIES",
minimumVersion: "2.7.5", minimumVersion: "2.7.5",
defaultValue: false,
}, },
[Feature.UploadFailedSarifEnabled]: { [Feature.UploadFailedSarifEnabled]: {
envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF", envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF",
minimumVersion: "2.11.3", minimumVersion: "2.11.3",
defaultValue: false,
}, },
}; };
exports.FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json"; exports.FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
@ -109,7 +114,8 @@ class Features {
return true; return true;
} }
// Ask the GitHub API if the feature is enabled. // Ask the GitHub API if the feature is enabled.
return await this.gitHubFeatureFlags.getValue(feature); return ((await this.gitHubFeatureFlags.getValue(feature)) ??
exports.featureConfig[feature].defaultValue);
} }
} }
exports.Features = Features; exports.Features = Features;
@ -185,13 +191,13 @@ class GitHubFeatureFlags {
async getValue(feature) { async getValue(feature) {
const response = await this.getAllFeatures(); const response = await this.getAllFeatures();
if (response === undefined) { if (response === undefined) {
this.logger.debug(`No feature flags API response for ${feature}, considering it disabled.`); this.logger.debug(`No feature flags API response for ${feature}.`);
return false; return undefined;
} }
const features = response[feature]; const features = response[feature];
if (features === undefined) { if (features === undefined) {
this.logger.debug(`Feature '${feature}' undefined in API response, considering it disabled.`); this.logger.debug(`Feature '${feature}' undefined in API response.`);
return false; return undefined;
} }
return !!features; return !!features;
} }

File diff suppressed because one or more lines are too long

View file

@ -53,7 +53,7 @@ for (const variant of ALL_FEATURES_DISABLED_VARIANTS) {
const loggedMessages = []; const loggedMessages = [];
const features = setUpFeatureFlagTests(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages), variant.gitHubVersion); const features = setUpFeatureFlagTests(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages), variant.gitHubVersion);
for (const feature of Object.values(feature_flags_1.Feature)) { for (const feature of Object.values(feature_flags_1.Feature)) {
t.false(await features.getValue(feature, includeCodeQlIfRequired(feature))); t.deepEqual(await features.getValue(feature, includeCodeQlIfRequired(feature)), feature_flags_1.featureConfig[feature].defaultValue);
} }
t.assert(loggedMessages.find((v) => v.type === "debug" && t.assert(loggedMessages.find((v) => v.type === "debug" &&
v.message === v.message ===
@ -61,26 +61,26 @@ for (const variant of ALL_FEATURES_DISABLED_VARIANTS) {
}); });
}); });
} }
(0, ava_1.default)("API response missing", async (t) => { (0, ava_1.default)("API response missing and features use default value", async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => { await (0, util_1.withTmpDir)(async (tmpDir) => {
const loggedMessages = []; const loggedMessages = [];
const features = setUpFeatureFlagTests(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages)); const features = setUpFeatureFlagTests(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(403, {}); (0, testing_utils_1.mockFeatureFlagApiEndpoint)(403, {});
for (const feature of Object.values(feature_flags_1.Feature)) { for (const feature of Object.values(feature_flags_1.Feature)) {
t.assert((await features.getValue(feature, includeCodeQlIfRequired(feature))) === t.assert((await features.getValue(feature, includeCodeQlIfRequired(feature))) ===
false); feature_flags_1.featureConfig[feature].defaultValue);
} }
assertAllFeaturesUndefinedInApi(t, loggedMessages); assertAllFeaturesUndefinedInApi(t, loggedMessages);
}); });
}); });
(0, ava_1.default)("Features are disabled if they're not returned in API response", async (t) => { (0, ava_1.default)("Features use default value if they're not returned in API response", async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => { await (0, util_1.withTmpDir)(async (tmpDir) => {
const loggedMessages = []; const loggedMessages = [];
const features = setUpFeatureFlagTests(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages)); const features = setUpFeatureFlagTests(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(200, {}); (0, testing_utils_1.mockFeatureFlagApiEndpoint)(200, {});
for (const feature of Object.values(feature_flags_1.Feature)) { for (const feature of Object.values(feature_flags_1.Feature)) {
t.assert((await features.getValue(feature, includeCodeQlIfRequired(feature))) === t.assert((await features.getValue(feature, includeCodeQlIfRequired(feature))) ===
false); feature_flags_1.featureConfig[feature].defaultValue);
} }
assertAllFeaturesUndefinedInApi(t, loggedMessages); assertAllFeaturesUndefinedInApi(t, loggedMessages);
}); });
@ -285,7 +285,7 @@ function assertAllFeaturesUndefinedInApi(t, loggedMessages) {
for (const feature of Object.keys(feature_flags_1.featureConfig)) { for (const feature of Object.keys(feature_flags_1.featureConfig)) {
t.assert(loggedMessages.find((v) => v.type === "debug" && t.assert(loggedMessages.find((v) => v.type === "debug" &&
v.message.includes(feature) && v.message.includes(feature) &&
v.message.includes("considering it disabled")) !== undefined); v.message.includes("undefined in API response")) !== undefined);
} }
} }
function initializeFeatures(initialValue) { function initializeFeatures(initialValue) {

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
{ {
"bundleVersion": "codeql-bundle-20230217", "bundleVersion": "codeql-bundle-20230304",
"cliVersion": "2.12.3", "cliVersion": "2.12.4",
"priorBundleVersion": "codeql-bundle-20230207", "priorBundleVersion": "codeql-bundle-20230217",
"priorCliVersion": "2.12.2" "priorCliVersion": "2.12.3"
} }

View file

@ -54,8 +54,9 @@ for (const variant of ALL_FEATURES_DISABLED_VARIANTS) {
); );
for (const feature of Object.values(Feature)) { for (const feature of Object.values(Feature)) {
t.false( t.deepEqual(
await features.getValue(feature, includeCodeQlIfRequired(feature)) await features.getValue(feature, includeCodeQlIfRequired(feature)),
featureConfig[feature].defaultValue
); );
} }
@ -71,7 +72,7 @@ for (const variant of ALL_FEATURES_DISABLED_VARIANTS) {
}); });
} }
test("API response missing", async (t) => { test("API response missing and features use default value", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const loggedMessages: LoggedMessage[] = []; const loggedMessages: LoggedMessage[] = [];
const features = setUpFeatureFlagTests( const features = setUpFeatureFlagTests(
@ -84,14 +85,14 @@ test("API response missing", async (t) => {
for (const feature of Object.values(Feature)) { for (const feature of Object.values(Feature)) {
t.assert( t.assert(
(await features.getValue(feature, includeCodeQlIfRequired(feature))) === (await features.getValue(feature, includeCodeQlIfRequired(feature))) ===
false featureConfig[feature].defaultValue
); );
} }
assertAllFeaturesUndefinedInApi(t, loggedMessages); assertAllFeaturesUndefinedInApi(t, loggedMessages);
}); });
}); });
test("Features are disabled if they're not returned in API response", async (t) => { test("Features use default value if they're not returned in API response", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const loggedMessages: LoggedMessage[] = []; const loggedMessages: LoggedMessage[] = [];
const features = setUpFeatureFlagTests( const features = setUpFeatureFlagTests(
@ -104,7 +105,7 @@ test("Features are disabled if they're not returned in API response", async (t)
for (const feature of Object.values(Feature)) { for (const feature of Object.values(Feature)) {
t.assert( t.assert(
(await features.getValue(feature, includeCodeQlIfRequired(feature))) === (await features.getValue(feature, includeCodeQlIfRequired(feature))) ===
false featureConfig[feature].defaultValue
); );
} }
@ -455,7 +456,7 @@ function assertAllFeaturesUndefinedInApi(
(v) => (v) =>
v.type === "debug" && v.type === "debug" &&
(v.message as string).includes(feature) && (v.message as string).includes(feature) &&
(v.message as string).includes("considering it disabled") (v.message as string).includes("undefined in API response")
) !== undefined ) !== undefined
); );
} }

View file

@ -43,27 +43,32 @@ export enum Feature {
export const featureConfig: Record< export const featureConfig: Record<
Feature, Feature,
{ envVar: string; minimumVersion: string | undefined } { envVar: string; minimumVersion: string | undefined; defaultValue: boolean }
> = { > = {
[Feature.DisableKotlinAnalysisEnabled]: { [Feature.DisableKotlinAnalysisEnabled]: {
envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS", envVar: "CODEQL_DISABLE_KOTLIN_ANALYSIS",
minimumVersion: undefined, minimumVersion: undefined,
defaultValue: false,
}, },
[Feature.CliConfigFileEnabled]: { [Feature.CliConfigFileEnabled]: {
envVar: "CODEQL_PASS_CONFIG_TO_CLI", envVar: "CODEQL_PASS_CONFIG_TO_CLI",
minimumVersion: "2.11.6", minimumVersion: "2.11.6",
defaultValue: true,
}, },
[Feature.ExportCodeScanningConfigEnabled]: { [Feature.ExportCodeScanningConfigEnabled]: {
envVar: "CODEQL_ACTION_EXPORT_CODE_SCANNING_CONFIG", envVar: "CODEQL_ACTION_EXPORT_CODE_SCANNING_CONFIG",
minimumVersion: CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG, minimumVersion: CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG,
defaultValue: false,
}, },
[Feature.MlPoweredQueriesEnabled]: { [Feature.MlPoweredQueriesEnabled]: {
envVar: "CODEQL_ML_POWERED_QUERIES", envVar: "CODEQL_ML_POWERED_QUERIES",
minimumVersion: "2.7.5", minimumVersion: "2.7.5",
defaultValue: false,
}, },
[Feature.UploadFailedSarifEnabled]: { [Feature.UploadFailedSarifEnabled]: {
envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF", envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF",
minimumVersion: "2.11.3", minimumVersion: "2.11.3",
defaultValue: false,
}, },
}; };
@ -146,11 +151,14 @@ export class Features implements FeatureEnablement {
return true; return true;
} }
// Ask the GitHub API if the feature is enabled. // Ask the GitHub API if the feature is enabled.
return await this.gitHubFeatureFlags.getValue(feature); return (
(await this.gitHubFeatureFlags.getValue(feature)) ??
featureConfig[feature].defaultValue
);
} }
} }
class GitHubFeatureFlags implements FeatureEnablement { class GitHubFeatureFlags {
private cachedApiResponse: GitHubFeatureFlagsApiResponse | undefined; private cachedApiResponse: GitHubFeatureFlagsApiResponse | undefined;
// We cache whether the feature flags were accessed or not in order to accurately report whether flags were // We cache whether the feature flags were accessed or not in order to accurately report whether flags were
@ -256,20 +264,16 @@ class GitHubFeatureFlags implements FeatureEnablement {
return { version: maxCliVersion, toolsFeatureFlagsValid: true }; return { version: maxCliVersion, toolsFeatureFlagsValid: true };
} }
async getValue(feature: Feature): Promise<boolean> { async getValue(feature: Feature): Promise<boolean | undefined> {
const response = await this.getAllFeatures(); const response = await this.getAllFeatures();
if (response === undefined) { if (response === undefined) {
this.logger.debug( this.logger.debug(`No feature flags API response for ${feature}.`);
`No feature flags API response for ${feature}, considering it disabled.` return undefined;
);
return false;
} }
const features = response[feature]; const features = response[feature];
if (features === undefined) { if (features === undefined) {
this.logger.debug( this.logger.debug(`Feature '${feature}' undefined in API response.`);
`Feature '${feature}' undefined in API response, considering it disabled.` return undefined;
);
return false;
} }
return !!features; return !!features;
} }