diff --git a/.github/workflows/multiOSReleases.yml b/.github/workflows/multiOSReleases.yml index 34f9e6f7f0..7c9d42b304 100644 --- a/.github/workflows/multiOSReleases.yml +++ b/.github/workflows/multiOSReleases.yml @@ -268,7 +268,7 @@ jobs: - name: Install frontend dependencies working-directory: ./frontend - run: npm install + run: npm ci # DigiCert KeyLocker Setup (Cloud HSM) - name: Setup DigiCert KeyLocker diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index b8a899c9b3..c3a440b128 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -174,7 +174,7 @@ jobs: - name: Install frontend dependencies working-directory: ./frontend - run: npm install + run: npm ci # DigiCert KeyLocker Setup (Cloud HSM) - name: Setup DigiCert KeyLocker diff --git a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertEmlToPDF.java b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertEmlToPDF.java index 6a88d8c4f4..489978af8b 100644 --- a/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertEmlToPDF.java +++ b/app/core/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertEmlToPDF.java @@ -10,6 +10,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.util.HtmlUtils; import io.github.pixee.security.Filenames; import io.swagger.v3.oas.annotations.Operation; @@ -152,20 +153,24 @@ public class ConvertEmlToPDF { } private static @NotNull String buildErrorMessage(Exception e, String originalFilename) { + String safeFilename = HtmlUtils.htmlEscape(originalFilename); + String exceptionMessage = e.getMessage(); + String safeExceptionMessage = + exceptionMessage == null ? "Unknown error" : HtmlUtils.htmlEscape(exceptionMessage); String errorMessage; - if (e.getMessage() != null && e.getMessage().contains("Invalid EML")) { + if (exceptionMessage != null && exceptionMessage.contains("Invalid EML")) { errorMessage = "Invalid EML file format. Please ensure you've uploaded a valid email" + " file (" - + originalFilename + + safeFilename + ")."; - } else if (e.getMessage() != null && e.getMessage().contains("WeasyPrint")) { + } else if (exceptionMessage != null && exceptionMessage.contains("WeasyPrint")) { errorMessage = "PDF generation failed for " - + originalFilename + + safeFilename + ". This may be due to complex email formatting."; } else { - errorMessage = "Conversion failed for " + originalFilename + ": " + e.getMessage(); + errorMessage = "Conversion failed for " + safeFilename + ": " + safeExceptionMessage; } return errorMessage; } diff --git a/docker/Dockerfile.unified b/docker/Dockerfile.unified index a07d294ee8..6d045b4f27 100644 --- a/docker/Dockerfile.unified +++ b/docker/Dockerfile.unified @@ -2,7 +2,7 @@ # Supports MODE parameter: BOTH (default), FRONTEND, BACKEND # Stage 1: Build Frontend -FROM node:20-alpine AS frontend-build +FROM node:20-alpine@sha256:658d0f63e501824d6c23e06d4bb95c71e7d704537c9d9272f488ac03a370d448 AS frontend-build WORKDIR /app @@ -15,7 +15,7 @@ COPY frontend . RUN DISABLE_ADDITIONAL_FEATURES=false VITE_API_BASE_URL=/ npm run build # Stage 2: Build Backend (server-only JAR - no UI) -FROM gradle:8.14-jdk21 AS backend-build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS backend-build COPY build.gradle . COPY settings.gradle . @@ -35,7 +35,7 @@ RUN DISABLE_ADDITIONAL_FEATURES=false \ ./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube # Stage 3: Final unified image -FROM alpine:3.22.1 +FROM alpine:3.22.1@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 ARG VERSION_TAG diff --git a/docker/Dockerfile.unified-lite b/docker/Dockerfile.unified-lite index 8219a2173e..264d03d676 100644 --- a/docker/Dockerfile.unified-lite +++ b/docker/Dockerfile.unified-lite @@ -2,7 +2,7 @@ # Supports MODE parameter: BOTH (default), FRONTEND, BACKEND # Stage 1: Build Frontend -FROM node:20-alpine AS frontend-build +FROM node:20-alpine@sha256:658d0f63e501824d6c23e06d4bb95c71e7d704537c9d9272f488ac03a370d448 AS frontend-build WORKDIR /app @@ -15,7 +15,7 @@ COPY frontend . RUN DISABLE_ADDITIONAL_FEATURES=true VITE_API_BASE_URL=/ npm run build # Stage 2: Build Backend -FROM gradle:8.14-jdk21 AS backend-build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS backend-build COPY build.gradle . COPY settings.gradle . @@ -34,7 +34,7 @@ RUN DISABLE_ADDITIONAL_FEATURES=true \ ./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube # Stage 3: Final unified ultra-lite image -FROM alpine:3.22.1 +FROM alpine:3.22.1@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 ARG VERSION_TAG diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 45809b0960..8ebccfaf7b 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -6,7 +6,7 @@ # ======================================== # STAGE 1: Build stage - Alpine with Gradle # ======================================== -FROM gradle:8.14-jdk21 AS build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS build COPY build.gradle . COPY settings.gradle . diff --git a/docker/backend/Dockerfile.fat b/docker/backend/Dockerfile.fat index 37a99add8e..8e31c27c0f 100644 --- a/docker/backend/Dockerfile.fat +++ b/docker/backend/Dockerfile.fat @@ -6,7 +6,7 @@ # ======================================== # STAGE 1: Build stage - Gradle # ======================================== -FROM gradle:8.14-jdk21 AS build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS build COPY build.gradle . COPY settings.gradle . diff --git a/docker/backend/Dockerfile.ultra-lite b/docker/backend/Dockerfile.ultra-lite index 813a01d73b..2bf85f7a8d 100644 --- a/docker/backend/Dockerfile.ultra-lite +++ b/docker/backend/Dockerfile.ultra-lite @@ -2,7 +2,7 @@ # ======================================== # STAGE 1: Build stage - Gradle # ======================================== -FROM gradle:8.14-jdk21 AS build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS build COPY build.gradle . COPY settings.gradle . diff --git a/docker/embedded/Dockerfile b/docker/embedded/Dockerfile index 515fdf1b91..dd2124b18f 100644 --- a/docker/embedded/Dockerfile +++ b/docker/embedded/Dockerfile @@ -2,7 +2,7 @@ # Single JAR contains both frontend and backend # Stage 1: Build application with embedded frontend -FROM gradle:8.14-jdk21 AS build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS build # Install Node.js and npm for frontend build RUN apt-get update && apt-get install -y \ diff --git a/docker/embedded/Dockerfile.fat b/docker/embedded/Dockerfile.fat index 721ac21a0f..00f0e259de 100644 --- a/docker/embedded/Dockerfile.fat +++ b/docker/embedded/Dockerfile.fat @@ -2,7 +2,7 @@ # Single JAR contains both frontend and backend with extra fonts for air-gapped environments # Stage 1: Build application with embedded frontend -FROM gradle:8.14-jdk21 AS build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS build # Install Node.js and npm for frontend build RUN apt-get update && apt-get install -y \ diff --git a/docker/embedded/Dockerfile.ultra-lite b/docker/embedded/Dockerfile.ultra-lite index 317265d57b..8556d4585e 100644 --- a/docker/embedded/Dockerfile.ultra-lite +++ b/docker/embedded/Dockerfile.ultra-lite @@ -2,7 +2,7 @@ # Single JAR contains both frontend and backend with minimal dependencies # Stage 1: Build application with embedded frontend -FROM gradle:8.14-jdk21 AS build +FROM gradle:8.14-jdk21@sha256:051d9a116793bdc5175a3f97a545718b750489eee85a7da20913c8a53f722a72 AS build # Install Node.js and npm for frontend build RUN apt-get update && apt-get install -y \ @@ -36,7 +36,7 @@ RUN DISABLE_ADDITIONAL_FEATURES=true \ ./gradlew clean build -PbuildWithFrontend=true -x spotlessApply -x spotlessCheck -x test -x sonarqube # Stage 2: Runtime image -FROM alpine:3.22.1 +FROM alpine:3.22.1@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 ARG VERSION_TAG diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index c7d6189e52..a53dec5db4 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -1,5 +1,5 @@ # Frontend Dockerfile - React/Vite application -FROM node:20-alpine AS build +FROM node:20-alpine@sha256:658d0f63e501824d6c23e06d4bb95c71e7d704537c9d9272f488ac03a370d448 AS build WORKDIR /app @@ -16,7 +16,7 @@ COPY frontend . RUN npm run build # Production stage -FROM nginx:alpine +FROM nginx:alpine@sha256:8491795299c8e739b7fcc6285d531d9812ce2666e07bd3dd8db00020ad132295 # Copy built files from build stage COPY --from=build /app/dist /usr/share/nginx/html