-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a new file that contains functions are intended to ease repo integration with CodeQL, ensure consistent command-line usage across repos, and define standard scopes that other plugins and tools can depend on for CodeQL operations. Signed-off-by: Michael Kubacki <[email protected]>
- Loading branch information
Showing
4 changed files
with
273 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# CodeQL | ||
|
||
## About CodeQL | ||
|
||
CodeQL is open source and free for open-source projects. It is maintained by GitHub and naturally has excellent | ||
integration with GitHub projects. CodeQL generates a "database" during the firmware build process that enables queries | ||
to run against that database. Many open-source queries are officially supported and comprise the vulnerability analysis | ||
performed against the database. These queries are maintained here - [github/codeql](https://github.com/github/codeql). | ||
|
||
Queries are written in an object-oriented query language called QL. CodeQL provides: | ||
|
||
1. A [command-line (CLI) interface](https://codeql.github.com/docs/codeql-cli/#codeql-cli) | ||
2. A [VS Code extension](https://codeql.github.com/docs/codeql-for-visual-studio-code/#codeql-for-visual-studio-code) | ||
to help write queries and run queries | ||
3. [GitHub action](https://github.com/github/codeql-action) support for repo integration via | ||
[code scanning](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning) | ||
4. In addition to other features described in the [CodeQL overview](https://codeql.github.com/docs/codeql-overview/) | ||
|
||
## CodeQL Usage | ||
|
||
CodeQL provides the capability to debug the actual queries and for our (Tianocore) community to write our own queries | ||
and even contribute back to the upstream repo when appropriate. In other cases, we might choose to keep our own queries | ||
in a separate TianoCore repo or within a directory in the edk2 code tree. | ||
|
||
This is all part of CodeQL Scanning. [This page](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning) | ||
has information concerning how to configure CodeQL scanning within a GitHub project such as edk2. Information on the | ||
topic of running additional custom queries is documented [here](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#running-additional-queries) | ||
in that page. | ||
|
||
In addition, CodeQL offers the flexibility to: | ||
|
||
- Build databases locally | ||
- Retrieve databases from server builds | ||
- Relatively quickly test queries locally against a database for a fast feedback loop | ||
- Suppress false positives | ||
- Customize the files and queries used in the edk2 project and quickly keep this list in sync between the server and | ||
local execution | ||
|
||
## EDK2 PyTool Extensions CodeQL Module | ||
|
||
A Python module located in `edk2toolext/codeql.py` provides helper functions that platform and CI build files can use | ||
to support CodeQL enabled builds with minimal effort. | ||
|
||
The following functions are available. The expected actions to be taken per scope are included as well. | ||
|
||
- `add_command_line_option()` - Adds the CodeQL command (`--codeql`) to the platform command line options. | ||
- `get_scopes()` - Returns the active CodeQL scopes for this build. | ||
- Host OS neutral scopes: | ||
- `codeql-build` - Build the CodeQL database during firmware build. | ||
- `codeql-analyze` - Analyze the CodeQL database after firmware build (post-build). | ||
- Linux scope: | ||
- `codeql-linux-ext-dep` - Download the Linux CodeQL CLI external dependency. | ||
- Windows scope: | ||
- `codeql-windows-ext-dep` - Download the Windows CodeQL CLI external dependency. | ||
- `is_codeql_enabled_on_command_line` - Returns whether CodeQL was enabled (via `--codeql`) on the command line. | ||
- `set_audit_only_mode` - Configures the CodeQL plugin to run in audit only mode. | ||
|
||
These functions are intended to ease repo integration with CodeQL, ensure consistent command-line usage across repos, | ||
and define standard scopes that other plugins and tools can depend on for CodeQL operations. | ||
|
||
### Integration Examples | ||
|
||
This section provides examples of how to use the functions available in `edk2toolext/codeql.py`. | ||
|
||
#### add_common_line_option() | ||
|
||
Call when the command-line options are registered for the build: | ||
|
||
```python | ||
import edk2toolext.codeql as codeql | ||
|
||
def AddCommandLineOptions(self, parserObj): | ||
codeql_helpers.add_command_line_option(parserObj) | ||
``` | ||
|
||
#### get_scopes() | ||
|
||
Call when scopes are returned for the build file: | ||
|
||
```python | ||
def GetActiveScopes(self): | ||
scopes = ("cibuild", "edk2-build", "host-based-test") | ||
|
||
scopes += codeql_helpers.get_scopes(self.codeql) | ||
return scopes | ||
``` | ||
|
||
#### is_codeql_enabled_on_command_line() | ||
|
||
Call when code needs to know if the CodeQL argument was given on the command line: | ||
|
||
```python | ||
def RetrieveCommandLineOptions(self, args): | ||
super().RetrieveCommandLineOptions(args) | ||
|
||
try: | ||
self.codeql = codeql_helpers.is_codeql_enabled_on_command_line(args) | ||
``` | ||
|
||
In the above example, a `Settings` class is creating an instance variable that can be referred to later based on the | ||
value returned from the function. | ||
|
||
#### set_audit_only_mode() | ||
|
||
Call when a CI or platform build would like to enable audit mode for the current build invocation. | ||
|
||
```python | ||
def GetActiveScopes(self): | ||
set_audit_only_mode() | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# @file codeql.py | ||
# | ||
# Exports functions commonly needed for Stuart-based platforms to easily | ||
# enable CodeQL in their platform build. | ||
# | ||
# Copyright (c) Microsoft Corporation. All rights reserved. | ||
# SPDX-License-Identifier: BSD-2-Clause-Patent | ||
## | ||
"""CodeQL Helper Functions. | ||
Contains functions are intended to ease repo integration with CodeQL, ensure | ||
consistent command-line usage across repos, and define standard scopes that | ||
other plugins and tools can depend on for CodeQL operations. | ||
""" | ||
from argparse import ArgumentParser, Namespace | ||
from typing import Tuple | ||
|
||
from edk2toollib.utility_functions import GetHostInfo | ||
|
||
from edk2toolext.environment.uefi_build import UefiBuilder | ||
|
||
|
||
def add_command_line_option(parser: ArgumentParser) -> None: | ||
"""Add the CodeQL command to the platform command line options. | ||
Args: | ||
parser (ArgumentParser): The argument parser used in this build. | ||
""" | ||
parser.add_argument( | ||
'--codeql', | ||
dest='codeql', | ||
action='store_true', | ||
default=False, | ||
help="Optional - Produces CodeQL results from the build.") | ||
|
||
|
||
def get_scopes(codeql_enabled: bool) -> Tuple[str]: | ||
"""Return the active CodeQL scopes for this build. | ||
Args: | ||
codeql_enabled (bool): Whether CodeQL is enabled. | ||
Returns: | ||
Tuple[str]: A tuple of strings containing scopes that enable the | ||
CodeQL plugin. | ||
""" | ||
active_scopes = () | ||
|
||
if codeql_enabled: | ||
if GetHostInfo().os == "Linux": | ||
active_scopes += ("codeql-linux-ext-dep",) | ||
else: | ||
active_scopes += ("codeql-windows-ext-dep",) | ||
active_scopes += ("codeql-build", "codeql-analyze") | ||
|
||
return active_scopes | ||
|
||
|
||
def is_codeql_enabled_on_command_line(args: Namespace) -> bool: | ||
"""Return whether CodeQL was enabled on the command line. | ||
Args: | ||
args (Namespace): Object holding a string representation of command | ||
line arguments. | ||
Returns: | ||
bool: True if CodeQL is enabled on the command line. Otherwise, false. | ||
""" | ||
return args.codeql | ||
|
||
|
||
def set_audit_only_mode(uefi_builder: UefiBuilder) -> None: | ||
"""Configure the CodeQL plugin to run in audit only mode. | ||
Args: | ||
uefi_builder (UefiBuilder): The UefiBuilder object for this platform | ||
build. | ||
""" | ||
uefi_builder.env.SetValue( | ||
"STUART_CODEQL_AUDIT_ONLY", | ||
"true", | ||
"Platform Defined") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# @file utility_functions_test.py | ||
# unit test for utility_functions module. | ||
# | ||
# | ||
# Copyright (c) Microsoft Corporation | ||
# | ||
# SPDX-License-Identifier: BSD-2-Clause-Patent | ||
## | ||
"""CodeQL module unit tests.""" | ||
|
||
import unittest | ||
from argparse import ArgumentParser, Namespace | ||
from unittest.mock import Mock, patch | ||
|
||
import edk2toolext.codeql as codeql | ||
|
||
|
||
class CodeQlTests(unittest.TestCase): | ||
"""CodeQL unit tests.""" | ||
|
||
def test_codeql_option(self): | ||
"""Tests that common line options are added as expected.""" | ||
parser = ArgumentParser() | ||
codeql.add_command_line_option(parser) | ||
|
||
# Test that the default value is False | ||
args = parser.parse_args([]) | ||
self.assertFalse(args.codeql) | ||
|
||
# Test that the option sets the value to True | ||
args_with_option = parser.parse_args(['--codeql']) | ||
self.assertTrue(args_with_option.codeql) | ||
|
||
@patch('edk2toolext.codeql.GetHostInfo') | ||
def test_codeql_enabled_linux(self, mock_host_info): | ||
"""Tests that the proper scope is returned on a Linux host.""" | ||
mock_host_info.return_value.os = "Linux" | ||
result = codeql.get_scopes(codeql_enabled=True) | ||
expected_result = ("codeql-linux-ext-dep", "codeql-build", | ||
"codeql-analyze") | ||
self.assertEqual(result, expected_result) | ||
|
||
@patch('edk2toolext.codeql.GetHostInfo') | ||
def test_codeql_enabled_windows(self, mock_host_info): | ||
"""Tests that the proper scope is returned on a Windows host.""" | ||
mock_host_info.return_value.os = "Windows" | ||
result = codeql.get_scopes(codeql_enabled=True) | ||
expected_result = ("codeql-windows-ext-dep", "codeql-build", | ||
"codeql-analyze") | ||
self.assertEqual(result, expected_result) | ||
|
||
@patch('edk2toolext.codeql.GetHostInfo') | ||
def test_codeql_disabled(self, mock_host_info): | ||
"""Tests that the proper scopes are returned if CodeQL is disabled.""" | ||
result = codeql.get_scopes(codeql_enabled=False) | ||
expected_result = () | ||
self.assertEqual(result, expected_result) | ||
|
||
def test_codeql_enabled(self): | ||
"""Tests that CodeQL is recognized on the command-line properly.""" | ||
mock_args = Namespace(codeql=True) | ||
result = codeql.is_codeql_enabled_on_command_line(mock_args) | ||
self.assertTrue(result) | ||
|
||
def test_codeql_not_enabled(self): | ||
"""Tests that CodeQL is recognized on the command-line properly.""" | ||
mock_args = Namespace(codeql=False) | ||
result = codeql.is_codeql_enabled_on_command_line(mock_args) | ||
self.assertFalse(result) | ||
|
||
def test_set_audit_only_mode(self): | ||
"""Tests that CodeQL audit mode is enabled as expected.""" | ||
mock_uefi_builder = Mock() | ||
codeql.set_audit_only_mode(mock_uefi_builder) | ||
mock_uefi_builder.env.SetValue.assert_called_once_with( | ||
"STUART_CODEQL_AUDIT_ONLY", | ||
"true", | ||
"Platform Defined") |