Skip to content

Commit ead6312

Browse files
build: harden docker image for scanner findings (#881)
## Summary - pin the Ruby and Node base images by digest - shrink the runtime image by copying only app runtime files and removing curl-based health checks - prune Bundler cache and git metadata, remove unnecessary runtime XML packages, and upgrade runtime zlib ## Verification - docker compose -f .devcontainer/docker-compose.yml exec -T app make ready - docker build -t html2rss/web-security-smoke -f Dockerfile . - docker run --rm html2rss/web-security-smoke ruby -e 'require "nokogiri"; puts Nokogiri::VERSION_INFO.to_h' ## Notes - Nokogiri still loads correctly after removing Alpine runtime libxml2/libxslt packages. - curl is no longer shipped in the final runtime image. - An unrelated local change in AGENTS.md was left unstaged and is not part of this PR. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent ec6673b commit ead6312

1 file changed

Lines changed: 23 additions & 10 deletions

File tree

Dockerfile

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
ARG RUBY_BASE_IMAGE=ruby:4.0.1-alpine3.23
1+
ARG RUBY_BASE_IMAGE=ruby:4.0.1-alpine3.23@sha256:7d1c4a23da9b3539fdeb5f970950a8fe044a707219e546f12152b84bbd5755d1
2+
ARG NODE_BASE_IMAGE=node:22-alpine@sha256:8094c002d08262dba12645a3b4a15cd6cd627d30bc782f53229a2ec13ee22a00
23

34
# Stage 1: Frontend Build
4-
FROM node:22-alpine AS frontend-builder
5+
FROM ${NODE_BASE_IMAGE} AS frontend-builder
56

67
WORKDIR /app/frontend
78
COPY frontend/package*.json ./
@@ -31,7 +32,11 @@ RUN apk add --no-cache \
3132
&& gem install bundler:$(tail -1 Gemfile.lock | tr -d ' ') \
3233
&& bundle config set --local without 'development test' \
3334
&& bundle install --retry=5 --jobs=$(nproc) \
34-
&& bundle binstubs bundler html2rss
35+
&& bundle binstubs bundler html2rss \
36+
&& bundle clean --force \
37+
&& rm -rf /usr/local/bundle/cache \
38+
/usr/local/bundle/bundler/gems/*/.git \
39+
/usr/local/bundle/cache/bundler/git
3540

3641
# Stage 3: Runtime
3742
FROM ${RUBY_BASE_IMAGE}
@@ -47,20 +52,25 @@ ENV PORT=4000 \
4752
EXPOSE $PORT
4853

4954
HEALTHCHECK --interval=30m --timeout=60s --start-period=5s \
50-
CMD TOKEN="${HEALTH_CHECK_TOKEN:-CHANGE_ME_HEALTH_CHECK_TOKEN}" && \
51-
curl -f -H "Authorization: Bearer ${TOKEN}" http://localhost:${PORT}/api/v1/health || exit 1
55+
CMD ruby -ruri -rnet/http -e ' \
56+
port = ENV.fetch("PORT", "4000") \
57+
token = ENV["HEALTH_CHECK_TOKEN"] \
58+
token = "CHANGE_ME_HEALTH_CHECK_TOKEN" if token.nil? || token.empty? \
59+
uri = URI("http://localhost:#{port}/api/v1/health") \
60+
request = Net::HTTP::Get.new(uri) \
61+
request["Authorization"] = "Bearer #{token}" \
62+
response = Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(request) } \
63+
exit(response.is_a?(Net::HTTPSuccess) ? 0 : 1) \
64+
'
5265

5366
ARG USER=html2rss
5467
ARG UID=991
5568
ARG GID=991
5669

5770
RUN apk add --no-cache \
5871
'ca-certificates>=2024' \
59-
'curl>=8' \
60-
'gcompat>=0' \
6172
'tzdata>=2024' \
62-
'libxml2>=2' \
63-
'libxslt>=1' \
73+
'zlib>=1.3.2-r0' \
6474
&& addgroup --gid "$GID" "$USER" \
6575
&& adduser \
6676
--disabled-password \
@@ -79,7 +89,10 @@ WORKDIR /app
7989
USER html2rss
8090

8191
COPY --from=builder /usr/local/bundle /usr/local/bundle
82-
COPY --chown=$USER:$USER . /app
92+
COPY --chown=$USER:$USER Gemfile Gemfile.lock app.rb config.ru ./
93+
COPY --chown=$USER:$USER app ./app
94+
COPY --chown=$USER:$USER config ./config
95+
COPY --chown=$USER:$USER public ./public
8396
COPY --from=frontend-builder --chown=$USER:$USER /app/public/frontend ./public/frontend
8497

8598
CMD ["bundle", "exec", "puma", "-C", "./config/puma.rb"]

0 commit comments

Comments
 (0)