Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 78 additions & 2 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
echo "python_deps_changed=$(has_file '^(pyproject\.toml|uv\.lock)$')"
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 "dockerfile_changed=$(has_file '^(Dockerfile|scripts/docker-entrypoint\.sh)$')"
echo "workflow_or_action_changed=$(has_file '^\.github/workflows/|^\.github/actions/|^\.github/dependabot\.yml$')"
} >> "$GITHUB_OUTPUT"

Expand Down Expand Up @@ -562,7 +562,18 @@ jobs:
dockerfile-smoke:
needs: inspect
if: needs.inspect.outputs.dockerfile_changed == 'true'
runs-on: ubuntu-latest
# Build every architecture the release publishes, each on a native runner,
# so an arm64-only break shows up here rather than in release.yml.
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
name: dockerfile-smoke (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -576,6 +587,71 @@ jobs:
--build-arg USE_LOCAL_INSTALL=true
-t socket-python-cli:dependabot-smoke .

- name: Check the tools in the image
run: |
docker run --rm --entrypoint sh socket-python-cli:dependabot-smoke -c '
set -eu
echo "node $(node -v), npm $(npm -v), yarn $(yarn -v)"
socket --version
socketcli --version
'

# 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".
- name: Check TLS clients reject the compromised eMudhra intermediates
run: |
docker run --rm --entrypoint sh socket-python-cli:dependabot-smoke -c '
set -u
status=0
for host in dv-g3b-emudhra.we-have-a-problem.com dv-g3b-aaa-emudhra.we-have-a-problem.com; do
code=0
curl -sS -m 30 -o /dev/null "https://$host/" 2>/dev/null || code=$?
if [ "$code" = 60 ]; then
echo "curl rejects $host"
else
echo "::error::curl on $host exited $code, expected 60 (certificate verification failed)"
status=1
fi
result=$(node -e "
const req = require(\"https\").get(process.argv[1], { timeout: 30000 }, (res) => {
console.log(\"ACCEPTED\"); process.exit(0);
});
req.on(\"timeout\", () => { console.log(\"TIMEOUT\"); process.exit(0); });
req.on(\"error\", (e) => { console.log(e.code || e.message); process.exit(0); });
" "https://$host/")
case "$result" in
CERT_REVOKED|UNABLE_TO_GET_ISSUER_CERT_LOCALLY|UNABLE_TO_VERIFY_LEAF_SIGNATURE) echo "node rejects $host ($result)" ;;
*) echo "::error::node on $host: $result"; status=1 ;;
esac
result=$(python -c "
import sys, requests
try:
requests.get(sys.argv[1], timeout=30)
print(\"ACCEPTED\")
except requests.exceptions.SSLError:
print(\"SSLError\")
except Exception as e:
print(type(e).__name__)
" "https://$host/")
if [ "$result" = SSLError ]; then
echo "python rejects $host"
else
echo "::error::python requests on $host: $result"
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
echo "curl connects to $url"
else
echo "::error::curl cannot connect to $url"
status=1
fi
done
exit $status
'

workflow-notice:
needs: inspect
if: needs.inspect.outputs.workflow_or_action_changed == 'true'
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## 2.11.2

### Fixed: stop trusting the compromised eMudhra intermediate CAs

- The CLI and every tool in the Docker image now reject certificates issued
from the compromised eMudhra intermediate CAs.
- Bumped `certifi` to `2026.10.19` and the image's bundled `uv` to `0.13.4`.
- The image now takes Node.js from the official `node:24-alpine` image (`24.22.1`)
instead of Alpine's `nodejs` package, which hasn't shipped Node's fix.

### Changed: bump pinned Coana CLI to 15.12.11

- Bumped the pinned reachability engine (the Coana CLI) from `15.12.10` to
Expand Down
31 changes: 27 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Node.js from the official Alpine image instead of Alpine's nodejs package, which
# lags Node's security releases. Match the major version to what reachability needs
# and the Alpine release to the python:3-alpine base below.
FROM node:24-alpine3.24 AS node

FROM python:3-alpine
LABEL org.opencontainers.image.authors="socket.dev"

Expand All @@ -14,9 +19,27 @@ ARG PIP_EXTRA_INDEX_URL=https://pypi.org/simple
ARG USE_LOCAL_INSTALL=false

# Install base packages first
# ca-certificates must be 20261020-r0 or newer, which drops the roots the
# compromised eMudhra intermediates chain to. The base image ships an older
# one, and `apk add` does not upgrade installed packages, so upgrade it here.
RUN apk update && apk add --no-cache \
git nodejs npm yarn curl wget \
ruby ruby-dev build-base strace
git curl wget \
ruby ruby-dev build-base strace && \
apk upgrade --no-cache ca-certificates && \
[ "$(apk version -t "$(apk list -I ca-certificates | awk '{print $1}' | sed 's/^ca-certificates-//')" 20261020-r0)" != "<" ]

# 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
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=node /opt /opt
# Node rejects the compromised eMudhra intermediates from 24.22.1.
RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \
ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx && \
ln -s ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack && \
ln -s /opt/yarn-v*/bin/yarn /usr/local/bin/yarn && \
ln -s /opt/yarn-v*/bin/yarnpkg /usr/local/bin/yarnpkg && \
node -e 'const [a,b,c]=process.versions.node.split(".").map(Number); process.exit(a>24||(a===24&&(b>22||(b===22&&c>=1)))?0:1)'

# Install Go with version control
RUN if [ "$GO_VERSION" = "system" ]; then \
Expand Down Expand Up @@ -85,8 +108,8 @@ RUN npm install socket -g && \
ENV PATH="/usr/local/go/bin:/usr/lib/go/bin:/root/.cargo/bin:${PATH}"
ENV GOPATH="/go"

# Install uv
COPY --from=ghcr.io/astral-sh/uv:0.10.4 /uv /usr/local/bin/uv
# Install uv (0.13.4+ ships the patched webpki-roots)
COPY --from=ghcr.io/astral-sh/uv:0.13.4 /uv /usr/local/bin/uv

# Install pyenv
# pyenv lets us build/install arbitrary Python versions on demand. We install
Expand Down
Loading