From 8633bdd38ca605f88ea08dd1bec4b1aa7bfaf950 Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Sat, 2 May 2026 16:01:31 +0100 Subject: [PATCH 01/12] ci: add CodeQL query to enforce OpenSSL return code handling Adds a custom CodeQL query (.github/codeql/openssl-return-check.ql) that detects calls to OpenSSL EVP_* functions whose return value is not guarded by the OQS_OPENSSL_GUARD macro. Integrates the query into a new CodeQL workflow (.github/workflows/codeql.yml) that runs on every PR and push to main. The query was originally written by Trail of Bits during their audit of liboqs and reported in issue #1867. Closes #1867 Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/codeql/openssl-return-check.ql | 26 ++++++++++++++++++ .github/workflows/codeql.yml | 37 ++++++++++++++++++++++++++ .github/workflows/pr.yml | 8 ++++++ 3 files changed, 71 insertions(+) create mode 100644 .github/codeql/openssl-return-check.ql create mode 100644 .github/workflows/codeql.yml diff --git a/.github/codeql/openssl-return-check.ql b/.github/codeql/openssl-return-check.ql new file mode 100644 index 0000000000..5ef7e8dd94 --- /dev/null +++ b/.github/codeql/openssl-return-check.ql @@ -0,0 +1,26 @@ +/** + * @name Unchecked OpenSSL EVP return value + * @description Calls to OpenSSL EVP functions whose return value is not + * checked by the OQS_OPENSSL_GUARD macro may silently ignore + * errors, leading to undefined behaviour. + * @kind problem + * @problem.severity warning + * @precision medium + * @id cpp/openssl-unchecked-return + * @tags security + * correctness + */ + +import cpp + +from FunctionCall call, Function f +where + f = call.getTarget() and + f.getName().matches("EVP%") and + not f.getType() instanceof PointerType and + not f.getType() instanceof VoidType and + not exists(MacroAccess m | + m.getLocation().subsumes(call.getLocation()) and + m.getMacroName() = "OQS_OPENSSL_GUARD" + ) +select call, "Return value of " + f.getName() + "() is not checked by OQS_OPENSSL_GUARD." diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..e7312b0927 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,37 @@ +name: CodeQL analysis + +permissions: + contents: read + security-events: write + +on: + workflow_call: + workflow_dispatch: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + codeql: + name: CodeQL + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 + with: + languages: cpp + queries: .github/codeql/openssl-return-check.ql + + - name: Build liboqs + run: | + cmake -S . -B build -DOQS_MINIMAL_BUILD="KEM_ml_kem_768;SIG_ml_dsa_65" + cmake --build build --parallel $(nproc) + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 + with: + category: "/language:cpp" diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 9765399fec..009c8663a7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -87,3 +87,11 @@ jobs: contents: read id-token: write security-events: write + + codeql: + needs: basic-checks + uses: ./.github/workflows/codeql.yml + secrets: inherit + permissions: + contents: read + security-events: write From a61f1604d382fd7e155bc7336ce2e9da5e1781b5 Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Sat, 2 May 2026 16:30:50 +0100 Subject: [PATCH 02/12] ci: move security-events permission to job level in CodeQL workflow Addresses Scorecard Token-Permissions finding: security-events write permission scoped to codeql job only, not workflow level. Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/workflows/codeql.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e7312b0927..078f85c99c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,7 +2,6 @@ name: CodeQL analysis permissions: contents: read - security-events: write on: workflow_call: @@ -14,6 +13,10 @@ on: jobs: codeql: + permissions: + contents: read + security-events: write + name: CodeQL runs-on: ubuntu-latest steps: From 0826b32b7f807a3a78082c9711ed8a52b5270ea6 Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Tue, 5 May 2026 15:43:26 +0100 Subject: [PATCH 03/12] ci: fix CodeQL query path using config file Replace inline queries: path with a codeql-config.yml config file. The CodeQL init action requires local queries to be referenced via a config file rather than a direct relative path in the queries field. Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/codeql/codeql-config.yml | 3 +++ .github/workflows/codeql.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .github/codeql/codeql-config.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000000..f75f16dd7d --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,3 @@ +name: "OQS CodeQL config" +queries: + - uses: ./.github/codeql/openssl-return-check.ql diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 078f85c99c..fe0856a524 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,7 +27,7 @@ jobs: uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 with: languages: cpp - queries: .github/codeql/openssl-return-check.ql + config-file: ./.github/codeql/codeql-config.yml - name: Build liboqs run: | From 9a7e632bdb0d418e58990e13c54781446c286eae Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Tue, 5 May 2026 15:50:42 +0100 Subject: [PATCH 04/12] ci: disable CodeQL TRAP caching to fix pre-finalize crash The CodeQL cpp extractor pre-finalize script crashes with exit code 134 when the trapCaches/cpp/tarballs directory does not exist on the runner. Disabling TRAP caching avoids the crash. Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/workflows/codeql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index fe0856a524..f4982944f3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,6 +19,8 @@ jobs: name: CodeQL runs-on: ubuntu-latest + env: + CODEQL_EXTRACTOR_CPP_TRAP_CACHING: false steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 From 5bf7d65a9e7a052c9715846f5e2751412bcb86f9 Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Mon, 11 May 2026 21:30:55 +0100 Subject: [PATCH 05/12] ci: use inline queries field per maintainer suggestion Replace config-file approach with inline queries field combining security-and-quality suite with the custom EVP return check query, as suggested by @baentsch. Remove codeql-config.yml. Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/codeql/codeql-config.yml | 3 --- .github/workflows/codeql.yml | 11 +---------- 2 files changed, 1 insertion(+), 13 deletions(-) delete mode 100644 .github/codeql/codeql-config.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml deleted file mode 100644 index f75f16dd7d..0000000000 --- a/.github/codeql/codeql-config.yml +++ /dev/null @@ -1,3 +0,0 @@ -name: "OQS CodeQL config" -queries: - - uses: ./.github/codeql/openssl-return-check.ql diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f4982944f3..68d2e41d6f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,8 +1,6 @@ name: CodeQL analysis - permissions: contents: read - on: workflow_call: workflow_dispatch: @@ -10,32 +8,25 @@ on: branches: [main] pull_request: branches: [main] - jobs: codeql: permissions: contents: read security-events: write - name: CodeQL runs-on: ubuntu-latest - env: - CODEQL_EXTRACTOR_CPP_TRAP_CACHING: false steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - name: Initialize CodeQL uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 with: languages: cpp - config-file: ./.github/codeql/codeql-config.yml - + queries: security-and-quality,./.github/codeql/openssl-return-check.ql - name: Build liboqs run: | cmake -S . -B build -DOQS_MINIMAL_BUILD="KEM_ml_kem_768;SIG_ml_dsa_65" cmake --build build --parallel $(nproc) - - name: Perform CodeQL analysis uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 with: From 7e850e6ccd681cb362bd1f08d13fb11fbabde7ff Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Mon, 11 May 2026 21:34:11 +0100 Subject: [PATCH 06/12] ci: re-add TRAP caching workaround after queries field change Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/workflows/codeql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 68d2e41d6f..5afb9bf4ee 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,6 +13,8 @@ jobs: permissions: contents: read security-events: write + env: + CODEQL_EXTRACTOR_CPP_TRAP_CACHING: false name: CodeQL runs-on: ubuntu-latest steps: From b66dd422ef7fbfc7143dd1838885b8ea19904f85 Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Mon, 11 May 2026 21:43:21 +0100 Subject: [PATCH 07/12] ci: disable SARIF upload to avoid default setup conflict Adding upload: false to the analyze step prevents the SARIF conflict with GitHub's default CodeQL setup which is enabled on this repo. The query still runs and catches issues but does not upload results. Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/workflows/codeql.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5afb9bf4ee..cc85ffdc54 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,3 +33,4 @@ jobs: uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 with: category: "/language:cpp" + upload: false From 7ff4b526f1788779bf824bdc46d5fb1dffa671f3 Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Tue, 14 Jul 2026 23:56:47 +0100 Subject: [PATCH 08/12] ci: add CodeQL query test example and CONTRIBUTING docs Add bad example at .github/codeql/test/openssl-return-check-bad.c to confirm the query catches unchecked EVP_* calls. Document verification steps in CONTRIBUTING.md. Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/codeql/test/openssl-return-check-bad.c | 5 +++++ CONTRIBUTING.md | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 .github/codeql/test/openssl-return-check-bad.c diff --git a/.github/codeql/test/openssl-return-check-bad.c b/.github/codeql/test/openssl-return-check-bad.c new file mode 100644 index 0000000000..dad16fba01 --- /dev/null +++ b/.github/codeql/test/openssl-return-check-bad.c @@ -0,0 +1,5 @@ +/* expect-fail: EVP return value not checked via OQS_OPENSSL_GUARD */ +#include +void bad_example(EVP_MD_CTX *ctx, const EVP_MD *md) { + EVP_DigestInit_ex(ctx, md, NULL); +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f6a09cf283..612b8950aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,3 +119,11 @@ If you feel like contributing but don't know what specific topic to work on, please check the [open issues tagged "good first issue" or "help wanted"](https://github.com/open-quantum-safe/liboqs/issues). You can also take a look at the [contribution wishlist](https://github.com/open-quantum-safe/liboqs/wiki/Contribution-wishlist) for more substantial contributions we are interested in. + +## Verifying the CodeQL OpenSSL return-check query +The query detects EVP_* calls not wrapped in OQS_OPENSSL_GUARD. +A deliberately bad example lives in +.github/codeql/test/openssl-return-check-bad.c. +When CodeQL runs, any match against this file confirms the query +is scanning correctly. CI will flag new violations automatically +on every PR. From 87eff636f286c3ac380dab925de6e5823c65bd54 Mon Sep 17 00:00:00 2001 From: Vishnu Ajith <27vishnu07@gmail.com> Date: Wed, 15 Jul 2026 00:06:04 +0100 Subject: [PATCH 09/12] ci: add SPDX header to CodeQL test file Signed-off-by: Vishnu Ajith <27vishnu07@gmail.com> --- .github/codeql/test/openssl-return-check-bad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/codeql/test/openssl-return-check-bad.c b/.github/codeql/test/openssl-return-check-bad.c index dad16fba01..42d8fb0e86 100644 --- a/.github/codeql/test/openssl-return-check-bad.c +++ b/.github/codeql/test/openssl-return-check-bad.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT /* expect-fail: EVP return value not checked via OQS_OPENSSL_GUARD */ #include void bad_example(EVP_MD_CTX *ctx, const EVP_MD *md) { From 5b896917b8ed197a9b7abff83bedf1296ec2aef3 Mon Sep 17 00:00:00 2001 From: Owen Waldron Date: Fri, 16 Oct 2026 12:23:11 -0400 Subject: [PATCH 10/12] Rework CodeQL custom-query workflow. Address the review concerns from #2415: - Do not upload results to code scanning, which rejects CodeQL results from other workflows while default setup is enabled. Instead, print each result as an error annotation and fail the job if there are any - Run only liboqs's own queries, since the standard suite already runs via default setup. - Add CodeQL unit tests for each query which run in a seperate job - Analyse a full default build with OpenSSL used for AES, SHA2 and SHA3 - Flag only EVP_* calls whose return value is discarded, since there are other safe ways beyond OQS_OPENSSL_GUARD to check this. - In pr.yml, depend on check-push-result rather than basic-checks and drop `security-events: write`, so the job can be run in forks. The workflow currently reports 119 unchecked EVP_* calls in src/common. These will be fixed separately. Assisted-by: Claude:claude-opus-5.5 Signed-off-by: Owen Waldron --- .github/codeql/codeql-config.yml | 6 ++ .github/codeql/codeql-workspace.yml | 3 + .github/codeql/openssl-return-check.ql | 26 -------- .../codeql/queries/openssl-return-check.ql | 25 ++++++++ .github/codeql/queries/qlpack.yml | 4 ++ .github/codeql/report_results.py | 35 +++++++++++ .../codeql/test/openssl-return-check-bad.c | 6 -- .../openssl-return-check.expected | 2 + .../openssl-return-check.qlref | 1 + .../codeql/test/openssl-return-check/test.c | 45 ++++++++++++++ .github/codeql/test/qlpack.yml | 5 ++ .github/workflows/codeql.yml | 62 +++++++++++++------ .github/workflows/pr.yml | 6 +- CONTRIBUTING.md | 42 ++++++++++--- 14 files changed, 205 insertions(+), 63 deletions(-) create mode 100644 .github/codeql/codeql-config.yml create mode 100644 .github/codeql/codeql-workspace.yml delete mode 100644 .github/codeql/openssl-return-check.ql create mode 100644 .github/codeql/queries/openssl-return-check.ql create mode 100644 .github/codeql/queries/qlpack.yml create mode 100755 .github/codeql/report_results.py delete mode 100644 .github/codeql/test/openssl-return-check-bad.c create mode 100644 .github/codeql/test/openssl-return-check/openssl-return-check.expected create mode 100644 .github/codeql/test/openssl-return-check/openssl-return-check.qlref create mode 100644 .github/codeql/test/openssl-return-check/test.c create mode 100644 .github/codeql/test/qlpack.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000000..fb4cafd616 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,6 @@ +# Configuration for .github/workflows/codeql.yml: run only liboqs's own +# queries. GitHub's stock suite already runs through CodeQL default setup. +name: liboqs custom queries +disable-default-queries: true +queries: + - uses: ./.github/codeql/queries diff --git a/.github/codeql/codeql-workspace.yml b/.github/codeql/codeql-workspace.yml new file mode 100644 index 0000000000..32544f86ac --- /dev/null +++ b/.github/codeql/codeql-workspace.yml @@ -0,0 +1,3 @@ +# Lets the test pack resolve the query pack locally. +provide: + - "*/qlpack.yml" diff --git a/.github/codeql/openssl-return-check.ql b/.github/codeql/openssl-return-check.ql deleted file mode 100644 index 5ef7e8dd94..0000000000 --- a/.github/codeql/openssl-return-check.ql +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @name Unchecked OpenSSL EVP return value - * @description Calls to OpenSSL EVP functions whose return value is not - * checked by the OQS_OPENSSL_GUARD macro may silently ignore - * errors, leading to undefined behaviour. - * @kind problem - * @problem.severity warning - * @precision medium - * @id cpp/openssl-unchecked-return - * @tags security - * correctness - */ - -import cpp - -from FunctionCall call, Function f -where - f = call.getTarget() and - f.getName().matches("EVP%") and - not f.getType() instanceof PointerType and - not f.getType() instanceof VoidType and - not exists(MacroAccess m | - m.getLocation().subsumes(call.getLocation()) and - m.getMacroName() = "OQS_OPENSSL_GUARD" - ) -select call, "Return value of " + f.getName() + "() is not checked by OQS_OPENSSL_GUARD." diff --git a/.github/codeql/queries/openssl-return-check.ql b/.github/codeql/queries/openssl-return-check.ql new file mode 100644 index 0000000000..ec3b29ad56 --- /dev/null +++ b/.github/codeql/queries/openssl-return-check.ql @@ -0,0 +1,25 @@ +/** + * @name Unchecked OpenSSL EVP return value + * @description Calls to OpenSSL EVP functions whose status code is discarded + * silently ignore OpenSSL errors. Check the result, normally + * with the OQS_OPENSSL_GUARD macro. + * @kind problem + * @problem.severity warning + * @precision high + * @id liboqs/openssl-unchecked-return + * @tags security + * correctness + */ + +import cpp + +from FunctionCall call, Function f +where + f = call.getTarget() and + f.getName().matches("EVP%") and + not f.getUnspecifiedType() instanceof PointerType and + not f.getUnspecifiedType() instanceof VoidType and + // The status code is thrown away. Tested (e.g. by OQS_OPENSSL_GUARD), stored, + // or explicitly discarded with a (void) cast are all accepted. + call instanceof ExprInVoidContext +select call, "Return value of " + f.getName() + "() is discarded; check it with OQS_OPENSSL_GUARD." diff --git a/.github/codeql/queries/qlpack.yml b/.github/codeql/queries/qlpack.yml new file mode 100644 index 0000000000..31e2c96c9a --- /dev/null +++ b/.github/codeql/queries/qlpack.yml @@ -0,0 +1,4 @@ +name: liboqs/ci-queries +version: 0.0.0 +dependencies: + codeql/cpp-all: "*" diff --git a/.github/codeql/report_results.py b/.github/codeql/report_results.py new file mode 100755 index 0000000000..005bf847dd --- /dev/null +++ b/.github/codeql/report_results.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT + +import json +import sys + +# Report the results of liboqs's custom CodeQL queries, failing if there are any. + +def escape(text): + # Characters with a special meaning in workflow command messages + return text.replace('%', '%25').replace('\r', '%0D').replace('\n', '%0A') + + +def main(sarif_files): + count = 0 + for sarif_file in sarif_files: + with open(sarif_file) as f: + sarif = json.load(f) + for run in sarif['runs']: + for result in run.get('results', []): + location = result['locations'][0]['physicalLocation'] + path = location['artifactLocation']['uri'] + line = location['region']['startLine'] + message = f"[{result['ruleId']}] {result['message']['text']}" + print(f"::error file={path},line={line}::{escape(message)}") + count += 1 + + print(f"{count} result(s) from liboqs CodeQL queries") + return 1 if count else 0 + + +if __name__ == '__main__': + if len(sys.argv) < 2: + sys.exit(__doc__) + sys.exit(main(sys.argv[1:])) diff --git a/.github/codeql/test/openssl-return-check-bad.c b/.github/codeql/test/openssl-return-check-bad.c deleted file mode 100644 index 42d8fb0e86..0000000000 --- a/.github/codeql/test/openssl-return-check-bad.c +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -/* expect-fail: EVP return value not checked via OQS_OPENSSL_GUARD */ -#include -void bad_example(EVP_MD_CTX *ctx, const EVP_MD *md) { - EVP_DigestInit_ex(ctx, md, NULL); -} diff --git a/.github/codeql/test/openssl-return-check/openssl-return-check.expected b/.github/codeql/test/openssl-return-check/openssl-return-check.expected new file mode 100644 index 0000000000..2e1a0b8af9 --- /dev/null +++ b/.github/codeql/test/openssl-return-check/openssl-return-check.expected @@ -0,0 +1,2 @@ +| test.c:22:2:22:18 | call to EVP_DigestInit_ex | Return value of EVP_DigestInit_ex() is discarded; check it with OQS_OPENSSL_GUARD. | +| test.c:23:2:23:17 | call to EVP_DigestUpdate | Return value of EVP_DigestUpdate() is discarded; check it with OQS_OPENSSL_GUARD. | diff --git a/.github/codeql/test/openssl-return-check/openssl-return-check.qlref b/.github/codeql/test/openssl-return-check/openssl-return-check.qlref new file mode 100644 index 0000000000..196399338d --- /dev/null +++ b/.github/codeql/test/openssl-return-check/openssl-return-check.qlref @@ -0,0 +1 @@ +openssl-return-check.ql diff --git a/.github/codeql/test/openssl-return-check/test.c b/.github/codeql/test/openssl-return-check/test.c new file mode 100644 index 0000000000..29de78b45d --- /dev/null +++ b/.github/codeql/test/openssl-return-check/test.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +// Test cases for queries/openssl-return-check.ql. +// Self-contained: OpenSSL is stubbed so no headers are needed. + +typedef struct evp_md_ctx_st EVP_MD_CTX; +typedef struct evp_md_st EVP_MD; + +int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, void *impl); +int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, unsigned long cnt); +EVP_MD_CTX *EVP_MD_CTX_new(void); +void EVP_MD_CTX_free(EVP_MD_CTX *ctx); +void exit(int status); + +#define OQS_OPENSSL_GUARD(x) \ + do { \ + if (1 != (x)) { \ + exit(1); \ + } \ + } while (0) + +void bad_discarded(EVP_MD_CTX *ctx, const EVP_MD *md) { + EVP_DigestInit_ex(ctx, md, 0); // BAD + EVP_DigestUpdate(ctx, "x", 1); // BAD +} + +void good_guarded(EVP_MD_CTX *ctx, const EVP_MD *md) { + OQS_OPENSSL_GUARD(EVP_DigestInit_ex(ctx, md, 0)); +} + +int good_tested(EVP_MD_CTX *ctx, const EVP_MD *md) { + if (EVP_DigestInit_ex(ctx, md, 0) != 1) { + return -1; + } + int ok = EVP_DigestUpdate(ctx, "x", 1); + return ok ? 0 : -1; +} + +void good_explicitly_discarded(EVP_MD_CTX *ctx) { + (void)EVP_DigestUpdate(ctx, "x", 1); +} + +void good_pointer_and_void_returns(void) { + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_MD_CTX_free(ctx); +} diff --git a/.github/codeql/test/qlpack.yml b/.github/codeql/test/qlpack.yml new file mode 100644 index 0000000000..86cb8d6c91 --- /dev/null +++ b/.github/codeql/test/qlpack.yml @@ -0,0 +1,5 @@ +name: liboqs/ci-queries-tests +version: 0.0.0 +extractor: cpp +dependencies: + liboqs/ci-queries: "*" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cc85ffdc54..12d355676c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,36 +1,60 @@ -name: CodeQL analysis +name: CodeQL custom queries + permissions: contents: read + +# Runs liboqs's own CodeQL queries (.github/codeql/queries) and fails if any of +# them reports a result. Results are not uploaded to code scanning: this +# repository uses CodeQL default setup, which rejects CodeQL SARIF uploads from +# other workflows. on: workflow_call: workflow_dispatch: - push: - branches: [main] - pull_request: - branches: [main] + jobs: - codeql: - permissions: - contents: read - security-events: write + + query-tests: + name: CodeQL query unit tests + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v4 + - name: Set up CodeQL CLI + id: init + uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 + with: + languages: cpp + build-mode: none + - name: Run query unit tests + env: + CODEQL: ${{ steps.init.outputs.codeql-path }} + run: '"$CODEQL" test run .github/codeql/test' + + analyze: + name: CodeQL custom queries + runs-on: ubuntu-latest env: + # Works around a crash while finalizing the database (see #2415). CODEQL_EXTRACTOR_CPP_TRAP_CACHING: false - name: CodeQL - runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 + uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: languages: cpp - queries: security-and-quality,./.github/codeql/openssl-return-check.ql + build-mode: manual + config-file: ./.github/codeql/codeql-config.yml - name: Build liboqs + # CodeQL only sees compiled code: build the default algorithms, with + # OpenSSL backing every primitive that supports it. run: | - cmake -S . -B build -DOQS_MINIMAL_BUILD="KEM_ml_kem_768;SIG_ml_dsa_65" - cmake --build build --parallel $(nproc) - - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3 + cmake -S . -B build -DOQS_USE_AES_OPENSSL=ON -DOQS_USE_SHA2_OPENSSL=ON -DOQS_USE_SHA3_OPENSSL=ON + cmake --build build --parallel "$(nproc)" + - name: Run CodeQL queries + uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: - category: "/language:cpp" upload: false + output: sarif-results + - name: Report results + run: python3 .github/codeql/report_results.py sarif-results/*.sarif diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 009c8663a7..f7a9c7d9d1 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -89,9 +89,5 @@ jobs: security-events: write codeql: - needs: basic-checks + needs: check-push-result uses: ./.github/workflows/codeql.yml - secrets: inherit - permissions: - contents: read - security-events: write diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 612b8950aa..eac650617b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -120,10 +120,38 @@ please check the [open issues tagged "good first issue" or "help wanted"](https: You can also take a look at the [contribution wishlist](https://github.com/open-quantum-safe/liboqs/wiki/Contribution-wishlist) for more substantial contributions we are interested in. -## Verifying the CodeQL OpenSSL return-check query -The query detects EVP_* calls not wrapped in OQS_OPENSSL_GUARD. -A deliberately bad example lives in -.github/codeql/test/openssl-return-check-bad.c. -When CodeQL runs, any match against this file confirms the query -is scanning correctly. CI will flag new violations automatically -on every PR. +## Custom CodeQL queries + +There are two ways in which CodeQL runs in this repo: via GitHub's standard +code scanning settings, and via `.github/workflows/codeql.yml`: +- GitHub's standard scans are run via liboqs' repository code scanning settings. + Results appear as code scanning alerts in the repository's Security tab. +- `.github/workflows/codeql.yml` runs liboqs's own CodeQL queries on every pull + request and fails if any of them reports a result. Each result is shown as an + error annotation on the pull request and in the job log. Its results are not uploaded + to code scanning: while default setup is enabled, GitHub rejects CodeQL results uploaded + by other workflows ([details](https://docs.github.com/en/code-security/reference/code-scanning/sarif-files/troubleshoot-sarif-uploads/default-setup-enabled)). + The workflow can also be run manually from the repository's Actions tab. + +The custom queries and their tests live in `.github/codeql/`: +- `.github/codeql/queries/` holds the queries (e.g. `openssl-return-check.ql`, + which flags `EVP_*` calls whose status code is discarded instead of checked + with `OQS_OPENSSL_GUARD`) +- `.github/codeql/test//` holds a unit test for each query: a small + C file with good and bad examples, a `.qlref` naming the query, and a + `.expected` file listing the results the query must report. + +The commands below need the CodeQL CLI: either install the GitHub CLI extension +(`gh extension install github/gh-codeql`, then use `gh codeql` in place of +`codeql`), or download a CodeQL bundle from the +[github/codeql-action releases](https://github.com/github/codeql-action/releases). + +To add a query, add the `.ql` file and its test directory, then generate the +`.expected` file and check that it lists exactly the bad examples: + +```bash +codeql test run --learn .github/codeql/test +``` + +Run `codeql test run .github/codeql/test` (without `--learn`) to check that +existing queries still behave as expected. From e97c3766d05e20a246fea3ee9bd4e55c66950cdb Mon Sep 17 00:00:00 2001 From: Owen Waldron Date: Fri, 16 Oct 2026 13:13:52 -0400 Subject: [PATCH 11/12] Small documentation changes to codeql workflow. Assited-by: Claude:claude-opus-5.5 Signed-off-by: Owen Waldron --- .github/codeql/report_results.py | 2 +- .github/workflows/codeql.yml | 4 +++- CONTRIBUTING.md | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/codeql/report_results.py b/.github/codeql/report_results.py index 005bf847dd..6714916f8a 100755 --- a/.github/codeql/report_results.py +++ b/.github/codeql/report_results.py @@ -31,5 +31,5 @@ def main(sarif_files): if __name__ == '__main__': if len(sys.argv) < 2: - sys.exit(__doc__) + sys.exit("usage: report_results.py SARIF_FILE...") sys.exit(main(sys.argv[1:])) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 12d355676c..e3812ac5b8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,9 @@ jobs: config-file: ./.github/codeql/codeql-config.yml - name: Build liboqs # CodeQL only sees compiled code: build the default algorithms, with - # OpenSSL backing every primitive that supports it. + # OpenSSL backing every primitive that supports it. On x86_64 runners + # OQS_DIST_BUILD forces OQS_USE_AES_OPENSSL off, so aes_ossl.c is not + # analysed. run: | cmake -S . -B build -DOQS_USE_AES_OPENSSL=ON -DOQS_USE_SHA2_OPENSSL=ON -DOQS_USE_SHA3_OPENSSL=ON cmake --build build --parallel "$(nproc)" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eac650617b..fac414a708 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,8 +127,9 @@ code scanning settings, and via `.github/workflows/codeql.yml`: - GitHub's standard scans are run via liboqs' repository code scanning settings. Results appear as code scanning alerts in the repository's Security tab. - `.github/workflows/codeql.yml` runs liboqs's own CodeQL queries on every pull - request and fails if any of them reports a result. Each result is shown as an - error annotation on the pull request and in the job log. Its results are not uploaded + request and fails if any of them reports a result. Every result is listed in + the job log; GitHub also shows the first 10 as error annotations on the pull + request. Its results are not uploaded to code scanning: while default setup is enabled, GitHub rejects CodeQL results uploaded by other workflows ([details](https://docs.github.com/en/code-security/reference/code-scanning/sarif-files/troubleshoot-sarif-uploads/default-setup-enabled)). The workflow can also be run manually from the repository's Actions tab. From d3757ce02a85d82cf4d579f91b495e8edb44199a Mon Sep 17 00:00:00 2001 From: Owen Waldron Date: Fri, 16 Oct 2026 12:48:25 -0400 Subject: [PATCH 12/12] TEST ONLY: run just the CodeQL job on the fork Temporary commit for the fork test run; to be dropped before review. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Owen Waldron --- .github/workflows/pr.yml | 79 +------------------------------------- .github/workflows/push.yml | 2 +- 2 files changed, 2 insertions(+), 79 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f7a9c7d9d1..04249f11d3 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -9,85 +9,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# TEST ONLY: trimmed to the CodeQL job for the fork test run. Drop this commit. jobs: - check-push-result: - name: Check if push CI passed for this commit - runs-on: ubuntu-latest - outputs: - skip_basic: ${{ steps.set_output.outputs.skip_basic }} - steps: - - name: Determine skip status - id: set_output - run: | - if [ "${{ github.event.pull_request.head.repo.fork }}" = "true" ]; then - echo "Fork PR - basic-checks cannot be skipped" - echo "skip_basic=false" >> $GITHUB_OUTPUT - else - SHA="${{ github.event.pull_request.head.sha }}" - RESPONSE=$(curl -s -H "Authorization: Bearer $GH_TOKEN" \ - "https://api.github.com/repos/${{ github.repository }}/actions/runs?head_sha=$SHA&per_page=5") - - # Pruefe ob "Push tests" Workflow mit conclusion=success existiert - if echo "$RESPONSE" | grep -q '"name": "Push tests"' && \ - echo "$RESPONSE" | grep -q '"conclusion": "success"'; then - echo "Found successful Push tests for SHA $SHA - skipping basic-checks" - echo "skip_basic=true" >> $GITHUB_OUTPUT - else - echo "No successful Push tests found for SHA $SHA" - echo "skip_basic=false" >> $GITHUB_OUTPUT - fi - fi - env: - GH_TOKEN: ${{ github.token }} - - basic-checks: - needs: check-push-result - if: needs.check-push-result.outputs.skip_basic == 'false' - concurrency: - group: basic-checks-${{ github.sha }} - cancel-in-progress: true - uses: ./.github/workflows/basic.yml - permissions: - contents: read - actions: write # required to cancel the run on fail-fast (see basic.yml) - - platform-tests: - needs: check-push-result - uses: ./.github/workflows/platforms.yml - - code-coverage: - needs: check-push-result - if: needs.check-push-result.outputs.skip_basic == 'false' - uses: ./.github/workflows/code-coverage.yml - secrets: inherit - - nix-full-build: - needs: check-push-result - if: needs.check-push-result.outputs.skip_basic == 'false' && (contains( github.event.pull_request.title, '[full tests]' ) || contains( github.event.pull_request.body, '[full tests]' )) - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: DeterminateSystems/nix-installer-action@3138316df39ed29be04236d7ffc686fa525866aa # v23 - - name: Checkout code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v4 - - name: Check devShell - run: nix develop --command echo - - name: Check flake syntax - run: nix flake check --no-build - - name: Full flake build - run: nix build - - scorecard: - needs: check-push-result - uses: ./.github/workflows/supplychain.yml - secrets: inherit - permissions: - contents: read - id-token: write - security-events: write - codeql: - needs: check-push-result uses: ./.github/workflows/codeql.yml diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 2eb2db5bea..eb285fdcdd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -5,7 +5,7 @@ permissions: on: push: - branches-ignore: 'main' + branches-ignore: ['main', 'feat/revived-codeql-openssl-return-check'] # TEST ONLY concurrency: group: ${{ github.workflow }}-${{ github.ref }}