From 02ea05c25dfffd9f7d52b7b053fae045a472ac3b Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:47:55 +0100 Subject: [PATCH 1/5] chore(docker): bookworm-slim base + apt upgrade on build --- docker/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index c075d3f800..69311cc916 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG NODE_IMAGE=node:20.20.2-bullseye-slim@sha256:65ef49f7d24aefd012a7fc6f9a2b734bcc19e424976a81f60c86b47266ef5b28 +ARG NODE_IMAGE=node:20.20.2-bookworm-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0 FROM golang:1.23-alpine AS goose_builder RUN go install github.com/pressly/goose/v3/cmd/goose@v3.26.0 @@ -13,7 +13,7 @@ RUN find . -name "node_modules" -type d -prune -exec rm -rf '{}' + # Base strategy to have layer caching FROM ${NODE_IMAGE} AS base -RUN apt-get update && apt-get install -y openssl dumb-init +RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends openssl dumb-init && rm -rf /var/lib/apt/lists/* WORKDIR /triggerdotdev COPY --chown=node:node .gitignore .gitignore COPY --from=pruner --chown=node:node /triggerdotdev/out/json/ . @@ -43,7 +43,7 @@ RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install ## Builder (builds the webapp) FROM base AS builder # This is needed for the sentry-cli binary while building the webapp -RUN apt-get update && apt-get install -y openssl dumb-init ca-certificates +RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends openssl dumb-init ca-certificates && rm -rf /var/lib/apt/lists/* WORKDIR /triggerdotdev # Corepack is used to install pnpm with the exact version from packageManager RUN corepack enable && corepack prepare pnpm@10.33.2 --activate @@ -75,7 +75,7 @@ RUN --mount=type=secret,id=sentry_auth_token \ # Runner FROM ${NODE_IMAGE} AS runner -RUN apt-get update && apt-get install -y openssl netcat-openbsd ca-certificates +RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends openssl netcat-openbsd ca-certificates && rm -rf /var/lib/apt/lists/* WORKDIR /triggerdotdev ENV NODE_ENV=production From 170bafbb2c2c5e7d22e0561ea0254e50e736a786 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:49:00 +0100 Subject: [PATCH 2/5] chore(emails): move react-email CLI to devDependencies --- internal-packages/emails/package.json | 4 ++-- pnpm-lock.yaml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal-packages/emails/package.json b/internal-packages/emails/package.json index e4f8d20d98..a3091eb97d 100644 --- a/internal-packages/emails/package.json +++ b/internal-packages/emails/package.json @@ -16,7 +16,6 @@ "nodemailer": "^8.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-email": "^6.5.0", "resend": "^3.2.0", "tiny-invariant": "^1.2.0", "zod": "3.25.76" @@ -24,7 +23,8 @@ "devDependencies": { "@types/nodemailer": "^8.0.0", "@types/react": "18.2.69", - "@types/react-dom": "18.2.7" + "@types/react-dom": "18.2.7", + "react-email": "^6.5.0" }, "engines": { "node": ">=18.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 03e96dcac7..c2b2df6bd8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1170,9 +1170,6 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.3.1) - react-email: - specifier: ^6.5.0 - version: 6.5.0(bufferutil@4.0.9)(react-dom@18.2.0(react@18.3.1))(react@18.3.1) resend: specifier: ^3.2.0 version: 3.2.0 @@ -1192,6 +1189,9 @@ importers: '@types/react-dom': specifier: 18.2.7 version: 18.2.7 + react-email: + specifier: ^6.5.0 + version: 6.5.0(bufferutil@4.0.9)(react-dom@18.2.0(react@18.3.1))(react@18.3.1) internal-packages/llm-model-catalog: dependencies: From 5d8c2e2662f331e7c892fe84aca93b38d87ac624 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Thu, 4 Jun 2026 14:48:15 +0100 Subject: [PATCH 3/5] ci: scan published webapp image post-publish (OS, summary-only) --- .github/workflows/publish-webapp.yml | 7 +++ .github/workflows/publish.yml | 11 ++++ .github/workflows/trivy-image-webapp.yml | 65 ++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 .github/workflows/trivy-image-webapp.yml diff --git a/.github/workflows/publish-webapp.yml b/.github/workflows/publish-webapp.yml index 377c994f63..399ed321b9 100644 --- a/.github/workflows/publish-webapp.yml +++ b/.github/workflows/publish-webapp.yml @@ -14,6 +14,13 @@ on: type: string required: false default: "" + outputs: + version: + description: The published image tag + value: ${{ jobs.publish.outputs.version }} + short_sha: + description: Short commit SHA of the published build + value: ${{ jobs.publish.outputs.short_sha }} secrets: SENTRY_AUTH_TOKEN: required: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a238395c8c..64b0f11ea0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -96,3 +96,14 @@ jobs: uses: ./.github/workflows/publish-worker-v4.yml with: image_tag: ${{ inputs.image_tag }} + + # OS-level CVE scan of the image just published above. Report-only (writes to + # the run summary); runs alongside the worker publishes and never blocks them. + scan-webapp: + needs: [publish-webapp] + permissions: + contents: read + packages: read # pull the just-published image from GHCR + uses: ./.github/workflows/trivy-image-webapp.yml + with: + image-ref: ghcr.io/triggerdotdev/trigger.dev:${{ needs.publish-webapp.outputs.version }} diff --git a/.github/workflows/trivy-image-webapp.yml b/.github/workflows/trivy-image-webapp.yml new file mode 100644 index 0000000000..37eb270fc4 --- /dev/null +++ b/.github/workflows/trivy-image-webapp.yml @@ -0,0 +1,65 @@ +name: Trivy Image Scan (webapp) + +# OS-level CVE scan of a published webapp image. Called by the publish pipeline +# (publish.yml) to scan each build right after it's pushed to GHCR — so every +# main build and every release is scanned, not rebuilt. Also runnable ad-hoc +# via workflow_dispatch against any image ref. +# +# Report-only: writes a table to the run summary. No SARIF upload, no gate. +# Library/dependency CVEs are covered by Dependabot, so this is restricted to +# OS packages (`vuln-type: os`) to avoid double-reporting. + +on: + workflow_call: + inputs: + image-ref: + description: "Full image ref to scan (e.g. ghcr.io/triggerdotdev/trigger.dev:main)" + type: string + required: true + workflow_dispatch: + inputs: + image-ref: + description: "Full image ref to scan" + type: string + required: false + default: "ghcr.io/triggerdotdev/trigger.dev:main" + +permissions: {} + +concurrency: + group: trivy-image-webapp-${{ inputs.image-ref }} + cancel-in-progress: true + +jobs: + scan: + name: Scan + runs-on: ubuntu-latest + permissions: + contents: read + packages: read # pull the image from GHCR + steps: + - name: Run Trivy image scan + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + scan-type: image + image-ref: ${{ inputs.image-ref }} + # vuln-type maps to --pkg-types: OS packages only (library deps are + # Dependabot's job). ignore-unfixed drops vulns with no patch yet. + vuln-type: os + ignore-unfixed: true + severity: HIGH,CRITICAL + format: table + output: trivy-image-webapp.txt + + - name: Job summary + if: always() + env: + IMAGE_REF: ${{ inputs.image-ref }} + run: | + { + echo "## Trivy Image Scan (webapp) — \`${IMAGE_REF}\`" + echo '```' + # GitHub step summary is capped at 1 MiB; truncate large reports. + head -c 900000 trivy-image-webapp.txt 2>/dev/null || echo "(no report produced)" + echo '```' + } >> "$GITHUB_STEP_SUMMARY" From 4b9b996895fee7fc3e471d3628b22a4ddeeee6dc Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Thu, 4 Jun 2026 14:59:38 +0100 Subject: [PATCH 4/5] chore(docker): bump goose v3.26.0->v3.27.1, builder go 1.23->1.26 --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 69311cc916..81fe4afe3c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ ARG NODE_IMAGE=node:20.20.2-bookworm-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0 -FROM golang:1.23-alpine AS goose_builder -RUN go install github.com/pressly/goose/v3/cmd/goose@v3.26.0 +FROM golang:1.26-alpine AS goose_builder +RUN go install github.com/pressly/goose/v3/cmd/goose@v3.27.1 FROM ${NODE_IMAGE} AS pruner From 93199f28d0c85998f3c013913a2b2e5766aa528a Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:35:07 +0100 Subject: [PATCH 5/5] ci: log in to GHCR before image scan for private-image support --- .github/workflows/trivy-image-webapp.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/trivy-image-webapp.yml b/.github/workflows/trivy-image-webapp.yml index 37eb270fc4..7dae65ef2b 100644 --- a/.github/workflows/trivy-image-webapp.yml +++ b/.github/workflows/trivy-image-webapp.yml @@ -38,6 +38,16 @@ jobs: contents: read packages: read # pull the image from GHCR steps: + # Authenticate to GHCR so the scan also works for private images + # (GITHUB_TOKEN isn't forwarded to Docker automatically). Harmless for + # public images. Pairs with the packages: read permission above. + - name: Log in to GitHub Container Registry + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Run Trivy image scan uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: