fix(code): rebase Dockerfile bait so Module 4 fix is a single FROM bump#51
Merged
sanchezpaco merged 1 commit intoMay 1, 2026
Merged
Conversation
Module 4 (Container Security Scanning) currently uses 'node:16.14.0-alpine'
as the planted base image. Both Trivy and Grype legitimately fire on it,
but the fix path is unstable: bumping to 'node:22-alpine' or 'node:25-alpine'
leaves a residual HIGH (picomatch 4.0.3) bundled inside npm 11.12.x's
transitive 'tinyglobby' dependency. The fix only fully clears once npm is
upgraded to >= 11.13.0. The dry-run report previously chased this through
multi-stage / distroless / Chainguard images, none of which is a good
recommendation for an attendee.
This change rebaselines the bait so the workshop fix stays as a single
'FROM' line edit:
bait: FROM node:20-alpine3.19 (alpine 3.19 — EOL, surfaces clean
HIGH/CRITICAL findings on both Trivy
and Grype)
fix : FROM node:25-alpine (alpine 3.23+, ships node 25 with the
necessary npm baseline)
The 'RUN npm install -g npm@11.13.0' line is permanent (the user does not
add or remove it) so the picomatch issue is silently handled in both
states. npm 11.x requires Node >= 20.17, so the bait must already be on a
Node 20 line — 'node:20-alpine3.19' fits exactly while preserving useful
findings (the EOL alpine version surfaces real OS CVEs that the bump
clears).
Empirically verified end-to-end on a test PR
(https://github.com/sanchezpaco/secure-pipeline-workshop/pull/11):
bait Trivy 4 HIGH (musl, node) Grype many HIGH + 1 CRITICAL
+ explicit 'alpine 3.19.4
EOL — vulnerability data may
be incomplete' notice
fix Trivy 0 Grype 0 (No vulnerabilities
found)
andoniaf
approved these changes
May 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In Module 4 (Container Security Scanning) the planted base image is
node:16.14.0-alpine. Trivy and Grype both fire on it correctly, but the fix path is unstable: any obvious bump leaves a residual HIGH that the attendee can't easily resolve.Specifically:
node:22-alpine/node:24-alpine/node:25-alpine(single bump)picomatch 4.0.3bundled inside the npm shipped with each of those images (transitive oftinyglobby)gcr.io/distroless/nodejs22-debian12libssl3cgr.dev/chainguard/nodeThe picomatch finding only fully clears once npm itself is updated to >= 11.13.0 (where
tinyglobbywas bumped). And npm 11.x requires Node >= 20.17.The dry-run report previously called this out as
"the README's TIP nailed the trajectory but stops one image short"— meaning the attendee can spend a long time chasing base images instead of practicing the actual lesson (run scanner → see finding → fix → move on).Fix
Rebaseline the Dockerfile so:
node:20-alpine3.19— Node 20.18.1 (compatible with npm 11.13.0) on alpine 3.19 (EOL since 2025-11). Both Trivy and Grype surface real findings (alpine OS package CVEs, musl, node binary CVEs). Grype additionally prints an explicit188 packages from EOL distro "alpine 3.19.4" — vulnerability data may be incomplete or outdatednotice, which is itself a teachable moment.RUN npm install -g npm@11.13.0 && npm cache clean --forceline is added. The attendee does not edit this line. Its job is to silently handle the npm/picomatch story so it does not become the lesson.The attendee's fix in Module 4 becomes a single
FROMline edit:After that, both scanners go to 0.
Verification
Empirical, end-to-end on a test PR using the same orchestrator workflow (with both Trivy and Grype configured to run in parallel against the same built image): https://github.com/sanchezpaco/secure-pipeline-workshop/pull/11
FROM node:20-alpine3.19musl,nodebinary CVEs)nodebinary,musl,libssl3,libcrypto3) plus explicit alpine-EOL warningFROM node:25-alpineCI runs:
(The fix run also requires Module 3's
process.env.*change tocode/src/simple-app.js, since Trivy's secret scanner would otherwise flag the planted AKIA. In the workshop's natural module ordering, Module 3 is fixed before Module 4, so this composes cleanly. No change to the Trivy snippet is required.)Why this matters didactically
The other module fixes in the workshop are one-line edits (pin an action in Module 1, change a value in Module 2, move secrets to env in Module 3, restrict a security group in Module 5). Module 4 was the exception: with the previous bait, the attendee either chased base images for three or four iterations or ended up using a vendor-specific rolling-release image. This change brings Module 4's fix path back in line with the rest.
Related
Independent from PRs #48, #49 (Module 1 fixes) and #50 (Module 2 fixes). Composes cleanly with all of them.