From 72407a200daee752068f6e7c62d12072b2d190ad Mon Sep 17 00:00:00 2001 From: longieirl Date: Mon, 4 May 2026 11:23:56 +0100 Subject: [PATCH 1/2] fix(sec): harden remaining workflow shell injection points and urllib guard Move all remaining ${{ github.* }} and ${{ needs.*.outputs.* }} context expressions out of run: blocks into env: in release.yml, ci.yml, and thank-you-sync.yml. Addresses the injection pattern found by independent code review on PR #170 (closes #168 follow-on gaps). Also adds a PyPI name regex guard in supply_chain_risk.py to reject package names that do not conform to PEP 508, future-proofing the urllib call before this script is promoted to CI (closes #169). --- .github/workflows/ci.yml | 4 +++- .github/workflows/release.yml | 25 +++++++++++++++++-------- .github/workflows/thank-you-sync.yml | 6 ++++-- scripts/supply_chain_risk.py | 5 +++++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6b4720..fe15197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -371,9 +371,11 @@ jobs: cache-to: type=gha,mode=max - name: Smoke-test image + env: + PR_NUMBER: ${{ github.event.pull_request.number }} run: | docker run --rm \ - bankstatementsprocessor:pr-${{ github.event.pull_request.number }} \ + bankstatementsprocessor:pr-${PR_NUMBER} \ python -c "import bankstatements_free; import bankstatements_core; print('imports OK')" # --------------------------------------------------------------------------- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9bbebb..e0369f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,8 +27,9 @@ jobs: echo "Version from tag: ${TAG_VERSION}" - name: Verify version consistency + env: + TAG_VERSION: ${{ steps.get-version.outputs.version }} run: | - TAG_VERSION=${{ steps.get-version.outputs.version }} CODE_VERSION=$(grep '__version__ = ' packages/parser-core/src/bankstatements_core/__version__.py | cut -d'"' -f2) TOML_VERSION=$(grep '^version = ' packages/parser-core/pyproject.toml | cut -d'"' -f2) @@ -68,8 +69,10 @@ jobs: - name: Extract metadata id: meta + env: + RELEASE_VERSION: ${{ needs.validate-version.outputs.version }} run: | - VERSION=${{ needs.validate-version.outputs.version }} + VERSION="${RELEASE_VERSION}" MAJOR=$(echo ${VERSION} | cut -d. -f1) MINOR=$(echo ${VERSION} | cut -d. -f1-2) BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") @@ -103,9 +106,11 @@ jobs: cache-to: type=gha,mode=max - name: Verify image + env: + RELEASE_VERSION: ${{ steps.meta.outputs.version }} run: | - docker pull ghcr.io/longieirl/bankstatements:${{ steps.meta.outputs.version }} - docker inspect ghcr.io/longieirl/bankstatements:${{ steps.meta.outputs.version }} | jq '.[0].Config.Labels' + docker pull ghcr.io/longieirl/bankstatements:${RELEASE_VERSION} + docker inspect ghcr.io/longieirl/bankstatements:${RELEASE_VERSION} | jq '.[0].Config.Labels' - name: Run Trivy vulnerability scanner on release uses: aquasecurity/trivy-action@master @@ -141,8 +146,10 @@ jobs: - name: Extract changelog for version id: changelog + env: + RELEASE_VERSION: ${{ needs.validate-version.outputs.version }} run: | - VERSION=${{ needs.validate-version.outputs.version }} + VERSION="${RELEASE_VERSION}" # Extract changelog section for this version CHANGELOG=$(awk "/## \[${VERSION}\]/,/## \[/{if(/## \[${VERSION}\]/)p=1;else if(/## \[/)p=0;if(p)print}" CHANGELOG.md | tail -n +2) @@ -167,16 +174,18 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Release summary + env: + RELEASE_VERSION: ${{ needs.validate-version.outputs.version }} run: | { - echo "## 🚀 Release v${{ needs.validate-version.outputs.version }}" + echo "## 🚀 Release v${RELEASE_VERSION}" echo "" echo "### Docker Images" - echo "- \`ghcr.io/longieirl/bankstatements:${{ needs.validate-version.outputs.version }}\`" + echo "- \`ghcr.io/longieirl/bankstatements:${RELEASE_VERSION}\`" echo "- \`ghcr.io/longieirl/bankstatements:latest\`" echo "" echo "### Pull Command" echo "\`\`\`bash" - echo "docker pull ghcr.io/longieirl/bankstatements:${{ needs.validate-version.outputs.version }}" + echo "docker pull ghcr.io/longieirl/bankstatements:${RELEASE_VERSION}" echo "\`\`\`" } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/thank-you-sync.yml b/.github/workflows/thank-you-sync.yml index 87da730..ed45945 100644 --- a/.github/workflows/thank-you-sync.yml +++ b/.github/workflows/thank-you-sync.yml @@ -16,9 +16,9 @@ jobs: - name: Add all committers if new env: GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | # 1. Get all unique authors from the commits in this merged PR - PR_NUMBER=${{ github.event.pull_request.number }} COMMITTERS=$(gh pr view $PR_NUMBER --json commits --jq '.commits[].author.login' | sort -u) # 2. Loop through each committer for USER in $COMMITTERS; do @@ -32,8 +32,10 @@ jobs: fi done - name: Commit and Push + env: + PR_NUMBER: ${{ github.event.pull_request.number }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add THANKYOU.md - git diff --quiet --staged || (git commit -m "docs: add new contributors from PR #${{ github.event.pull_request.number }}" && git push) \ No newline at end of file + git diff --quiet --staged || (git commit -m "docs: add new contributors from PR #${PR_NUMBER}" && git push) \ No newline at end of file diff --git a/scripts/supply_chain_risk.py b/scripts/supply_chain_risk.py index c39e2fd..a8a0fdf 100755 --- a/scripts/supply_chain_risk.py +++ b/scripts/supply_chain_risk.py @@ -11,6 +11,7 @@ import argparse import json +import re import sys from datetime import datetime, timedelta from pathlib import Path @@ -18,6 +19,8 @@ import urllib.request import urllib.error +_PYPI_NAME_RE = re.compile(r"^[A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?$") + def load_sbom(path: Path) -> Dict[str, Any]: """Load and parse an SBOM file.""" @@ -54,6 +57,8 @@ def fetch_pypi_metadata(package_name: str) -> Dict[str, Any]: Returns empty dict if package not found or error occurs. """ + if not _PYPI_NAME_RE.match(package_name): + return {} try: url = f"https://pypi.org/pypi/{package_name}/json" with urllib.request.urlopen(url, timeout=5) as response: From 7bd0e3016a5856246d3bda3ba3bdb2682749cf61 Mon Sep 17 00:00:00 2001 From: longieirl Date: Mon, 4 May 2026 11:26:28 +0100 Subject: [PATCH 2/2] fix: quote shell variables to satisfy SC2086 (actionlint) --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 8 ++++---- .github/workflows/thank-you-sync.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe15197..aef0237 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -375,7 +375,7 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} run: | docker run --rm \ - bankstatementsprocessor:pr-${PR_NUMBER} \ + "bankstatementsprocessor:pr-${PR_NUMBER}" \ python -c "import bankstatements_free; import bankstatements_core; print('imports OK')" # --------------------------------------------------------------------------- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e0369f6..80805d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,8 +73,8 @@ jobs: RELEASE_VERSION: ${{ needs.validate-version.outputs.version }} run: | VERSION="${RELEASE_VERSION}" - MAJOR=$(echo ${VERSION} | cut -d. -f1) - MINOR=$(echo ${VERSION} | cut -d. -f1-2) + MAJOR=$(echo "${VERSION}" | cut -d. -f1) + MINOR=$(echo "${VERSION}" | cut -d. -f1-2) BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") VCS_REF=$(git rev-parse --short HEAD) @@ -109,8 +109,8 @@ jobs: env: RELEASE_VERSION: ${{ steps.meta.outputs.version }} run: | - docker pull ghcr.io/longieirl/bankstatements:${RELEASE_VERSION} - docker inspect ghcr.io/longieirl/bankstatements:${RELEASE_VERSION} | jq '.[0].Config.Labels' + docker pull "ghcr.io/longieirl/bankstatements:${RELEASE_VERSION}" + docker inspect "ghcr.io/longieirl/bankstatements:${RELEASE_VERSION}" | jq '.[0].Config.Labels' - name: Run Trivy vulnerability scanner on release uses: aquasecurity/trivy-action@master diff --git a/.github/workflows/thank-you-sync.yml b/.github/workflows/thank-you-sync.yml index ed45945..c01545f 100644 --- a/.github/workflows/thank-you-sync.yml +++ b/.github/workflows/thank-you-sync.yml @@ -19,7 +19,7 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} run: | # 1. Get all unique authors from the commits in this merged PR - COMMITTERS=$(gh pr view $PR_NUMBER --json commits --jq '.commits[].author.login' | sort -u) + COMMITTERS=$(gh pr view "$PR_NUMBER" --json commits --jq '.commits[].author.login' | sort -u) # 2. Loop through each committer for USER in $COMMITTERS; do if [ "$USER" == "web-flow" ] || [ -z "$USER" ]; then continue; fi