Skip to content

Commit e9d462c

Browse files
committed
security: distroless image, dependency overrides, fix CodeQL prototype pollution
- Dockerfile: switch runtime to gcr.io/distroless/java21-debian12:nonroot, build shadowJar; removes wget/tar/libssh/pam/gnupg/curl from image (Trivy CVEs) - backend: force commons-lang3 (CVE-2025-48924) and Rhino 1.7.14.1 (CVE-2025-66453) - design-system/build-tokens.js: use Object.create(null) and hasOwnProperty to satisfy CodeQL prototype-polluting function finding Made-with: Cursor
1 parent 32d8978 commit e9d462c

3 files changed

Lines changed: 19 additions & 25 deletions

File tree

Dockerfile

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,33 +38,22 @@ ENV GRADLE_OPTS="-Xmx2048m -XX:MaxMetaspaceSize=512m"
3838
WORKDIR /app
3939
RUN chmod +x ./gradlew
4040

41-
RUN ./gradlew :backend:installDist --no-daemon --stacktrace
41+
RUN ./gradlew :backend:installDist :backend:shadowJar --no-daemon --stacktrace
4242

4343
RUN ./gradlew :frontend:jsBrowserDevelopmentWebpack --no-daemon
4444

4545
######################################
46-
# Runtime stage (Jammy: avoids Alpine libpng/gnutls CVEs in Trivy scan)
46+
# Runtime stage (distroless: no shell, wget, tar, libssh, pam, gnupg — reduces Trivy CVEs)
4747
######################################
48-
FROM eclipse-temurin:21-jre-jammy
48+
FROM gcr.io/distroless/java21-debian12:nonroot
4949

5050
WORKDIR /app
5151

52-
# Runtime deps: tzdata, wget for HEALTHCHECK; create non-root user
53-
RUN apt-get update && apt-get install -y --no-install-recommends tzdata wget && \
54-
groupadd -r appgroup && useradd -r -g appgroup appuser && \
55-
rm -rf /var/lib/apt/lists/*
56-
57-
# Copy built application from build stage
58-
COPY --from=build /app/backend/build/install/backend ./backend
52+
# Single fat JAR + static assets (no OS packages → no wget/tar/libssh/curl/gnupg CVEs)
53+
COPY --from=build /app/backend/build/libs/backend-*.jar ./backend.jar
5954
COPY --from=build /app/frontend/build/kotlin-webpack/js/developmentExecutable ./static
6055

61-
# Set ownership
62-
RUN chown -R appuser:appgroup /app
63-
64-
# Switch to non-root user
65-
USER appuser
66-
67-
# Set default environment variables
56+
# Default environment variables
6857
ENV HOST=0.0.0.0
6958
ENV PORT=18000
7059
ENV FLAGENT_DB_DBDRIVER=sqlite3
@@ -76,11 +65,8 @@ ENV FLAGENT_ADMIN_EMAIL=admin@local
7665
ENV FLAGENT_ADMIN_PASSWORD=admin
7766
ENV FLAGENT_JWT_AUTH_SECRET=dev-secret-min-32-chars-required-for-jwt
7867

79-
# Expose port
68+
# Expose port (health: use HTTP GET from orchestrator, e.g. k8s readinessProbe to /api/v1/health)
8069
EXPOSE 18000
8170

82-
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
83-
CMD wget --no-verbose --tries=1 --spider http://localhost:18000/api/v1/health || exit 1
84-
85-
# Run the application
86-
CMD ["./backend/bin/backend"]
71+
# Distroless entrypoint is "java -jar"; CMD is the JAR path
72+
CMD ["/app/backend.jar"]

backend/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ plugins {
1010
jacoco
1111
}
1212

13+
// Force safe versions for transitive deps (CVEs)
14+
configurations.all {
15+
resolutionStrategy {
16+
force("org.apache.commons:commons-lang3:${libs.versions.commons.lang3.get()}") // CVE-2025-48924
17+
force("org.mozilla:rhino:1.7.14.1") // CVE-2025-66453 (toFixed DoS); from swagger-parser -> json-schema-core
18+
}
19+
}
20+
1321
dependencyCheck {
1422
failBuildOnCVSS = 11f // Don't fail on CVSS, only report
1523
failOnError = false // Don't fail when NVD is unreachable (403, 429, etc.)

design-system/build-tokens.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ function setNested(obj, path, value) {
108108
for (let i = 0; i < parts.length - 1; i++) {
109109
const p = parts[i];
110110
if (PROTOTYPE_POLLUTION_KEYS.has(p)) return;
111-
if (!(p in cur)) cur[p] = {};
111+
if (!Object.prototype.hasOwnProperty.call(cur, p)) cur[p] = Object.create(null);
112112
cur = cur[p];
113113
}
114114
const last = parts[parts.length - 1];
@@ -117,7 +117,7 @@ function setNested(obj, path, value) {
117117
}
118118

119119
function emitTypeScript(tokens) {
120-
const flat = {};
120+
const flat = Object.create(null);
121121
for (const [name, value] of walk(tokens)) {
122122
setNested(flat, name, value);
123123
}

0 commit comments

Comments
 (0)