diff --git a/.github/workflows/multiOSReleases.yml b/.github/workflows/multiOSReleases.yml index 34f9e6f7f..7c9d42b30 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 b8a899c9b..c3a440b12 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 6a88d8c4f..489978af8 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 a07d294ee..6d045b4f2 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 8219a2173..264d03d67 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 45809b096..8ebccfaf7 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 37a99add8..8e31c27c0 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 813a01d73..2bf85f7a8 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 515fdf1b9..dd2124b18 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 721ac21a0..00f0e259d 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 317265d57..8556d4585 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 c7d6189e5..a53dec5db 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