Skip to content
Draft
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
81 changes: 57 additions & 24 deletions .github/workflows/core-tool-watch.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
name: core-tool-watch

# Supply-chain / malware watch for the four core OSS tools that Socket Basics
# orchestrates. Three of them (OpenGrep, TruffleHog, Trivy) ship as
# binaries / container images / GitHub releases that Dependabot cannot cleanly
# track; the fourth (Socket's own SCA SDK) is a PyPI package. This workflow
# Supply-chain / malware watch for the core tools that Socket Basics
# orchestrates, including Socket's SDK and both of its CLI implementations.
# Several ship as binaries / container images / GitHub releases that
# Dependabot cannot cleanly track. This workflow
# closes that gap by running scripts/check_core_tools.py, which discovers the
# latest upstream version of each tool and scores the relevant package
# coordinates through the Socket API (dogfooding the socketdev SDK that Socket
# Basics already depends on).
#
# Two triggers, two intents:
# - schedule / workflow_dispatch → mode=watch: discover latest versions,
# analyze BOTH pinned and latest, report drift, upsert a tracking issue.
# - pull_request / push touching the pins → mode=build: analyze the versions
# this change would bake into the image. Fails on a malware/critical alert.
# - schedule / workflow_dispatch / main push → mode=watch: discover latest
# versions, analyze BOTH pinned and latest, and reconcile the tracking issue.
# Running watch mode after a merge prevents the issue from retaining the
# pre-merge pins until the next weekly schedule.
# - pull_request touching the pins → mode=build: analyze the versions this
# change would bake into the image. Fails on a malware/critical alert.
#
# Socket scoring needs SOCKET_SFW_API_TOKEN, scoped to the `socket-firewall`
# environment (which must carry NO approval rule -- see dependency-review.yml).
Expand Down Expand Up @@ -41,6 +43,7 @@ on:
pull_request:
paths:
- "Dockerfile"
- "Dockerfile.heavy"
- "app_tests/Dockerfile"
- "pyproject.toml"
- "uv.lock"
Expand All @@ -50,6 +53,7 @@ on:
branches: [main]
paths:
- "Dockerfile"
- "Dockerfile.heavy"
- "app_tests/Dockerfile"
- "pyproject.toml"
- "uv.lock"
Expand Down Expand Up @@ -78,7 +82,8 @@ jobs:
environment: socket-firewall
permissions:
contents: read
issues: write # upsert the drift tracking issue on scheduled runs
issues: write # reconcile the drift tracking issue on default-branch runs
packages: read # inspect tags on the repo-authorized private GHCR Trivy package
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -117,12 +122,12 @@ jobs:
env:
EVENT: ${{ github.event_name }}
run: |
# Scheduled/manual runs watch for upstream drift; PR/push runs guard
# the versions a build would actually pull in.
if [ "$EVENT" = "schedule" ] || [ "$EVENT" = "workflow_dispatch" ]; then
echo "mode=watch" >> "$GITHUB_OUTPUT"
else
# PRs guard proposed pins without mutating issues. Every default-branch
# event reconciles upstream state and the canonical tracking issue.
if [ "$EVENT" = "pull_request" ]; then
echo "mode=build" >> "$GITHUB_OUTPUT"
else
echo "mode=watch" >> "$GITHUB_OUTPUT"
fi

- name: Run core-tool supply-chain analysis
Expand Down Expand Up @@ -159,28 +164,56 @@ jobs:
if-no-files-found: warn
retention-days: 30

- name: Open/update drift tracking issue
if: ${{ always() && steps.mode.outputs.mode == 'watch' && steps.scan.outputs.drift == 'true' }}
- name: Reconcile drift tracking issue
if: ${{ always() && steps.mode.outputs.mode == 'watch' }}
env:
GH_TOKEN: ${{ github.token }}
DRIFT: ${{ steps.scan.outputs.drift }}
DISCOVERY_COMPLETE: ${{ steps.scan.outputs.discovery_complete }}
run: |
if [ ! -s core-tools-report.md ] \
|| { [ "$DRIFT" != "true" ] && [ "$DRIFT" != "false" ]; } \
|| { [ "$DISCOVERY_COMPLETE" != "true" ] && [ "$DISCOVERY_COMPLETE" != "false" ]; }; then
echo "::warning::Skipping issue reconciliation because the scan did not produce a complete report."
exit 0
fi

gh label create core-tool-drift \
--color FBCA04 \
--description "A core OSS tool has a newer upstream release" 2>/dev/null || true

title="Core tool version drift detected"
# `// empty` so an absent issue yields "" (not the literal "null",
# which is non-empty in bash and would send us to `gh issue edit null`).
existing="$(gh issue list --label core-tool-drift --state open \
--json number --jq '.[0].number // empty' 2>/dev/null || true)"
run_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
# Reuse the newest labeled issue even after it has been closed. This
# keeps one canonical history instead of creating a duplicate every
# time drift disappears and later returns.
existing="$(gh issue list --label core-tool-drift --state all --limit 100 \
--json number --jq 'sort_by(.number) | last | .number // empty' 2>/dev/null || true)"

if [ -n "$existing" ]; then
if [ "$DRIFT" = "true" ] && [ -n "$existing" ]; then
state="$(gh issue view "$existing" --json state --jq '.state')"
gh issue edit "$existing" --body-file core-tools-report.md
gh issue comment "$existing" \
--body "Drift re-detected by [run #${GITHUB_RUN_ID}](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}); body updated."
else
if [ "$state" = "CLOSED" ]; then
gh issue reopen "$existing" \
--comment "Drift re-detected by [run #${GITHUB_RUN_ID}](${run_url}); body updated."
else
gh issue comment "$existing" \
--body "Drift re-detected by [run #${GITHUB_RUN_ID}](${run_url}); body updated."
fi
elif [ "$DRIFT" = "true" ]; then
gh issue create \
--title "$title" \
--label core-tool-drift \
--body-file core-tools-report.md
elif [ "$DISCOVERY_COMPLETE" = "true" ] && [ -n "$existing" ]; then
state="$(gh issue view "$existing" --json state --jq '.state')"
gh issue edit "$existing" --body-file core-tools-report.md
if [ "$state" = "OPEN" ]; then
gh issue close "$existing" \
--comment "No core tool version drift remains as of [run #${GITHUB_RUN_ID}](${run_url}); body updated with the reconciled pins."
fi
elif [ -n "$existing" ]; then
gh issue edit "$existing" --body-file core-tools-report.md
gh issue comment "$existing" \
--body "[Run #${GITHUB_RUN_ID}](${run_url}) refreshed the report, but latest-version discovery was incomplete; issue state was left unchanged."
fi
29 changes: 4 additions & 25 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ name: publish-docker
# resolve-version
# → build-test-push (matrix: image variant + native arch, pushes by digest)
# → merge-manifests (assembles per-image per-arch digests into manifest lists)
# → create-release (tag pushes only)
#
# GitHub releases are intentionally human-authored after the images publish.
# Maintainers may use GitHub's generated release notes as a starting point, but
# this workflow does not create or edit the release itself.
#
# Tag convention:
# v2.0.0 — immutable exact release (floating major tags intentionally not published)
Expand Down Expand Up @@ -307,27 +310,3 @@ jobs:
fi
done
done

# ── Job 4: Create GitHub release ───────────────────────────────────────────
# Runs once after the manifest is published (not for workflow_dispatch
# re-publishes — those don't create new releases).
# Generates categorised release notes from merged PR labels (.github/release.yml).
# CHANGELOG updates are intentionally human-authored in the release PR so this
# workflow never needs to push commits to the protected default branch.
create-release:
needs: [resolve-version, merge-manifests]
if: github.ref_type == 'tag'
permissions:
contents: write # create GitHub release
runs-on: ubuntu-latest
steps:
- name: 📝 Create GitHub release with auto-generated notes
env:
GH_TOKEN: ${{ github.token }}
REF_NAME: ${{ github.ref_name }}
run: |
gh release create "$REF_NAME" \
--title "$REF_NAME" \
--generate-notes \
--verify-tag \
|| echo "Release already exists (re-run scenario) — skipping creation"
8 changes: 6 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ARG UV_VERSION=0.12.1
#
# NOT Dependabot-trackable (no official Docker image with a stable binary path):
ARG OPENGREP_VERSION=v1.26.0
ARG SOCKET_NPM_CLI_VERSION=1.1.154
#
# NOT Dependabot-trackable — Socket-built Trivy, rebuilt from unmodified upstream
# source and published by Socket's own release pipeline. Pinned by digest; both
Expand Down Expand Up @@ -61,8 +62,9 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
curl git wget ca-certificates
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs
ARG SOCKET_NPM_CLI_VERSION
RUN --mount=type=cache,target=/root/.npm \
npm install -g socket
npm install -g "socket@${SOCKET_NPM_CLI_VERSION}"

# Python project files
COPY socket_basics /socket-basics/socket_basics
Expand All @@ -81,14 +83,16 @@ ARG BUILD_DATE=unknown
ARG TRIVY_VERSION
ARG TRUFFLEHOG_VERSION
ARG OPENGREP_VERSION
ARG SOCKET_NPM_CLI_VERSION
LABEL org.opencontainers.image.title="Socket Basics" \
org.opencontainers.image.source="https://github.com/SocketDev/socket-basics" \
org.opencontainers.image.version="${SOCKET_BASICS_VERSION}" \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.revision="${VCS_REF}" \
com.socket.trivy-version="${TRIVY_VERSION}" \
com.socket.trufflehog-version="${TRUFFLEHOG_VERSION}" \
com.socket.opengrep-version="${OPENGREP_VERSION}"
com.socket.opengrep-version="${OPENGREP_VERSION}" \
com.socket.npm-cli-version="${SOCKET_NPM_CLI_VERSION}"

ENV PATH="/socket-basics/.venv/bin:/root/.opengrep/cli/latest:/usr/local/bin:$PATH"

Expand Down
15 changes: 10 additions & 5 deletions Dockerfile.heavy
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ ARG PYTHON_VERSION=3.12
ARG TRUFFLEHOG_VERSION=3.96.0
ARG UV_VERSION=0.12.1
ARG OPENGREP_VERSION=v1.26.0
ARG SOCKET_CLI_VERSION=2.6.3
ARG SOCKET_NPM_CLI_VERSION=1.1.154
ARG SOCKET_PYTHON_CLI_VERSION=2.6.3
# Socket-built Trivy, pinned by digest — see the note in ./Dockerfile.
ARG TRIVY_IMAGE=ghcr.io/socketdev/trivy:0.73.0@sha256:e3d9d5f10250cb73b0ea9446ae1191c0f2da2f5e6173eac08a840b1812f02e0b

Expand Down Expand Up @@ -35,18 +36,19 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
curl git wget ca-certificates
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs
ARG SOCKET_NPM_CLI_VERSION
RUN --mount=type=cache,target=/root/.npm \
npm install -g socket
npm install -g "socket@${SOCKET_NPM_CLI_VERSION}"

COPY socket_basics /socket-basics/socket_basics
COPY pyproject.toml README.md LICENSE uv.lock /socket-basics/

ENV UV_LINK_MODE=copy
ARG SOCKET_CLI_VERSION
ARG SOCKET_PYTHON_CLI_VERSION
RUN --mount=type=cache,target=/root/.cache/uv \
pip install -e . \
&& uv sync --frozen --no-dev \
&& pip install --no-cache-dir "socketsecurity==${SOCKET_CLI_VERSION}"
&& pip install --no-cache-dir "socketsecurity==${SOCKET_PYTHON_CLI_VERSION}"

COPY scripts/docker-heavy-entrypoint.sh /usr/local/bin/docker-heavy-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-heavy-entrypoint.sh
Expand All @@ -56,12 +58,15 @@ ARG VCS_REF=unknown
ARG BUILD_DATE=unknown
ARG TRUFFLEHOG_VERSION
ARG OPENGREP_VERSION
ARG SOCKET_NPM_CLI_VERSION
ARG SOCKET_PYTHON_CLI_VERSION
LABEL org.opencontainers.image.title="Socket Basics Heavy" \
org.opencontainers.image.source="https://github.com/SocketDev/socket-basics" \
org.opencontainers.image.version="${SOCKET_BASICS_VERSION}" \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.revision="${VCS_REF}" \
com.socket.cli-version="${SOCKET_CLI_VERSION}" \
com.socket.python-cli-version="${SOCKET_PYTHON_CLI_VERSION}" \
com.socket.npm-cli-version="${SOCKET_NPM_CLI_VERSION}" \
com.socket.trufflehog-version="${TRUFFLEHOG_VERSION}" \
com.socket.opengrep-version="${OPENGREP_VERSION}"

Expand Down
10 changes: 8 additions & 2 deletions app_tests/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ ARG UV_VERSION=0.12.1
# NOT Dependabot-trackable (no official Docker image with a stable binary path):
ARG GOSEC_VERSION=v2.28.0
ARG OPENGREP_VERSION=v1.26.0
ARG SOCKET_NPM_CLI_VERSION=1.1.154
ARG SOCKET_PYTHON_CLI_VERSION=2.6.3
#
# NOT Dependabot-trackable — Socket-built Trivy, pinned by digest; updated by
# Socket's trivy-dist release process. See the note in the root ./Dockerfile.
Expand Down Expand Up @@ -83,18 +85,22 @@ RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx

# System deps + ESLint + Socket CLI (npm now available from node stage above)
ARG SOCKET_NPM_CLI_VERSION
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
curl git wget ca-certificates libatomic1
RUN --mount=type=cache,target=/root/.npm \
npm install -g eslint eslint-plugin-security \
@typescript-eslint/parser @typescript-eslint/eslint-plugin socket
@typescript-eslint/parser @typescript-eslint/eslint-plugin \
"socket@${SOCKET_NPM_CLI_VERSION}"

# Bandit + socketsecurity via uv
ENV UV_LINK_MODE=copy
ARG SOCKET_PYTHON_CLI_VERSION
RUN --mount=type=cache,target=/root/.cache/uv \
uv tool install bandit && uv tool install socketsecurity
uv tool install bandit \
&& uv tool install "socketsecurity==${SOCKET_PYTHON_CLI_VERSION}"
ENV PATH="/root/.local/bin:$PATH"

# NOTE: the legacy socket-security-tools runner (src/, entrypoint.sh) predates
Expand Down
Loading