diff --git a/java/ql/integration-tests/java/maven-download-failure/.gitattributes b/java/ql/integration-tests/java/maven-download-failure/.gitattributes
new file mode 100644
index 000000000000..36e4b9d7df91
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+*.cmd text eol=crlf
diff --git a/java/ql/integration-tests/java/maven-download-failure/diagnostics.expected b/java/ql/integration-tests/java/maven-download-failure/diagnostics.expected
new file mode 100644
index 000000000000..daabe47a9e9f
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/diagnostics.expected
@@ -0,0 +1,84 @@
+{
+ "markdownMessage": "Java analysis used build tool Maven to pick a JDK version and/or to recommend external dependencies.",
+ "severity": "unknown",
+ "source": {
+ "extractorName": "java",
+ "id": "java/autobuilder/buildless/using-build-tool-advice",
+ "name": "Java analysis used build tool Maven to pick a JDK version and/or to recommend external dependencies"
+ },
+ "visibility": {
+ "cliSummaryTable": true,
+ "statusPage": false,
+ "telemetry": true
+ }
+}
+{
+ "markdownMessage": "Java analysis used the system default JDK.",
+ "severity": "unknown",
+ "source": {
+ "extractorName": "java",
+ "id": "java/autobuilder/buildless/jdk-system-default",
+ "name": "Java analysis used the system default JDK"
+ },
+ "visibility": {
+ "cliSummaryTable": true,
+ "statusPage": false,
+ "telemetry": true
+ }
+}
+{
+ "markdownMessage": "Java analysis with build-mode 'none' completed.",
+ "severity": "unknown",
+ "source": {
+ "extractorName": "java",
+ "id": "java/autobuilder/buildless/complete",
+ "name": "Java analysis with build-mode 'none' completed"
+ },
+ "visibility": {
+ "cliSummaryTable": true,
+ "statusPage": false,
+ "telemetry": true
+ }
+}
+{
+ "markdownMessage": "Java was extracted with build-mode set to 'none'. This means that all Java source in the working directory will be scanned, with build tools such as Maven and Gradle only contributing information about external dependencies.",
+ "severity": "note",
+ "source": {
+ "extractorName": "java",
+ "id": "java/autobuilder/buildless/mode-active",
+ "name": "Java was extracted with build-mode set to 'none'"
+ },
+ "visibility": {
+ "cliSummaryTable": true,
+ "statusPage": true,
+ "telemetry": true
+ }
+}
+{
+ "markdownMessage": "Reading the dependency graph from build files provided 2 classpath entries",
+ "severity": "unknown",
+ "source": {
+ "extractorName": "java",
+ "id": "java/autobuilder/buildless/depgraph-provided-by-maven",
+ "name": "Java analysis extracted precise dependency graph information from tool Maven"
+ },
+ "visibility": {
+ "cliSummaryTable": true,
+ "statusPage": false,
+ "telemetry": true
+ }
+}
+{
+ "markdownMessage": "The maven-enforcer-plugin recommended a specific Maven version be used. Trying to download it failed; extraction will continue, but retrieving dependency information from Maven may fail if the system default version is incompatible with project build scripts. Consider checking if a firewall configuration or similar is preventing downloading the required version.",
+ "severity": "note",
+ "source": {
+ "extractorName": "java",
+ "id": "java/autobuilder/buildless/maven-download-failed",
+ "name": "Java analysis failed to download a Maven version recommended by the maven-enforcer-plugin"
+ },
+ "visibility": {
+ "cliSummaryTable": true,
+ "statusPage": true,
+ "telemetry": true
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-download-failure/mvn b/java/ql/integration-tests/java/maven-download-failure/mvn
new file mode 100755
index 000000000000..53df501df2b6
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/mvn
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+>&2 echo "Running mvn wrapper script"
+
+if [ "$1" == "dependency:copy" ]; then
+ >&2 echo "Arguments ($@) look like a dependency:copy command; failing"
+ exit 1
+fi
+
+${REAL_MVN_PATH} "$@"
diff --git a/java/ql/integration-tests/java/maven-download-failure/mvn.cmd b/java/ql/integration-tests/java/maven-download-failure/mvn.cmd
new file mode 100755
index 000000000000..740817413368
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/mvn.cmd
@@ -0,0 +1 @@
+@bash mvn %*
diff --git a/java/ql/integration-tests/java/maven-download-failure/pom.xml b/java/ql/integration-tests/java/maven-download-failure/pom.xml
new file mode 100644
index 000000000000..ca376ad5ba9d
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/pom.xml
@@ -0,0 +1,133 @@
+
+
+
+ 4.0.0
+
+ com.example
+ maven-sample
+ 1.0-SNAPSHOT
+
+ maven-sample
+
+ http://www.example.com
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+
+
+ exec-maven-plugin
+ org.codehaus.mojo
+ 1.1.1
+
+
+ check-maven-version
+ package
+
+ java
+
+
+
+
+ com.example.App
+
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+ 2.19.1
+
+
+
+ check
+
+ compile
+
+
+
+
+
+ /* FAIL ME */
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ [3.1.1,)
+
+
+
+
+
+
+
+
+
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-jar-plugin
+ 3.0.2
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+ maven-site-plugin
+ 3.7.1
+
+
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+
+
diff --git a/java/ql/integration-tests/java/maven-download-failure/source_archive.expected b/java/ql/integration-tests/java/maven-download-failure/source_archive.expected
new file mode 100644
index 000000000000..0a86ff54645c
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/source_archive.expected
@@ -0,0 +1,7 @@
+pom.xml
+src/main/java/com/example/App.java
+src/main/resources/my-app.properties
+src/main/resources/page.xml
+src/main/resources/struts.xml
+src/test/java/com/example/AppTest.java
+test-db/working/settings.xml
diff --git a/java/ql/integration-tests/java/maven-download-failure/src/main/java/com/example/App.java b/java/ql/integration-tests/java/maven-download-failure/src/main/java/com/example/App.java
new file mode 100644
index 000000000000..c9eec918587c
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/src/main/java/com/example/App.java
@@ -0,0 +1,30 @@
+package com.example;
+
+import java.util.regex.Pattern;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Hello world!
+ *
+ */
+public class App
+{
+ public static void main( String[] args )
+ {
+ System.out.println( "Hello World!" );
+ String expectedVersion = System.getenv("EXPECT_MAVEN");
+ Path mavenHome = Paths.get(System.getProperty("maven.home")).normalize();
+ String observedVersion = mavenHome.getFileName().toString();
+ if (expectedVersion != null && !expectedVersion.equals(observedVersion)) {
+ System.err.println("Wrong maven version, expected '" + expectedVersion + "' but got '" + observedVersion + "'" + mavenHome);
+ System.exit(1);
+ }
+ String commandMatcher = System.getenv("EXPECT_COMMAND_REGEX");
+ String command = System.getProperty("sun.java.command");
+ if (commandMatcher != null && !Pattern.matches(commandMatcher, command)) {
+ System.err.println("Wrong command line, '" + command + "' does not match '" + commandMatcher + "'");
+ System.exit(1);
+ }
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-download-failure/src/main/resources/my-app.properties b/java/ql/integration-tests/java/maven-download-failure/src/main/resources/my-app.properties
new file mode 100644
index 000000000000..e566b49a29a8
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/src/main/resources/my-app.properties
@@ -0,0 +1 @@
+version=1.0
diff --git a/java/ql/integration-tests/java/maven-download-failure/src/main/resources/page.xml b/java/ql/integration-tests/java/maven-download-failure/src/main/resources/page.xml
new file mode 100644
index 000000000000..2bab459cb031
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/src/main/resources/page.xml
@@ -0,0 +1,8 @@
+
+
+A sample
+
+
+Hello world!
+
+
diff --git a/java/ql/integration-tests/java/maven-download-failure/src/main/resources/struts.xml b/java/ql/integration-tests/java/maven-download-failure/src/main/resources/struts.xml
new file mode 100644
index 000000000000..73fc0c6b9cb6
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/src/main/resources/struts.xml
@@ -0,0 +1,4 @@
+
+
+This is a sample file
+
diff --git a/java/ql/integration-tests/java/maven-download-failure/src/test/java/com/example/AppTest.java b/java/ql/integration-tests/java/maven-download-failure/src/test/java/com/example/AppTest.java
new file mode 100644
index 000000000000..22a94ca6f01c
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/src/test/java/com/example/AppTest.java
@@ -0,0 +1,20 @@
+package com.example;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest
+{
+ /**
+ * Rigorous Test :-)
+ */
+ @Test
+ public void shouldAnswerWithTrue()
+ {
+ assertTrue( true );
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-download-failure/test.py b/java/ql/integration-tests/java/maven-download-failure/test.py
new file mode 100644
index 000000000000..a86d970e3fe0
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-download-failure/test.py
@@ -0,0 +1,14 @@
+import os
+import os.path
+import shutil
+
+def test(codeql, java, check_diagnostics):
+
+ # Avoid shutil resolving mvn to the wrapper script in the test dir:
+ os.environ["NoDefaultCurrentDirectoryInExePath"] = "0"
+ runenv = {
+ "PATH": os.path.realpath(os.path.dirname(__file__)) + os.pathsep + os.getenv("PATH"),
+ "REAL_MVN_PATH": shutil.which("mvn"),
+ }
+ del os.environ["NoDefaultCurrentDirectoryInExePath"]
+ codeql.database.create(build_mode = "none", _env = runenv)
diff --git a/java/ql/lib/change-notes/2025-03-03-maven-fixes.md b/java/ql/lib/change-notes/2025-03-03-maven-fixes.md
new file mode 100644
index 000000000000..0ec0c40dc175
--- /dev/null
+++ b/java/ql/lib/change-notes/2025-03-03-maven-fixes.md
@@ -0,0 +1,5 @@
+---
+category: fix
+---
+* Java build-mode `none` no longer fails when a required version of Maven cannot be downloaded, such as due to a firewall. It will now attempt to use the system version of Maven if present, or otherwise proceed without detailed dependency information.
+* Java build-mode `none` now correctly uses Maven dependency information on Windows platforms.