From 55836e72e367b718c6f45bc12f490f6fa7fcc731 Mon Sep 17 00:00:00 2001 From: Artem Shishkin Date: Mon, 13 Jul 2026 22:28:50 +0400 Subject: [PATCH] ci: pin workflow actions --- .github/workflows/ci.yml | 13 ++++++++++--- tests/test_ci_supply_chain.py | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 tests/test_ci_supply_chain.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92116cd..ab80801 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,9 @@ on: pull_request: branches: ["master", "main"] +permissions: + contents: read + jobs: test: name: Tests + Lint @@ -37,10 +40,12 @@ jobs: - 6379:6379 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.12" cache: "pip" @@ -83,7 +88,9 @@ jobs: LLM_MODE: demo steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Start default application topology run: docker compose up --build -d postgres redis migrate agent diff --git a/tests/test_ci_supply_chain.py b/tests/test_ci_supply_chain.py new file mode 100644 index 0000000..7dabafb --- /dev/null +++ b/tests/test_ci_supply_chain.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +WORKFLOW = ROOT / ".github/workflows/ci.yml" + + +def test_ci_actions_are_immutable_and_least_privilege() -> None: + workflow = WORKFLOW.read_text(encoding="utf-8") + action_refs = re.findall(r"uses:\s*([^@\s]+)@([^\s#]+)", workflow) + + assert action_refs + assert all(re.fullmatch(r"[0-9a-f]{40}", revision) for _, revision in action_refs) + assert "\npermissions:\n contents: read\n" in workflow + checkout_count = sum(action == "actions/checkout" for action, _ in action_refs) + assert checkout_count == 2 + assert workflow.count("persist-credentials: false") == checkout_count