From 5bde67f47f82b19b95c6cd9b34d5764ecdc25f71 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Fri, 29 May 2026 18:50:44 -0400 Subject: [PATCH 01/16] ci: skip PR Preview and Version Check on Dependabot PRs Both workflows failed on every Dependabot PR for reasons that don't apply to dependency bumps: - PR Preview publishes a dev build to Test PyPI + Docker Hub. On a dependency bump there's no version change, so the publish 400s ("File already exists") -- and it needs publish secrets a Dependabot PR shouldn't carry anyway. - Version Check requires an incremented app version, but Dependabot PRs touch uv.lock / pyproject.toml without bumping socketsecurity's version, so the check always fails. Add a job-level `if` to skip each on `dependabot[bot]`-authored PRs (same pattern already used for e2e-test.yml). Job-level skips report as "skipped" rather than blocking, and these stay required for human-authored PRs. Follow-up to #207 (the Dependabot review hardening), addressing fallout observed once that config went live on real Dependabot PRs. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/workflows/pr-preview.yml | 8 +++++++- .github/workflows/version-check.yml | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index 1d7115a..ef47ea0 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -5,7 +5,13 @@ on: jobs: preview: - if: github.event.pull_request.head.repo.full_name == github.repository + # Skip on: + # - PRs from forks (no access to publish secrets) + # - Dependabot PRs: preview-publishing a dependency bump to Test PyPI / + # Docker Hub is pointless and fails (no version bump, secret access). + 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 diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index 1eefa27..dc50819 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 app version, so the increment check + # would always fail. App-version bumps come from maintainer PRs. + if: github.event.pull_request.user.login != 'dependabot[bot]' runs-on: ubuntu-latest steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 From cfd5f475b14956c0e7518cc15287611e4cfbe230 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Fri, 29 May 2026 19:06:32 -0400 Subject: [PATCH 02/16] ci: pin sfw uv sync to the locked dependency set on Dependabot review `sfw uv sync` is the intended way to route uv through Socket Firewall (per Socket's own uv-wrapper guidance), so the python-sfw-smoke job was already exercising the firewall -- uv's integration is just quieter than npm/pip (no "N packages fetched" footer), which made it look like a no-op. Add `--locked` so the check verifies the exact uv.lock set and fails on lockfile drift instead of silently re-resolving to newer versions than the PR locked. This makes the firewall inspect precisely what would be installed and aligns with the deterministic-verification guidance for uv-based repos. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/workflows/dependabot-review.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index 486ccb3..069c9d0 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -109,7 +109,13 @@ jobs: run: python -m pip install --upgrade pip uv - name: Sync project through Socket Firewall - run: sfw uv sync --extra test --extra dev + # `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: | From c2bc561130cb9c3025b23f72dd131e204c83aa38 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Fri, 29 May 2026 19:09:40 -0400 Subject: [PATCH 03/16] ci: use official socketdev/action for Socket Firewall setup Replace the hand-rolled `npm install -g sfw` in all three sfw smoke jobs with the official setup action (socketdev/action@v1.3.2, mode: firewall-free). Why: - It's the documented GitHub Actions integration for Socket Firewall Free and wires up sfw routing correctly, rather than relying on an ad-hoc global npm install. This is the right mitigation for the class of Wrapper-Mode routing gaps where sfw can fail to proxy fetches from files.pythonhosted.org (tracked upstream as ENG-4871) -- exactly the "no interception" symptom that made the python job look like a no-op. - The Python jobs no longer need actions/setup-node at all (the action provides sfw directly), so those steps are dropped; the npm fixture job keeps setup-node since `npm install` needs it. Setup mode is firewall-free (anonymous, no API token) -- unchanged, and the reason this is safe to run on Dependabot/untrusted PRs. Our setup is Wrapper Mode + free edition + no CodeArtifact, so the Registry Mode + CodeArtifact `uv sync`/`uv lock` issue (CE-171) does not apply. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/workflows/dependabot-review.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index 069c9d0..55b42dd 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -98,12 +98,12 @@ jobs: with: python-version: "3.12" - - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + # Official Socket setup action (free edition). Installs sfw and wires up + # routing correctly -- preferred over a hand-rolled `npm install -g sfw`. + - name: Set up Socket Firewall (free) + uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 with: - node-version: "20" - - - name: Install Socket Firewall - run: npm install -g sfw + mode: firewall-free - name: Install uv run: python -m pip install --upgrade pip uv @@ -145,8 +145,10 @@ jobs: with: node-version: "20" - - name: Install Socket Firewall - run: npm install -g sfw + - name: Set up Socket Firewall (free) + uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 + with: + mode: firewall-free - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm @@ -167,12 +169,10 @@ jobs: with: python-version: "3.12" - - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + - name: Set up Socket Firewall (free) + uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 with: - node-version: "20" - - - name: Install Socket Firewall - run: npm install -g sfw + mode: firewall-free - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-pypi From 728bb15b1ff624e1c0758a5d4fdf7753b5478a42 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Sat, 30 May 2026 18:38:52 -0400 Subject: [PATCH 04/16] ci: factor Socket Firewall setup into a composite action The three sfw smoke jobs (python / npm-fixture / pypi-fixture) repeated the same setup: toolchain bootstrap + socketdev/action install. GitHub Actions doesn't support YAML anchors, so extract the shared setup into a local composite action instead. - New .github/actions/setup-sfw: optional Python/Node/uv toolchain inputs + the socketdev/action (firewall-free) install. - Each job now just declares the toolchain it needs (`uv`, `node`, or `python`) and runs its own distinct sfw command. Net effect: the pinned socketdev/action SHA now lives in ONE place (future bumps touch a single line), the per-job setup-python/setup-node duplication is gone, and each job body is reduced to its actual firewall check. No behavior change. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/actions/setup-sfw/action.yml | 40 +++++++++++++++++++++++++ .github/workflows/dependabot-review.yml | 32 ++++---------------- 2 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 .github/actions/setup-sfw/action.yml diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml new file mode 100644 index 0000000..7f7b8db --- /dev/null +++ b/.github/actions/setup-sfw/action.yml @@ -0,0 +1,40 @@ +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" + node: + description: "Set up Node 20 (needed for npm-wrapped checks)" + 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@f677139bbe7f9c59b41e40162b753c062f5d49a3 + with: + python-version: "3.12" + + - if: ${{ inputs.node == 'true' }} + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + with: + node-version: "20" + + # Official Socket setup action. Wires up sfw routing correctly -- preferred + # over a hand-rolled `npm install -g sfw`. Pinned to a commit SHA (v1.3.2). + - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f + 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/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index 55b42dd..c3be28a 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -94,19 +94,9 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + - uses: ./.github/actions/setup-sfw with: - python-version: "3.12" - - # Official Socket setup action (free edition). Installs sfw and wires up - # routing correctly -- preferred over a hand-rolled `npm install -g sfw`. - - name: Set up Socket Firewall (free) - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 - with: - mode: firewall-free - - - name: Install uv - run: python -m pip install --upgrade pip uv + uv: "true" - name: Sync project through Socket Firewall # `sfw uv sync` is the intended way to route uv through Socket Firewall @@ -141,14 +131,9 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + - uses: ./.github/actions/setup-sfw with: - node-version: "20" - - - name: Set up Socket Firewall (free) - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 - with: - mode: firewall-free + node: "true" - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm @@ -165,14 +150,9 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 - with: - python-version: "3.12" - - - name: Set up Socket Firewall (free) - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 + - uses: ./.github/actions/setup-sfw with: - mode: firewall-free + python: "true" - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-pypi From 6f89e0fd8a4df5d01a892905fd5654ce981430ba Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Sat, 30 May 2026 20:44:21 -0400 Subject: [PATCH 05/16] ci: speed up and de-duplicate the release & preview pipelines Performance (PR preview, the iterative-feedback path): - Add a concurrency group with cancel-in-progress so pushing a PR again cancels the superseded (slow) preview run instead of letting it churn. - Build preview images amd64-only. arm64 under QEMU emulation was the slowest part of the job, and preview images are for quick testing; release/stable keep multi-arch. - Enable GitHub Actions Docker layer cache (type=gha) on all image builds so unchanged layers are reused across runs. De-duplication (GitHub Actions has no YAML anchors, so use composite actions): - New .github/actions/setup-docker-publish: the QEMU + Buildx + Docker Hub login trio, shared by release.yml, pr-preview.yml, and docker-stable.yml. These had drifted to three different pinned SHA sets; now there is one. (Docker Hub creds are passed as inputs since composite actions can't read secrets directly.) - New .github/actions/setup-hatch: the pinned virtualenv/hatchling/hatch install shared by release.yml and pr-preview.yml. No behavior change to what gets published; only how the pipelines are assembled and how fast/parallel they run. Stacked on #217 (lelia/fix-dependabot-checks) to avoid a pr-preview.yml conflict with that PR's Dependabot skip; rebase onto main once #217 lands. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .../actions/setup-docker-publish/action.yml | 24 +++++++++++++ .github/actions/setup-hatch/action.yml | 13 +++++++ .github/workflows/docker-stable.yml | 16 ++++----- .github/workflows/pr-preview.yml | 35 ++++++++++--------- .github/workflows/release.yml | 26 +++++--------- 5 files changed, 70 insertions(+), 44 deletions(-) create mode 100644 .github/actions/setup-docker-publish/action.yml create mode 100644 .github/actions/setup-hatch/action.yml diff --git a/.github/actions/setup-docker-publish/action.yml b/.github/actions/setup-docker-publish/action.yml new file mode 100644 index 0000000..9144ac6 --- /dev/null +++ b/.github/actions/setup-docker-publish/action.yml @@ -0,0 +1,24 @@ +name: "Set up Docker publish" +description: >- + Set up QEMU + Docker Buildx and authenticate to Docker Hub for multi-arch + image builds. Centralizes the QEMU/Buildx/login trio that the release, + preview, and stable workflows previously each copied (and which had drifted + to different pinned SHAs). + +inputs: + dockerhub-username: + description: "Docker Hub username (pass from secrets)" + required: true + dockerhub-token: + description: "Docker Hub token/password (pass from secrets)" + required: true + +runs: + using: "composite" + steps: + - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 + - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + username: ${{ inputs.dockerhub-username }} + password: ${{ inputs.dockerhub-token }} 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/workflows/docker-stable.yml b/.github/workflows/docker-stable.yml index 3639ffc..24fc3a4 100644 --- a/.github/workflows/docker-stable.yml +++ b/.github/workflows/docker-stable.yml @@ -28,23 +28,19 @@ jobs: fi echo "Version ${INPUT_VERSION} found on PyPI - proceeding with release" - - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 - - - name: Login to Docker Hub with Organization Token - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + - name: Set up Docker publishing + uses: ./.github/actions/setup-docker-publish with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build & Push Stable Docker uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 with: push: true platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max tags: socketdev/cli:stable build-args: | CLI_VERSION=${{ inputs.version }} diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index ef47ea0..5b4a892 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -3,6 +3,12 @@ on: pull_request: types: [opened, synchronize, ready_for_review] +# Cancel an in-flight preview when the PR is pushed again -- previews are slow +# (publish + multi-step Docker build), so superseded runs shouldn't keep going. +concurrency: + group: pr-preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + jobs: preview: # Skip on: @@ -26,12 +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 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 @@ -139,18 +141,12 @@ jobs: echo "success=false" >> $GITHUB_OUTPUT exit 1 - - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 - - - name: Login to Docker Hub with Organization Token + - name: Set up Docker publishing if: steps.verify_package.outputs.success == 'true' - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + uses: ./.github/actions/setup-docker-publish with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build & Push Docker Preview if: steps.verify_package.outputs.success == 'true' @@ -159,7 +155,12 @@ jobs: VERSION: ${{ env.VERSION }} with: push: true - platforms: linux/amd64,linux/arm64 + # Preview images are for quick testing -- build amd64 only. arm64 via + # QEMU emulation is the slowest part of the job; release builds keep + # multi-arch. GHA layer cache speeds up repeated preview builds. + platforms: linux/amd64 + cache-from: type=gha + cache-to: type=gha,mode=max tags: | socketdev/cli:pr-${{ github.event.pull_request.number }} build-args: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5549b88..535a269 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,13 +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 hatch==1.14.0 - + - name: Install build tooling + uses: ./.github/actions/setup-hatch + - name: Get Version id: version env: @@ -72,17 +68,11 @@ jobs: if: steps.version_check.outputs.pypi_exists != 'true' uses: pypa/gh-action-pypi-publish@ab69e431e9c9f48a3310be0a56527c679f56e04d - - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 - - - name: Login to Docker Hub with Organization Token - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + - name: Set up Docker publishing + uses: ./.github/actions/setup-docker-publish with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} - name: Verify package is installable id: verify_package @@ -112,6 +102,8 @@ jobs: with: push: true platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max tags: | socketdev/cli:latest socketdev/cli:${{ env.VERSION }} From 1f5857fa9b1c95ff9240566ea1fa83a5dc6704f5 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Sat, 30 May 2026 21:17:09 -0400 Subject: [PATCH 06/16] ci: pin all GitHub Actions to latest release SHAs with version comments Bump every third-party action to its latest git-tagged release, pinned to the resolved commit SHA with a trailing '# vX.Y.Z' comment for readability: actions/checkout -> v6.0.2 actions/setup-python -> v6.2.0 actions/setup-node -> v6.4.0 actions/github-script -> v9.0.0 pypa/gh-action-pypi-publish -> v1.14.0 docker/setup-qemu-action -> v4.1.0 docker/setup-buildx-action -> v4.1.0 docker/login-action -> v4.2.0 docker/build-push-action -> v7.2.0 socketdev/action -> v1.3.2 (comment only) Applied across the setup-sfw composite action and all workflows, including docker-stable.yml which previously used floating major-version comments. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com> --- .github/actions/setup-sfw/action.yml | 8 ++++---- .github/workflows/dependabot-review.yml | 10 +++++----- .github/workflows/docker-stable.yml | 10 +++++----- .github/workflows/e2e-test.yml | 6 +++--- .github/workflows/pr-preview.yml | 16 ++++++++-------- .github/workflows/python-tests.yml | 8 ++++---- .github/workflows/release.yml | 14 +++++++------- .github/workflows/version-check.yml | 4 ++-- 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 7f7b8db..34db12d 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -19,18 +19,18 @@ runs: using: "composite" steps: - if: ${{ inputs.python == 'true' || inputs.uv == 'true' }} - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - if: ${{ inputs.node == 'true' }} - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "20" # Official Socket setup action. Wires up sfw routing correctly -- preferred - # over a hand-rolled `npm install -g sfw`. Pinned to a commit SHA (v1.3.2). - - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f + # over a hand-rolled `npm install -g sfw`. + - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 with: mode: firewall-free diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index c3be28a..f6248fa 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -34,7 +34,7 @@ jobs: dockerfile_changed: ${{ steps.diff.outputs.dockerfile_changed }} workflow_or_action_changed: ${{ steps.diff.outputs.workflow_or_action_changed }} steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false @@ -89,7 +89,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false @@ -126,7 +126,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false @@ -145,7 +145,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false @@ -168,7 +168,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false diff --git a/.github/workflows/docker-stable.yml b/.github/workflows/docker-stable.yml index 3639ffc..8f98fd8 100644 --- a/.github/workflows/docker-stable.yml +++ b/.github/workflows/docker-stable.yml @@ -13,7 +13,7 @@ jobs: stable: runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -29,19 +29,19 @@ jobs: echo "Version ${INPUT_VERSION} found on PyPI - proceeding with release" - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to Docker Hub with Organization Token - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build & Push Stable Docker - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: push: true platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 83a6fa4..1ce306f 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -70,16 +70,16 @@ jobs: name: e2e-${{ matrix.name }} steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.12' - - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 if: matrix.setup-node == 'true' with: node-version: '20' diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index ef47ea0..465d480 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -18,11 +18,11 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.13' @@ -63,14 +63,14 @@ jobs: - name: Publish to Test PyPI if: steps.version_check.outputs.exists != 'true' - uses: pypa/gh-action-pypi-publish@ab69e431e9c9f48a3310be0a56527c679f56e04d + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: repository-url: https://test.pypi.org/legacy/ verbose: true - name: Comment on PR if: steps.version_check.outputs.exists != 'true' - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: VERSION: ${{ env.VERSION }} with: @@ -140,21 +140,21 @@ jobs: exit 1 - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to Docker Hub with Organization Token if: steps.verify_package.outputs.success == 'true' - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build & Push Docker Preview if: steps.verify_package.outputs.success == 'true' - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 env: VERSION: ${{ env.VERSION }} with: diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 94f4f82..3247275 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -35,12 +35,12 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false - name: 🐍 setup python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: 🛠️ install deps @@ -71,12 +71,12 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false - name: 🐍 setup python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.10" - name: 🚫 verify install is rejected on unsupported python diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5549b88..f21955a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,11 +10,11 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.13' @@ -70,16 +70,16 @@ jobs: - name: Publish to PyPI if: steps.version_check.outputs.pypi_exists != 'true' - uses: pypa/gh-action-pypi-publish@ab69e431e9c9f48a3310be0a56527c679f56e04d + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf + uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 + uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - name: Login to Docker Hub with Organization Token - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -106,7 +106,7 @@ jobs: if: | steps.verify_package.outputs.success == 'true' && steps.docker_check.outputs.docker_exists != 'true' - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 env: VERSION: ${{ env.VERSION }} with: diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index dc50819..a097208 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -20,7 +20,7 @@ jobs: if: github.event.pull_request.user.login != 'dependabot[bot]' runs-on: ubuntu-latest steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # Fetch all history for all branches persist-credentials: false @@ -90,7 +90,7 @@ jobs: fi - name: Manage PR Comment - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 if: always() && github.event.pull_request.head.repo.full_name == github.repository env: MAIN_VERSION: ${{ env.MAIN_VERSION }} From cb7bc9fcc830703a6936dda420c5835a86e2e28e Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Sun, 31 May 2026 00:36:47 -0400 Subject: [PATCH 07/16] Keep dependabot action pins visible --- .github/actions/setup-sfw/action.yml | 40 ------------------------- .github/workflows/dependabot-review.yml | 27 +++++++++++++---- 2 files changed, 21 insertions(+), 46 deletions(-) delete mode 100644 .github/actions/setup-sfw/action.yml diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml deleted file mode 100644 index 34db12d..0000000 --- a/.github/actions/setup-sfw/action.yml +++ /dev/null @@ -1,40 +0,0 @@ -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" - node: - description: "Set up Node 20 (needed for npm-wrapped checks)" - 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" - - - if: ${{ inputs.node == 'true' }} - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: "20" - - # Official Socket setup action. Wires up sfw routing correctly -- preferred - # over a hand-rolled `npm install -g sfw`. - - 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/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index f6248fa..502dd4a 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -94,9 +94,16 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: ./.github/actions/setup-sfw + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: - uv: "true" + python-version: "3.12" + + - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 + with: + mode: firewall-free + + - name: Install uv + run: python -m pip install --upgrade pip uv - name: Sync project through Socket Firewall # `sfw uv sync` is the intended way to route uv through Socket Firewall @@ -131,9 +138,13 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: ./.github/actions/setup-sfw + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node: "true" + node-version: "20" + + - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 + with: + mode: firewall-free - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm @@ -150,9 +161,13 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: ./.github/actions/setup-sfw + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.12" + + - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 with: - python: "true" + mode: firewall-free - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-pypi From ba9b94692b37a55a4eeaaedcb497fa6d1547396b Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Sun, 31 May 2026 00:44:07 -0400 Subject: [PATCH 08/16] Configure Dependabot for composite actions --- .../actions/setup-docker-publish/action.yml | 23 +++++++++++ .github/actions/setup-sfw/action.yml | 39 +++++++++++++++++++ .github/dependabot.yml | 4 +- .github/workflows/dependabot-review.yml | 27 +++---------- .github/workflows/docker-stable.yml | 14 ++----- .github/workflows/pr-preview.yml | 14 ++----- .github/workflows/release.yml | 14 ++----- 7 files changed, 83 insertions(+), 52 deletions(-) create mode 100644 .github/actions/setup-docker-publish/action.yml create mode 100644 .github/actions/setup-sfw/action.yml diff --git a/.github/actions/setup-docker-publish/action.yml b/.github/actions/setup-docker-publish/action.yml new file mode 100644 index 0000000..dd0a17c --- /dev/null +++ b/.github/actions/setup-docker-publish/action.yml @@ -0,0 +1,23 @@ +name: "Set up Docker publish" +description: >- + Set up QEMU + Docker Buildx and authenticate to Docker Hub for multi-arch + image builds. Centralizes the QEMU/Buildx/login trio used by release, + preview, and stable workflows. + +inputs: + dockerhub-username: + description: "Docker Hub username (pass from secrets)" + required: true + dockerhub-token: + description: "Docker Hub token/password (pass from secrets)" + required: true + +runs: + using: "composite" + steps: + - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 + - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 + - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + with: + username: ${{ inputs.dockerhub-username }} + password: ${{ inputs.dockerhub-token }} diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml new file mode 100644 index 0000000..2804701 --- /dev/null +++ b/.github/actions/setup-sfw/action.yml @@ -0,0 +1,39 @@ +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" + node: + description: "Set up Node 20 (needed for npm-wrapped checks)" + 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" + + - if: ${{ inputs.node == 'true' }} + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "20" + + # 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 index 7c05ee5..08a2a62 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -38,7 +38,9 @@ updates: # GitHub Actions used in workflows - package-ecosystem: "github-actions" - directory: "/" + directories: + - "/" + - "/.github/actions/*" schedule: interval: "weekly" open-pull-requests-limit: 2 diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index 502dd4a..f6248fa 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -94,16 +94,9 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: ./.github/actions/setup-sfw with: - python-version: "3.12" - - - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 - with: - mode: firewall-free - - - name: Install uv - run: python -m pip install --upgrade pip uv + uv: "true" - name: Sync project through Socket Firewall # `sfw uv sync` is the intended way to route uv through Socket Firewall @@ -138,13 +131,9 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: ./.github/actions/setup-sfw with: - node-version: "20" - - - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 - with: - mode: firewall-free + node: "true" - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm @@ -161,13 +150,9 @@ jobs: fetch-depth: 1 persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.12" - - - uses: socketdev/action@ba6de6cc0565af1f42295590380973573297e31f # v1.3.2 + - uses: ./.github/actions/setup-sfw with: - mode: firewall-free + python: "true" - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-pypi diff --git a/.github/workflows/docker-stable.yml b/.github/workflows/docker-stable.yml index 55b9e2d..fcb3ba2 100644 --- a/.github/workflows/docker-stable.yml +++ b/.github/workflows/docker-stable.yml @@ -28,17 +28,11 @@ jobs: fi echo "Version ${INPUT_VERSION} found on PyPI - proceeding with release" - - name: Set up QEMU - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Login to Docker Hub with Organization Token - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + - name: Set up Docker publishing + uses: ./.github/actions/setup-docker-publish with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build & Push Stable Docker uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index e5bc117..c7d70dc 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -141,18 +141,12 @@ jobs: echo "success=false" >> $GITHUB_OUTPUT exit 1 - - name: Set up QEMU - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Login to Docker Hub with Organization Token + - name: Set up Docker publishing if: steps.verify_package.outputs.success == 'true' - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + uses: ./.github/actions/setup-docker-publish with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build & Push Docker Preview if: steps.verify_package.outputs.success == 'true' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db8a2de..5e8d2f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,17 +68,11 @@ jobs: if: steps.version_check.outputs.pypi_exists != 'true' uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 - - name: Set up QEMU - uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Login to Docker Hub with Organization Token - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + - name: Set up Docker publishing + uses: ./.github/actions/setup-docker-publish with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} - name: Verify package is installable id: verify_package From d9cb95cd80252f7d634f8a1397d707c26ab04131 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Sun, 31 May 2026 00:59:08 -0400 Subject: [PATCH 09/16] Include composite actions in Dependabot review notice --- .github/dependabot.yml | 2 +- .github/workflows/dependabot-review.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 08a2a62..89e2ed0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -36,7 +36,7 @@ updates: cooldown: default-days: 7 - # GitHub Actions used in workflows + # GitHub Actions used in workflows and local composite actions. - package-ecosystem: "github-actions" directories: - "/" diff --git a/.github/workflows/dependabot-review.yml b/.github/workflows/dependabot-review.yml index f6248fa..2bc8879 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependabot-review.yml @@ -68,7 +68,7 @@ jobs: echo "fixture_npm_changed=$(has_file '^tests/e2e/fixtures/simple-npm/')" echo "fixture_pypi_changed=$(has_file '^tests/e2e/fixtures/simple-pypi/')" echo "dockerfile_changed=$(has_file '^Dockerfile$')" - echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/dependabot\.yml$')" + echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/actions/|^\.github/dependabot\.yml$')" } >> "$GITHUB_OUTPUT" - name: Summarize review expectations From e3bef31921b652717763094cf225445cde8c912b Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Sun, 31 May 2026 01:00:39 -0400 Subject: [PATCH 10/16] Rename Docker setup composite action --- .../actions/{setup-docker-publish => setup-docker}/action.yml | 2 +- .github/workflows/docker-stable.yml | 2 +- .github/workflows/pr-preview.yml | 2 +- .github/workflows/release.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename .github/actions/{setup-docker-publish => setup-docker}/action.yml (96%) diff --git a/.github/actions/setup-docker-publish/action.yml b/.github/actions/setup-docker/action.yml similarity index 96% rename from .github/actions/setup-docker-publish/action.yml rename to .github/actions/setup-docker/action.yml index dd0a17c..846efd4 100644 --- a/.github/actions/setup-docker-publish/action.yml +++ b/.github/actions/setup-docker/action.yml @@ -1,4 +1,4 @@ -name: "Set up Docker publish" +name: "Set up Docker" description: >- Set up QEMU + Docker Buildx and authenticate to Docker Hub for multi-arch image builds. Centralizes the QEMU/Buildx/login trio used by release, diff --git a/.github/workflows/docker-stable.yml b/.github/workflows/docker-stable.yml index fcb3ba2..934e0d9 100644 --- a/.github/workflows/docker-stable.yml +++ b/.github/workflows/docker-stable.yml @@ -29,7 +29,7 @@ jobs: echo "Version ${INPUT_VERSION} found on PyPI - proceeding with release" - name: Set up Docker publishing - uses: ./.github/actions/setup-docker-publish + uses: ./.github/actions/setup-docker with: dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml index c7d70dc..eb29ef9 100644 --- a/.github/workflows/pr-preview.yml +++ b/.github/workflows/pr-preview.yml @@ -143,7 +143,7 @@ jobs: - name: Set up Docker publishing if: steps.verify_package.outputs.success == 'true' - uses: ./.github/actions/setup-docker-publish + uses: ./.github/actions/setup-docker with: dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e8d2f8..6c41e9c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 - name: Set up Docker publishing - uses: ./.github/actions/setup-docker-publish + uses: ./.github/actions/setup-docker with: dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} From e653f3f7fa6f4f3abbd862ad31f312136debaf9f Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:06:17 -0400 Subject: [PATCH 11/16] Extend dependency review to maintainer PRs --- .github/actions/setup-sfw/action.yml | 18 ++++- ...dabot-review.yml => dependency-review.yml} | 78 +++++++++++++++---- .github/workflows/e2e-test.yml | 2 +- 3 files changed, 80 insertions(+), 18 deletions(-) rename .github/workflows/{dependabot-review.yml => dependency-review.yml} (65%) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 2804701..456f90b 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. + or enterprise edition) 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: @@ -14,6 +16,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" @@ -29,9 +37,11 @@ runs: node-version: "20" # 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 65% rename from .github/workflows/dependabot-review.yml rename to .github/workflows/dependency-review.yml index 2bc8879..c43791d 100644 --- a/.github/workflows/dependabot-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,13 +1,22 @@ -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. # -# Runs only on PRs opened by dependabot[bot]. Inspects which files -# changed, then conditionally runs Socket Firewall (sfw) install smoke -# jobs for the affected manifests. 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. +# Inspects the changed files, then conditionally runs Socket Firewall (sfw) +# install smoke jobs for the affected manifests, picking the firewall edition +# per PR: +# +# - 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 degrades to free whenever the token is absent, so this workflow is +# safe to ship before the secret exists and starts using enterprise +# automatically once SOCKET_API_TOKEN is configured. # # Pattern adapted from SocketDev/socket-basics. @@ -19,12 +28,11 @@ 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: @@ -33,6 +41,7 @@ jobs: fixture_pypi_changed: ${{ steps.diff.outputs.fixture_pypi_changed }} dockerfile_changed: ${{ steps.diff.outputs.dockerfile_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: @@ -71,15 +80,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" @@ -97,6 +133,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 @@ -105,9 +143,19 @@ 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. The firewall is here to vet PyPI installs, not the + # interpreter/toolchain download path. + env: + UV_PYTHON: "3.12" + UV_PYTHON_DOWNLOADS: never run: sfw uv sync --locked --extra test --extra dev - name: Import smoke test + env: + UV_PYTHON: "3.12" + UV_PYTHON_DOWNLOADS: never run: | uv run python -c " from socketsecurity.socketcli import cli, build_socket_sdk @@ -134,6 +182,8 @@ jobs: - uses: ./.github/actions/setup-sfw with: node: "true" + mode: ${{ needs.inspect.outputs.sfw_mode }} + socket-token: ${{ secrets.SOCKET_API_TOKEN }} - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm @@ -153,6 +203,8 @@ jobs: - uses: ./.github/actions/setup-sfw with: python: "true" + mode: ${{ needs.inspect.outputs.sfw_mode }} + socket-token: ${{ secrets.SOCKET_API_TOKEN }} - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-pypi @@ -186,6 +238,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" diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 1ce306f..f233115 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -14,7 +14,7 @@ jobs: # Skip e2e on: # - PRs from forks (no secrets) # - Dependabot PRs (no secrets, and dependency-bump risk is already - # covered by dependabot-review.yml's Socket Firewall smoke jobs) + # covered by dependency-review.yml's Socket Firewall smoke jobs) if: >- (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && From 1ca3aa73d928d9245baa99b8a123005ed6059512 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:06:54 -0400 Subject: [PATCH 12/16] Use CLI Socket token for enterprise dependency review --- .github/workflows/dependency-review.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index c43791d..96a512c 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -7,8 +7,9 @@ name: dependency-review # install smoke jobs for the affected manifests, picking the firewall edition # per PR: # -# - Trusted SocketDev members on an in-repo (non-fork) PR, when the -# SOCKET_API_TOKEN secret is present -> Socket Firewall ENTERPRISE +# - Trusted SocketDev members on an in-repo (non-fork) PR, when +# SOCKET_API_TOKEN or SOCKET_CLI_API_TOKEN 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 @@ -16,7 +17,7 @@ name: dependency-review # # The mode degrades to free whenever the token is absent, so this workflow is # safe to ship before the secret exists and starts using enterprise -# automatically once SOCKET_API_TOKEN is configured. +# automatically once a Socket API token secret is configured. # # Pattern adapted from SocketDev/socket-basics. @@ -86,8 +87,8 @@ jobs: 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 }} + # Empty for fork PRs (secrets withheld) and until a token secret is added. + SOCKET_API_TOKEN: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_API_TOKEN }} run: | mode=firewall-free # Enterprise only for a trusted SocketDev member (OWNER/MEMBER) or @@ -134,7 +135,7 @@ jobs: with: uv: "true" mode: ${{ needs.inspect.outputs.sfw_mode }} - socket-token: ${{ secrets.SOCKET_API_TOKEN }} + socket-token: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_API_TOKEN }} - name: Sync project through Socket Firewall # `sfw uv sync` is the intended way to route uv through Socket Firewall @@ -183,7 +184,7 @@ jobs: with: node: "true" mode: ${{ needs.inspect.outputs.sfw_mode }} - socket-token: ${{ secrets.SOCKET_API_TOKEN }} + socket-token: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_API_TOKEN }} - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm @@ -204,7 +205,7 @@ jobs: with: python: "true" mode: ${{ needs.inspect.outputs.sfw_mode }} - socket-token: ${{ secrets.SOCKET_API_TOKEN }} + socket-token: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_API_TOKEN }} - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-pypi From cf2f02f5c94fc7274829ec594f26c8f715b108d9 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:14:46 -0400 Subject: [PATCH 13/16] Restrict enterprise SFW to org members --- .github/workflows/dependency-review.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 96a512c..ceace8b 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -7,13 +7,13 @@ name: dependency-review # install smoke jobs for the affected manifests, picking the firewall edition # per PR: # -# - Trusted SocketDev members on an in-repo (non-fork) PR, when +# - SocketDev org members on an in-repo (non-fork) PR, when # SOCKET_API_TOKEN or SOCKET_CLI_API_TOKEN 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. +# - Everything else -- Dependabot, forks, outside collaborators, external +# contributors, or a missing token -> Socket Firewall FREE (anonymous, no +# API token), which is safe in the unprivileged `pull_request` context. # # The mode degrades to free whenever the token is absent, so this workflow is # safe to ship before the secret exists and starts using enterprise @@ -91,13 +91,14 @@ jobs: SOCKET_API_TOKEN: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_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 41be66a2521ba3e4844b4ea7e3cc7f8d225409fd Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:16:14 -0400 Subject: [PATCH 14/16] Use dedicated SFW token secret --- .github/workflows/dependency-review.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index ceace8b..7e6ea64 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -8,8 +8,7 @@ name: dependency-review # per PR: # # - SocketDev org members on an in-repo (non-fork) PR, when -# SOCKET_API_TOKEN or SOCKET_CLI_API_TOKEN is present -> Socket Firewall -# ENTERPRISE +# SOCKET_SFW_API_TOKEN is present -> Socket Firewall ENTERPRISE # (authenticated, full org-policy enforcement). # - Everything else -- Dependabot, forks, outside collaborators, external # contributors, or a missing token -> Socket Firewall FREE (anonymous, no @@ -88,7 +87,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 a token secret is added. - SOCKET_API_TOKEN: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_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 @@ -97,7 +96,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 @@ -136,7 +135,7 @@ jobs: with: uv: "true" mode: ${{ needs.inspect.outputs.sfw_mode }} - socket-token: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_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 @@ -185,7 +184,7 @@ jobs: with: node: "true" mode: ${{ needs.inspect.outputs.sfw_mode }} - socket-token: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_API_TOKEN }} + socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }} - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm @@ -206,7 +205,7 @@ jobs: with: python: "true" mode: ${{ needs.inspect.outputs.sfw_mode }} - socket-token: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_CLI_API_TOKEN }} + socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }} - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-pypi From 64a4d96d25e354b92db6f56d1338bf95a05a0655 Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:50:02 -0400 Subject: [PATCH 15/16] Use environment-scoped SFW token --- .github/workflows/dependency-review.yml | 150 ++++++++++++++++++++---- 1 file changed, 126 insertions(+), 24 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 7e6ea64..eb42ac0 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -7,16 +7,15 @@ name: dependency-review # install smoke jobs for the affected manifests, picking the firewall edition # per PR: # -# - SocketDev org members on an in-repo (non-fork) PR, when -# SOCKET_SFW_API_TOKEN is present -> Socket Firewall ENTERPRISE -# (authenticated, full org-policy enforcement). -# - Everything else -- Dependabot, forks, outside collaborators, external -# contributors, or a missing token -> Socket Firewall FREE (anonymous, no -# API token), which is safe in the unprivileged `pull_request` context. +# - SocketDev org members on an in-repo (non-fork) PR -> Socket Firewall +# ENTERPRISE through the socket-firewall environment and its +# SOCKET_SFW_API_TOKEN secret (authenticated, full org-policy enforcement). +# - Everything else -- Dependabot, forks, outside collaborators, or external +# contributors -> Socket Firewall FREE (anonymous, no API token), which is +# safe in the unprivileged `pull_request` context. # -# The mode degrades to free whenever the token is absent, so this workflow is -# safe to ship before the secret exists and starts using enterprise -# automatically once a Socket API token secret is configured. +# Only Enterprise jobs declare the socket-firewall environment. Free jobs do +# not touch that environment or its token. # # Pattern adapted from SocketDev/socket-basics. @@ -86,17 +85,13 @@ jobs: 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 a token 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. + # in-repo PR. Everything else -- Dependabot, forks, outside + # collaborators, and external contributors -- uses the free edition. if [ "$IS_DEPENDABOT" != "true" ] \ && [ "$IS_FORK" != "true" ] \ - && [ -n "$SOCKET_SFW_API_TOKEN" ] \ && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER)$'; then mode=firewall-enterprise fi @@ -120,9 +115,11 @@ jobs: echo "- This workflow runs in pull_request context only; no publish secrets are exposed" } >> "$GITHUB_STEP_SUMMARY" - python-sfw-smoke: + 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.sfw_mode == 'firewall-free' runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -134,7 +131,58 @@ jobs: - uses: ./.github/actions/setup-sfw with: uv: "true" - mode: ${{ needs.inspect.outputs.sfw_mode }} + mode: firewall-free + + - 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. The firewall is here to vet PyPI installs, not the + # interpreter/toolchain download path. + env: + UV_PYTHON: "3.12" + UV_PYTHON_DOWNLOADS: never + run: sfw uv sync --locked --extra test --extra dev + + - name: Import smoke test + env: + UV_PYTHON: "3.12" + UV_PYTHON_DOWNLOADS: never + run: | + uv run python -c " + from socketsecurity.socketcli import cli, build_socket_sdk + from socketsecurity.core import Core + from socketsecurity.core.exceptions import ( + APIFailure, RequestTimeoutExceeded, APIResourceNotFound, + ) + from socketsecurity.core.git_interface import Git + from socketsecurity.config import CliConfig + print('import smoke OK') + " + + python-sfw-smoke-enterprise: + needs: inspect + if: | + needs.inspect.outputs.python_deps_changed == 'true' && + needs.inspect.outputs.sfw_mode == 'firewall-enterprise' + 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 @@ -169,9 +217,11 @@ jobs: print('import smoke OK') " - fixture-npm-sfw-smoke: + fixture-npm-sfw-smoke-free: needs: inspect - if: needs.inspect.outputs.fixture_npm_changed == 'true' + if: | + needs.inspect.outputs.fixture_npm_changed == 'true' && + needs.inspect.outputs.sfw_mode == 'firewall-free' runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -183,18 +233,70 @@ jobs: - uses: ./.github/actions/setup-sfw with: node: "true" - mode: ${{ needs.inspect.outputs.sfw_mode }} + mode: firewall-free + + - name: Install fixture through Socket Firewall + working-directory: tests/e2e/fixtures/simple-npm + run: sfw npm install --no-audit --no-fund --ignore-scripts + + fixture-npm-sfw-smoke-enterprise: + needs: inspect + if: | + needs.inspect.outputs.fixture_npm_changed == 'true' && + needs.inspect.outputs.sfw_mode == 'firewall-enterprise' + 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: + node: "true" + mode: firewall-enterprise socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }} - name: Install fixture through Socket Firewall working-directory: tests/e2e/fixtures/simple-npm run: sfw npm install --no-audit --no-fund --ignore-scripts - fixture-pypi-sfw-smoke: + fixture-pypi-sfw-smoke-free: + needs: inspect + if: | + needs.inspect.outputs.fixture_pypi_changed == 'true' && + needs.inspect.outputs.sfw_mode == 'firewall-free' + 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: + python: "true" + mode: firewall-free + + - name: Install fixture through Socket Firewall + working-directory: tests/e2e/fixtures/simple-pypi + run: | + python -m venv .venv + # shellcheck disable=SC1091 + source .venv/bin/activate + sfw pip install -r requirements.txt + + fixture-pypi-sfw-smoke-enterprise: needs: inspect - if: needs.inspect.outputs.fixture_pypi_changed == 'true' + if: | + needs.inspect.outputs.fixture_pypi_changed == 'true' && + needs.inspect.outputs.sfw_mode == 'firewall-enterprise' runs-on: ubuntu-latest timeout-minutes: 15 + environment: socket-firewall steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -204,7 +306,7 @@ jobs: - uses: ./.github/actions/setup-sfw with: python: "true" - mode: ${{ needs.inspect.outputs.sfw_mode }} + mode: firewall-enterprise socket-token: ${{ secrets.SOCKET_SFW_API_TOKEN }} - name: Install fixture through Socket Firewall From 4f68ba17075857349702978690d86b7301361e7e Mon Sep 17 00:00:00 2001 From: lelia <2418071+lelia@users.noreply.github.com> Date: Mon, 1 Jun 2026 16:55:00 -0400 Subject: [PATCH 16/16] Gate enterprise SFW on non-fork PRs --- .github/workflows/dependency-review.yml | 31 +++++++++++++++---------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index eb42ac0..dffa8a8 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -7,12 +7,13 @@ name: dependency-review # install smoke jobs for the affected manifests, picking the firewall edition # per PR: # -# - SocketDev org members on an in-repo (non-fork) PR -> Socket Firewall -# ENTERPRISE through the socket-firewall environment and its -# SOCKET_SFW_API_TOKEN secret (authenticated, full org-policy enforcement). -# - Everything else -- Dependabot, forks, outside collaborators, or external -# contributors -> Socket Firewall FREE (anonymous, no API token), which is -# safe in the unprivileged `pull_request` context. +# - Trusted authors: any in-repo (non-fork) PR other than Dependabot's +# (i.e. someone with write access) -> Socket Firewall ENTERPRISE through +# the socket-firewall environment and its SOCKET_SFW_API_TOKEN secret +# (authenticated, full org-policy enforcement). +# - Everyone else: Dependabot and all fork PRs from external contributors -> +# Socket Firewall FREE (anonymous, no API token), which is safe in the +# unprivileged `pull_request` context. # # Only Enterprise jobs declare the socket-firewall environment. Free jobs do # not touch that environment or its token. @@ -81,18 +82,24 @@ jobs: - name: Determine Socket Firewall mode id: mode + # 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: | mode=firewall-free - # Enterprise only for a SocketDev org member (OWNER/MEMBER) on an - # in-repo PR. Everything else -- Dependabot, forks, outside - # collaborators, and external contributors -- uses the free edition. - if [ "$IS_DEPENDABOT" != "true" ] \ - && [ "$IS_FORK" != "true" ] \ - && printf '%s' "$AUTHOR_ASSOC" | grep -qE '^(OWNER|MEMBER)$'; then + if [ "$IS_DEPENDABOT" != "true" ] && [ "$IS_FORK" != "true" ]; then mode=firewall-enterprise fi