Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codeqlversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.20.1
31 changes: 31 additions & 0 deletions .github/actions/install-codeql-packs/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Install CodeQL library pack dependencies
description: |
Downloads any necessary CodeQL library packs needed by packs in the repo.
inputs:
cli_path:
description: |
The path to the CodeQL CLI directory.
required: false

mode:
description: |
The `--mode` option to `codeql pack install`.
required: true
default: verify

language:
description: |
The language of the CodeQL library pack to install.
required: false
default: common

runs:
using: composite
steps:
- name: Install CodeQL library packs
shell: bash
env:
CODEQL_CLI: ${{ inputs.cli_path }}
run: |
PATH=$PATH:$CODEQL_CLI
python .github/scripts/install-packs.py --mode ${{ inputs.mode }} --language ${{ inputs.language }}
62 changes: 62 additions & 0 deletions .github/actions/install-codeql/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Setup CodeQL CLI
description: |
Install a CodeQL CLI or re-use an existing one from the cache and it to the path.

outputs:
codeql-cli-version:
description: "The version of the CodeQL CLI that was installed or retrieved from cache"
value: ${{ steps.codeql-version.outputs.codeql-cli-version }}

runs:
using: composite
steps:
- name: "CodeQL Version"
id: codeql-version
shell: bash
run: |
echo "Reading CodeQL CLI version from .codeqlversion file."
CODEQL_CLI_VERSION=$(cat ./.codeqlversion)
echo "CODEQL_CLI_VERSION=${CODEQL_CLI_VERSION}" >> $GITHUB_ENV
echo "codeql-cli-version=${CODEQL_CLI_VERSION}" >> $GITHUB_OUTPUT

- name: Cache CodeQL
id: cache-codeql
uses: actions/cache@v4
with:
# A list of files, directories, and wildcard patterns to cache and restore
path: ${{github.workspace}}/codeql_home
# An explicit key for restoring and saving the cache
key: codeql-home-${{ steps.codeql-version.outputs.codeql-cli-version }}

- name: Install CodeQL
id: install-codeql
if: steps.cache-codeql.outputs.cache-hit != 'true'
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
CODEQL_HOME: ${{ github.workspace }}/codeql_home
CODEQL_CLI_VERSION: ${{ steps.codeql-version.outputs.codeql-cli-version }}
run: |
echo "Installing CodeQL CLI v${CODEQL_CLI_VERSION}."

mkdir -p $CODEQL_HOME
echo "Change directory to $CODEQL_HOME"
pushd $CODEQL_HOME

echo "Downloading CodeQL CLI v${CODEQL_CLI_VERSION}."
gh release download "v${CODEQL_CLI_VERSION}" --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip

echo "Unzipping CodeQL CLI."
unzip -q codeql-linux64.zip

popd
echo "Done."
echo "codeql-cli-version=${CODEQL_CLI_VERSION}" >> $GITHUB_OUTPUT

- name: Add CodeQL to the PATH
shell: bash
env:
CODEQL_HOME: ${{ github.workspace }}/codeql_home
run: |
echo "Adding CodeQL CLI to the PATH."
echo "$CODEQL_HOME/codeql" >> $GITHUB_PATH
43 changes: 43 additions & 0 deletions .github/scripts/install-packs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import argparse
import os
import subprocess
import glob
import json

def get_workspace_packs(root):
# Find the packs by globbing using the 'provide' patterns in the manifest.
os.chdir(root)
with open('.codeqlmanifest.json') as manifest_file:
manifest = json.load(manifest_file)
packs = []
for pattern in manifest['provide']:
packs.extend(glob.glob(pattern, recursive=True))

return packs

def install_pack(pack_path, codeql_executable, mode):
# Run `codeql pack install` to install dependencies.
command = [codeql_executable, 'pack', 'install', '--allow-prerelease', '--mode', mode, pack_path]
print(f'Running `{" ".join(command)}`')
subprocess.check_call(command)

parser = argparse.ArgumentParser(description="Install CodeQL library pack dependencies.")
parser.add_argument('--mode', required=False, choices=['use-lock', 'update', 'verify', 'no-lock'], default="use-lock", help="Installation mode, identical to the `--mode` argument to `codeql pack install`")
parser.add_argument('--codeql', required=False, default='codeql', help="Path to the `codeql` executable.")
parser.add_argument('--language', required=False, help="Which language (or 'common') to install pack dependencies for.")
args = parser.parse_args()

# Find the root of the repo
root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

packs = get_workspace_packs(root)

# Always install the qtil source pack, which is used by all languages.
install_pack(os.path.join(root, 'src'), args.codeql, args.mode)
if args.language == 'common':
# common means we want to run test/.
install_pack(os.path.join(root, 'test'), args.codeql, args.mode)
else:
# Otherwise, we need to install the language-specific src and test packs.
install_pack(os.path.join(root, args.language, 'src'), args.codeql, args.mode)
install_pack(os.path.join(root, args.language, 'test'), args.codeql, args.mode)
27 changes: 27 additions & 0 deletions .github/workflows/codeql-query-format.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: "Validate Query Formatting"
on:
pull_request:
branches:
- main

env:
XARGS_MAX_PROCS: 4

jobs:
validate-query-formatting:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: Setup CodeQL
id: install-codeql
uses: ./.github/actions/install-codeql

- name: Validate query format
run: |
codeql version
find . \( -name \*.ql -or -name \*.qll \) -print0 | xargs -0 --max-procs "$XARGS_MAX_PROCS" codeql query format --in-place

git diff
git diff --compact-summary
git diff --quiet
130 changes: 130 additions & 0 deletions .github/workflows/codeql-unit-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Build CodeQL Packs

on:
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
compile-and-test:
name: Compile and Test CodeQL Packs
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
language: [ 'common', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]

steps:
- uses: actions/checkout@v4

- name: Setup CodeQL
id: install-codeql
uses: ./.github/actions/install-codeql

- name: Install CodeQL packs
uses: ./.github/actions/install-codeql-packs
with:
cli_path: ${{ github.workspace }}/codeql_home/codeql
language: ${{ matrix.language }}

- name: Pre-Compile Queries
id: pre-compile-queries
if: ${{ matrix.language != 'common' }}
run: |
${{ github.workspace }}/codeql_home/codeql/codeql query compile --threads 0 ${{ matrix.language }}

- name: Test Queries
env:
RUNNER_TEMP: ${{ runner.temp }}
shell: python
run: |
import os
import sys
import subprocess
from pathlib import Path

def print_error(fmt, *args):
print(f"::error::{fmt}", *args)

def print_error_and_fail(fmt, *args):
print_error(fmt, args)
sys.exit(1)

runner_temp = os.environ['RUNNER_TEMP']

if '${{ matrix.language }}' == 'common':
test_root = Path('${{ github.workspace }}', 'test')
else:
test_root = Path('${{ github.workspace }}', '${{ matrix.language }}', 'test')
print(f"Executing tests found (recursively) in the directory '{test_root}'")
files_to_close = []
try:
# Runners have 4 cores, so split the tests into 4 "slices", and run one per thread
num_slices = 4
procs = []

for slice in range(1, num_slices+1):
test_report_path = os.path.join(runner_temp, "${{ matrix.language }}", f"test_report_slice_{slice}_of_{num_slices}.json")
os.makedirs(os.path.dirname(test_report_path), exist_ok=True)
test_report_file = open(test_report_path, 'w')
files_to_close.append(test_report_file)
procs.append(subprocess.Popen(["codeql", "test", "run", "--failing-exitcode=122", f"--slice={slice}/{num_slices}", "--ram=2048", "--format=json", test_root], stdout=test_report_file, stderr=subprocess.PIPE))

for p in procs:
_, err = p.communicate()
if p.returncode != 0:
if p.returncode == 122:
# Failed because a test case failed, so just print the regular output.
# This will allow us to proceed to validate-test-results, which will fail if
# any test cases failed
print(f"{err.decode()}")
else:
# Some more serious problem occurred, so print and fail fast
print_error_and_fail(f"Failed to run tests with return code {p.returncode}\n{err.decode()}")
finally:
for file in files_to_close:
file.close()

- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.language }}-test-results
path: |
${{ runner.temp }}/${{ matrix.language }}/test_report_slice_*.json
if-no-files-found: error

validate-test-results:
name: Validate test results
needs: compile-and-test
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
- name: Check if compile-and-test job failed to complete, if so fail
if: ${{ needs.compile-and-test.result == 'failure' }}
uses: actions/github-script@v7
with:
script: |
core.setFailed('Test run job failed')

- name: Collect test results
uses: actions/download-artifact@v4

- name: Validate test results
run: |
if [[ ! -n "$(find . -name 'test_report_*' -print -quit)" ]]; then
echo "No test results found"
exit 0
fi

for json_report in *-test-results/test_report_*
do
jq --raw-output '"PASS \(map(select(.pass == true)) | length)/\(length)'" $json_report\"" "$json_report"
done
FAILING_TESTS=$(jq --raw-output '.[] | select(.pass == false)' *-test-results/test_report_*.json)
if [[ ! -z "$FAILING_TESTS" ]]; then
echo "ERROR: The following tests failed:"
echo $FAILING_TESTS | jq .
exit 1
fi

4 changes: 2 additions & 2 deletions cpp/src/qtil/Cpp.qll
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Qtil {
private import qtil.Qtil as Common
// Importing qtil.Cpp should import all of Qtil.
import Common::Qtil
import qtil.cpp.ast.TwoOperands
import qtil.cpp.ast.TwoOperands
import qtil.cpp.format.QlFormat
import qtil.cpp.graph.CustomPathProblem
}
}
2 changes: 1 addition & 1 deletion cpp/src/qtil/cpp/ast/TwoOperands.qll
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ import qtil.parameterization.SignatureTypes
module TwoOperands<Signature<cpp::BinaryOperation>::Type BinOp> {
private import qtil.ast.TwoOperands as Make
import Make::TwoOperands<cpp::Expr, BinOp>
}
}
3 changes: 1 addition & 2 deletions cpp/src/qtil/cpp/format/QlFormat.qll
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
private import qtil.format.QLFormat
private import cpp
private import qtil.cpp.locations.Locatable

import QlFormat<Location, CppLocatableConfig>
import QlFormat<Location, CppLocatableConfig>
3 changes: 1 addition & 2 deletions cpp/src/qtil/cpp/graph/CustomPathProblem.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
private import qtil.locations.CustomPathProblem
private import qtil.cpp.locations.Locatable
private import cpp

// Import the C++ specific configuration for making custom path problems.
import PathProblem<Location, CppLocatableConfig>
import PathProblem<Location, CppLocatableConfig>
2 changes: 1 addition & 1 deletion cpp/test/qtil/cpp/format/QlFormatTest.ql
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ predicate problem(Locatable elem, Template msg) {
)
}

import Problem<problem/2>::Query
import Problem<problem/2>::Query
8 changes: 2 additions & 6 deletions cpp/test/qtil/cpp/graph/CustomPathProblemTest.ql
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@ module CallGraphPathProblemConfig implements CustomPathProblemConfigSig {

predicate start(Node n) { n.getName() = "start" }

predicate end(Node n) {
n.getName() = "end"
}
predicate end(Node n) { n.getName() = "end" }

predicate edge(Variable a, Variable b) {
a.getAnAssignedValue().(VariableAccess).getTarget() = b
}
predicate edge(Variable a, Variable b) { a.getAnAssignedValue().(VariableAccess).getTarget() = b }
}

import CustomPathProblem<CallGraphPathProblemConfig>
Expand Down
2 changes: 1 addition & 1 deletion csharp/src/qtil/csharp/ast/TwoOperands.qll
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ import qtil.parameterization.SignatureTypes
module TwoOperands<Signature<csharp::BinaryOperation>::Type BinOp> {
private import qtil.ast.TwoOperands as Make
import Make::TwoOperands<csharp::Expr, BinOp>
}
}
3 changes: 1 addition & 2 deletions csharp/src/qtil/csharp/format/QlFormat.qll
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
private import qtil.format.QLFormat
private import csharp
private import qtil.csharp.locations.Locatable

import QlFormat<Location, CsharpLocatableConfig>
import QlFormat<Location, CsharpLocatableConfig>
3 changes: 1 addition & 2 deletions csharp/src/qtil/csharp/graph/CustomPathProblem.qll
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
private import qtil.locations.CustomPathProblem
private import qtil.csharp.locations.Locatable
private import csharp

// Import the C# specific configuration for making custom path problems.
import PathProblem<Location, CsharpLocatableConfig>
import PathProblem<Location, CsharpLocatableConfig>
2 changes: 1 addition & 1 deletion csharp/test/qtil/csharp/format/QlFormatTest.ql
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ predicate problem(Element elem, Template msg) {
)
}

import Problem<problem/2>::Query
import Problem<problem/2>::Query
Loading