diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index df2ac35..1e41e14 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -598,7 +598,8 @@ jobs: # The eMudhra test hosts serve certificates from the compromised # intermediates. Require the specific rejection, not just any failure, so - # a DNS or network error cannot pass as "rejected". + # a DNS or network error cannot pass as "rejected". pip is checked on its + # own because it uses its vendored CA bundle, not the system store. - name: Check TLS clients reject the compromised eMudhra intermediates run: | docker run --rm --entrypoint sh socket-python-cli:dependabot-smoke -c ' @@ -640,6 +641,14 @@ jobs: echo "::error::python requests on $host: $result" status=1 fi + if python -m pip download --no-deps --no-cache-dir --retries 0 --timeout 30 \ + --dest /tmp/pip-tls --index-url "https://$host/simple/" certifi 2>&1 \ + | grep -q CERTIFICATE_VERIFY_FAILED; then + echo "pip rejects $host" + else + echo "::error::pip on $host did not fail certificate verification" + status=1 + fi done for url in https://api.socket.dev/v0/ https://registry.npmjs.org/ https://pypi.org/simple/; do if curl -sS -m 30 -o /dev/null "$url"; then diff --git a/CHANGELOG.md b/CHANGELOG.md index d79801a..199a3ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 2.11.3 + +### Fixed: finish rejecting the compromised eMudhra intermediate CAs + +- `socketsecurity` now requires `certifi>=2026.10.19`. Previously only the + Docker image's lockfile pinned it, so `pip install socketsecurity` could keep + an older CA bundle that trusts the compromised intermediates. +- The Docker image upgrades `pip` to `26.2.2` or newer. Earlier versions use + their own vendored CA bundle, which still trusted them. + +### Changed: bump the Socket Python SDK to 3.6.1 + +- Bumped the pinned Socket Python SDK (`socketdev`) from `3.6.0` to `3.6.1`, + which also requires `certifi>=2026.10.19`. + ## 2.11.2 ### Fixed: stop trusting the compromised eMudhra intermediate CAs diff --git a/Dockerfile b/Dockerfile index 15a6ada..42abb4d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,12 @@ RUN apk update && apk add --no-cache \ apk upgrade --no-cache ca-certificates && \ [ "$(apk version -t "$(apk list -I ca-certificates | awk '{print $1}' | sed 's/^ca-certificates-//')" 20261020-r0)" != "<" ] +# pip uses its own vendored CA bundle, not the system store, and the base image's +# pip trusts the compromised eMudhra intermediates. 26.2.2 vendors certifi +# 2026.10.19. Upgrade it against the system store fixed above. +RUN python -m pip install --no-cache-dir --cert /etc/ssl/certs/ca-certificates.crt --upgrade "pip>=26.2.2" && \ + python -c 'import sys, pip; sys.exit(tuple(map(int, pip.__version__.split(".")[:3])) < (26, 2, 2))' + # npm, npx and yarn are symlinks in the node image. COPY would dereference them # into flat files and break their relative requires, so recreate the links. COPY --from=node /usr/local/bin/node /usr/local/bin/node diff --git a/pyproject.toml b/pyproject.toml index 6880d95..2165990 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "socketsecurity" -version = "2.11.2" +version = "2.11.3" requires-python = ">= 3.11" license = {"file" = "LICENSE"} dependencies = [ @@ -16,11 +16,15 @@ dependencies = [ "gitpython==3.2.0", "packaging==26.3", "python-dotenv==1.2.4", - "socketdev==3.6.0", + "socketdev==3.6.1", "beautifulsoup4==4.15.0", "markdown==3.11.1", "brotli==1.2.0; platform_python_implementation == 'CPython'", "brotlicffi==1.2.0.2; platform_python_implementation != 'CPython'", + # requests' CA bundle. A floor rather than an exact pin, so later trust-store + # fixes still reach installs; 2026.10.19 drops the roots behind the + # compromised eMudhra intermediates. + "certifi>=2026.10.19", ] readme = "README.md" description = "Socket Security CLI for CI/CD" diff --git a/socketsecurity/__init__.py b/socketsecurity/__init__.py index 753c46e..0d315d5 100644 --- a/socketsecurity/__init__.py +++ b/socketsecurity/__init__.py @@ -1,3 +1,3 @@ __author__ = 'socket.dev' -__version__ = '2.11.2' +__version__ = '2.11.3' USER_AGENT = f'SocketPythonCLI/{__version__}' diff --git a/uv.lock b/uv.lock index aa5510c..dfb182b 100644 --- a/uv.lock +++ b/uv.lock @@ -1279,25 +1279,27 @@ wheels = [ [[package]] name = "socketdev" -version = "3.6.0" +version = "3.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "certifi" }, { name = "requests" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4f/64/7c44c1b1f739db9c40ea0a3d429d27b1810f43153b0680ec4bfc3aada644/socketdev-3.6.0.tar.gz", hash = "sha256:8453da37520db79735479e0892b74cef8ce5bcba52c99f15ffacc6a70f9ea355", size = 201023, upload-time = "2026-09-09T22:44:01.761Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/de/c62e2e76ae5f7f55901d1d1cefc5b141309342fd1ae19d44f350fd264efc/socketdev-3.6.1.tar.gz", hash = "sha256:4086d6842a51dd4fd91ecf6aa3f1e24e7c7200f110e50c866784bc09e1fc9986", size = 176415, upload-time = "2026-10-21T21:30:12.548Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/c3/e4ac2746b803109eab1fa7b44b02697f365fb418340411df5091e50d4a07/socketdev-3.6.0-py3-none-any.whl", hash = "sha256:fe017aefa72638375bb0fb1697bf5704f64143a5fab634534dfc3e77c851b374", size = 73352, upload-time = "2026-09-09T22:44:00.167Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/fe1e09c59dd566391aaf7bd0fb89ed9d1011e13f8fc4f4e8cc9376243888/socketdev-3.6.1-py3-none-any.whl", hash = "sha256:2297c7442c5debfa14ac464fd08b2755edbd71063b4cee3844bff173167f30e4", size = 73395, upload-time = "2026-10-21T21:30:10.718Z" }, ] [[package]] name = "socketsecurity" -version = "2.11.2" +version = "2.11.3" source = { editable = "." } dependencies = [ { name = "beautifulsoup4" }, { name = "brotli", marker = "platform_python_implementation == 'CPython'" }, { name = "brotlicffi", marker = "platform_python_implementation != 'CPython'" }, + { name = "certifi" }, { name = "gitpython" }, { name = "markdown" }, { name = "mdutils" }, @@ -1334,6 +1336,7 @@ requires-dist = [ { name = "beautifulsoup4", specifier = "==4.15.0" }, { name = "brotli", marker = "platform_python_implementation == 'CPython'", specifier = "==1.2.0" }, { name = "brotlicffi", marker = "platform_python_implementation != 'CPython'", specifier = "==1.2.0.2" }, + { name = "certifi", specifier = ">=2026.10.19" }, { name = "gitpython", specifier = "==3.2.0" }, { name = "hatch", marker = "extra == 'dev'", specifier = "==1.18.1" }, { name = "markdown", specifier = "==3.11.1" }, @@ -1349,7 +1352,7 @@ requires-dist = [ { name = "python-dotenv", specifier = "==1.2.4" }, { name = "requests", specifier = "==2.34.2" }, { name = "ruff", marker = "extra == 'dev'", specifier = "==0.17.0" }, - { name = "socketdev", specifier = "==3.6.0" }, + { name = "socketdev", specifier = "==3.6.1" }, { name = "twine", marker = "extra == 'dev'", specifier = "==7.0.0" }, { name = "uv", marker = "extra == 'dev'", specifier = "==0.13.4" }, ]