From 4f5e7b7da063f0e114399c31079afc069c92137d Mon Sep 17 00:00:00 2001 From: arpan Date: Sun, 6 Sep 2026 22:25:17 +0530 Subject: [PATCH] CodeQL: static analysis on the pull request, not in a weekly digest OpenSSF Scorecard reads SAST as 0/10 -- "0 commits out of 30 are checked with a SAST tool" -- and it is one of the six zeroes holding the score at 6.0. `security-and-quality` rather than the default suite. This is a small codebase sitting in the execution path of consequential actions, and the extra queries cost a few minutes a project this size can afford. `build-mode: none`, because the core is stdlib plus pyyaml and click and every extra is imported lazily, so there is nothing to build for the analyser to see the code. **It deliberately does not gate a merge**, and that is recorded in a test rather than left as an omission somebody later reads as an oversight. A static analyser's first run on an unfamiliar codebase is a reading list, not a verdict; the required checks stay `check (3.11)`, `check (3.12)` and `package`. If that changes, `test_codeql_does_not_gate_a_merge` is where the argument gets rewritten. Runs on pull requests, on pushes to main, and weekly -- the schedule so that a new query release reaches code that was merged before it existed. Both actions are pinned to the SHA `scorecard.yml` already uses for `upload-sarif`. Six mutations, six caught: the pull_request trigger dropped, the wrong language analysed, security-events downgraded to read, the timeout removed, the analyze step deleted, and the schedule dropped. --- .github/workflows/codeql.yml | 46 ++++++++++++++++++++++++++++++++ tests/test_repository_signals.py | 28 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..f06250f6 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,46 @@ +name: CodeQL + +# Static analysis on every pull request, so a finding arrives while the change is still being +# read rather than in a weekly digest nobody opens. `security-and-quality` rather than the +# default suite: this is a small codebase in the execution path of consequential actions, and +# the extra queries cost a few minutes that a project this size can afford. +# +# Findings land in the repository's code-scanning tab. Nothing here gates a merge -- the +# required checks stay `check (3.11)`, `check (3.12)` and `package` -- because a static +# analyser's first run on an unfamiliar codebase is a reading list, not a verdict. + +on: + pull_request: + push: + branches: [main] + schedule: + - cron: "41 5 * * 1" + +permissions: + contents: read + +jobs: + analyze: + name: Analyze Python + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + security-events: write # to upload the findings to code scanning + contents: read + + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + languages: python + # The core is stdlib plus pyyaml and click, and every extra is imported lazily, so + # there is nothing to build and nothing to install for the analyser to see the code. + build-mode: none + queries: security-and-quality + + - uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + category: "/language:python" diff --git a/tests/test_repository_signals.py b/tests/test_repository_signals.py index 735e7691..520e299c 100644 --- a/tests/test_repository_signals.py +++ b/tests/test_repository_signals.py @@ -125,6 +125,34 @@ def test_the_scorecard_workflow_publishes_and_the_readme_shows_it(): assert "api.scorecard.dev/projects/github.com/CTRLRun/ctrlrun/badge" in readme +def test_codeql_analyses_every_pull_request(): + """Static analysis that runs on a schedule alone reports findings against code that was + merged a week ago. This asserts it runs on the pull request, covers the language the + project is written in, and can write its findings somewhere a person will see them.""" + workflow = _workflow("codeql.yml") + triggers = workflow[True] if True in workflow else workflow["on"] + assert "pull_request" in triggers, "findings would arrive after the merge" + assert "schedule" in triggers, "a new query release should reach old code" + + job = workflow["jobs"]["analyze"] + assert workflow["permissions"] == {"contents": "read"} + assert job["permissions"]["security-events"] == "write" + assert job["timeout-minutes"] <= 30, "an analysis that can hang is a queue nobody clears" + + init = next(s for s in job["steps"] if "codeql-action/init" in str(s.get("uses", ""))) + assert init["with"]["languages"] == "python" + assert "codeql-action/analyze" in "".join(str(s.get("uses", "")) for s in job["steps"]) + + +def test_codeql_does_not_gate_a_merge(): + """Deliberate, and recorded here so it is a decision rather than an oversight: a static + analyser's first run on an unfamiliar codebase is a reading list, not a verdict. The + required checks stay the three that were already required, and the workflow's own comment + says so -- if that changes, this test is where the argument gets rewritten.""" + workflow = (WORKFLOWS / "codeql.yml").read_text(encoding="utf-8") + assert "Nothing here gates a merge" in workflow + + # --- community files -----------------------------------------------------------------------