mirror of https://github.com/acidanthera/audk.git
BaseTools/Plugin/CodeQL: Add CodeQL build plugin
Adds a CodeQL plugin that supports CodeQL in the build system. 1. CodeQlBuildPlugin - Generates a CodeQL database for a given build. 2. CodeQlAnalyzePlugin - Analyzes a CodeQL database and interprets results. 3. External dependencies - Assist with downloading the CodeQL CLI and making it available to the CodeQL plugins. 4. CodeQlQueries.qls - A C/C++ CodeQL query set run against the code. 5. Readme.md - A comprehensive readme file to help: - Platform integrators understand how to configure the plugin - Developers understand how to modify the plugin - Users understand how to use the plugin Read Readme.md for additional details. Cc: Bob Feng <bob.c.feng@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Rebecca Cran <rebecca@bsdio.com> Cc: Sean Brogan <sean.brogan@microsoft.com> Cc: Yuwei Chen <yuwei.chen@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Yuwei Chen <yuwei.chen@intel.com> Reviewed-by: Sean Brogan <sean.brogan@microsoft.com> Acked-by: Laszlo Ersek <lersek@redhat.com> Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
This commit is contained in:
parent
c1393bd486
commit
5464d0bed6
|
@ -0,0 +1,222 @@
|
||||||
|
# @file CodeQAnalyzePlugin.py
|
||||||
|
#
|
||||||
|
# A build plugin that analyzes a CodeQL database.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from analyze import analyze_filter
|
||||||
|
from common import codeql_plugin
|
||||||
|
|
||||||
|
from edk2toolext import edk2_logging
|
||||||
|
from edk2toolext.environment.plugintypes.uefi_build_plugin import \
|
||||||
|
IUefiBuildPlugin
|
||||||
|
from edk2toolext.environment.uefi_build import UefiBuilder
|
||||||
|
from edk2toollib.uefi.edk2.path_utilities import Edk2Path
|
||||||
|
from edk2toollib.utility_functions import RunCmd
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class CodeQlAnalyzePlugin(IUefiBuildPlugin):
|
||||||
|
|
||||||
|
def do_post_build(self, builder: UefiBuilder) -> int:
|
||||||
|
"""CodeQL analysis post-build functionality.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
builder (UefiBuilder): A UEFI builder object for this build.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The number of CodeQL errors found. Zero indicates that
|
||||||
|
AuditOnly mode is enabled or no failures were found.
|
||||||
|
"""
|
||||||
|
self.builder = builder
|
||||||
|
self.package = builder.edk2path.GetContainingPackage(
|
||||||
|
builder.edk2path.GetAbsolutePathOnThisSystemFromEdk2RelativePath(
|
||||||
|
builder.env.GetValue("ACTIVE_PLATFORM")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.package_path = Path(
|
||||||
|
builder.edk2path.GetAbsolutePathOnThisSystemFromEdk2RelativePath(
|
||||||
|
self.package
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.target = builder.env.GetValue("TARGET")
|
||||||
|
|
||||||
|
self.codeql_db_path = codeql_plugin.get_codeql_db_path(
|
||||||
|
builder.ws, self.package, self.target,
|
||||||
|
new_path=False)
|
||||||
|
|
||||||
|
self.codeql_path = codeql_plugin.get_codeql_cli_path()
|
||||||
|
if not self.codeql_path:
|
||||||
|
logging.critical("CodeQL build enabled but CodeQL CLI application "
|
||||||
|
"not found.")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
codeql_sarif_dir_path = self.codeql_db_path[
|
||||||
|
:self.codeql_db_path.rindex('-')]
|
||||||
|
codeql_sarif_dir_path = codeql_sarif_dir_path.replace(
|
||||||
|
"-db-", "-analysis-")
|
||||||
|
self.codeql_sarif_path = os.path.join(
|
||||||
|
codeql_sarif_dir_path,
|
||||||
|
(os.path.basename(
|
||||||
|
self.codeql_db_path) +
|
||||||
|
".sarif"))
|
||||||
|
|
||||||
|
edk2_logging.log_progress(f"Analyzing {self.package} ({self.target}) "
|
||||||
|
f"CodeQL database at:\n"
|
||||||
|
f" {self.codeql_db_path}")
|
||||||
|
edk2_logging.log_progress(f"Results will be written to:\n"
|
||||||
|
f" {self.codeql_sarif_path}")
|
||||||
|
|
||||||
|
# Packages are allowed to specify package-specific query specifiers
|
||||||
|
# in the package CI YAML file that override the global query specifier.
|
||||||
|
audit_only = False
|
||||||
|
query_specifiers = None
|
||||||
|
package_config_file = Path(os.path.join(
|
||||||
|
self.package_path, self.package + ".ci.yaml"))
|
||||||
|
plugin_data = None
|
||||||
|
if package_config_file.is_file():
|
||||||
|
with open(package_config_file, 'r') as cf:
|
||||||
|
package_config_file_data = yaml.safe_load(cf)
|
||||||
|
if "CodeQlAnalyze" in package_config_file_data:
|
||||||
|
plugin_data = package_config_file_data["CodeQlAnalyze"]
|
||||||
|
if "AuditOnly" in plugin_data:
|
||||||
|
audit_only = plugin_data["AuditOnly"]
|
||||||
|
if "QuerySpecifiers" in plugin_data:
|
||||||
|
logging.debug(f"Loading CodeQL query specifiers in "
|
||||||
|
f"{str(package_config_file)}")
|
||||||
|
query_specifiers = plugin_data["QuerySpecifiers"]
|
||||||
|
|
||||||
|
global_audit_only = builder.env.GetValue("STUART_CODEQL_AUDIT_ONLY")
|
||||||
|
if global_audit_only:
|
||||||
|
if global_audit_only.strip().lower() == "true":
|
||||||
|
audit_only = True
|
||||||
|
|
||||||
|
if audit_only:
|
||||||
|
logging.info(f"CodeQL Analyze plugin is in audit only mode for "
|
||||||
|
f"{self.package} ({self.target}).")
|
||||||
|
|
||||||
|
# Builds can override the query specifiers defined in this plugin
|
||||||
|
# by setting the value in the STUART_CODEQL_QUERY_SPECIFIERS
|
||||||
|
# environment variable.
|
||||||
|
if not query_specifiers:
|
||||||
|
query_specifiers = builder.env.GetValue(
|
||||||
|
"STUART_CODEQL_QUERY_SPECIFIERS")
|
||||||
|
|
||||||
|
# Use this plugins query set file as the default fallback if it is
|
||||||
|
# not overridden. It is possible the file is not present if modified
|
||||||
|
# locally. In that case, skip the plugin.
|
||||||
|
plugin_query_set = Path(Path(__file__).parent, "CodeQlQueries.qls")
|
||||||
|
|
||||||
|
if not query_specifiers and plugin_query_set.is_file():
|
||||||
|
query_specifiers = str(plugin_query_set.resolve())
|
||||||
|
|
||||||
|
if not query_specifiers:
|
||||||
|
logging.warning("Skipping CodeQL analysis since no CodeQL query "
|
||||||
|
"specifiers were provided.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
codeql_params = (f'database analyze {self.codeql_db_path} '
|
||||||
|
f'{query_specifiers} --format=sarifv2.1.0 '
|
||||||
|
f'--output={self.codeql_sarif_path} --download '
|
||||||
|
f'--threads=0')
|
||||||
|
|
||||||
|
# CodeQL requires the sarif file parent directory to exist already.
|
||||||
|
Path(self.codeql_sarif_path).parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
|
||||||
|
cmd_ret = RunCmd(self.codeql_path, codeql_params)
|
||||||
|
if cmd_ret != 0:
|
||||||
|
logging.critical(f"CodeQL CLI analysis failed with return code "
|
||||||
|
f"{cmd_ret}.")
|
||||||
|
|
||||||
|
if not os.path.isfile(self.codeql_sarif_path):
|
||||||
|
logging.critical(f"The sarif file {self.codeql_sarif_path} was "
|
||||||
|
f"not created. Analysis cannot continue.")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
filter_pattern_data = []
|
||||||
|
global_filter_file_value = builder.env.GetValue(
|
||||||
|
"STUART_CODEQL_FILTER_FILES")
|
||||||
|
if global_filter_file_value:
|
||||||
|
global_filter_files = global_filter_file_value.strip().split(',')
|
||||||
|
global_filter_files = [Path(f) for f in global_filter_files]
|
||||||
|
|
||||||
|
for global_filter_file in global_filter_files:
|
||||||
|
if global_filter_file.is_file():
|
||||||
|
with open(global_filter_file, 'r') as ff:
|
||||||
|
global_filter_file_data = yaml.safe_load(ff)
|
||||||
|
if "Filters" in global_filter_file_data:
|
||||||
|
current_pattern_data = \
|
||||||
|
global_filter_file_data["Filters"]
|
||||||
|
if type(current_pattern_data) is not list:
|
||||||
|
logging.critical(
|
||||||
|
f"CodeQL pattern data must be a list of "
|
||||||
|
f"strings. Data in "
|
||||||
|
f"{str(global_filter_file.resolve())} is "
|
||||||
|
f"invalid. CodeQL analysis is incomplete.")
|
||||||
|
return -1
|
||||||
|
filter_pattern_data += current_pattern_data
|
||||||
|
else:
|
||||||
|
logging.critical(
|
||||||
|
f"CodeQL global filter file "
|
||||||
|
f"{str(global_filter_file.resolve())} is "
|
||||||
|
f"malformed. Missing Filters section. CodeQL "
|
||||||
|
f"analysis is incomplete.")
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
logging.critical(
|
||||||
|
f"CodeQL global filter file "
|
||||||
|
f"{str(global_filter_file.resolve())} was not found. "
|
||||||
|
f"CodeQL analysis is incomplete.")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if plugin_data and "Filters" in plugin_data:
|
||||||
|
if type(plugin_data["Filters"]) is not list:
|
||||||
|
logging.critical(
|
||||||
|
"CodeQL pattern data must be a list of strings. "
|
||||||
|
"CodeQL analysis is incomplete.")
|
||||||
|
return -1
|
||||||
|
filter_pattern_data.extend(plugin_data["Filters"])
|
||||||
|
|
||||||
|
if filter_pattern_data:
|
||||||
|
logging.info("Applying CodeQL SARIF result filters.")
|
||||||
|
analyze_filter.filter_sarif(
|
||||||
|
self.codeql_sarif_path,
|
||||||
|
self.codeql_sarif_path,
|
||||||
|
filter_pattern_data,
|
||||||
|
split_lines=False)
|
||||||
|
|
||||||
|
with open(self.codeql_sarif_path, 'r') as sf:
|
||||||
|
sarif_file_data = json.load(sf)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Perform minimal JSON parsing to find the number of errors.
|
||||||
|
total_errors = 0
|
||||||
|
for run in sarif_file_data['runs']:
|
||||||
|
total_errors += len(run['results'])
|
||||||
|
except KeyError:
|
||||||
|
logging.critical("Sarif file does not contain expected data. "
|
||||||
|
"Analysis cannot continue.")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if total_errors > 0:
|
||||||
|
if audit_only:
|
||||||
|
# Show a warning message so CodeQL analysis is not forgotten.
|
||||||
|
# If the repo owners truly do not want to fix CodeQL issues,
|
||||||
|
# analysis should be disabled entirely.
|
||||||
|
logging.warning(f"{self.package} ({self.target}) CodeQL "
|
||||||
|
f"analysis ignored {total_errors} errors due "
|
||||||
|
f"to audit mode being enabled.")
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
logging.error(f"{self.package} ({self.target}) CodeQL "
|
||||||
|
f"analysis failed with {total_errors} errors.")
|
||||||
|
|
||||||
|
return total_errors
|
|
@ -0,0 +1,13 @@
|
||||||
|
## @file CodeQlAnalyze_plug_in.py
|
||||||
|
#
|
||||||
|
# Build plugin used to analyze CodeQL results.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
{
|
||||||
|
"scope": "codeql-analyze",
|
||||||
|
"name": "CodeQL Analyze Plugin",
|
||||||
|
"module": "CodeQlAnalyzePlugin"
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
# @file CodeQlBuildPlugin.py
|
||||||
|
#
|
||||||
|
# A build plugin that produces CodeQL results for the present build.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
|
from common import codeql_plugin
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from edk2toolext import edk2_logging
|
||||||
|
from edk2toolext.environment.plugintypes.uefi_build_plugin import \
|
||||||
|
IUefiBuildPlugin
|
||||||
|
from edk2toolext.environment.uefi_build import UefiBuilder
|
||||||
|
from edk2toollib.uefi.edk2.path_utilities import Edk2Path
|
||||||
|
from edk2toollib.utility_functions import GetHostInfo, RemoveTree
|
||||||
|
|
||||||
|
|
||||||
|
class CodeQlBuildPlugin(IUefiBuildPlugin):
|
||||||
|
|
||||||
|
def do_pre_build(self, builder: UefiBuilder) -> int:
|
||||||
|
"""CodeQL pre-build functionality.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
builder (UefiBuilder): A UEFI builder object for this build.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: The plugin return code. Zero indicates the plugin ran
|
||||||
|
successfully. A non-zero value indicates an unexpected error
|
||||||
|
occurred during plugin execution.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not builder.SkipBuild:
|
||||||
|
self.builder = builder
|
||||||
|
self.package = builder.edk2path.GetContainingPackage(
|
||||||
|
builder.edk2path.GetAbsolutePathOnThisSystemFromEdk2RelativePath(
|
||||||
|
builder.env.GetValue("ACTIVE_PLATFORM")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.target = builder.env.GetValue("TARGET")
|
||||||
|
|
||||||
|
self.build_output_dir = builder.env.GetValue("BUILD_OUTPUT_BASE")
|
||||||
|
|
||||||
|
self.codeql_db_path = codeql_plugin.get_codeql_db_path(
|
||||||
|
builder.ws, self.package, self.target)
|
||||||
|
|
||||||
|
edk2_logging.log_progress(f"{self.package} will be built for CodeQL")
|
||||||
|
edk2_logging.log_progress(f" CodeQL database will be written to "
|
||||||
|
f"{self.codeql_db_path}")
|
||||||
|
|
||||||
|
self.codeql_path = codeql_plugin.get_codeql_cli_path()
|
||||||
|
if not self.codeql_path:
|
||||||
|
logging.critical("CodeQL build enabled but CodeQL CLI application "
|
||||||
|
"not found.")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
# CodeQL can only generate a database on clean build
|
||||||
|
#
|
||||||
|
# Note: builder.CleanTree() cannot be used here as some platforms
|
||||||
|
# have build steps that run before this plugin that store
|
||||||
|
# files in the build output directory.
|
||||||
|
#
|
||||||
|
# CodeQL does not care about with those files or many others such
|
||||||
|
# as the FV directory, build logs, etc. so instead focus on
|
||||||
|
# removing only the directories with compilation/linker output
|
||||||
|
# for the architectures being built (that need clean runs for
|
||||||
|
# CodeQL to work).
|
||||||
|
targets = self.builder.env.GetValue("TARGET_ARCH").split(" ")
|
||||||
|
for target in targets:
|
||||||
|
directory_to_delete = Path(self.build_output_dir, target)
|
||||||
|
|
||||||
|
if directory_to_delete.is_dir():
|
||||||
|
logging.debug(f"Removing {str(directory_to_delete)} to have a "
|
||||||
|
f"clean build for CodeQL.")
|
||||||
|
RemoveTree(str(directory_to_delete))
|
||||||
|
|
||||||
|
# CodeQL CLI does not handle spaces passed in CLI commands well
|
||||||
|
# (perhaps at all) as discussed here:
|
||||||
|
# 1. https://github.com/github/codeql-cli-binaries/issues/73
|
||||||
|
# 2. https://github.com/github/codeql/issues/4910
|
||||||
|
#
|
||||||
|
# Since it's unclear how quotes are handled and may change in the
|
||||||
|
# future, this code is going to use the workaround to place the
|
||||||
|
# command in an executable file that is instead passed to CodeQL.
|
||||||
|
self.codeql_cmd_path = Path(self.build_output_dir, "codeql_build_command")
|
||||||
|
|
||||||
|
build_params = self._get_build_params()
|
||||||
|
|
||||||
|
codeql_build_cmd = ""
|
||||||
|
if GetHostInfo().os == "Windows":
|
||||||
|
self.codeql_cmd_path = self.codeql_cmd_path.parent / (
|
||||||
|
self.codeql_cmd_path.name + '.bat')
|
||||||
|
elif GetHostInfo().os == "Linux":
|
||||||
|
self.codeql_cmd_path = self.codeql_cmd_path.parent / (
|
||||||
|
self.codeql_cmd_path.name + '.sh')
|
||||||
|
codeql_build_cmd += f"#!/bin/bash{os.linesep * 2}"
|
||||||
|
codeql_build_cmd += "build " + build_params
|
||||||
|
|
||||||
|
self.codeql_cmd_path.parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
self.codeql_cmd_path.write_text(encoding='utf8', data=codeql_build_cmd)
|
||||||
|
|
||||||
|
if GetHostInfo().os == "Linux":
|
||||||
|
os.chmod(self.codeql_cmd_path,
|
||||||
|
os.stat(self.codeql_cmd_path).st_mode | stat.S_IEXEC)
|
||||||
|
for f in glob.glob(os.path.join(
|
||||||
|
os.path.dirname(self.codeql_path), '**/*'), recursive=True):
|
||||||
|
os.chmod(f, os.stat(f).st_mode | stat.S_IEXEC)
|
||||||
|
|
||||||
|
codeql_params = (f'database create {self.codeql_db_path} '
|
||||||
|
f'--language=cpp '
|
||||||
|
f'--source-root={builder.ws} '
|
||||||
|
f'--command={self.codeql_cmd_path}')
|
||||||
|
|
||||||
|
# Set environment variables so the CodeQL build command is picked up
|
||||||
|
# as the active build command.
|
||||||
|
#
|
||||||
|
# Note: Requires recent changes in edk2-pytool-extensions (0.20.0)
|
||||||
|
# to support reading these variables.
|
||||||
|
builder.env.SetValue(
|
||||||
|
"EDK_BUILD_CMD", self.codeql_path, "Set in CodeQL Build Plugin")
|
||||||
|
builder.env.SetValue(
|
||||||
|
"EDK_BUILD_PARAMS", codeql_params, "Set in CodeQL Build Plugin")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _get_build_params(self) -> str:
|
||||||
|
"""Returns the build command parameters for this build.
|
||||||
|
|
||||||
|
Based on the well-defined `build` command-line parameters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A string representing the parameters for the build command.
|
||||||
|
"""
|
||||||
|
build_params = f"-p {self.builder.env.GetValue('ACTIVE_PLATFORM')}"
|
||||||
|
build_params += f" -b {self.target}"
|
||||||
|
build_params += f" -t {self.builder.env.GetValue('TOOL_CHAIN_TAG')}"
|
||||||
|
|
||||||
|
max_threads = self.builder.env.GetValue('MAX_CONCURRENT_THREAD_NUMBER')
|
||||||
|
if max_threads is not None:
|
||||||
|
build_params += f" -n {max_threads}"
|
||||||
|
|
||||||
|
rt = self.builder.env.GetValue("TARGET_ARCH").split(" ")
|
||||||
|
for t in rt:
|
||||||
|
build_params += " -a " + t
|
||||||
|
|
||||||
|
if (self.builder.env.GetValue("BUILDREPORTING") == "TRUE"):
|
||||||
|
build_params += (" -y " +
|
||||||
|
self.builder.env.GetValue("BUILDREPORT_FILE"))
|
||||||
|
rt = self.builder.env.GetValue("BUILDREPORT_TYPES").split(" ")
|
||||||
|
for t in rt:
|
||||||
|
build_params += " -Y " + t
|
||||||
|
|
||||||
|
# add special processing to handle building a single module
|
||||||
|
mod = self.builder.env.GetValue("BUILDMODULE")
|
||||||
|
if (mod is not None and len(mod.strip()) > 0):
|
||||||
|
build_params += " -m " + mod
|
||||||
|
edk2_logging.log_progress("Single Module Build: " + mod)
|
||||||
|
|
||||||
|
build_vars = self.builder.env.GetAllBuildKeyValues(self.target)
|
||||||
|
for key, value in build_vars.items():
|
||||||
|
build_params += " -D " + key + "=" + value
|
||||||
|
|
||||||
|
return build_params
|
|
@ -0,0 +1,13 @@
|
||||||
|
## @file CodeQlBuild_plug_in.py
|
||||||
|
#
|
||||||
|
# Build plugin used to produce a CodeQL database from a build.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
{
|
||||||
|
"scope": "codeql-build",
|
||||||
|
"name": "CodeQL Build Plugin",
|
||||||
|
"module": "CodeQlBuildPlugin"
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
---
|
||||||
|
- description: C++ queries
|
||||||
|
|
||||||
|
- queries: '.'
|
||||||
|
from: codeql/cpp-queries
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Queries
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
## Enable When Time is Available to Fix Issues
|
||||||
|
# Hundreds of issues. Most appear valid. Type: Recommendation.
|
||||||
|
#- include:
|
||||||
|
# id: cpp/missing-null-test
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
- include:
|
||||||
|
id: cpp/overrunning-write
|
||||||
|
- include:
|
||||||
|
id: cpp/overrunning-write-with-float
|
||||||
|
- include:
|
||||||
|
id: cpp/pointer-overflow-check
|
||||||
|
- include:
|
||||||
|
id: cpp/very-likely-overrunning-write
|
||||||
|
|
||||||
|
## Warnings
|
||||||
|
- include:
|
||||||
|
id: cpp/conditionallyuninitializedvariable
|
||||||
|
- include:
|
||||||
|
id: cpp/infinite-loop-with-unsatisfiable-exit-condition
|
||||||
|
- include:
|
||||||
|
id: cpp/overflow-buffer
|
||||||
|
|
||||||
|
# Note: Some queries above are not active by default with the below filter.
|
||||||
|
# Update the filter and run the queries again to get all results.
|
||||||
|
- include:
|
||||||
|
tags:
|
||||||
|
- "security"
|
||||||
|
- "correctness"
|
||||||
|
severity:
|
||||||
|
- "error"
|
||||||
|
- "warning"
|
||||||
|
- "recommendation"
|
||||||
|
|
||||||
|
# Specifically hide the results of these.
|
||||||
|
#
|
||||||
|
# The following rules have been evaluated and explicitly not included for the following reasons:
|
||||||
|
# - `cpp/allocation-too-small` - Appears to be hardcoded for C standard library functions `malloc`, `calloc`,
|
||||||
|
# `realloc`, so it consumes time without much value with custom allocation functions in the codebase.
|
||||||
|
# - `cpp/commented-out-code` - Triggers often. Needs further review.
|
||||||
|
# - `cpp/duplicate-include-guard` - The <Phase>EntryPoint.h files includes a common include guard value
|
||||||
|
# `__MODULE_ENTRY_POINT_H__`. This was the only occurrence found. So not very useful.
|
||||||
|
# - `cpp/invalid-pointer-deref` - Very limited results with what appear to be false positives.
|
||||||
|
# - `cpp/use-of-goto` - Goto is valid and allowed in the codebase.
|
||||||
|
# - `cpp/useless-expression` - Triggers too often on cases where a NULL lib implementation is provided for a function.
|
||||||
|
# Because the implementation simply returns, the check considers it useless.
|
||||||
|
# - `cpp/weak-crypto/*` - Crypto algorithms are tracked outside CodeQL.
|
||||||
|
- exclude:
|
||||||
|
id: cpp/allocation-too-small
|
||||||
|
- exclude:
|
||||||
|
id: cpp/commented-out-code
|
||||||
|
- exclude:
|
||||||
|
id: cpp/duplicate-include-guard
|
||||||
|
- exclude:
|
||||||
|
id: cpp/invalid-pointer-deref
|
||||||
|
- exclude:
|
||||||
|
id: cpp/use-of-goto
|
||||||
|
- exclude:
|
||||||
|
id: cpp/useless-expression
|
||||||
|
- exclude:
|
||||||
|
id: cpp/weak-crypto/banned-hash-algorithms
|
||||||
|
- exclude:
|
||||||
|
id: cpp/weak-crypto/capi/banned-modes
|
||||||
|
- exclude:
|
||||||
|
id: cpp/weak-crypto/openssl/banned-hash-algorithms
|
|
@ -0,0 +1,388 @@
|
||||||
|
# CodeQL Plugin
|
||||||
|
|
||||||
|
The set of CodeQL plugins provided include two main plugins that seamlessly integrate into a Stuart build environment:
|
||||||
|
|
||||||
|
1. `CodeQlBuildPlugin` - Used to produce a CodeQL database from a build.
|
||||||
|
2. `CodeQlAnalyzePlugin` - Used to analyze a CodeQL database.
|
||||||
|
|
||||||
|
While CodeQL can be run in a CI environment with other approaches. This plugin offers the following advantages:
|
||||||
|
|
||||||
|
1. Provides exactly the same results locally as on a CI server.
|
||||||
|
2. Integrates very well into VS Code.
|
||||||
|
3. Very simple to use - just use normal Stuart update and build commands.
|
||||||
|
4. Very simple to understand - minimally wraps the official CodeQL CLI.
|
||||||
|
5. Very simple to integrate - works like any other Stuart build plugin.
|
||||||
|
- Integration is usually just a few lines of code.
|
||||||
|
6. Portable - not tied to Azure DevOps specific, GitHub specific, or other host infrastructure.
|
||||||
|
7. Versioned - the query and filters are versioned in source control so easy to find and track.
|
||||||
|
|
||||||
|
It is very important to read the Integration Instructions in this file and determine how to best integrate the
|
||||||
|
CodeQL plugin into your environment.
|
||||||
|
|
||||||
|
Due to the total size of dependencies required to run CodeQL and the flexibility needed by a platform to determine what
|
||||||
|
CodeQL queries to run and how to interpret results, a number of configuration options are provided to allow a high
|
||||||
|
degree of flexibility during platform integration.
|
||||||
|
|
||||||
|
This document is focused on those setting up the CodeQL plugin in their environment. Once setup, end users simply need
|
||||||
|
to use their normal build commands and process and CodeQL will be integrated with it. The most relevant section for
|
||||||
|
such users is [Local Development Tips](#local-development-tips).
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Database and Analysis Result Locations](#database-and-analysis-result-locations)
|
||||||
|
2. [Global Configuration](#global-configuration)
|
||||||
|
3. [Package-Specific Configuration](#package-specific-configuration)
|
||||||
|
4. [Filter Patterns](#filter-patterns)
|
||||||
|
5. [Integration Instructions](#integration-instructions)
|
||||||
|
- [Integration Step 1 - Choose Scopes](#integration-step-1---choose-scopes)
|
||||||
|
- [Scopes Available](#scopes-available)
|
||||||
|
- [Integration Step 2 - Choose CodeQL Queries](#integration-step-2---choose-codeql-queries)
|
||||||
|
- [Integration Step 3 - Determine Global Configuration Values](#integration-step-3---determine-global-configuration-values)
|
||||||
|
- [Integration Step 4 - Determine Package-Specific Configuration Values](#integration-step-4---determine-package-specific-configuration-values)
|
||||||
|
- [Integration Step 5 - Testing](#integration-step-5---testing)
|
||||||
|
- [Integration Step 6 - Define Inclusion and Exclusion Filter Patterns](#integration-step-6---define-inclusion-and-exclusion-filter-patterns)
|
||||||
|
6. [High-Level Operation](#high-level-operation)
|
||||||
|
- [CodeQlBuildPlugin](#codeqlbuildplugin)
|
||||||
|
- [CodeQlAnalyzePlugin](#codeqlanalyzeplugin)
|
||||||
|
7. [Local Development Tips](#local-development-tips)
|
||||||
|
8. [Resolution Guidelines](#resolution-guidelines)
|
||||||
|
|
||||||
|
## Database and Analysis Result Locations
|
||||||
|
|
||||||
|
The CodeQL database is written to a directory unique to the package and target being built:
|
||||||
|
|
||||||
|
`Build/codeql-db-<package>-<target>-<instance>`
|
||||||
|
|
||||||
|
For example: `Build/codeql-db-mdemodulepkg-debug-0`
|
||||||
|
|
||||||
|
The plugin does not delete or overwrite existing databases, the instance value is simply increased. This is
|
||||||
|
because databases are large, take a long time to generate, and are important for reproducing analysis results. The user
|
||||||
|
is responsible for deleting database directories when they are no longer needed.
|
||||||
|
|
||||||
|
Similarly, analysis results are written to a directory unique to the package and target. For analysis, results are
|
||||||
|
stored in individual files so those files are stored in a single directory.
|
||||||
|
|
||||||
|
For example, all analysis results for the above package and target will be stored in:
|
||||||
|
`codeql-analysis-mdemodulepkg-debug`
|
||||||
|
|
||||||
|
CodeQL results are stored in [SARIF](https://sarifweb.azurewebsites.net/) (Static Analysis Results Interchange Format)
|
||||||
|
([CodeQL SARIF documentation](https://codeql.github.com/docs/codeql-cli/sarif-output/)) files. Each SARIF file
|
||||||
|
corresponding to a database will be stored in a file with an instance matching the database instance.
|
||||||
|
|
||||||
|
For example, the analysis result file for the above database would be stored in this file:
|
||||||
|
`codeql-analysis-mdemodulepkg-debug/codeql-db-mdemodulepkg-debug-0.sarif`
|
||||||
|
|
||||||
|
Result files are overwritten. This is because result files are quick to generate and need to represent the latest
|
||||||
|
results for the last analysis operation performed. The user is responsible for backing up SARIF result files if they
|
||||||
|
need to saved.
|
||||||
|
|
||||||
|
## Global Configuration
|
||||||
|
|
||||||
|
Global configuration values are specified with build environment variables.
|
||||||
|
|
||||||
|
These values are all optional. They provide a convenient mechanism for a build script to set the value for all packages
|
||||||
|
built by the script.
|
||||||
|
|
||||||
|
- `STUART_CODEQL_AUDIT_ONLY` - If `true` (case insensitive), `CodeQlAnalyzePlugin` will be in audit-only mode. In this
|
||||||
|
mode all CodeQL failures are ignored.
|
||||||
|
- `STUART_CODEQL_PATH` - The path to the CodeQL CLI application to use.
|
||||||
|
- `STUART_CODEQL_QUERY_SPECIFIERS` - The CodeQL CLI query specifiers to use. See [Running codeql database analyze](https://codeql.github.com/docs/codeql-cli/analyzing-databases-with-the-codeql-cli/#running-codeql-database-analyze)
|
||||||
|
for possible options.
|
||||||
|
- `STUART_CODEQL_FILTER_FILES` - The path to "filter" files that contains filter patterns as described in
|
||||||
|
[Filter Patterns](#filter-patterns).
|
||||||
|
- More than one file may be specified by separating each absolute file path with a comma.
|
||||||
|
- This might be useful to reference a global filter file from an upstream repo and also include a global filter
|
||||||
|
file for the local repo.
|
||||||
|
- Filters are concatenated in the order of files in the variable. Patterns in later files can override patterns
|
||||||
|
in earlier files.
|
||||||
|
- The file only needs to contain a list of filter pattern strings under a `"Filters"` key. For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
{
|
||||||
|
"Filters": [
|
||||||
|
"<pattern-line-1>",
|
||||||
|
"<pattern-line-2>"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Comments are allowed in the filter files and begin with `#` (like a normal YAML file).
|
||||||
|
|
||||||
|
## Package-Specific Configuration
|
||||||
|
|
||||||
|
Package-specific configuration values reuse existing package-level configuration approaches to simplify adjusting
|
||||||
|
CodeQL plugin behavior per package.
|
||||||
|
|
||||||
|
These values are all optional. They provide a convenient mechanism for a package owner to adjust settings specific to
|
||||||
|
the package.
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
"CodeQlAnalyze": {
|
||||||
|
"AuditOnly": False, # Don't fail the build if there are errors. Just log them.
|
||||||
|
"QuerySpecifiers": "" # Query specifiers to pass to CodeQL CLI.
|
||||||
|
"Filters": "" # Inclusion/exclusion filters
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> _NOTE:_ If a global filter set is provided via `STUART_CODEQL_FILTER_FILES` and a package has a package-specific
|
||||||
|
> list, then the package-specific filter list (in a package CI YAML file) is appended onto the global filter list and
|
||||||
|
> may be used to override settings in the global list.
|
||||||
|
|
||||||
|
The format used to specify items in `"Filters"` is specified in [Filter Patterns](#filter-patterns).
|
||||||
|
|
||||||
|
## Filter Patterns
|
||||||
|
|
||||||
|
As you inspect results, you may want to include or exclude certain sets of results. For example, exclude some files by
|
||||||
|
file path entirely or adjust the CodeQL rule applied to a certain file. This plugin reuses logic from a popular
|
||||||
|
GitHub Action called [`filter-sarif`](https://github.com/advanced-security/filter-sarif) to allow filtering as part of
|
||||||
|
the plugin analysis process.
|
||||||
|
|
||||||
|
If any results are excluded using filters, the results are removed from the SARIF file. This allows the exclude results
|
||||||
|
seen locally to exactly match the results on the CI server.
|
||||||
|
|
||||||
|
Read the ["Patterns"](https://github.com/advanced-security/filter-sarif#patterns) section there for more details. The
|
||||||
|
patterns section is also copied below with some updates to make the information more relevant for an edk2 codebase
|
||||||
|
for convenience.
|
||||||
|
|
||||||
|
Each pattern line is of the form:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
[+/-]<file pattern>[:<rule pattern>]
|
||||||
|
```
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
-**/*Test*.c:** # exclusion pattern: remove all alerts from all test files
|
||||||
|
-**/*Test*.c # ditto, short form of the line above
|
||||||
|
+**/*.c:cpp/infiniteloop # inclusion pattern: This line has precedence over the first two
|
||||||
|
# and thus "allow lists" alerts of type "cpp/infiniteloop"
|
||||||
|
**/*.c:cpp/infiniteloop # ditto, the "+" in inclusion patterns is optional
|
||||||
|
** # allow all alerts in all files (reverses all previous lines)
|
||||||
|
```
|
||||||
|
|
||||||
|
- The path separator character in patterns is always `/`, independent of the platform the code is running on and
|
||||||
|
independent of the paths in the SARIF file.
|
||||||
|
- `*` matches any character, except a path separator
|
||||||
|
- `**` matches any character and is only allowed between path separators, e.g. `/**/file.txt`, `**/file.txt` or `**`.
|
||||||
|
NOT allowed: `**.txt`, `/etc**`
|
||||||
|
- The rule pattern is optional. If omitted, it will apply to alerts of all types.
|
||||||
|
- Subsequent lines override earlier ones. By default all alerts are included.
|
||||||
|
- If you need to use the literals `+`, `-`, `\` or `:` in your pattern, you can escape them with `\`, e.g.
|
||||||
|
`\-this/is/an/inclusion/file/pattern\:with-a-semicolon:and/a/rule/pattern/with/a/\\/backslash`. For `+` and `-`, this
|
||||||
|
is only necessary if they appear at the beginning of the pattern line.
|
||||||
|
|
||||||
|
## Integration Instructions
|
||||||
|
|
||||||
|
First, note that most CodeQL CLI operations will take a long time the first time they are run. This is due to:
|
||||||
|
|
||||||
|
1. Downloads - Downloading the CodeQL CLI binary (during `stuart_update`) and downloading CodeQL queries during
|
||||||
|
CodeQL plugin execution
|
||||||
|
2. Cache not established - CodeQL CLI caches data as it performs analysis. The first time analysis is performed will
|
||||||
|
take more time than in the future.
|
||||||
|
|
||||||
|
Second, these are build plugins. This means a build needs to take place for the plugins to run. This typically happens
|
||||||
|
in the following two scenarios:
|
||||||
|
|
||||||
|
1. `stuart_build` - A single package is built and the build process is started by the stuart tools.
|
||||||
|
2. `stuart_ci_build` - A number of packages may be built and the build process is started by the `CompilerPlugin`.
|
||||||
|
|
||||||
|
In any case, each time a package is built, the CodeQL plugins will be run if their scopes are active.
|
||||||
|
|
||||||
|
### Integration Step 1 - Choose Scopes
|
||||||
|
|
||||||
|
Decide which scopes need to be enabled in your platform, see [Scopes Available](#scopes-available).
|
||||||
|
|
||||||
|
Consider using a build profile to enable CodeQL so developers and pipelines can use the profile when they are
|
||||||
|
interested in CodeQL results but in other cases they can easily work without CodeQL in the way.
|
||||||
|
|
||||||
|
Furthermore, build-script specific command-line parameters might be useful to control CodeQL scopes and other
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
#### Scopes Available
|
||||||
|
|
||||||
|
This CodeQL plugin leverages scopes to control major pieces of functionality. Any combination of scopes can be
|
||||||
|
returned from the `GetActiveScopes()` function in the platform settings manager to add and remove functionality.
|
||||||
|
|
||||||
|
Plugin scopes:
|
||||||
|
|
||||||
|
- `codeql-analyze` - Activate `CodeQlAnalyzePlugin` to perform post-build analysis of the last generated database for
|
||||||
|
the package and target specified.
|
||||||
|
- `codeql-build` - Activate `CodeQlBuildPlugin` to hook the firmware build in pre-build such that the build will
|
||||||
|
generate a CodeQL database during build.
|
||||||
|
|
||||||
|
In most cases, to perform a full CodeQL run, `codeql-build` should be enabled so a new CodeQL database is generated
|
||||||
|
during build and `codeql-analyze` should be be enabled so analysis of that database is performed after the build is
|
||||||
|
completed.
|
||||||
|
|
||||||
|
External dependency scopes:
|
||||||
|
|
||||||
|
- `codeql-ext-dep` - Downloads the cross-platform CodeQL CLI as an external dependency.
|
||||||
|
- `codeql-linux-ext-dep` - Downloads the Linux CodeQL CLI as an external dependency.
|
||||||
|
- `codeql-windows-ext-dep` - Downloads the Windows CodeQL CLI as an external dependency.
|
||||||
|
|
||||||
|
Note, that the CodeQL CLI is large in size. Sizes as of the [v2.11.2 release](https://github.com/github/codeql-cli-binaries/releases/tag/v2.11.2).
|
||||||
|
|
||||||
|
| Cross-platform | Linux | Windows |
|
||||||
|
|:--------------:|:------:|:-------:|
|
||||||
|
| 934 MB | 415 MB | 290 MB |
|
||||||
|
|
||||||
|
Therefore, the following is recommended:
|
||||||
|
|
||||||
|
1. **Ideal** - Create container images for build agents and install the CodeQL CLI for the container OS into the
|
||||||
|
container.
|
||||||
|
2. Leverage host-OS detection (e.g. [`GetHostInfo()`](https://github.com/tianocore/edk2-pytool-library/blob/42ad6561af73ba34564f1577f64f7dbaf1d0a5a2/edk2toollib/utility_functions.py#L112))
|
||||||
|
to set the scope for the appropriate operating system. This will download the much smaller OS-specific application.
|
||||||
|
|
||||||
|
> _NOTE:_ You should never have more than one CodeQL external dependency scope enabled at a time.
|
||||||
|
|
||||||
|
### Integration Step 2 - Choose CodeQL Queries
|
||||||
|
|
||||||
|
Determine which queries need to be run against packages in your repo. In most cases, the same set of queries will be
|
||||||
|
run against all packages. It is also possible to customize the queries run at the package level.
|
||||||
|
|
||||||
|
The default set of Project Mu CodeQL queries is specified in the `MuCodeQlQueries.qls` file in this plugin.
|
||||||
|
|
||||||
|
> _NOTE:_ The queries in `MuCodeQlQueries.qls` may change at any time. If you do not want these changes to impact
|
||||||
|
> your platform, do not relay on option (3).
|
||||||
|
|
||||||
|
The plugin decides what queries to run based on the following, in order of preference:
|
||||||
|
|
||||||
|
1. Package CI YAML file query specifier
|
||||||
|
2. Build environment variable query specifier
|
||||||
|
3. Plugin default query set file
|
||||||
|
|
||||||
|
For details on how to set (1) and (2), see the Package CI Configuration and Environment Variable sections respectively.
|
||||||
|
|
||||||
|
> _NOTE:_ The value specified is directly passed as a `query specifier` to CodeQL CLI. Therefore, the arguments
|
||||||
|
> allowed by the `<query-specifiers>` argument of CodeQL CLI are allowed here. See
|
||||||
|
> [Running codeql database analyze](https://codeql.github.com/docs/codeql-cli/analyzing-databases-with-the-codeql-cli/#running-codeql-database-analyze).
|
||||||
|
|
||||||
|
A likely scenario is that a platform needs to run local/closed source queries in addition to the open-source queries.
|
||||||
|
There's various ways to handle that:
|
||||||
|
|
||||||
|
1. Create a query specifier that includes all the queries needed, both public and private and use that query specifier,
|
||||||
|
either globally or at package-level.
|
||||||
|
|
||||||
|
For example, at the global level - `STUART_CODEQL_QUERY_SPECIFIERS` = _"Absolute_path_to_AllMyQueries.qls"_
|
||||||
|
|
||||||
|
2. Specify a query specifier that includes the closed sources queries and reuse the public query list provided by
|
||||||
|
this plugin.
|
||||||
|
|
||||||
|
For example, at the global level - `STUART_CODEQL_QUERY_SPECIFIERS` = _"Absolute_path_to_MuCodeQlQueries.qls
|
||||||
|
Absolute_path_to_ClosedSourceQueries.qls"_
|
||||||
|
|
||||||
|
Refer to the CodeQL documentation noted above on query specifiers to devise other options.
|
||||||
|
|
||||||
|
### Integration Step 3 - Determine Global Configuration Values
|
||||||
|
|
||||||
|
Review the Environment Variable section to determine which, if any, global values need to be set in your build script.
|
||||||
|
|
||||||
|
### Integration Step 4 - Determine Package-Specific Configuration Values
|
||||||
|
|
||||||
|
Review the Package CI Configuration section to determine which, if any, global values need to be set in your
|
||||||
|
package's CI YAML file.
|
||||||
|
|
||||||
|
### Integration Step 5 - Testing
|
||||||
|
|
||||||
|
Verify a `stuart_update` and `stuart_build` (or `stuart_ci_build`) command work.
|
||||||
|
|
||||||
|
### Integration Step 6 - Define Inclusion and Exclusion Filter Patterns
|
||||||
|
|
||||||
|
After reviewing the test results from Step 5, determine if you need to apply any filters as described in
|
||||||
|
[Filter Patterns](#filter-patterns).
|
||||||
|
|
||||||
|
## High-Level Operation
|
||||||
|
|
||||||
|
This section summarizes the complete CodeQL plugin flow. This is to help developers understand basic theory of
|
||||||
|
operation behind the plugin and can be skipped by anyone not interested in those details.
|
||||||
|
|
||||||
|
### CodeQlBuildPlugin
|
||||||
|
|
||||||
|
1. Register a pre-build hook
|
||||||
|
2. Determine the package and target being built
|
||||||
|
3. Determine the best CodeQL CLI path to use
|
||||||
|
- First choice, the `STUART_CODEQL_PATH` environment variable
|
||||||
|
- Note: This is set by the CodeQL CLI external dependency if that is used
|
||||||
|
- Second choice, `codeql` as found on the system path
|
||||||
|
4. Determine the directory name for the CodeQL database
|
||||||
|
- Format: `Build/codeql-db-<package>-<target>-<instance>`
|
||||||
|
5. Clean the build directory of the active platform and target
|
||||||
|
- CodeQL database generation only works on clean builds
|
||||||
|
6. Ensure the "build" step is not skipped as a build is needed to generate a CodeQL database
|
||||||
|
7. Build a CodeQL file that wraps around the edk2 build
|
||||||
|
- Written to the package build directory
|
||||||
|
- Example: `Build/MdeModulePkg/VS2022/codeql_build_command.bat`
|
||||||
|
8. Set the variables necessary for stuart to call CodeQL CLI during the build phase
|
||||||
|
- Sets `EDK_BUILD_CMD` and `EDK_BUILD_PARAMS`
|
||||||
|
|
||||||
|
### CodeQlAnalyzePlugin
|
||||||
|
|
||||||
|
1. Register a post-build hook
|
||||||
|
2. Determine the package and target being built
|
||||||
|
3. Determine the best CodeQL CLI path to use
|
||||||
|
- First choice, the `STUART_CODEQL_PATH` environment variable
|
||||||
|
- Note: This is set by the CodeQL CLI external dependency if that is used
|
||||||
|
- Second choice, `codeql` as found on the system path
|
||||||
|
4. Determine the directory name for the most recent CodeQL database
|
||||||
|
- Format: `Build/codeql-db-<package>-<target>-<instance>`
|
||||||
|
5. Determine plugin audit status for the given package and target
|
||||||
|
- Check if `AuditOnly` is enabled either globally or for the package
|
||||||
|
6. Determine the CodeQL query specifiers to use for the given package and target
|
||||||
|
- First choice, the package CI YAML file value
|
||||||
|
- Second choice, the `STUART_CODEQL_QUERY_SPECIFIERS`
|
||||||
|
- Third choice, use `CodeQlQueries.qls` (in the plugin directory)
|
||||||
|
7. Run CodeQL CLI to perform database analysis
|
||||||
|
8. Parse the analysis SARIF file to determine the number of CodeQL failures
|
||||||
|
9. Return the number of failures (or zero if `AuditOnly` is enabled)
|
||||||
|
|
||||||
|
## Local Development Tips
|
||||||
|
|
||||||
|
This section contains helpful tips to expedite common scenarios when working with CodeQL locally.
|
||||||
|
|
||||||
|
1. Pre-build, Build, and Post-Build
|
||||||
|
|
||||||
|
Generating a database requires the pre-build and build steps. Analyzing a database requires the post-build step.
|
||||||
|
|
||||||
|
Therefore, if you are making tweaks that don't affect the build, such as modifying the CodeQL queries used or level
|
||||||
|
of severity reported, you can save time by skipping pre-build and post-build (e.g. `--skipprebuild` and
|
||||||
|
`--skipbuild`).
|
||||||
|
|
||||||
|
2. Scopes
|
||||||
|
|
||||||
|
Similar to (1), add/remove `codeql-build` and `codeql-analyze` from the active scopes to save time depending on what
|
||||||
|
you are trying to do.
|
||||||
|
|
||||||
|
If you are focusing on coding, remove the code CodeQL scopes if they are active. If you are ready to check your
|
||||||
|
changes against CodeQL, simply add the scopes back. It is recommended to use build profiles to do this more
|
||||||
|
conveniently.
|
||||||
|
|
||||||
|
If you already have CodeQL CLI enabled, you can remove the `codeql-ext-dep` scope locally. The build will use the
|
||||||
|
`codeql` command on your path.
|
||||||
|
|
||||||
|
3. CodeQL Output is in the CI Build Log
|
||||||
|
|
||||||
|
To see exactly which queries CodeQL ran or why it might be taking longer than expected, look in the CI build log
|
||||||
|
(i.e. `Build/CI_BUILDLOG.txt`) where the CodeQL CLI application output is written.
|
||||||
|
|
||||||
|
Search for the text you see in the progress output (e.g. "Analyzing _MdeModulePkg_ (_DEBUG_) CodeQL database at")
|
||||||
|
to jump to the section of the log just before the CodeQL CLI is invoked.
|
||||||
|
|
||||||
|
4. Use a SARIF Viewer to Read Results
|
||||||
|
|
||||||
|
The [SARIF Viewer extension for VS Code](https://marketplace.visualstudio.com/items?itemName=MS-SarifVSCode.sarif-viewer)
|
||||||
|
can open the .sarif file generated by this plugin and allow you to click links directly to the problem area in source
|
||||||
|
files.
|
||||||
|
|
||||||
|
## Resolution Guidelines
|
||||||
|
|
||||||
|
This section captures brief guidelines to keep in mind while resolving CodeQL issues.
|
||||||
|
|
||||||
|
1. Look at surrounding code. Changes should always take into account the context of nearby code. The new logic may
|
||||||
|
need to account conditions not immediately obvious based on the issue alone. It is easy to focus only on the line
|
||||||
|
of code highlighted by CodeQL and miss the code's role in the big picture.
|
||||||
|
2. A CodeQL alert may be benign but the code can be refactored to prevent the alert. Often refactoring the code makes
|
||||||
|
the code intention clearer and avoids an unnecessary exception.
|
||||||
|
3. Consider adding unit tests while making CodeQL fixes especially for commonly used code and code with a high volume
|
||||||
|
of CodeQL alerts.
|
|
@ -0,0 +1,184 @@
|
||||||
|
# @file analyze_filter.py
|
||||||
|
#
|
||||||
|
# Filters results in a SARIF file.
|
||||||
|
#
|
||||||
|
# Apache License
|
||||||
|
# Version 2.0, January 2004
|
||||||
|
# http://www.apache.org/licenses/
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# This file has been altered from its original form. Based on code in:
|
||||||
|
# https://github.com/advanced-security/filter-sarif
|
||||||
|
#
|
||||||
|
# It primarily contains modifications made to integrate with the CodeQL plugin.
|
||||||
|
#
|
||||||
|
# Specifically:
|
||||||
|
# https://github.com/advanced-security/filter-sarif/blob/main/filter_sarif.py
|
||||||
|
#
|
||||||
|
# View the full and complete license as provided by that repository here:
|
||||||
|
# https://github.com/advanced-security/filter-sarif/blob/main/LICENSE
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
##
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from os import PathLike
|
||||||
|
from typing import Iterable, List, Tuple
|
||||||
|
|
||||||
|
from analyze.globber import match
|
||||||
|
|
||||||
|
|
||||||
|
def _match_path_and_rule(
|
||||||
|
path: str, rule: str, patterns: Iterable[str]) -> bool:
|
||||||
|
"""Returns whether a given path matches a given rule.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path (str): A file path string.
|
||||||
|
rule (str): A rule file path string.
|
||||||
|
patterns (Iterable[str]): An iterable of pattern strings.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the path matches a rule. Otherwise, False.
|
||||||
|
"""
|
||||||
|
result = True
|
||||||
|
for s, fp, rp in patterns:
|
||||||
|
if match(rp, rule) and match(fp, path):
|
||||||
|
result = s
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_pattern(line: str) -> Tuple[str]:
|
||||||
|
"""Parses a given pattern line.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
line (str): The line string that contains the rule.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[str]: The parsed sign, file pattern, and rule pattern from the
|
||||||
|
line.
|
||||||
|
"""
|
||||||
|
sep_char = ':'
|
||||||
|
esc_char = '\\'
|
||||||
|
file_pattern = ''
|
||||||
|
rule_pattern = ''
|
||||||
|
seen_separator = False
|
||||||
|
sign = True
|
||||||
|
|
||||||
|
# inclusion or exclusion pattern?
|
||||||
|
u_line = line
|
||||||
|
if line:
|
||||||
|
if line[0] == '-':
|
||||||
|
sign = False
|
||||||
|
u_line = line[1:]
|
||||||
|
elif line[0] == '+':
|
||||||
|
u_line = line[1:]
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < len(u_line):
|
||||||
|
c = u_line[i]
|
||||||
|
i = i + 1
|
||||||
|
if c == sep_char:
|
||||||
|
if seen_separator:
|
||||||
|
raise Exception(
|
||||||
|
'Invalid pattern: "' + line + '" Contains more than one '
|
||||||
|
'separator!')
|
||||||
|
seen_separator = True
|
||||||
|
continue
|
||||||
|
elif c == esc_char:
|
||||||
|
next_c = u_line[i] if (i < len(u_line)) else None
|
||||||
|
if next_c in ['+' , '-', esc_char, sep_char]:
|
||||||
|
i = i + 1
|
||||||
|
c = next_c
|
||||||
|
if seen_separator:
|
||||||
|
rule_pattern = rule_pattern + c
|
||||||
|
else:
|
||||||
|
file_pattern = file_pattern + c
|
||||||
|
|
||||||
|
if not rule_pattern:
|
||||||
|
rule_pattern = '**'
|
||||||
|
|
||||||
|
return sign, file_pattern, rule_pattern
|
||||||
|
|
||||||
|
|
||||||
|
def filter_sarif(input_sarif: PathLike,
|
||||||
|
output_sarif: PathLike,
|
||||||
|
patterns: List[str],
|
||||||
|
split_lines: bool) -> None:
|
||||||
|
"""Filters a SARIF file with a given set of filter patterns.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input_sarif (PathLike): Input SARIF file path.
|
||||||
|
output_sarif (PathLike): Output SARIF file path.
|
||||||
|
patterns (PathLike): List of filter pattern strings.
|
||||||
|
split_lines (PathLike): Whether to split lines in individual patterns.
|
||||||
|
"""
|
||||||
|
if split_lines:
|
||||||
|
tmp = []
|
||||||
|
for p in patterns:
|
||||||
|
tmp = tmp + re.split('\r?\n', p)
|
||||||
|
patterns = tmp
|
||||||
|
|
||||||
|
patterns = [_parse_pattern(p) for p in patterns if p]
|
||||||
|
|
||||||
|
logging.debug('Given patterns:')
|
||||||
|
for s, fp, rp in patterns:
|
||||||
|
logging.debug(
|
||||||
|
'files: {file_pattern} rules: {rule_pattern} ({sign})'.format(
|
||||||
|
file_pattern=fp,
|
||||||
|
rule_pattern=rp,
|
||||||
|
sign='positive' if s else 'negative'))
|
||||||
|
|
||||||
|
with open(input_sarif, 'r') as f:
|
||||||
|
s = json.load(f)
|
||||||
|
|
||||||
|
for run in s.get('runs', []):
|
||||||
|
if run.get('results', []):
|
||||||
|
new_results = []
|
||||||
|
for r in run['results']:
|
||||||
|
if r.get('locations', []):
|
||||||
|
new_locations = []
|
||||||
|
for l in r['locations']:
|
||||||
|
# TODO: The uri field is optional. We might have to
|
||||||
|
# fetch the actual uri from "artifacts" via
|
||||||
|
# "index"
|
||||||
|
# (see https://github.com/microsoft/sarif-tutorials/blob/main/docs/2-Basics.md#-linking-results-to-artifacts)
|
||||||
|
uri = l.get(
|
||||||
|
'physicalLocation', {}).get(
|
||||||
|
'artifactLocation', {}).get(
|
||||||
|
'uri', None)
|
||||||
|
|
||||||
|
# TODO: The ruleId field is optional and potentially
|
||||||
|
# ambiguous. We might have to fetch the actual
|
||||||
|
# ruleId from the rule metadata via the ruleIndex
|
||||||
|
# field.
|
||||||
|
# (see https://github.com/microsoft/sarif-tutorials/blob/main/docs/2-Basics.md#rule-metadata)
|
||||||
|
ruleId = r['ruleId']
|
||||||
|
|
||||||
|
if (uri is None or
|
||||||
|
_match_path_and_rule(uri, ruleId, patterns)):
|
||||||
|
new_locations.append(l)
|
||||||
|
r['locations'] = new_locations
|
||||||
|
if new_locations:
|
||||||
|
new_results.append(r)
|
||||||
|
else:
|
||||||
|
# locations array doesn't exist or is empty, so we can't
|
||||||
|
# match on anything. Therefore, we include the result in
|
||||||
|
# the output.
|
||||||
|
new_results.append(r)
|
||||||
|
run['results'] = new_results
|
||||||
|
|
||||||
|
with open(output_sarif, 'w') as f:
|
||||||
|
json.dump(s, f, indent=2)
|
|
@ -0,0 +1,127 @@
|
||||||
|
# @file globber.py
|
||||||
|
#
|
||||||
|
# Provides global functionality for use by the CodeQL plugin.
|
||||||
|
#
|
||||||
|
# Copyright 2019 Jaakko Kangasharju
|
||||||
|
#
|
||||||
|
# Apache License
|
||||||
|
# Version 2.0, January 2004
|
||||||
|
# http://www.apache.org/licenses/
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# This file has been altered from its original form. Based on code in:
|
||||||
|
# https://github.com/advanced-security/filter-sarif
|
||||||
|
#
|
||||||
|
# Specifically:
|
||||||
|
# https://github.com/advanced-security/filter-sarif/blob/main/filter_sarif.py
|
||||||
|
#
|
||||||
|
# It primarily contains modifications made to integrate with the CodeQL plugin.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
##
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
_double_star_after_invalid_regex = re.compile(r'[^/\\]\*\*')
|
||||||
|
_double_star_first_before_invalid_regex = re.compile('^\\*\\*[^/]')
|
||||||
|
_double_star_middle_before_invalid_regex = re.compile(r'[^\\]\*\*[^/]')
|
||||||
|
|
||||||
|
|
||||||
|
def _match_component(pattern_component, file_name_component):
|
||||||
|
if len(pattern_component) == 0 and len(file_name_component) == 0:
|
||||||
|
return True
|
||||||
|
elif len(pattern_component) == 0:
|
||||||
|
return False
|
||||||
|
elif len(file_name_component) == 0:
|
||||||
|
return pattern_component == '*'
|
||||||
|
elif pattern_component[0] == '*':
|
||||||
|
return (_match_component(pattern_component, file_name_component[1:]) or
|
||||||
|
_match_component(pattern_component[1:], file_name_component))
|
||||||
|
elif pattern_component[0] == '?':
|
||||||
|
return _match_component(pattern_component[1:], file_name_component[1:])
|
||||||
|
elif pattern_component[0] == '\\':
|
||||||
|
return (len(pattern_component) >= 2 and
|
||||||
|
pattern_component[1] == file_name_component[0] and
|
||||||
|
_match_component(
|
||||||
|
pattern_component[2:], file_name_component[1:]))
|
||||||
|
elif pattern_component[0] != file_name_component[0]:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return _match_component(pattern_component[1:], file_name_component[1:])
|
||||||
|
|
||||||
|
|
||||||
|
def _match_components(pattern_components, file_name_components):
|
||||||
|
if len(pattern_components) == 0 and len(file_name_components) == 0:
|
||||||
|
return True
|
||||||
|
if len(pattern_components) == 0:
|
||||||
|
return False
|
||||||
|
if len(file_name_components) == 0:
|
||||||
|
return len(pattern_components) == 1 and pattern_components[0] == '**'
|
||||||
|
if pattern_components[0] == '**':
|
||||||
|
return (_match_components(pattern_components, file_name_components[1:])
|
||||||
|
or _match_components(
|
||||||
|
pattern_components[1:], file_name_components))
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
_match_component(
|
||||||
|
pattern_components[0], file_name_components[0]) and
|
||||||
|
_match_components(
|
||||||
|
pattern_components[1:], file_name_components[1:]))
|
||||||
|
|
||||||
|
|
||||||
|
def match(pattern: str, file_name: str):
|
||||||
|
"""Match a glob pattern against a file name.
|
||||||
|
|
||||||
|
Glob pattern matching is for file names, which do not need to exist as
|
||||||
|
files on the file system.
|
||||||
|
|
||||||
|
A file name is a sequence of directory names, possibly followed by the name
|
||||||
|
of a file, with the components separated by a path separator. A glob
|
||||||
|
pattern is similar, except it may contain special characters: A '?' matches
|
||||||
|
any character in a name. A '*' matches any sequence of characters (possibly
|
||||||
|
empty) in a name. Both of these match only within a single component, i.e.,
|
||||||
|
they will not match a path separator. A component in a pattern may also be
|
||||||
|
a literal '**', which matches zero or more components in the complete file
|
||||||
|
name. A backslash '\\' in a pattern acts as an escape character, and
|
||||||
|
indicates that the following character is to be matched literally, even if
|
||||||
|
it is a special character.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern (str): The pattern to match. The path separator in patterns is
|
||||||
|
always '/'.
|
||||||
|
file_name (str): The file name to match against. The path separator in
|
||||||
|
file names is the platform separator
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the pattern matches, False otherwise.
|
||||||
|
"""
|
||||||
|
if (_double_star_after_invalid_regex.search(pattern) is not None or
|
||||||
|
_double_star_first_before_invalid_regex.search(
|
||||||
|
pattern) is not None or
|
||||||
|
_double_star_middle_before_invalid_regex.search(pattern) is not None):
|
||||||
|
raise ValueError(
|
||||||
|
'** in {} not alone between path separators'.format(pattern))
|
||||||
|
|
||||||
|
pattern = pattern.rstrip('/')
|
||||||
|
file_name = file_name.rstrip('/')
|
||||||
|
|
||||||
|
while '**/**' in pattern:
|
||||||
|
pattern = pattern.replace('**/**', '**')
|
||||||
|
|
||||||
|
pattern_components = pattern.split('/')
|
||||||
|
|
||||||
|
# We split on '\' as well as '/' to support unix and windows-style paths
|
||||||
|
file_name_components = re.split(r'[\\/]', file_name)
|
||||||
|
|
||||||
|
return _match_components(pattern_components, file_name_components)
|
|
@ -0,0 +1,26 @@
|
||||||
|
## @file codeqlcli_ext_dep.yaml
|
||||||
|
#
|
||||||
|
# Downloads the CodeQL Command-Line Interface (CLI) application that support Linux, Windows, and Mac OS X.
|
||||||
|
#
|
||||||
|
# This download is very large but conveniently provides support for all operating systems. Use it if you
|
||||||
|
# need CodeQL CLI support without concern for the host operating system.
|
||||||
|
#
|
||||||
|
# In an environment where a platform might build in different operating systems, it is recommended to set
|
||||||
|
# the scope for the appropriate CodeQL external dependency based on the host operating system being used.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
{
|
||||||
|
"scope": "codeql-ext-dep",
|
||||||
|
"type": "web",
|
||||||
|
"name": "codeql_cli",
|
||||||
|
"source": "https://github.com/github/codeql-cli-binaries/releases/download/v2.12.4/codeql.zip",
|
||||||
|
"version": "2.12.4",
|
||||||
|
"sha256": "f682f1155d627ad97f10b1bcad97f682011986717bd3823e9cf831ed83ac96e7",
|
||||||
|
"compression_type": "zip",
|
||||||
|
"internal_path": "/codeql/",
|
||||||
|
"flags": ["set_shell_var", ],
|
||||||
|
"var_name": "STUART_CODEQL_PATH"
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
## @file codeqlcli_linux_ext_dep.yaml
|
||||||
|
#
|
||||||
|
# Downloads the Linux CodeQL Command-Line Interface (CLI) application.
|
||||||
|
#
|
||||||
|
# This download only supports Linux. In an environment where a platform might build in different operating
|
||||||
|
# systems, it is recommended to set the scope for the appropriate CodeQL external dependency based on the
|
||||||
|
# host operating system being used.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
{
|
||||||
|
"scope": "codeql-linux-ext-dep",
|
||||||
|
"type": "web",
|
||||||
|
"name": "codeql_linux_cli",
|
||||||
|
"source": "https://github.com/github/codeql-cli-binaries/releases/download/v2.14.5/codeql-linux64.zip",
|
||||||
|
"version": "2.14.5",
|
||||||
|
"sha256": "72aa5d748ff9ab57cfd86045560683bdc4897e0fe6d9f9a2786d9394674ae733",
|
||||||
|
"compression_type": "zip",
|
||||||
|
"internal_path": "/codeql/",
|
||||||
|
"flags": ["set_shell_var", ],
|
||||||
|
"var_name": "STUART_CODEQL_PATH"
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
## @file codeqlcli_windows_ext_dep.yaml
|
||||||
|
#
|
||||||
|
# Downloads the Windows CodeQL Command-Line Interface (CLI) application.
|
||||||
|
#
|
||||||
|
# This download only supports Windows. In an environment where a platform might build in different operating
|
||||||
|
# systems, it is recommended to set the scope for the appropriate CodeQL external dependency based on the
|
||||||
|
# host operating system being used.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
{
|
||||||
|
"scope": "codeql-windows-ext-dep",
|
||||||
|
"type": "web",
|
||||||
|
"name": "codeql_windows_cli",
|
||||||
|
"source": "https://github.com/github/codeql-cli-binaries/releases/download/v2.14.5/codeql-win64.zip",
|
||||||
|
"version": "2.14.5",
|
||||||
|
"sha256": "861fcb38365cc311efee0c3a28c77494e93c69a969885b72e53173ad473f61aa",
|
||||||
|
"compression_type": "zip",
|
||||||
|
"internal_path": "/codeql/",
|
||||||
|
"flags": ["set_shell_var", ],
|
||||||
|
"var_name": "STUART_CODEQL_PATH"
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
# @file codeql_plugin.py
|
||||||
|
#
|
||||||
|
# Common logic shared across the CodeQL plugin.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from os import PathLike
|
||||||
|
|
||||||
|
from edk2toollib.utility_functions import GetHostInfo
|
||||||
|
|
||||||
|
|
||||||
|
def get_codeql_db_path(workspace: PathLike, package: str, target: str,
|
||||||
|
new_path: bool = True) -> str:
|
||||||
|
"""Return the CodeQL database path for this build.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
workspace (PathLike): The workspace path.
|
||||||
|
package (str): The package name (e.g. "MdeModulePkg")
|
||||||
|
target (str): The target (e.g. "DEBUG")
|
||||||
|
new_path (bool, optional): Whether to create a new database path or
|
||||||
|
return an existing path. Defaults to True.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The absolute path to the CodeQL database directory.
|
||||||
|
"""
|
||||||
|
codeql_db_dir_name = "codeql-db-" + package + "-" + target
|
||||||
|
codeql_db_dir_name = codeql_db_dir_name.lower()
|
||||||
|
codeql_db_path = os.path.join("Build", codeql_db_dir_name)
|
||||||
|
codeql_db_path = os.path.join(workspace, codeql_db_path)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while os.path.isdir(f"{codeql_db_path + '-%s' % i}"):
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if not new_path:
|
||||||
|
if i == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
return codeql_db_path + f"-{i}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_codeql_cli_path() -> str:
|
||||||
|
"""Return the current CodeQL CLI path.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The absolute path to the CodeQL CLI application to use for
|
||||||
|
this build.
|
||||||
|
"""
|
||||||
|
# The CodeQL executable path can be passed via the
|
||||||
|
# STUART_CODEQL_PATH environment variable (to override with a
|
||||||
|
# custom value for this run) or read from the system path.
|
||||||
|
codeql_path = None
|
||||||
|
|
||||||
|
if "STUART_CODEQL_PATH" in os.environ:
|
||||||
|
codeql_path = os.environ["STUART_CODEQL_PATH"]
|
||||||
|
|
||||||
|
if GetHostInfo().os == "Windows":
|
||||||
|
codeql_path = os.path.join(codeql_path, "codeql.exe")
|
||||||
|
else:
|
||||||
|
codeql_path = os.path.join(codeql_path, "codeql")
|
||||||
|
|
||||||
|
if not os.path.isfile(codeql_path):
|
||||||
|
codeql_path = None
|
||||||
|
|
||||||
|
if not codeql_path:
|
||||||
|
codeql_path = shutil.which("codeql")
|
||||||
|
|
||||||
|
return codeql_path
|
Loading…
Reference in New Issue