Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/

**/.DS_Store
**/.classpath
**/.dockerignore
**/.env
**/.factorypath
**/.git
**/.gitignore
**/.idea
**/.project
**/.sts4-cache
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.next
**/.cache
**/*.dbmdl
**/*.jfm
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/secrets.dev.yaml
**/values.dev.yaml
**/vendor
LICENSE
README.md
**/*.class
**/*.iml
**/*.ipr
**/*.iws
**/*.log
**/.apt_generated
**/.gradle
**/.gradletasknamecache
**/.nb-gradle
**/.springBeans
**/build
**/dist
**/gradle-app.setting
**/nbbuild
**/nbdist
**/nbproject/private
**/target
*.ctxt
.mtj.tmp
.mvn/timing.properties
buildNumber.properties
dependency-reduced-pom.xml
hs_err_pid*
pom.xml.next
pom.xml.releaseBackup
pom.xml.tag
pom.xml.versionsBackup
release.properties
replay_pid*
117 changes: 117 additions & 0 deletions .mvn/wrapper/MavenWrapperDownloader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;

public class MavenWrapperDownloader {

private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";

Comment on lines +23 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Outdated Takari wrapper default; consider upgrading to Apache Maven Wrapper 3.x

0.5.6 is old and lacks built-in hardening (checksum verification, better mirrors). Prefer regenerating scripts via the Maven Wrapper Plugin (Apache 3.x) rather than maintaining this downloader.


🌐 Web query:

What is the latest Apache Maven Wrapper release, and does it support verifying wrapper and distribution checksums out of the box?

💡 Result:

Latest Apache Maven Wrapper release: 3.3.3. [1]

Yes — it supports verifying both the wrapper JAR and the downloaded Maven distribution out of the box. Add SHA‑256 sums (hex, lowercase) to maven-wrapper.properties using the keys wrapperSha256Sum and distributionSha256Sum to enable verification. [2][1]

Sources:
[1] Apache Maven Wrapper download (3.3.3).
[2] Apache Maven Wrapper documentation — checksum verification instructions.


Upgrade to Apache Maven Wrapper 3.3.3 and enable checksum verification

  • Update WRAPPER_VERSION in .mvn/wrapper/MavenWrapperDownloader.java from "0.5.6" to "3.3.3"
  • Add wrapperSha256Sum and distributionSha256Sum entries to maven-wrapper.properties for built-in SHA-256 verification
🤖 Prompt for AI Agents
.mvn/wrapper/MavenWrapperDownloader.java lines 23-29: the project is still
referencing the old wrapper version "0.5.6" and the repo needs checksum
verification enabled; update WRAPPER_VERSION to "3.3.3" in this file and then
add wrapperSha256Sum and distributionSha256Sum entries to the
.mvn/wrapper/maven-wrapper.properties file (use the official SHA-256 sums for
the maven-wrapper jar and the Maven distribution you want) so the wrapper
performs built-in SHA-256 verification when downloading artifacts.

/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";

/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";

/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";

public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());

// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
Comment on lines +53 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Plumb expected SHA-256 from properties (wrapperSha256Sum)

Read an optional checksum from maven-wrapper.properties for later verification.

Apply:

         File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
-        String url = DEFAULT_DOWNLOAD_URL;
+        String url = DEFAULT_DOWNLOAD_URL;
+        String expectedSha256 = null;
         if(mavenWrapperPropertyFile.exists()) {
             FileInputStream mavenWrapperPropertyFileInputStream = null;
             try {
                 mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
                 Properties mavenWrapperProperties = new Properties();
                 mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
                 url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+                expectedSha256 = mavenWrapperProperties.getProperty("wrapperSha256Sum");

And add this constant near Line 46:

-    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+    private static final String PROPERTY_NAME_WRAPPER_SHA256 = "wrapperSha256Sum";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
private static final String PROPERTY_NAME_WRAPPER_SHA256 = "wrapperSha256Sum";
// ...
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
String expectedSha256 = null;
if (mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
expectedSha256 = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_SHA256);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if (mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}

System.out.println("- Downloading from: " + url);

File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
Comment on lines +86 to +90
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pass expected checksum into downloader

Wire the value through to the download method.

Apply:

-            downloadFileFromURL(url, outputFile);
+            downloadFileFromURL(url, outputFile, expectedSha256);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
try {
downloadFileFromURL(url, outputFile, expectedSha256);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
🤖 Prompt for AI Agents
.mvn/wrapper/MavenWrapperDownloader.java around lines 86-90: the code calls
downloadFileFromURL(url, outputFile) but does not pass the expected checksum
through to the downloader; update the call to include the expected checksum
(e.g., downloadFileFromURL(url, outputFile, expectedChecksum)), ensure
expectedChecksum is obtained from the surrounding context/arguments, then update
the downloadFileFromURL method signature and all internal calls to accept and
use the checksum for verification after download.

System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}

private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
Comment on lines +97 to +115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Add timeouts, try-with-resources, and SHA-256 verification

Harden network I/O and verify the downloaded JAR to mitigate supply-chain/mitM risks.

Apply:

-    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+    private static void downloadFileFromURL(String urlString, File destination, String expectedSha256) throws Exception {
         if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
             String username = System.getenv("MVNW_USERNAME");
             char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
             Authenticator.setDefault(new Authenticator() {
                 @Override
                 protected PasswordAuthentication getPasswordAuthentication() {
                     return new PasswordAuthentication(username, password);
                 }
             });
         }
-        URL website = new URL(urlString);
-        ReadableByteChannel rbc;
-        rbc = Channels.newChannel(website.openStream());
-        FileOutputStream fos = new FileOutputStream(destination);
-        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
-        fos.close();
-        rbc.close();
+        final URL url = new URL(urlString);
+        final java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();
+        conn.setInstanceFollowRedirects(true);
+        conn.setConnectTimeout(15_000);
+        conn.setReadTimeout(30_000);
+        final java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
+        try (InputStream in = conn.getInputStream();
+             BufferedInputStream bin = new BufferedInputStream(in);
+             FileOutputStream fos = new FileOutputStream(destination)) {
+            byte[] buf = new byte[8192];
+            int r;
+            while ((r = bin.read(buf)) != -1) {
+                md.update(buf, 0, r);
+                fos.write(buf, 0, r);
+            }
+        }
+        if (expectedSha256 != null && !expectedSha256.trim().isEmpty()) {
+            String actual = toHex(md.digest());
+            if (!actual.equalsIgnoreCase(expectedSha256.trim())) {
+                // remove bad file
+                try { destination.delete(); } catch (Throwable ignore) {}
+                throw new IOException("SHA-256 mismatch for maven-wrapper.jar. Expected "
+                    + expectedSha256 + ", got " + actual);
+            }
+        }
     }

Add this helper method anywhere below Line 115:

private static String toHex(byte[] bytes) {
    StringBuilder sb = new StringBuilder(bytes.length * 2);
    for (byte b : bytes) sb.append(String.format("%02x", b));
    return sb.toString();
}
🤖 Prompt for AI Agents
In .mvn/wrapper/MavenWrapperDownloader.java around lines 97 to 115, the
downloadFileFromURL method currently performs unprotected network I/O and file
writes without timeouts, try-with-resources, or integrity checks; update it to
use URLConnection/HttpURLConnection with connect and read timeouts, replace
manual stream/channel handling with try-with-resources for the
InputStream/ReadableByteChannel and FileOutputStream to ensure proper closing,
compute the SHA-256 digest of the downloaded bytes and verify it against an
expected checksum (throwing an exception on mismatch), and add the provided
private static String toHex(byte[] bytes) helper method below line 115 to
convert the digest to hex for comparison.


}
Binary file added .mvn/wrapper/maven-wrapper.jar
Binary file not shown.
2 changes: 2 additions & 0 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
distributionUrl=https://maven.aliyun.com/repository/public/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://maven.aliyun.com/repository/public/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
77 changes: 77 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# syntax=docker/dockerfile:1

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/go/dockerfile-reference/

# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7

################################################################################

# Create a stage for resolving and downloading dependencies.
FROM eclipse-temurin:8-jdk-jammy as deps

WORKDIR /build

# Copy the mvnw wrapper with executable permissions.
COPY --chmod=0755 mvnw mvnw
COPY .mvn/ .mvn/

# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.m2 so that subsequent builds don't have to
# re-download packages.
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests

################################################################################

# Create a stage for building the application based on the stage with downloaded dependencies.
# This Dockerfile is optimized for Java applications that output an uber jar, which includes
# all the dependencies needed to run your app inside a JVM. If your app doesn't output an uber
# jar and instead relies on an application server like Apache Tomcat, you'll need to update this
# stage with the correct filename of your package and update the base image of the "final" stage
# use the relevant app server, e.g., using tomcat (https://hub.docker.com/_/tomcat/) as a base image.
FROM deps as package

WORKDIR /build

COPY . src/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 \
./mvnw package -DskipTests && \
mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar


################################################################################

# Create a new stage for running the application that contains the minimal
# runtime dependencies for the application. This often uses a different base
# image from the install or build stage where the necessary files are copied
# from the install stage.
#
# The example below uses eclipse-turmin's JRE image as the foundation for running the app.
# By specifying the "8-jre-jammy" tag, it will also use whatever happens to be the
# most recent version of that tag when you build your Dockerfile.
# If reproducibility is important, consider using a specific digest SHA, like
# eclipse-temurin@sha256:99cede493dfd88720b610eb8077c8688d3cca50003d76d1d539b0efc8cca72b4.
FROM eclipse-temurin:8-jre-jammy AS final

# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser

# Copy the executable from the "package" stage.
COPY --from=package build/target/app.jar app.jar

EXPOSE 8008

ENTRYPOINT [ "java", "-jar", "app.jar" ]
19 changes: 19 additions & 0 deletions README.Docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### Building and running your application

When you're ready, start your application by running:
`docker compose up --build`.

Your application will be available at http://localhost:8008.

### Deploying your application to the cloud

First, build your image, e.g.: `docker build -t myapp .`.
If your cloud uses a different CPU architecture than your development
machine (e.g., you are on a Mac M1 and your cloud provider is amd64),
you'll want to build the image for that platform, e.g.:
`docker build --platform=linux/amd64 -t myapp .`.

Then, push it to your registry, e.g. `docker push myregistry.com/myapp`.

Consult Docker's [getting started](https://docs.docker.com/go/get-started-sharing/)
docs for more detail on building and pushing.
49 changes: 49 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker Compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/

# Here the instructions define your application as a service called "server".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
server:
build:
context: .
ports:
- 8008:8008

# The commented out section below is an example of how to define a PostgreSQL
# database that your application can use. `depends_on` tells Docker Compose to
# start the database before your application. The `db-data` volume persists the
# database data between container restarts. The `db-password` secret is used
# to set the database password. You must create `db/password.txt` and add
# a password of your choosing to it before running `docker-compose up`.
# depends_on:
# db:
# condition: service_healthy
# db:
# image: postgres
# restart: always
# user: postgres
# secrets:
# - db-password
# volumes:
# - db-data:/var/lib/postgresql/data
# environment:
# - POSTGRES_DB=example
# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
# expose:
# - 5432
# healthcheck:
# test: [ "CMD", "pg_isready" ]
# interval: 10s
# timeout: 5s
# retries: 5
# volumes:
# db-data:
# secrets:
# db-password:
# file: db/password.txt

Loading
Loading