From b77634dd10cfa82b698745d52f2149cb758a8c52 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:29:32 -0400 Subject: [PATCH 1/8] Harden Dependabot reviews and bundle dependency updates Mirrors the Dependabot hardening done in socket-python-cli (#207/#217/#218), adapted to this SDK (no Dockerfile, no e2e fixtures, hatch/pip build path). Bundle dependency updates (supersedes 4 open Dependabot PRs): - idna 3.11 -> 3.17 (security: CVE-2026-45409 quadratic-time DoS fix) - cryptography 46.0.5 -> 46.0.7 - pygments 2.19.2 -> 2.20.0 - uv 0.9.21 -> 0.11.17 Verified via uv sync --locked, import smoke, and pytest tests/unit (102 passed). Adds grouped/cooldowned dependabot.yml (uv + github-actions), a dependabot-review workflow running anonymous Socket Firewall smoke jobs, Version Check / PR Preview skips for Dependabot PRs, and setup-sfw / setup-hatch composite actions. Co-Authored-By: Claude Opus 4.8 --- .github/actions/setup-hatch/action.yml | 13 ++ .github/actions/setup-sfw/action.yml | 31 +++++ .github/dependabot.yml | 63 +++++++++ .github/workflows/dependabot-review.yml | 126 ++++++++++++++++++ .github/workflows/pr-preview.yml | 22 ++- .github/workflows/release.yml | 11 +- .github/workflows/version-check.yml | 4 + uv.lock | 169 +++++++++++++----------- 8 files changed, 345 insertions(+), 94 deletions(-) create mode 100644 .github/actions/setup-hatch/action.yml create mode 100644 .github/actions/setup-sfw/action.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependabot-review.yml diff --git a/.github/actions/setup-hatch/action.yml b/.github/actions/setup-hatch/action.yml new file mode 100644 index 0000000..0da5160 --- /dev/null +++ b/.github/actions/setup-hatch/action.yml @@ -0,0 +1,13 @@ +name: "Set up Hatch build tooling" +description: >- + Install the pinned hatch / hatchling / virtualenv toolchain used to build + and publish the package. Assumes Python is already set up by the caller. + +runs: + using: "composite" + steps: + - shell: bash + run: | + python -m pip install --upgrade pip + pip install "virtualenv<20.36" + pip install hatchling==1.27.0 hatch==1.14.0 diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml new file mode 100644 index 0000000..f7280bb --- /dev/null +++ b/.github/actions/setup-sfw/action.yml @@ -0,0 +1,31 @@ +name: "Set up Socket Firewall (free)" +description: >- + Set up the requested language toolchain and install Socket Firewall (free + edition) so subsequent steps can run package-manager commands wrapped with + `sfw`. Free/anonymous mode -- no API token, safe on untrusted/Dependabot PRs. + +inputs: + python: + description: "Set up Python 3.12" + default: "false" + uv: + description: "Install uv (implies Python)" + default: "false" + +runs: + using: "composite" + steps: + - if: ${{ inputs.python == 'true' || inputs.uv == 'true' }} + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.12" + + # Official Socket setup action. Wires up sfw routing correctly. + - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 + with: + mode: firewall-free + + - if: ${{ inputs.uv == 'true' }} + name: Install uv + shell: bash + run: python -m pip install --upgrade pip uv diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c02d0dd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,63 @@ +# Dependabot configuration for socket-sdk-python. +# +# Design notes: +# - Python deps are grouped into a weekly PR (minor/patch), with a +# separate group for majors so breaking bumps stay reviewable. +# - GitHub Actions are grouped similarly into one weekly PR, and Dependabot +# scans both the workflows and the local composite actions. +# - 7-day cooldown enforced across all ecosystems. +# - This repo ships no Dockerfile, so there is no docker ecosystem entry. + +version: 2 +updates: + + # Python deps (uv-tracked via uv.lock) + - package-ecosystem: "uv" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 2 + groups: + python-minor-patch: + patterns: + - "*" + update-types: + - "minor" + - "patch" + python-major: + patterns: + - "*" + update-types: + - "major" + labels: + - "dependencies" + - "python:uv" + commit-message: + prefix: "chore" + include: "scope" + cooldown: + default-days: 7 + + # GitHub Actions used in workflows and local composite actions. + - package-ecosystem: "github-actions" + directories: + - "/" + - "/.github/actions/*" + schedule: + interval: "weekly" + open-pull-requests-limit: 2 + groups: + github-actions-minor-patch: + patterns: + - "*" + update-types: + - "minor" + - "patch" + labels: + - "dependencies" + - "github-actions" + commit-message: + prefix: "ci" + include: "scope" + cooldown: + default-days: 7 diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml new file mode 100644 index 0000000..d9f9dd1 --- /dev/null +++ b/.github/workflows/dependabot-review.yml @@ -0,0 +1,126 @@ +name: dependabot-review + +# Dependency-update PR guardrails for Dependabot-authored PRs. +# +# Runs only on PRs opened by dependabot[bot]. Inspects which files +# changed, then conditionally runs a Socket Firewall (sfw) install smoke +# job for the Python dependency set. Because sfw uses the free, anonymous +# Socket public-data path it needs NO API key, so we can run it from the +# unprivileged `pull_request` context without pull_request_target or any +# of its security tradeoffs. +# +# Pattern adapted from SocketDev/socket-python-cli. + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +permissions: + contents: read + +concurrency: + group: dependabot-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + inspect: + if: github.event.pull_request.user.login == 'dependabot[bot]' + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + python_deps_changed: ${{ steps.diff.outputs.python_deps_changed }} + workflow_or_action_changed: ${{ steps.diff.outputs.workflow_or_action_changed }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Inspect changed files + id: diff + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + CHANGED_FILES="$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")" + + { + echo "## Changed files" + echo '```' + printf '%s\n' "$CHANGED_FILES" + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + + has_file() { + local pattern="$1" + if printf '%s\n' "$CHANGED_FILES" | grep -Eq "$pattern"; then + echo "true" + else + echo "false" + fi + } + + { + echo "python_deps_changed=$(has_file '^(pyproject\.toml|uv\.lock)$')" + echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/actions/|^\.github/dependabot\.yml$')" + } >> "$GITHUB_OUTPUT" + + - name: Summarize review expectations + env: + PR_URL: ${{ github.event.pull_request.html_url }} + run: | + { + echo "## Dependabot Review Checklist" + echo "- PR: $PR_URL" + echo "- Confirm upstream release notes before merge" + echo "- Do not treat a Dependabot PR as trusted solely because of the actor" + echo "- This workflow runs in pull_request context only; no publish secrets are exposed" + } >> "$GITHUB_STEP_SUMMARY" + + python-sfw-smoke: + needs: inspect + if: needs.inspect.outputs.python_deps_changed == 'true' + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + persist-credentials: false + + - uses: ./.github/actions/setup-sfw + with: + uv: "true" + + - name: Sync project through Socket Firewall + # `sfw uv sync` is the intended way to route uv through Socket Firewall + # (per Socket's own uv wrapper guidance). --locked verifies the exact + # uv.lock set and fails on lockfile drift rather than silently + # re-resolving, so the firewall inspects precisely what would install. + # Note: uv's sfw integration is quieter than npm/pip -- it does not + # print the "N packages fetched" footer, but interception is active. + run: sfw uv sync --locked --extra test --extra dev + + - name: Import smoke test + run: | + uv run python -c " + import socketdev + from socketdev import socketdev as SocketDevClient + from socketdev.core.api import API + from socketdev.version import __version__ + print('import smoke OK', __version__) + " + + workflow-notice: + needs: inspect + if: needs.inspect.outputs.workflow_or_action_changed == 'true' + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Flag workflow-sensitive updates + run: | + { + echo "## Sensitive File Notice" + echo "This Dependabot PR changes workflow or dependabot config files." + echo "Require explicit human review before merge." + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index 6701058..f45ff55 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -3,8 +3,21 @@ on: pull_request: types: [opened, synchronize, ready_for_review] +# Cancel an in-flight preview when the PR is pushed again -- previews publish +# to Test PyPI, so superseded runs shouldn't keep churning. +concurrency: + group: pr-preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + jobs: preview: + # Skip on: + # - PRs from forks (no access to publish secrets / OIDC) + # - Dependabot PRs: preview-publishing a dependency bump to Test PyPI is + # pointless (no package version bump) and would fail the version check. + if: >- + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.user.login != 'dependabot[bot]' runs-on: ubuntu-latest permissions: id-token: write @@ -19,13 +32,8 @@ jobs: with: python-version: '3.13' - # Install all dependencies from pyproject.toml - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install "virtualenv<20.36" - pip install hatchling==1.27.0 - pip install hatch==1.14.0 + - name: Install build tooling + uses: ./.github/actions/setup-hatch - name: Inject full dynamic version run: python .hooks/sync_version.py --dev diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index acc8981..a2a940e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,14 +18,9 @@ jobs: with: python-version: '3.13' - # Install all dependencies from pyproject.toml - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install "virtualenv<20.36" - pip install hatchling==1.27.0 - pip install hatch==1.14.0 - + - name: Install build tooling + uses: ./.github/actions/setup-hatch + - name: Get Version id: version env: diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index 6db1426..e2c9292 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -14,6 +14,10 @@ permissions: jobs: check_version: + # Skip on Dependabot PRs: they bump dependencies (touching uv.lock / + # pyproject.toml) without bumping the package version, so the increment + # check would always fail. Package-version bumps come from maintainer PRs. + if: github.event.pull_request.user.login != 'dependabot[bot]' runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/uv.lock b/uv.lock index 9c96b4d..8222d54 100644 --- a/uv.lock +++ b/uv.lock @@ -3,7 +3,8 @@ revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.10'", - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] [[package]] @@ -331,7 +332,8 @@ name = "click" version = "8.1.8" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] dependencies = [ { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, @@ -370,7 +372,8 @@ name = "coverage" version = "7.10.7" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } wheels = [ @@ -593,51 +596,51 @@ toml = [ [[package]] name = "cryptography" -version = "46.0.5" +version = "46.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, - { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, - { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, - { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, - { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, - { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, - { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, - { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, - { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, - { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, - { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, - { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, - { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, - { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, - { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, - { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, - { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, - { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, - { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, - { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, - { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, + { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, + { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" }, + { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" }, + { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" }, + { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" }, + { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" }, + { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" }, + { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, + { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" }, + { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" }, ] [[package]] @@ -675,7 +678,8 @@ name = "filelock" version = "3.19.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } wheels = [ @@ -708,7 +712,8 @@ name = "hatch" version = "1.15.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, @@ -769,7 +774,8 @@ name = "hatchling" version = "1.27.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] dependencies = [ { name = "packaging", marker = "python_full_version < '3.10'" }, @@ -856,11 +862,11 @@ wheels = [ [[package]] name = "idna" -version = "3.11" +version = "3.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, ] [[package]] @@ -880,7 +886,8 @@ name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ @@ -968,7 +975,8 @@ name = "markdown-it-py" version = "3.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] dependencies = [ { name = "mdurl", marker = "python_full_version < '3.10'" }, @@ -1079,7 +1087,8 @@ name = "platformdirs" version = "4.4.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } wheels = [ @@ -1127,11 +1136,11 @@ wheels = [ [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] [[package]] @@ -1148,7 +1157,8 @@ name = "pytest" version = "8.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] dependencies = [ { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, @@ -1305,7 +1315,8 @@ name = "secretstorage" version = "3.3.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version < '3.10'", + "python_full_version > '3.9' and python_full_version < '3.10'", + "python_full_version <= '3.9'", ] dependencies = [ { name = "cryptography", marker = "python_full_version < '3.10'" }, @@ -1508,28 +1519,28 @@ wheels = [ [[package]] name = "uv" -version = "0.9.21" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/2b/4e2090bc3a6265b445b3d31ca6fff20c6458d11145069f7e48ade3e2d75b/uv-0.9.21.tar.gz", hash = "sha256:aa4ca6ccd68e81b5ebaa3684d3c4df2b51a982ac16211eadf0707741d36e6488", size = 3834762, upload-time = "2025-12-30T16:12:51.927Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/26/0750c5bb1637ebefe1db0936dc76ead8ce97f17368cda950642bfd90fa3f/uv-0.9.21-py3-none-linux_armv6l.whl", hash = "sha256:0b330eaced2fd9d94e2a70f3bb6c8fd7beadc9d9bf9f1227eb14da44039c413a", size = 21266556, upload-time = "2025-12-30T16:12:47.311Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ef/f019466c1e367ea68003cf35f4d44cc328694ed4a59b6004aa7dcacb2b35/uv-0.9.21-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1d8e0940bddd37a55f4479d61adaa6b302b780d473f037fc084e48b09a1678e7", size = 20485648, upload-time = "2025-12-30T16:12:15.746Z" }, - { url = "https://files.pythonhosted.org/packages/2a/41/f735bd9a5b4848b6f4f1028e6d768f581559d68eddb6403eb0f19ca4c843/uv-0.9.21-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cb420ddab7bcdd12c2352d4b551ced428d104311c0b98ce205675ab5c97072db", size = 18986976, upload-time = "2025-12-30T16:12:25.034Z" }, - { url = "https://files.pythonhosted.org/packages/9a/5f/01d537e05927594dc379ff8bc04f8cde26384d25108a9f63758eae2a7936/uv-0.9.21-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:a36d164438a6310c9fceebd041d80f7cffcc63ba80a7c83ee98394fadf2b8545", size = 20819312, upload-time = "2025-12-30T16:12:41.802Z" }, - { url = "https://files.pythonhosted.org/packages/18/89/9497395f57e007a2daed8172042ecccade3ff5569fd367d093f49bd6a4a8/uv-0.9.21-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c0ad83ce874cbbf9eda569ba793a9fb70870db426e9862300db8cf2950a7fe3b", size = 20900227, upload-time = "2025-12-30T16:12:19.242Z" }, - { url = "https://files.pythonhosted.org/packages/04/61/a3f6dfc75d278cce96b370e00b6f03d73ec260e5304f622504848bad219d/uv-0.9.21-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9076191c934b813147060e4cd97e33a58999de0f9c46f8ac67f614e154dae5c8", size = 21965424, upload-time = "2025-12-30T16:12:01.589Z" }, - { url = "https://files.pythonhosted.org/packages/18/3e/344e8c1078cfea82159c6608b8694f24fdfe850ce329a4708c026cb8b0ff/uv-0.9.21-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2ce0f6aca91f7fbf1192e43c063f4de3666fd43126aacc71ff7d5a79f831af59", size = 23540343, upload-time = "2025-12-30T16:12:13.139Z" }, - { url = "https://files.pythonhosted.org/packages/7f/20/5826659a81526687c6e5b5507f3f79f4f4b7e3022f3efae2ba36b19864c3/uv-0.9.21-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b4817642d5ef248b74ca7be3505e5e012a06be050669b80d1f7ced5ad50d188", size = 23171564, upload-time = "2025-12-30T16:12:22.219Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8d/404c54e019bb99ce474dc21e6b96c8a1351ba3c06e5e19fd8dcae0ba1899/uv-0.9.21-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4fb42237fa309d79905fb73f653f63c1fe45a51193411c614b13512cf5506df3", size = 22202400, upload-time = "2025-12-30T16:12:04.612Z" }, - { url = "https://files.pythonhosted.org/packages/1a/f0/aa3d0081a2004050564364a1ef3277ddf889c9989a7278c0a9cce8284926/uv-0.9.21-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d22f0ac03635d661e811c69d7c0b292751f90699acc6a1fb1509e17c936474", size = 22206448, upload-time = "2025-12-30T16:12:30.626Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a9/7a375e723a588f31f305ddf9ae2097af0b9dc7f7813641788b5b9764a237/uv-0.9.21-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:cdd805909d360ad67640201376c8eb02de08dcf1680a1a81aebd9519daed6023", size = 20940568, upload-time = "2025-12-30T16:12:27.533Z" }, - { url = "https://files.pythonhosted.org/packages/18/d5/6187ffb7e1d24df34defe2718db8c4c3c08f153d3e7da22c250134b79cd1/uv-0.9.21-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:82e438595a609cbe4e45c413a54bd5756d37c8c39108ce7b2799aff15f7d3337", size = 22085077, upload-time = "2025-12-30T16:12:10.153Z" }, - { url = "https://files.pythonhosted.org/packages/ee/fa/8e211167d0690d9f15a08da610a0383d2f43a6c838890878e14948472284/uv-0.9.21-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:fc1c06e1e5df423e1517e350ea2c9d85ecefd0919188a0a9f19bd239bbbdeeaf", size = 20862893, upload-time = "2025-12-30T16:12:49.87Z" }, - { url = "https://files.pythonhosted.org/packages/33/b2/9d24d84cb9a1a6a5ea98d03a29abf800d87e5710d25e53896dc73aeb63a5/uv-0.9.21-py3-none-musllinux_1_1_i686.whl", hash = "sha256:9ef3d2a213c7720f4dae336e5123fe88427200d7523c78091c4ab7f849c3f13f", size = 21428397, upload-time = "2025-12-30T16:12:07.483Z" }, - { url = "https://files.pythonhosted.org/packages/4f/40/1e8e4c2e1308432c708eaa66dccdb83d2ee6120ea2b7d65e04fc06f48ff8/uv-0.9.21-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:8da20914d92ba4cc35f071414d3da7365294fc0b7114da8ac2ab3a86c695096f", size = 22450537, upload-time = "2025-12-30T16:12:33.36Z" }, - { url = "https://files.pythonhosted.org/packages/18/b8/99c4731d001f512e844dfdc740db2bf2fea56d538749b639d21f5117a74a/uv-0.9.21-py3-none-win32.whl", hash = "sha256:e716e23bc0ec8cbb0811f99e653745e0cf15223e7ba5d8857d46be5b40b3045b", size = 20032654, upload-time = "2025-12-30T16:12:36.007Z" }, - { url = "https://files.pythonhosted.org/packages/29/6b/da441bf335f5e1c0c100b7dfb9702b6fed367ba703e543037bf1e70bf8c3/uv-0.9.21-py3-none-win_amd64.whl", hash = "sha256:64a7bb0e4e6a4c2d98c2d55f42aead7c2df0ceb17d5911d1a42b76228cab4525", size = 22206744, upload-time = "2025-12-30T16:12:38.953Z" }, - { url = "https://files.pythonhosted.org/packages/98/02/afbed8309fe07aaa9fa58a98941cebffbcd300fe70499a02a6806d93517b/uv-0.9.21-py3-none-win_arm64.whl", hash = "sha256:6c13c40966812f6bd6ecb6546e5d3e27e7fe9cefa07018f074f51d703cb29e1c", size = 20591604, upload-time = "2025-12-30T16:12:44.634Z" }, +version = "0.11.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/8e/ec34c19d0f254fcbcc5c1ce8c7f06e47e0f69a7e1a0269c1d59cb0b0f279/uv-0.11.17.tar.gz", hash = "sha256:1d1be74deec997db1dda05a7e67541c904d65cbfd72e455d3c0a2a1e4bf2cddf", size = 4203607, upload-time = "2026-05-28T20:39:47.707Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/2e/e6d42f9d39009eee976f1e5dfd31d3d1943e6e593ad7b191cf11e9744a36/uv-0.11.17-py3-none-linux_armv6l.whl", hash = "sha256:8426bfe315564d414cbc5ba5467595dc6348965e19acec742914f47da3ff269f", size = 23551216, upload-time = "2026-05-28T20:39:05.395Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ee/d72bcc60f3585653a4b768425854d737d98d65c1765547d25c2999547ea9/uv-0.11.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6d1a033cc68cabb4141d6c1e3b66ffc6e970b98ba42e210f33270251e0bd8697", size = 22997377, upload-time = "2026-05-28T20:39:25.21Z" }, + { url = "https://files.pythonhosted.org/packages/58/34/1bc69798d9ae998fbc42c61b02883f2ba00d04bdd858e589604d01846287/uv-0.11.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:58c07ffc272c847d29cd98ca5082fa4304a645f87c718ec900e3cca9026bd096", size = 21630197, upload-time = "2026-05-28T20:39:28.935Z" }, + { url = "https://files.pythonhosted.org/packages/6b/93/1be48ec6a8933d9a77d0ce5240ed63f68869f68517ccf5d62268ed03f3e8/uv-0.11.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:036d6e2940afe8b79637530b01b9241d8cfd174b07f1179a1ebbd42409c38ca3", size = 23414940, upload-time = "2026-05-28T20:39:55.015Z" }, + { url = "https://files.pythonhosted.org/packages/00/31/b7488ff49d80090ea9d05d67a4d381a1b4479502e9853e654caa1c1c678e/uv-0.11.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:283186700c3e65a4644a73a917232da7d3e4a94d25ea0377a44f5b263fa49577", size = 23096330, upload-time = "2026-05-28T20:39:01.284Z" }, + { url = "https://files.pythonhosted.org/packages/fe/95/42b6137c5de06278d229c7eef2f314df2a738cd799795bbb44dace21bd6e/uv-0.11.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2e44dfbfc7778d0d90edc6738f237c91e5e37e4e3cfe94c8a312cec56a41485", size = 23101906, upload-time = "2026-05-28T20:39:17.149Z" }, + { url = "https://files.pythonhosted.org/packages/17/7c/0ca03b2d19965db6d5dfe0c8cf96a3d0b424503c8cbc3cd2ffdc5869a15d/uv-0.11.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a817eeb3026f27a53d3f4b7855a5105f6787dd192140e201eda4d2b9a11b72e", size = 24444409, upload-time = "2026-05-28T20:39:59.218Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fb/179f55a3b19d47c30ec1f41b9b964da74dfa7053ff310a70a9c4d8cb998d/uv-0.11.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf8f5ad959583dcd2c4ae445c754a97c05700246ff89259f3fd285c9c20f4c00", size = 25540153, upload-time = "2026-05-28T20:39:09.535Z" }, + { url = "https://files.pythonhosted.org/packages/f7/29/592f42012765c43ae45c112110e214bca7b0cfc08c4c1b52e1dfa47dedd5/uv-0.11.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce16892a45134d20165c1ceababe06f3e9ce6a58902db1eff812c8c93626823f", size = 24665906, upload-time = "2026-05-28T20:39:41.254Z" }, + { url = "https://files.pythonhosted.org/packages/0e/51/b75808766f895248553c6370968509cd4f726e6943e310a8f7a171036ad0/uv-0.11.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da839e5a491c9a701d7d327a199cafc76ac27a03ac84fd2a8d4bf32c3af2448", size = 24863325, upload-time = "2026-05-28T20:39:51.006Z" }, + { url = "https://files.pythonhosted.org/packages/ee/6a/6f27ee69e97f480104bb8ec335f04c2a12add98edfcc4844a68e9538b6e2/uv-0.11.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:ec004b3c9bf9cb7756067ad1bd0bf64eb843e6fa2edbfbb3135ee152c14cea91", size = 23521674, upload-time = "2026-05-28T20:38:55.869Z" }, + { url = "https://files.pythonhosted.org/packages/df/11/1344aca7c710f794750f74de0e552a54ab24193ecc01fa3b3ae22ff822a1/uv-0.11.17-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:659227cac719b618cc91e02be9e274ad5bd72d74fa278123e6373537e9f28216", size = 24224725, upload-time = "2026-05-28T20:39:32.945Z" }, + { url = "https://files.pythonhosted.org/packages/ad/44/7b11550c1453ea13b81e549c83523e6ab6ed3231d09b2fd6b9eb19acceaf/uv-0.11.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e301d844eed9401f0f0351de12c55f1306ca05372acb0f28d35717c8ba663a22", size = 24301643, upload-time = "2026-05-28T20:39:45.183Z" }, + { url = "https://files.pythonhosted.org/packages/1a/36/8f683bc60547b8f93d0e752a8574d13fad776999cb978482b360c053ca22/uv-0.11.17-py3-none-musllinux_1_1_i686.whl", hash = "sha256:f0bf483c0d9fa14283992d56061b498b9d3d4adebd285af8744dc33f64dadfba", size = 23786049, upload-time = "2026-05-28T20:39:20.999Z" }, + { url = "https://files.pythonhosted.org/packages/10/dc/7a495db39c2970de4fa375c337dbd617b16780911f88f0511f8fe7f6747c/uv-0.11.17-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:2ccd5487a4a192bc832ea04c867a26883757db8fdfe88bed85d8129c82f9e505", size = 25049786, upload-time = "2026-05-28T20:40:03.292Z" }, + { url = "https://files.pythonhosted.org/packages/37/dd/74eff72d749eaf7e19f489878e21a368a7fef58d26ea0c63ec044ecd78b1/uv-0.11.17-py3-none-win32.whl", hash = "sha256:12b701fa32c5be3691759a73956e4462f30fa7b0dfa52ec66cb305bbb6ea4129", size = 22479213, upload-time = "2026-05-28T20:39:13.316Z" }, + { url = "https://files.pythonhosted.org/packages/79/99/8af4a92b99a8a4823297c26df727fe957267e03e1196e3caa803c3f6ccb2/uv-0.11.17-py3-none-win_amd64.whl", hash = "sha256:44ec1fe3af839f87370dcf0400c0cab917cc1ce697d563e860fc7d9ed72655e7", size = 25083161, upload-time = "2026-05-28T20:40:07.931Z" }, + { url = "https://files.pythonhosted.org/packages/00/76/a689077832d585d29d87f9cd0d65eca1af58abd29a4eab004d0a8a858b9c/uv-0.11.17-py3-none-win_arm64.whl", hash = "sha256:37c915bfcf86f99c1c5be7c9ed21e0d80624067ba47bc8916a3cb0530bc94d27", size = 23544936, upload-time = "2026-05-28T20:39:37.137Z" }, ] [[package]] From 69524b9fe4b66637c099feeb90ddddf9f4adefcf Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:15:25 -0400 Subject: [PATCH 2/8] chore(release): bump to 3.1.2 Version Check requires a package-version increment on maintainer PRs, and this PR bundles dependency bumps + Dependabot hardening. Bump version.py, pyproject.toml, and the uv.lock project version in sync. Co-Authored-By: Claude Opus 4.8 --- pyproject.toml | 2 +- socketdev/version.py | 2 +- uv.lock | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0fc8167..b97b217 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "socketdev" -version = "3.1.1" +version = "3.1.2" requires-python = ">= 3.9" dependencies = [ 'requests', diff --git a/socketdev/version.py b/socketdev/version.py index d539d50..911557b 100644 --- a/socketdev/version.py +++ b/socketdev/version.py @@ -1 +1 @@ -__version__ = "3.1.1" +__version__ = "3.1.2" diff --git a/uv.lock b/uv.lock index 8222d54..29be8fa 100644 --- a/uv.lock +++ b/uv.lock @@ -1354,7 +1354,7 @@ wheels = [ [[package]] name = "socketdev" -version = "3.1.1" +version = "3.1.2" source = { editable = "." } dependencies = [ { name = "requests" }, From bca454d8af6ad8b6bf0032b6ba70864f96469c54 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:26:52 -0400 Subject: [PATCH 3/8] Extend dependency review to maintainers (free + enterprise SFW) Broaden dependabot-review into dependency-review so the Socket Firewall guardrail covers maintainer PRs too, not just Dependabot: - inspect now runs on every PR and computes the SFW edition per-PR: enterprise for a trusted SocketDev member (author_association OWNER/ MEMBER/COLLABORATOR) on an in-repo (non-fork) PR when SOCKET_API_TOKEN is present; free (anonymous) for Dependabot, forks, external contributors, or when the token is absent. - The mode degrades to free whenever the token is missing, so this is safe to ship before the secret exists and auto-upgrades to enterprise once SOCKET_API_TOKEN is added (repo or org level). The SDK has no Socket token today (cf. socket-python-cli's SOCKET_CLI_API_TOKEN). - setup-sfw composite action gains `mode` + `socket-token` inputs, forwarded to socketdev/action (same action, firewall-free vs firewall-enterprise). - Rename workflow dependabot-review.yml -> dependency-review.yml to match the broadened scope (not a required status check). Co-Authored-By: Claude Opus 4.8 --- .github/actions/setup-sfw/action.yml | 20 ++++-- ...dabot-review.yml => dependency-review.yml} | 63 +++++++++++++++---- 2 files changed, 65 insertions(+), 18 deletions(-) rename .github/workflows/{dependabot-review.yml => dependency-review.yml} (57%) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index f7280bb..ffe006d 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -1,8 +1,10 @@ -name: "Set up Socket Firewall (free)" +name: "Set up Socket Firewall" description: >- - Set up the requested language toolchain and install Socket Firewall (free - edition) so subsequent steps can run package-manager commands wrapped with - `sfw`. Free/anonymous mode -- no API token, safe on untrusted/Dependabot PRs. + Set up the requested Python/uv toolchain and install Socket Firewall so + subsequent steps can run package-manager commands wrapped with `sfw`. + Defaults to free/anonymous mode (no API token -- safe on untrusted / + Dependabot / fork PRs). Pass mode: firewall-enterprise + socket-token for + full org-policy enforcement on trusted maintainer PRs. inputs: python: @@ -11,6 +13,12 @@ inputs: uv: description: "Install uv (implies Python)" default: "false" + mode: + description: "socketdev/action mode: firewall-free or firewall-enterprise" + default: "firewall-free" + socket-token: + description: "Socket API token (only used/required for firewall-enterprise)" + default: "" runs: using: "composite" @@ -21,9 +29,11 @@ runs: python-version: "3.12" # Official Socket setup action. Wires up sfw routing correctly. + # socket-token is ignored in firewall-free mode and empty when absent. - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 with: - mode: firewall-free + mode: ${{ inputs.mode }} + socket-token: ${{ inputs.socket-token }} - if: ${{ inputs.uv == 'true' }} name: Install uv diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependency-review.yml similarity index 57% rename from .github/workflows/dependabot-review.yml rename to .github/workflows/dependency-review.yml index d9f9dd1..b416c5f 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,13 +1,21 @@ -name: dependabot-review +name: dependency-review -# Dependency-update PR guardrails for Dependabot-authored PRs. +# Supply-chain guardrails for dependency-update PRs -- for BOTH Dependabot +# and maintainers. Inspects the changed files, then runs a Socket Firewall +# (sfw) install smoke job for Python dependency changes, picking the firewall +# edition per-PR: # -# Runs only on PRs opened by dependabot[bot]. Inspects which files -# changed, then conditionally runs a Socket Firewall (sfw) install smoke -# job for the Python dependency set. Because sfw uses the free, anonymous -# Socket public-data path it needs NO API key, so we can run it from the -# unprivileged `pull_request` context without pull_request_target or any -# of its security tradeoffs. +# - Trusted SocketDev members on an in-repo (non-fork) PR, when the +# SOCKET_API_TOKEN secret is present -> Socket Firewall ENTERPRISE +# (authenticated, full org-policy enforcement). +# - Everything else -- Dependabot, forks, external contributors, or a +# missing token -> Socket Firewall FREE (anonymous, no API token), which +# is safe in the unprivileged `pull_request` context. +# +# The mode is computed in `inspect` and degrades to free whenever the token is +# absent (e.g. before it has been added to the repo/org, or on fork PRs where +# GitHub withholds secrets), so this workflow is safe to ship as-is and starts +# using the enterprise edition automatically once the secret exists. # # Pattern adapted from SocketDev/socket-python-cli. @@ -19,17 +27,17 @@ permissions: contents: read concurrency: - group: dependabot-review-${{ github.event.pull_request.number }} + group: dependency-review-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: inspect: - if: github.event.pull_request.user.login == 'dependabot[bot]' runs-on: ubuntu-latest timeout-minutes: 5 outputs: python_deps_changed: ${{ steps.diff.outputs.python_deps_changed }} workflow_or_action_changed: ${{ steps.diff.outputs.workflow_or_action_changed }} + sfw_mode: ${{ steps.mode.outputs.sfw_mode }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -65,15 +73,42 @@ jobs: echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/actions/|^\.github/dependabot\.yml$')" } >> "$GITHUB_OUTPUT" + - name: Determine Socket Firewall mode + id: mode + env: + IS_DEPENDABOT: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + AUTHOR_ASSOC: ${{ github.event.pull_request.author_association }} + # Empty for fork PRs (secrets withheld) and until the secret is added. + SOCKET_API_TOKEN: ${{ secrets.SOCKET_API_TOKEN }} + run: | + mode=firewall-free + # Enterprise only for a trusted SocketDev member (OWNER/MEMBER) or + # repo collaborator on an in-repo PR, and only when the token is + # actually present. Anything else falls back to the free edition. + if [ "$IS_DEPENDABOT" != "true" ] \ + && [ "$IS_FORK" != "true" ] \ + && [ -n "$SOCKET_API_TOKEN" ] \ + && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER|COLLABORATOR)$'; then + mode=firewall-enterprise + fi + + echo "sfw_mode=$mode" >> "$GITHUB_OUTPUT" + { + echo "## Socket Firewall mode: \`$mode\`" + echo "- author_association: \`$AUTHOR_ASSOC\`" + echo "- dependabot: \`$IS_DEPENDABOT\` | fork: \`$IS_FORK\`" + } >> "$GITHUB_STEP_SUMMARY" + - name: Summarize review expectations env: PR_URL: ${{ github.event.pull_request.html_url }} run: | { - echo "## Dependabot Review Checklist" + echo "## Dependency Review Checklist" echo "- PR: $PR_URL" echo "- Confirm upstream release notes before merge" - echo "- Do not treat a Dependabot PR as trusted solely because of the actor" + echo "- Do not treat a dependency PR as trusted solely because of the actor" echo "- This workflow runs in pull_request context only; no publish secrets are exposed" } >> "$GITHUB_STEP_SUMMARY" @@ -91,6 +126,8 @@ jobs: - uses: ./.github/actions/setup-sfw with: uv: "true" + mode: ${{ needs.inspect.outputs.sfw_mode }} + socket-token: ${{ secrets.SOCKET_API_TOKEN }} - name: Sync project through Socket Firewall # `sfw uv sync` is the intended way to route uv through Socket Firewall @@ -121,6 +158,6 @@ jobs: run: | { echo "## Sensitive File Notice" - echo "This Dependabot PR changes workflow or dependabot config files." + echo "This PR changes workflow, composite-action, or dependabot config files." echo "Require explicit human review before merge." } >> "$GITHUB_STEP_SUMMARY" From 37d06add8d2ede23f84c34782c2d5a96911e6d63 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:29:51 -0400 Subject: [PATCH 4/8] fix(dependency-review): use runner Python, forbid uv interpreter download .python-version pins 3.12.7; setup-python provides 3.12.13, so `uv sync` tried to download the exact managed CPython from GitHub, which Socket Firewall's TLS interception blocked (UnknownIssuer). Set UV_PYTHON=3.12 + UV_PYTHON_DOWNLOADS=never so uv uses the runner interpreter and only PyPI package fetches route through sfw. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependency-review.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index b416c5f..0dfe8f4 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -136,6 +136,14 @@ jobs: # re-resolving, so the firewall inspects precisely what would install. # Note: uv's sfw integration is quieter than npm/pip -- it does not # print the "N packages fetched" footer, but interception is active. + # + # Use the runner's setup-python interpreter and forbid managed-Python + # downloads: .python-version pins an exact patch (3.12.7) that uv would + # otherwise fetch from GitHub, which the firewall's TLS interception + # blocks. The firewall is here to vet PyPI installs, not the toolchain. + env: + UV_PYTHON: "3.12" + UV_PYTHON_DOWNLOADS: never run: sfw uv sync --locked --extra test --extra dev - name: Import smoke test From b0e50b09d404e8eb27629073140c5d70fbb2e287 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:06:45 -0400 Subject: [PATCH 5/8] fix(dependency-review): require strict org membership for enterprise SFW Tighten the enterprise-mode gate to author_association OWNER/MEMBER only. Outside collaborators (COLLABORATOR) now fall through to the free edition, same as Dependabot / forks / external contributors. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependency-review.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 0dfe8f4..db362bf 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -83,13 +83,14 @@ jobs: SOCKET_API_TOKEN: ${{ secrets.SOCKET_API_TOKEN }} run: | mode=firewall-free - # Enterprise only for a trusted SocketDev member (OWNER/MEMBER) or - # repo collaborator on an in-repo PR, and only when the token is - # actually present. Anything else falls back to the free edition. + # Enterprise only for a SocketDev org member (OWNER/MEMBER) on an + # in-repo PR, and only when the token is actually present. Everything + # else -- Dependabot, forks, outside collaborators, external + # contributors, or a missing token -- uses the free edition. if [ "$IS_DEPENDABOT" != "true" ] \ && [ "$IS_FORK" != "true" ] \ && [ -n "$SOCKET_API_TOKEN" ] \ - && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER|COLLABORATOR)$'; then + && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER)$'; then mode=firewall-enterprise fi From 9f5b0fb336c7635e05e713fba96107a23fde173b Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:15:03 -0400 Subject: [PATCH 6/8] chore(dependency-review): rename enterprise secret to SOCKET_SFW_API_TOKEN Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependency-review.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index db362bf..91dd871 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -6,7 +6,7 @@ name: dependency-review # edition per-PR: # # - Trusted SocketDev members on an in-repo (non-fork) PR, when the -# SOCKET_API_TOKEN secret is present -> Socket Firewall ENTERPRISE +# SOCKET_SFW_API_TOKEN secret is present -> Socket Firewall ENTERPRISE # (authenticated, full org-policy enforcement). # - Everything else -- Dependabot, forks, external contributors, or a # missing token -> Socket Firewall FREE (anonymous, no API token), which @@ -80,7 +80,7 @@ jobs: IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }} AUTHOR_ASSOC: ${{ github.event.pull_request.author_association }} # Empty for fork PRs (secrets withheld) and until the secret is added. - SOCKET_API_TOKEN: ${{ secrets.SOCKET_API_TOKEN }} + SOCKET_SFW_API_TOKEN: ${{ secrets.SOCKET_SFW_API_TOKEN }} run: | mode=firewall-free # Enterprise only for a SocketDev org member (OWNER/MEMBER) on an @@ -89,7 +89,7 @@ jobs: # contributors, or a missing token -- uses the free edition. if [ "$IS_DEPENDABOT" != "true" ] \ && [ "$IS_FORK" != "true" ] \ - && [ -n "$SOCKET_API_TOKEN" ] \ + && [ -n "$SOCKET_SFW_API_TOKEN" ] \ && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER)$'; then mode=firewall-enterprise fi @@ -128,7 +128,7 @@ jobs: with: uv: "true" mode: ${{ needs.inspect.outputs.sfw_mode }} - socket-token: ${{ secrets.SOCKET_API_TOKEN }} + socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }} - name: Sync project through Socket Firewall # `sfw uv sync` is the intended way to route uv through Socket Firewall From 6d2433c71e2144df3b76e454548ce8d1b08c650e Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:34:22 -0400 Subject: [PATCH 7/8] fix(dependency-review): scope SFW token to a dedicated environment Resolve zizmor secrets-outside-env (medium) without suppressing it. Split the single mode-switching smoke job into two: - python-sfw-smoke-free: untrusted PRs (Dependabot, forks, outside collaborators, externals). Anonymous free edition, never references the token. - python-sfw-smoke-enterprise: SocketDev org members (OWNER/MEMBER) on an in-repo PR. Authenticated enterprise edition; SOCKET_SFW_API_TOKEN is scoped to the `socket-firewall` GitHub environment, so only this job can read it. inspect now classifies PR trust (author_association OWNER/MEMBER, non-fork, non-Dependabot) and references no secret. No required-reviewer protection on the environment, so trusted dep PRs still run automatically. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependency-review.yml | 110 +++++++++++++++--------- 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 91dd871..35ec90b 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,21 +1,22 @@ name: dependency-review # Supply-chain guardrails for dependency-update PRs -- for BOTH Dependabot -# and maintainers. Inspects the changed files, then runs a Socket Firewall -# (sfw) install smoke job for Python dependency changes, picking the firewall -# edition per-PR: +# and maintainers. `inspect` classifies the PR, then exactly one Socket +# Firewall (sfw) install smoke job runs when Python deps change: # -# - Trusted SocketDev members on an in-repo (non-fork) PR, when the -# SOCKET_SFW_API_TOKEN secret is present -> Socket Firewall ENTERPRISE -# (authenticated, full org-policy enforcement). -# - Everything else -- Dependabot, forks, external contributors, or a -# missing token -> Socket Firewall FREE (anonymous, no API token), which -# is safe in the unprivileged `pull_request` context. +# - python-sfw-smoke-enterprise -- trusted SocketDev org members +# (author_association OWNER/MEMBER) on an in-repo (non-fork) PR. Runs the +# authenticated enterprise edition for full org-policy enforcement. The +# SOCKET_SFW_API_TOKEN is scoped to the `socket-firewall` environment, so +# it is the ONLY job that can read the token. +# - python-sfw-smoke-free -- everyone else (Dependabot, forks, outside +# collaborators, external contributors). Anonymous free edition, no token. +# This job never references the secret. # -# The mode is computed in `inspect` and degrades to free whenever the token is -# absent (e.g. before it has been added to the repo/org, or on fork PRs where -# GitHub withholds secrets), so this workflow is safe to ship as-is and starts -# using the enterprise edition automatically once the secret exists. +# Splitting the jobs (rather than picking a mode in one job) keeps the token +# out of scope on every untrusted run and satisfies zizmor's +# `secrets-outside-env` audit without suppressing it. The free path runs in +# the unprivileged `pull_request` context with no secret-leak surface. # # Pattern adapted from SocketDev/socket-python-cli. @@ -37,7 +38,7 @@ jobs: outputs: python_deps_changed: ${{ steps.diff.outputs.python_deps_changed }} workflow_or_action_changed: ${{ steps.diff.outputs.workflow_or_action_changed }} - sfw_mode: ${{ steps.mode.outputs.sfw_mode }} + is_trusted: ${{ steps.trust.outputs.is_trusted }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -73,30 +74,26 @@ jobs: echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/actions/|^\.github/dependabot\.yml$')" } >> "$GITHUB_OUTPUT" - - name: Determine Socket Firewall mode - id: mode + - name: Classify PR trust + id: trust + # Trusted == a SocketDev org member (OWNER/MEMBER) on an in-repo + # (non-fork) PR. Note this references NO secret -- it only decides which + # smoke job runs; the token stays scoped to the enterprise job. env: IS_DEPENDABOT: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }} AUTHOR_ASSOC: ${{ github.event.pull_request.author_association }} - # Empty for fork PRs (secrets withheld) and until the secret is added. - SOCKET_SFW_API_TOKEN: ${{ secrets.SOCKET_SFW_API_TOKEN }} run: | - mode=firewall-free - # Enterprise only for a SocketDev org member (OWNER/MEMBER) on an - # in-repo PR, and only when the token is actually present. Everything - # else -- Dependabot, forks, outside collaborators, external - # contributors, or a missing token -- uses the free edition. + is_trusted=false if [ "$IS_DEPENDABOT" != "true" ] \ && [ "$IS_FORK" != "true" ] \ - && [ -n "$SOCKET_SFW_API_TOKEN" ] \ && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER)$'; then - mode=firewall-enterprise + is_trusted=true fi - echo "sfw_mode=$mode" >> "$GITHUB_OUTPUT" + echo "is_trusted=$is_trusted" >> "$GITHUB_OUTPUT" { - echo "## Socket Firewall mode: \`$mode\`" + echo "## Socket Firewall edition: \`$([ "$is_trusted" = true ] && echo enterprise || echo free)\`" echo "- author_association: \`$AUTHOR_ASSOC\`" echo "- dependabot: \`$IS_DEPENDABOT\` | fork: \`$IS_FORK\`" } >> "$GITHUB_STEP_SUMMARY" @@ -113,9 +110,11 @@ jobs: echo "- This workflow runs in pull_request context only; no publish secrets are exposed" } >> "$GITHUB_STEP_SUMMARY" - python-sfw-smoke: + # Untrusted PRs (Dependabot, forks, outside collaborators, externals): + # anonymous free edition. Never references the token. + python-sfw-smoke-free: needs: inspect - if: needs.inspect.outputs.python_deps_changed == 'true' + if: needs.inspect.outputs.python_deps_changed == 'true' && needs.inspect.outputs.is_trusted != 'true' runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -127,21 +126,48 @@ jobs: - uses: ./.github/actions/setup-sfw with: uv: "true" - mode: ${{ needs.inspect.outputs.sfw_mode }} + mode: firewall-free + + - name: Sync project through Socket Firewall (free) + env: + UV_PYTHON: "3.12" + UV_PYTHON_DOWNLOADS: never + run: sfw uv sync --locked --extra test --extra dev + + - name: Import smoke test + run: | + uv run python -c " + import socketdev + from socketdev import socketdev as SocketDevClient + from socketdev.core.api import API + from socketdev.version import __version__ + print('import smoke OK', __version__) + " + + # Trusted SocketDev members: authenticated enterprise edition. The token is + # scoped to the `socket-firewall` environment, so only this job can read it. + python-sfw-smoke-enterprise: + needs: inspect + if: needs.inspect.outputs.python_deps_changed == 'true' && needs.inspect.outputs.is_trusted == 'true' + runs-on: ubuntu-latest + timeout-minutes: 15 + environment: socket-firewall + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 1 + persist-credentials: false + + - uses: ./.github/actions/setup-sfw + with: + uv: "true" + mode: firewall-enterprise socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }} - - name: Sync project through Socket Firewall - # `sfw uv sync` is the intended way to route uv through Socket Firewall - # (per Socket's own uv wrapper guidance). --locked verifies the exact - # uv.lock set and fails on lockfile drift rather than silently - # re-resolving, so the firewall inspects precisely what would install. - # Note: uv's sfw integration is quieter than npm/pip -- it does not - # print the "N packages fetched" footer, but interception is active. - # - # Use the runner's setup-python interpreter and forbid managed-Python - # downloads: .python-version pins an exact patch (3.12.7) that uv would - # otherwise fetch from GitHub, which the firewall's TLS interception - # blocks. The firewall is here to vet PyPI installs, not the toolchain. + - name: Sync project through Socket Firewall (enterprise) + # See free job for the UV_PYTHON rationale: .python-version pins an + # exact patch that uv would otherwise fetch from GitHub through the + # firewall (blocked by its TLS interception); use the runner's Python. env: UV_PYTHON: "3.12" UV_PYTHON_DOWNLOADS: never From 8f1318eff2a14aab6b008f0b9369b1519695c644 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:38:23 -0400 Subject: [PATCH 8/8] fix(dependency-review): gate enterprise on write-access (non-fork), not author_association author_association only reflects PUBLIC org membership, so private members (the common case here) show as CONTRIBUTOR and were misclassified -> the enterprise job always skipped. Switch the trust gate to "non-fork PR and not Dependabot": only accounts with write access can push an in-repo branch, the same boundary GitHub uses for secret exposure. No read:org token needed. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/dependency-review.yml | 28 +++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 35ec90b..c0568d7 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -4,14 +4,14 @@ name: dependency-review # and maintainers. `inspect` classifies the PR, then exactly one Socket # Firewall (sfw) install smoke job runs when Python deps change: # -# - python-sfw-smoke-enterprise -- trusted SocketDev org members -# (author_association OWNER/MEMBER) on an in-repo (non-fork) PR. Runs the +# - python-sfw-smoke-enterprise -- trusted authors: any in-repo (non-fork) +# PR other than Dependabot's (i.e. someone with write access). Runs the # authenticated enterprise edition for full org-policy enforcement. The # SOCKET_SFW_API_TOKEN is scoped to the `socket-firewall` environment, so # it is the ONLY job that can read the token. -# - python-sfw-smoke-free -- everyone else (Dependabot, forks, outside -# collaborators, external contributors). Anonymous free edition, no token. -# This job never references the secret. +# - python-sfw-smoke-free -- everyone else (Dependabot + all fork PRs from +# external contributors). Anonymous free edition, no token. This job never +# references the secret. # # Splitting the jobs (rather than picking a mode in one job) keeps the token # out of scope on every untrusted run and satisfies zizmor's @@ -76,18 +76,24 @@ jobs: - name: Classify PR trust id: trust - # Trusted == a SocketDev org member (OWNER/MEMBER) on an in-repo - # (non-fork) PR. Note this references NO secret -- it only decides which - # smoke job runs; the token stays scoped to the enterprise job. + # Trusted == any in-repo (non-fork) PR that isn't Dependabot's. Only + # accounts with write access can push a branch to this repo, so a + # non-fork PR already implies a trusted author -- the same boundary + # GitHub uses to decide whether secrets are exposed at all. + # + # NB: author_association is deliberately NOT used to require strict org + # membership. It only reflects PUBLIC org membership, so private members + # (the common case) show up as CONTRIBUTOR and would be misclassified. + # Reliable strict-membership detection would need a read:org token or + # public membership. This step references NO secret regardless -- it + # only decides which smoke job runs. env: IS_DEPENDABOT: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }} AUTHOR_ASSOC: ${{ github.event.pull_request.author_association }} run: | is_trusted=false - if [ "$IS_DEPENDABOT" != "true" ] \ - && [ "$IS_FORK" != "true" ] \ - && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER)$'; then + if [ "$IS_DEPENDABOT" != "true" ] && [ "$IS_FORK" != "true" ]; then is_trusted=true fi