Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 14 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
* text eol=lf
# Normalize text files to LF, but let Git auto-detect binaries so they are
# stored byte-for-byte (a blanket "* text" corrupts binaries like the Gradle
# wrapper jar by stripping CR bytes during line-ending normalization).
* text=auto eol=lf

# Always treat these as binary regardless of auto-detection.
*.jar binary
*.zip binary
*.gz binary
*.class binary
*.png binary
*.jpg binary
*.gif binary
*.ico binary
30 changes: 15 additions & 15 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ jobs:
exit 1
fi

- name: Setup java 17 for building
- name: Setup java 25 for building
uses: actions/setup-java@v5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all workflow actions are pinned to SHAs (read-only check)
rg -n --glob ".github/workflows/*.yml" '^\s*uses:\s*[^@]+@[^0-9a-fA-F]'

Repository: minio/minio-java

Length of output: 209


🏁 Script executed:

#!/bin/bash
# First, find workflow files in the repository
find . -type f -name "*.yml" -o -name "*.yaml" | grep -E "\.github/workflows" | head -20

Repository: minio/minio-java

Length of output: 131


🏁 Script executed:

#!/bin/bash
# List contents of .github/workflows directory
if [ -d ".github/workflows" ]; then
  ls -la .github/workflows/
else
  echo "Directory does not exist"
  find . -type d -name "workflows" 2>/dev/null
fi

Repository: minio/minio-java

Length of output: 301


🏁 Script executed:

#!/bin/bash
# If gradle.yml exists, read it to check line 33
if [ -f ".github/workflows/gradle.yml" ]; then
  sed -n '25,40p' .github/workflows/gradle.yml | cat -n
else
  echo "gradle.yml not found at expected path"
fi

Repository: minio/minio-java

Length of output: 747


🌐 Web query:

actions/setup-java v5 commit SHA GitHub Actions pinning best practices 2026

💡 Result:

As of June 2026, pinning GitHub Actions to a full-length commit SHA is considered the industry-standard security best practice [1][2][3]. Use of Commit SHAs Pinning to a full-length 40-character commit SHA provides immutability [1][4][5]. Unlike tags (e.g., v5) or branches (e.g., main), which are mutable and can be moved or force-pushed by an attacker to point to malicious code, a commit SHA points to a specific, unchangeable state of the repository [1][6][7]. Best Practices for 2026 1. Always Use Full SHAs: Pin all third-party actions to their full commit SHA to prevent supply chain attacks [2][3][7]. 2. Maintain Human-Readability: Append the version tag as a comment to the SHA pin for clarity, for example: uses: actions/setup-java@d81c4e45f3ac973cc936d79104023e20054ba578 # v5.0.1 [2][3][7]. 3. Automate Maintenance: Do not manage pins manually. Use tools like Renovate (with the helpers:pinGitHubActionDigests preset) or dependabot to automatically open pull requests when new versions are released, ensuring your dependencies stay current without sacrificing security [2][3][6]. 4. Enforce via Policy: Organizations and repositories can now enable policies to require SHA pinning [1][8]. If enabled, any workflow using a mutable tag or branch will fail [2][5]. It is recommended to enable this enforcement where possible to ensure universal compliance [2][7]. 5. Audit Sub-actions: Be aware that even if you pin a primary action, it may internally reference other unpinned actions [3][7][5]. While full workflow-level dependency locking is a feature on the 2026 security roadmap to address this, users should be cautious and monitor action security [9][3]. Regarding setup-java v5: The latest release as of June 2026 is v5.3.0, released on 2026-06-16 [10]. To pin to a specific version of setup-java, navigate to the action's repository on GitHub, view the tags or commit history to find the 40-character SHA associated with the desired release, and use it in your workflow file [11][12][4]. Top results for reference: [1] GitHub Docs: Secure use of GitHub Actions [2] Lullabot: Always pin GitHub Actions to a commit hash [3] CNCF: Securing CI/CD for an open source project [8] GitHub Blog: GitHub Actions policy supports SHA pinning [7] Opera Omnia: Stop trusting mutable references

Citations:


🏁 Script executed:

#!/bin/bash
# Check if there are other actions in the workflow and their pinning status
cat .github/workflows/gradle.yml | grep -n "uses:" | head -20

Repository: minio/minio-java

Length of output: 175


Pin actions/setup-java to a full commit SHA.

Line 33 uses a mutable tag (@v5), which weakens supply-chain integrity in CI. Pin to an immutable commit digest.

Suggested change
       - name: Setup java 25 for building
-        uses: actions/setup-java@v5
+        uses: actions/setup-java@d81c4e45f3ac973cc936d79104023e20054ba578 # v5.3.0
         with:
           distribution: temurin
           java-version: '25'
📝 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
uses: actions/setup-java@v5
- name: Setup java 25 for building
uses: actions/setup-java@d81c4e45f3ac973cc936d79104023e20054ba578 # v5.3.0
with:
distribution: temurin
java-version: '25'
🧰 Tools
🪛 zizmor (1.25.2)

[error] 33-33: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/gradle.yml at line 33, The actions/setup-java action on
line 33 uses the mutable tag `@v5` which weakens supply-chain integrity. Replace
`@v5` with a full immutable commit SHA (e.g., `@abc1234def5678`...) to pin the
action to a specific version and ensure CI/CD security. You can find the
appropriate commit SHA by checking the GitHub releases page for the
actions/setup-java repository.

Source: Linters/SAST tools

with:
distribution: temurin
java-version: '17'
java-version: '25'

- name: Set environment variables on Ubuntu
if: matrix.os == 'ubuntu-latest'
Expand All @@ -59,7 +59,7 @@ jobs:
./gradlew.bat build

- name: Setup java ${{ matrix.java-version }} for testing
if: matrix.java-version != '17'
if: matrix.java-version != '25'
uses: actions/setup-java@v5
with:
distribution: temurin
Expand All @@ -69,20 +69,20 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
cd functional
curl -sSfLO https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.11.4/junit-platform-console-standalone-1.11.4.jar
curl -sSfLO https://repo1.maven.org/maven2/com/github/spotbugs/spotbugs-annotations/4.9.8/spotbugs-annotations-4.9.8.jar
javac -cp spotbugs-annotations-4.9.8.jar:junit-platform-console-standalone-1.11.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:../adminapi/build/libs/minio-admin-${DEV_VERSION}-all.jar:. FunctionalTest.java
java -cp spotbugs-annotations-4.9.8.jar:junit-platform-console-standalone-1.11.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:../adminapi/build/libs/minio-admin-${DEV_VERSION}-all.jar:. FunctionalTest
javac -cp spotbugs-annotations-4.9.8.jar:junit-platform-console-standalone-1.11.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:. ./TestUserAgent.java
java -Dversion=${DEV_VERSION} -cp spotbugs-annotations-4.9.8.jar:junit-platform-console-standalone-1.11.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:. TestUserAgent
javac -cp spotbugs-annotations-4.9.8.jar:junit-platform-console-standalone-1.11.4.jar:../api/build/libs/minio-${RELEASE_VERSION}-all.jar:. ./TestUserAgent.java
java -Dversion=${RELEASE_VERSION} -cp spotbugs-annotations-4.9.8.jar:junit-platform-console-standalone-1.11.4.jar:../api/build/libs/minio-${RELEASE_VERSION}-all.jar:. TestUserAgent
curl -sSfLO https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.14.4/junit-platform-console-standalone-1.14.4.jar
curl -sSfLO https://repo1.maven.org/maven2/com/github/spotbugs/spotbugs-annotations/4.10.2/spotbugs-annotations-4.10.2.jar
javac -cp spotbugs-annotations-4.10.2.jar:junit-platform-console-standalone-1.14.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:../adminapi/build/libs/minio-admin-${DEV_VERSION}-all.jar:. FunctionalTest.java
java -cp spotbugs-annotations-4.10.2.jar:junit-platform-console-standalone-1.14.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:../adminapi/build/libs/minio-admin-${DEV_VERSION}-all.jar:. FunctionalTest
javac -cp spotbugs-annotations-4.10.2.jar:junit-platform-console-standalone-1.14.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:. ./TestUserAgent.java
java -Dversion=${DEV_VERSION} -cp spotbugs-annotations-4.10.2.jar:junit-platform-console-standalone-1.14.4.jar:../api/build/libs/minio-${DEV_VERSION}-all.jar:. TestUserAgent
javac -cp spotbugs-annotations-4.10.2.jar:junit-platform-console-standalone-1.14.4.jar:../api/build/libs/minio-${RELEASE_VERSION}-all.jar:. ./TestUserAgent.java
java -Dversion=${RELEASE_VERSION} -cp spotbugs-annotations-4.10.2.jar:junit-platform-console-standalone-1.14.4.jar:../api/build/libs/minio-${RELEASE_VERSION}-all.jar:. TestUserAgent

- name: Run tests on Windows
if: matrix.os == 'windows-latest'
run: |
cd functional
curl -sSfLO https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.11.4/junit-platform-console-standalone-1.11.4.jar
curl -sSfLO https://repo1.maven.org/maven2/com/github/spotbugs/spotbugs-annotations/4.9.8/spotbugs-annotations-4.9.8.jar
javac -encoding UTF-8 -cp "spotbugs-annotations-4.9.8.jar;junit-platform-console-standalone-1.11.4.jar;../api/build/libs/minio-$Env:DEV_VERSION-all.jar;../adminapi/build/libs/minio-admin-$Env:DEV_VERSION-all.jar;." FunctionalTest.java
java -cp "spotbugs-annotations-4.9.8.jar;junit-platform-console-standalone-1.11.4.jar;../api/build/libs/minio-$Env:DEV_VERSION-all.jar;../adminapi/build/libs/minio-admin-$Env:DEV_VERSION-all.jar;." FunctionalTest
curl -sSfLO https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.14.4/junit-platform-console-standalone-1.14.4.jar
curl -sSfLO https://repo1.maven.org/maven2/com/github/spotbugs/spotbugs-annotations/4.10.2/spotbugs-annotations-4.10.2.jar
javac -encoding UTF-8 -cp "spotbugs-annotations-4.10.2.jar;junit-platform-console-standalone-1.14.4.jar;../api/build/libs/minio-$Env:DEV_VERSION-all.jar;../adminapi/build/libs/minio-admin-$Env:DEV_VERSION-all.jar;." FunctionalTest.java
java -cp "spotbugs-annotations-4.10.2.jar;junit-platform-console-standalone-1.14.4.jar;../api/build/libs/minio-$Env:DEV_VERSION-all.jar;../adminapi/build/libs/minio-admin-$Env:DEV_VERSION-all.jar;." FunctionalTest
6 changes: 4 additions & 2 deletions adminapi/src/main/java/io/minio/admin/MinioAdminClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,10 @@ public long getBucketQuota(String bucketName) throws MinioException {
OBJECT_MAPPER
.getTypeFactory()
.constructMapType(HashMap.class, String.class, JsonNode.class);
return OBJECT_MAPPER.<Map<String, JsonNode>>readValue(response.body().bytes(), mapType)
.entrySet().stream()
return OBJECT_MAPPER
.<Map<String, JsonNode>>readValue(response.body().bytes(), mapType)
.entrySet()
.stream()
.filter(entry -> "quota".equals(entry.getKey()))
.findFirst()
.map(entry -> Long.valueOf(entry.getValue().toString()))
Expand Down
1 change: 1 addition & 0 deletions api/src/main/java/io/minio/Checksum.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
public class Checksum {
/** MD5 hash of zero length byte array. */
public static final String ZERO_MD5_HASH = "1B2M2Y8AsgTpgAmY7PhCfg==";

/** SHA-256 hash of zero length byte array. */
public static final String ZERO_SHA256_HASH =
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
Expand Down
2 changes: 1 addition & 1 deletion api/src/main/java/io/minio/Http.java
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ public static OkHttpClient setTimeout(
.build();
}

/** HTTP body of {@link RandomAccessFile}, {@link ByteBuffer} or {@link byte} array. */
/** HTTP body of {@link RandomAccessFile}, {@link ByteBuffer} or {@code byte} array. */
public static class Body {
private okhttp3.RequestBody requestBody;
private RandomAccessFile file;
Expand Down
3 changes: 2 additions & 1 deletion api/src/main/java/io/minio/PartReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ private void readOneByte() throws MinioException {
int n = 0;

try {
while ((n = file != null ? file.read(oneByte) : stream.read(oneByte)) == 0) ;
while ((n = file != null ? file.read(oneByte) : stream.read(oneByte)) == 0)
;
} catch (IOException e) {
throw new MinioException(e);
}
Expand Down
26 changes: 20 additions & 6 deletions api/src/main/java/io/minio/errors/MinioException.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,26 @@ public String httpTrace() {

/** Throws encapsulated exception. */
public void throwEncapsulatedException()
throws BucketPolicyTooLargeException, CertificateException, EOFException,
ErrorResponseException, FileNotFoundException, GeneralSecurityException,
InsufficientDataException, InternalException, InvalidKeyException,
InvalidResponseException, IOException, JsonMappingException, JsonParseException,
JsonProcessingException, KeyManagementException, KeyStoreException, MinioException,
NoSuchAlgorithmException, ServerException, XmlParserException {
throws BucketPolicyTooLargeException,
CertificateException,
EOFException,
ErrorResponseException,
FileNotFoundException,
GeneralSecurityException,
InsufficientDataException,
InternalException,
InvalidKeyException,
InvalidResponseException,
IOException,
JsonMappingException,
JsonParseException,
JsonProcessingException,
KeyManagementException,
KeyStoreException,
MinioException,
NoSuchAlgorithmException,
ServerException,
XmlParserException {
Throwable e = getCause();

// Inherited by MinioException
Expand Down
50 changes: 32 additions & 18 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
*/

/********************************/
/* gradleVersion = '9.4.1' */
/* gradleVersion = '9.6.0' */
/********************************/

plugins {
id 'com.gradleup.shadow' version '9.0.0'
id 'com.github.spotbugs' version '6.5.1'
id 'org.jreleaser' version '1.23.0'
id 'com.diffplug.spotless' version '6.13.0'
id 'com.gradleup.shadow' version '9.4.2'
id 'com.github.spotbugs' version '6.5.8'
id 'org.jreleaser' version '1.24.0'
id 'com.diffplug.spotless' version '8.7.0'
}

/* Root project definitions */
Expand All @@ -48,22 +48,26 @@ subprojects {
dependencies {
api 'com.carrotsearch.thirdparty:simple-xml-safe:2.7.1'
api 'com.google.guava:guava:33.6.0-jre'
api 'com.squareup.okhttp3:okhttp:5.3.2'
api 'com.fasterxml.jackson.core:jackson-annotations:2.21'
api 'com.fasterxml.jackson.core:jackson-core:2.21.2'
api 'com.fasterxml.jackson.core:jackson-databind:2.21.2'
api 'com.squareup.okhttp3:okhttp:5.4.0'
api 'com.fasterxml.jackson.core:jackson-annotations:2.22'
api 'com.fasterxml.jackson.core:jackson-core:2.22.0'
api 'com.fasterxml.jackson.core:jackson-databind:2.22.0'
api 'org.bouncycastle:bcprov-jdk18on:1.84'
api 'org.apache.commons:commons-compress:1.28.0'
api 'commons-codec:commons-codec:1.21.0'
api 'commons-codec:commons-codec:1.22.0'
api 'org.xerial.snappy:snappy-java:1.1.10.8'
compileOnly 'com.github.spotbugs:spotbugs-annotations:4.9.8'
compileOnly 'com.github.spotbugs:spotbugs-annotations:4.10.2'

testImplementation 'com.squareup.okhttp3:mockwebserver:5.3.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.14.3'
testImplementation 'com.squareup.okhttp3:mockwebserver:5.4.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.14.4'
}

[compileJava, compileTestJava].each() {
it.options.fork = true
// Build runs on JDK 25 (see java.toolchain) but enforces the Java 8 API surface and
// emits Java 8 bytecode via --release 8. --release also avoids the "source/target 8 is
// obsolete" warning; -Xlint:-options is kept as a belt-and-braces guard for -Werror.
it.options.release = 8
it.options.compilerArgs += ['-Xlint:unchecked', '-Xlint:deprecation', '-Xlint:-options', '-Werror', '-Xdiags:verbose']
it.options.encoding = 'UTF-8'
}
Expand Down Expand Up @@ -103,6 +107,10 @@ subprojects {
check.dependsOn localeTest

java {
// Compile/test/javadoc run on JDK 25; bytecode/API level pinned to Java 8 via release below.
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
Expand All @@ -112,7 +120,7 @@ subprojects {
target '**/*.java'
importOrder 'edu', 'com', 'io', 'java', 'javax', 'org', ''
removeUnusedImports()
googleJavaFormat('1.7')
googleJavaFormat('1.35.0')
}
groovyGradle {
target '*.gradle'
Expand Down Expand Up @@ -226,7 +234,7 @@ project(':api') {
}

signing {
required {
required = {
gradle.taskGraph.allTasks.any { it.name.contains('LocalMavenWithChecksums') }
}
sign publishing.publications.minioJava
Expand Down Expand Up @@ -346,7 +354,7 @@ project(':adminapi') {
}

signing {
required {
required = {
gradle.taskGraph.allTasks.any { it.name.contains('LocalMavenWithChecksums') }
}
sign publishing.publications.minioJava
Expand All @@ -355,7 +363,7 @@ project(':adminapi') {

project(':examples') {
dependencies {
compileOnly 'me.tongfei:progressbar:0.9.5'
compileOnly 'me.tongfei:progressbar:0.10.2'
compileOnly project(':api')
}

Expand All @@ -367,14 +375,17 @@ project(':examples') {
main {
java {
srcDirs = ["$rootDir/examples"]
// srcDir is the module root, which also contains build/; keep generated
// files (e.g. Spotless lint output) out of compilation.
exclude 'build/**'
}
}
}
}

project(':functional') {
dependencies {
implementation 'org.junit.jupiter:junit-jupiter-api:5.14.3'
implementation 'org.junit.jupiter:junit-jupiter-api:5.14.4'
implementation project(':api')
implementation project(':adminapi')
}
Expand All @@ -387,6 +398,9 @@ project(':functional') {
main {
java {
srcDirs = ["$rootDir/functional"]
// srcDir is the module root, which also contains build/; keep generated
// files (e.g. Spotless lint output) out of compilation.
exclude 'build/**'
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions functional/TestMinioClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,9 @@ public void putObject() throws Exception {

testPutObject(
"[object name ends with '/']",
PutObjectArgs.builder().bucket(bucketName).object("path/to/" + getRandomName() + "/")
PutObjectArgs.builder()
.bucket(bucketName)
.object("path/to/" + getRandomName() + "/")
.stream(new ContentInputStream(0), 0L, null)
.contentType(CUSTOM_CONTENT_TYPE)
.build(),
Expand Down Expand Up @@ -1445,7 +1447,9 @@ public void testCopyObject(
client.makeBucket(MakeBucketArgs.builder().bucket(args.source().bucket()).build());
try {
PutObjectArgs.Builder builder =
PutObjectArgs.builder().bucket(args.source().bucket()).object(args.source().object())
PutObjectArgs.builder()
.bucket(args.source().bucket())
.object(args.source().object())
.stream(new ContentInputStream(1 * KB), 1L * KB, null);
if (sse != null) builder.sse(sse);
client.putObject(builder.build());
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
4 changes: 3 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
15 changes: 6 additions & 9 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading