diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 58c3f39..a39694f 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -586,3 +586,68 @@ jobs: echo "This PR changes workflow, composite-action, or dependabot config files." echo "Require explicit human review before merge." } >> "$GITHUB_STEP_SUMMARY" + + # Single required status check that aggregates the conditional smoke jobs + # above. Branch protection can't require those jobs individually: each is + # conditional (per-manifest, and Firewall-free vs -enterprise per author), so + # on any given PR most are legitimately skipped -- and a required check whose + # job is skipped sits at "Expected -- Waiting for status to be reported" + # forever, blocking merge (the same trap that stranded Dependabot PRs on the + # e2e-* checks). + # + # This gate always runs (if: always(), so it reports even when upstream jobs + # are skipped or fail) and collapses them into one pass/fail signal: it FAILS + # if any smoke job that ran ended in failure or was cancelled, and passes when + # everything either succeeded or was not applicable. 'skipped' is expected and + # allowed -- it just means the job didn't apply to this PR. + # + # Mark THIS check (dependency-review-gate) required in branch protection. It + # satisfies Dependabot/fork PRs (which run the Firewall-free job) and + # maintainer PRs (which run Firewall-enterprise) alike, and -- crucially -- a + # Socket Firewall BLOCK now fails the gate and blocks merge, instead of living + # in a non-required enterprise job that nobody is forced to run. + dependency-review-gate: + needs: + - inspect + - python-sfw-smoke-free + - python-sfw-smoke-enterprise + - fixture-npm-sfw-smoke-free + - fixture-npm-sfw-smoke-enterprise + - fixture-pypi-sfw-smoke-free + - fixture-pypi-sfw-smoke-enterprise + - dockerfile-smoke + if: always() + runs-on: ubuntu-latest + timeout-minutes: 2 + steps: + - name: Verify no smoke job failed + env: + RESULTS: ${{ toJSON(needs) }} + run: | + echo "Upstream job results:" + printf '%s\n' "$RESULTS" | python3 -m json.tool + + # Fail the gate if any needed job ended in failure or was cancelled. + # 'success' and 'skipped' both pass: skipped means the job did not + # apply to this PR (wrong manifest, or free-vs-enterprise mismatch). + failed="$(printf '%s\n' "$RESULTS" | python3 -c " + import json, sys + data = json.load(sys.stdin) + bad = [name for name, info in data.items() + if info.get('result') in ('failure', 'cancelled')] + print(' '.join(sorted(bad))) + ")" + + if [ -n "$failed" ]; then + echo "::error::dependency-review smoke job(s) failed: $failed" + { + echo "## Dependency Review Gate: FAILED" + echo "The following smoke job(s) failed or were cancelled: \`$failed\`" + echo "If a Socket Firewall job is listed, it likely BLOCKED an install --" + echo "inspect its uploaded sfw-artifacts/ report before merging." + } >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi + + echo "All dependency-review smoke jobs passed or were not applicable." + echo "## Dependency Review Gate: PASSED" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index f233115..1dab8a8 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -104,3 +104,34 @@ jobs: env: SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }} run: bash ${{ matrix.validate }} + + # Branch protection requires the e2e-* checks, but the `e2e` job above is + # skipped on PRs that can't access repository secrets -- fork PRs and + # Dependabot PRs. A job skipped via a job-level `if` never expands its + # matrix, so the e2e-* check contexts are never created and the required + # checks sit at "Expected -- Waiting for status to be reported" forever, + # permanently blocking merge. + # + # This bypass reports a green status under the SAME e2e-* check names for + # exactly those PRs, satisfying branch protection without running the real + # tests (which need SOCKET_CLI_API_TOKEN). Its `if` is the precise negation + # of the e2e job's run condition, so the two are mutually exclusive: any + # given PR runs one or the other, never both, and never neither. + # + # Dependency-bump risk on these PRs is still covered by dependency-review.yml's + # Socket Firewall smoke jobs, which run without repository secrets. + e2e-bypass: + if: >- + github.event_name == 'pull_request' && + (github.event.pull_request.head.repo.full_name != github.repository || + github.event.pull_request.user.login == 'dependabot[bot]') + runs-on: ubuntu-latest + strategy: + matrix: + name: [scan, sarif, reachability, gitlab, json, pypi] + name: e2e-${{ matrix.name }} + steps: + - name: Report skip status + run: | + echo "Skipping e2e-${{ matrix.name }} for a PR without repository secrets" + echo "(fork or Dependabot). Dependency risk is covered by dependency-review.yml." diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d25bb..b2ea93d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2.4.5 + +### Changed: Bump required SDK version to `>=3.2.1` + +- Picks up `socketdev 3.2.1`. +- No CLI logic changes. + ## 2.4.4 ### Changed: Bump required SDK version to `>=3.2.0` diff --git a/pyproject.toml b/pyproject.toml index fdbc17e..12955b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.4.4" +version = "2.4.5" requires-python = ">= 3.11" license = {"file" = "LICENSE"} dependencies = [ @@ -16,7 +16,7 @@ dependencies = [ 'GitPython', 'packaging', 'python-dotenv', - "socketdev>=3.2.0,<4.0.0", + "socketdev>=3.2.1,<4.0.0", "bs4>=0.0.2", "markdown>=3.10", "brotli>=1.0.9; platform_python_implementation == 'CPython'", diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index 4bda66b..de36ffe 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,3 +1,3 @@ __author__ = 'socket.dev' -__version__ = '2.4.4' +__version__ = '2.4.5' USER_AGENT = f'SocketPythonCLI/{__version__}' diff --git a/uv.lock b/uv.lock index dde139a..0a53c6b 100644 --- a/uv.lock +++ b/uv.lock @@ -1257,20 +1257,20 @@ wheels = [ [[package]] name = "socketdev" -version = "3.2.0" +version = "3.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/05/0748d1a357a743f968475aecfad4d53ce109ae65fc418d177faecbb25754/socketdev-3.2.0.tar.gz", hash = "sha256:d8743e1a83135f17e8713539c656b4847ada1450315b05e48ec8df1ed984c307", size = 178440, upload-time = "2026-06-03T02:47:26.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/65/07df2bf6e490c56544fb06e4cfde059b2572fdd5b02ff352c766b1d5f7ce/socketdev-3.2.1.tar.gz", hash = "sha256:7db910a98628473e8ec06822deb01b6bd465b385e9e8ea405f2b7526e8258074", size = 179279, upload-time = "2026-06-03T18:08:19.806Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/b5/6a3b2bcec759d5d306f416e1b167b985f44f89e990afcd25569a2e591ffd/socketdev-3.2.0-py3-none-any.whl", hash = "sha256:e4b97bdc22ec8e12899f218c8089eaa8e9696f7556930e13f05996ad210718af", size = 67267, upload-time = "2026-06-03T02:47:24.76Z" }, + { url = "https://files.pythonhosted.org/packages/53/01/fff70923755b3a187ca971189fb078a2aaedcad42d682abfdd06f3445def/socketdev-3.2.1-py3-none-any.whl", hash = "sha256:6dc762d78baea8011dc22f2afe49c84c926e640a6879bd7b58c3abdd4e29e8bb", size = 67266, upload-time = "2026-06-03T18:08:18.029Z" }, ] [[package]] name = "socketsecurity" -version = "2.4.4" +version = "2.4.5" source = { editable = "." } dependencies = [ { name = "brotli", marker = "platform_python_implementation == 'CPython'" }, @@ -1327,7 +1327,7 @@ requires-dist = [ { name = "python-dotenv" }, { name = "requests" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.3.0" }, - { name = "socketdev", specifier = ">=3.2.0,<4.0.0" }, + { name = "socketdev", specifier = ">=3.2.1,<4.0.0" }, { name = "twine", marker = "extra == 'dev'" }, { name = "uv", marker = "extra == 'dev'", specifier = ">=0.1.0" }, ]