From 9121220c13e2b93a1a8a17fc583588ffad3311c1 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 21 Sep 2026 12:27:43 -0400 Subject: [PATCH] Bump gitpython to 3.1.62 and soupsieve to 2.9.2 Both pins are flagged by pip-audit against the current lock: gitpython by CVE-2026-87817, CVE-2026-87818 and CVE-2026-87819, and the transitive soupsieve by GHSA-gjv8-xp57-g29c and GHSA-j934-xhv5-fg8f. Normalize the gitpython requirement to its lowercase PEP 503 name, matching uv.lock and the rest of the pins. Dependabot's uv updater errors on this entry with dependency_file_content_not_changed; the rename is a candidate fix for that. Move pip-audit out of the Unit Tests workflow into a Dependency Audit workflow with a daily schedule. The audit compares the lockfile against databases that publish continuously, so its result tracks the clock rather than the commit and needs a trigger to match. A failing scheduled run has no pull request to report on, so it opens a tracking issue and closes it once the audit is clean. Drop the paths filters from the pull_request triggers on Unit Tests and Version Check. A status check behind a paths filter produces no check context on a pull request that misses the filter, so those jobs cannot be marked required while the filters are in place. The push triggers keep theirs. --- .github/workflows/dependency-audit.yml | 157 +++++++++++++++++++++++++ .github/workflows/python-tests.yml | 13 +- .github/workflows/version-check.yml | 8 +- CHANGELOG.md | 15 +++ pyproject.toml | 4 +- socketsecurity/__init__.py | 2 +- uv.lock | 16 +-- 7 files changed, 187 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/dependency-audit.yml diff --git a/.github/workflows/dependency-audit.yml b/.github/workflows/dependency-audit.yml new file mode 100644 index 00000000..903dfdb0 --- /dev/null +++ b/.github/workflows/dependency-audit.yml @@ -0,0 +1,157 @@ +name: Dependency Audit + +env: + PYTHON_VERSION: "3.12" + +# pip-audit compares the lockfile against advisory databases that publish +# continuously, so the result tracks the clock, not the commit. Hence the cron. +on: + schedule: + - cron: "17 6 * * *" + # Unfiltered: a required check behind a paths filter never reports on a + # pull request that misses the filter, which blocks the merge. + pull_request: + push: + branches: [main] + paths: + - "pyproject.toml" + - "uv.lock" + - ".github/workflows/dependency-audit.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: dependency-audit-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + dependency-audit: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + persist-credentials: false + + - name: 🐍 setup python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: 🛠️ install uv + run: | + python -m pip install --upgrade pip + pip install uv + + - name: 🛡️ pip-audit (known CVEs in the locked deps) + id: audit + run: | + uv export --no-hashes --no-emit-project --format requirements-txt > /tmp/req-audit.txt + + set +e + uvx pip-audit --strict --progress-spinner off --disable-pip --no-deps \ + -r /tmp/req-audit.txt 2>&1 | tee /tmp/audit.log + status=${PIPESTATUS[0]} + set -e + + # Written before the exit so the next step can quote it. + { + echo 'report<> "$GITHUB_OUTPUT" + + exit "$status" + + # A scheduled run has no pull request to turn red, so record it instead. + - name: 📮 open or update the tracking issue + if: always() && steps.audit.outcome == 'failure' && github.event_name == 'schedule' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + AUDIT_REPORT: ${{ steps.audit.outputs.report }} + with: + script: | + const marker = ''; + const title = 'Dependency audit: known vulnerabilities in the locked dependencies'; + const runUrl = + `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}` + + `/actions/runs/${context.runId}`; + const body = [ + marker, + '`pip-audit` found known vulnerabilities in the locked dependency set.', + '', + 'Bump the affected pins in `pyproject.toml`, run `uv lock`, and open a PR.', + '', + '```', + process.env.AUDIT_REPORT.trim(), + '```', + '', + `Run: ${runUrl}`, + `Last checked: ${new Date().toISOString()}`, + ].join('\n'); + + const existing = await github.paginate(github.rest.issues.listForRepo, { + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + per_page: 100, + }); + const tracking = existing.find( + (issue) => !issue.pull_request && issue.body && issue.body.includes(marker), + ); + + if (tracking) { + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: tracking.number, + body, + }); + core.notice(`Updated tracking issue #${tracking.number}`); + } else { + const created = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title, + body, + labels: ['dependencies'], + }); + core.notice(`Opened tracking issue #${created.data.number}`); + } + + - name: ✅ close the tracking issue once the audit is clean + if: always() && steps.audit.outcome == 'success' && github.event_name == 'schedule' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const marker = ''; + const existing = await github.paginate(github.rest.issues.listForRepo, { + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + per_page: 100, + }); + const tracking = existing.find( + (issue) => !issue.pull_request && issue.body && issue.body.includes(marker), + ); + if (!tracking) { + return; + } + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: tracking.number, + body: 'The scheduled audit is clean again. Closing.', + }); + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: tracking.number, + state: 'closed', + }); diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index f05b27c9..44661669 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -13,14 +13,9 @@ on: - "pyproject.toml" - "uv.lock" - ".github/workflows/python-tests.yml" + # Unfiltered: a required check behind a paths filter never reports on a + # pull request that misses the filter, which blocks the merge. pull_request: - paths: - - "socketsecurity/**/*.py" - - "tests/unit/**/*.py" - - "tests/core/**/*.py" - - "pyproject.toml" - - "uv.lock" - - ".github/workflows/python-tests.yml" workflow_dispatch: permissions: @@ -62,10 +57,6 @@ jobs: from socketsecurity.config import CliConfig print('import smoke OK') " - - name: 🛡️ pip-audit (known CVEs in the locked deps) - run: | - uv export --no-hashes --no-emit-project --format requirements-txt > /tmp/req-audit.txt - uvx pip-audit --strict --progress-spinner off --disable-pip --no-deps -r /tmp/req-audit.txt ruff: runs-on: ubuntu-latest diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index 292e2980..d505c063 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -1,13 +1,9 @@ name: Version Check on: + # Unfiltered: a required check behind a paths filter never reports on a + # pull request that misses the filter, which blocks the merge. pull_request: types: [opened, synchronize, ready_for_review] - paths: - - 'socketsecurity/**' - - 'pyproject.toml' - - 'uv.lock' - # Included so a change to the check itself is exercised by its own PR. - - '.github/workflows/version-check.yml' permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 0598af05..6c55f1c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 2.9.5 + +### Changed: bump pinned gitpython to 3.1.62 and soupsieve to 2.9.2 + +- Bumped `gitpython` from `3.1.59` to `3.1.62` (CVE-2026-87817, CVE-2026-87818, + CVE-2026-87819) and the transitive `soupsieve` pin from `2.8.4` to `2.9.2` + (GHSA-gjv8-xp57-g29c, GHSA-j934-xhv5-fg8f). +- Normalized the `gitpython` requirement to its lowercase PEP 503 name. + +### Changed: audit the locked dependencies on a schedule + +- `pip-audit` moved out of the Unit Tests workflow into a new Dependency Audit + workflow that also runs daily, so advisories published against unchanged pins + are reported on their own schedule rather than on the next push. + ## 2.9.4 ### Changed: bump pinned @coana-tech/cli to 15.10.46 diff --git a/pyproject.toml b/pyproject.toml index 2425201b..0b880821 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,14 +6,14 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.9.4" +version = "2.9.5" requires-python = ">= 3.11" license = {"file" = "LICENSE"} dependencies = [ "requests==2.34.2", "mdutils==1.8.1", "prettytable==3.18.0", - "GitPython==3.1.59", + "gitpython==3.1.62", "packaging==26.3", "python-dotenv==1.2.3", "socketdev==3.6.0", diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index c0b5e467..3e5f0a0c 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,3 +1,3 @@ __author__ = 'socket.dev' -__version__ = '2.9.4' +__version__ = '2.9.5' USER_AGENT = f'SocketPythonCLI/{__version__}' diff --git a/uv.lock b/uv.lock index e3fdbc1f..3403266d 100644 --- a/uv.lock +++ b/uv.lock @@ -575,14 +575,14 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.59" +version = "3.1.62" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/dc/126b28e76b24a9268ba931ad3e012f71ebdadf62fd9f17758f7074bb0b20/gitpython-3.1.59.tar.gz", hash = "sha256:0a1475cfdc38a5bfba1a3e9a4a9da52a39749ecec322b772915c019f94e5b7e4", size = 230445, upload-time = "2026-08-10T12:03:20.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/db/3ca813cbacb23ab6fe46ff38a9b5ef8e73e970c8051f2ce903aacafe0446/gitpython-3.1.62.tar.gz", hash = "sha256:1791de66309bc0c7cfca40bf8d2e3de7ca091cbf94e6051be1ad0722c61062af", size = 231728, upload-time = "2026-09-07T02:57:21.155Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/ed/ae57eb7d344f43f87b74b3a281ead6ec7d6394eef72a7b1dcb28dd089550/gitpython-3.1.59-py3-none-any.whl", hash = "sha256:67a82f537384578643624c8b2c531938a9b82be431663e575dcf638526631d4c", size = 220996, upload-time = "2026-08-10T12:03:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/d6/0b/29d7965215f8ef830a7ca1f42997fe13e5693d85e9edb18f938d063ef5f2/gitpython-3.1.62-py3-none-any.whl", hash = "sha256:7002251225e10e29d2e1f49e6532613fe5d5d9f0b6f1f02997a52b38fe56899e", size = 222753, upload-time = "2026-09-07T02:57:19.762Z" }, ] [[package]] @@ -1293,7 +1293,7 @@ wheels = [ [[package]] name = "socketsecurity" -version = "2.9.4" +version = "2.9.5" source = { editable = "." } dependencies = [ { name = "beautifulsoup4" }, @@ -1335,7 +1335,7 @@ requires-dist = [ { name = "beautifulsoup4", specifier = "==4.15.0" }, { name = "brotli", marker = "platform_python_implementation == 'CPython'", specifier = "==1.2.0" }, { name = "brotlicffi", marker = "platform_python_implementation != 'CPython'", specifier = "==1.2.0.2" }, - { name = "gitpython", specifier = "==3.1.59" }, + { name = "gitpython", specifier = "==3.1.62" }, { name = "hatch", marker = "extra == 'dev'", specifier = "==1.18.0" }, { name = "markdown", specifier = "==3.10.3" }, { name = "mdutils", specifier = "==1.8.1" }, @@ -1361,11 +1361,11 @@ dev = [{ name = "pre-commit", specifier = ">=4.3.0" }] [[package]] name = "soupsieve" -version = "2.8.4" +version = "2.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/99/a6ca3beb3ccacb41fb3321d8a60e5566f9e6467601ef8eba6a17e1b89778/soupsieve-2.9.2.tar.gz", hash = "sha256:4a55d8cf158a9c2e587fa4922f1bbb91d68ac829e2d6f25403a85747c71daf74", size = 122445, upload-time = "2026-08-07T00:57:24.801Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, + { url = "https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl", hash = "sha256:8089a26fd974ca7a1f30276d3d8492ab266ab15af581642dfe8aa162e0c1c823", size = 37370, upload-time = "2026-08-07T00:57:23.524Z" }, ] [[package]]