Skip to content

Commit e6a9c42

Browse files
aschemanclaude
andauthored
Fix mvn script expanding ${...} in CLI arguments (#11983)
* Fix mvn script expanding ${...} in CLI arguments The eval in the mvn script causes shell expansion of ${...} patterns in user-provided arguments. Pass user arguments directly via "$@" instead of concatenating them into the eval string. This preserves MAVEN_OPTS word splitting while preventing unintended shell expansion. Fixes #11978 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add IT for mvn script expanding ${...} in CLI arguments The eval in the mvn script causes shell expansion of ${...} patterns in user-provided arguments. This regression test exercises the actual launcher script via setForkJvm(true) and verifies that ${...} is not expanded by the shell. Related: #11978 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Apply review suggestions from gnodet on PR #11983 - Use printf in mvn debug output so each user arg is shown individually quoted, preserving boundary information now that args are no longer embedded in $cmd. - Trim IT Javadoc to match codebase style; surefire context belongs in the linked issue, not the test. - Reword assertion comments to make primary/secondary checks parallel. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e2f8473 commit e6a9c42

3 files changed

Lines changed: 123 additions & 8 deletions

File tree

apache-maven/src/assembly/maven/bin/mvn

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ handle_args() {
275275
handle_args "$@"
276276
MAVEN_MAIN_CLASS=${MAVEN_MAIN_CLASS:=org.apache.maven.cling.MavenCling}
277277

278-
# Build command string for eval
278+
# Build base command string for eval (only contains Maven-controlled values)
279279
cmd="\"$JAVACMD\" \
280280
$MAVEN_OPTS \
281281
$MAVEN_DEBUG_OPTS \
@@ -289,14 +289,12 @@ cmd="\"$JAVACMD\" \
289289
$LAUNCHER_CLASS \
290290
$MAVEN_ARGS"
291291

292-
# Add remaining arguments with proper quoting
293-
for arg in "$@"; do
294-
cmd="$cmd \"$arg\""
295-
done
296-
297292
if [ -n "$MAVEN_DEBUG_SCRIPT" ]; then
298293
echo "[DEBUG] Launching JVM with command:" >&2
299-
echo "[DEBUG] $cmd" >&2
294+
printf '[DEBUG] %s' "$cmd" >&2; printf ' "%s"' "$@" >&2; echo >&2
300295
fi
301296

302-
eval exec "$cmd"
297+
# User arguments ("$@") are passed directly to preserve literal values
298+
# like ${...} Maven property placeholders without shell expansion.
299+
# Only the base command uses eval for MAVEN_OPTS word splitting.
300+
eval exec "$cmd" '"$@"'
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.it;
20+
21+
import java.nio.file.Path;
22+
import java.util.Properties;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
import static org.junit.jupiter.api.Assertions.assertEquals;
27+
28+
/**
29+
* This is a test set for <a href="https://github.com/apache/maven/issues/11978">gh-11978</a>.
30+
*
31+
* <p>Verifies that the launcher script does not expand {@code ${...}} patterns
32+
* in CLI arguments. The placeholder name intentionally contains dots (invalid as a
33+
* shell variable name) so without the fix, {@code eval exec} aborts with
34+
* {@code bad substitution} before Maven even starts.
35+
*/
36+
class MavenITgh11978PlaceholderInCliArgTest extends AbstractMavenIntegrationTestCase {
37+
38+
@Test
39+
void testIt() throws Exception {
40+
Path basedir = extractResources("/gh-11978-placeholder-in-cli-arg")
41+
.getAbsoluteFile()
42+
.toPath();
43+
44+
Verifier verifier = newVerifier(basedir.toString());
45+
verifier.setForkJvm(true); // NOTE: We want to go through the launcher script
46+
// The placeholder name contains dots, which is invalid as a shell variable name.
47+
// Without the fix, the shell's `eval exec` aborts with "bad substitution".
48+
// With the fix, the literal ${...} arrives at Maven.
49+
verifier.addCliArgument("-Dtest.placeholder=value_${some.maven.placeholder}_end");
50+
verifier.addCliArgument("validate");
51+
verifier.execute();
52+
verifier.verifyErrorFreeLog(); // primary check: shell crash produces non-zero exit
53+
54+
// Secondary: verify the literal placeholder flowed through Maven.
55+
// Maven resolves the unknown ${some.maven.placeholder} to empty.
56+
Properties props = verifier.loadProperties("target/pom.properties");
57+
assertEquals("-value__end-", props.getProperty("project.properties.pom.placeholder"));
58+
}
59+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
23+
<groupId>org.apache.maven.its.gh11978</groupId>
24+
<artifactId>test</artifactId>
25+
<version>1.0</version>
26+
27+
<name>Maven Integration Test :: GH-11978</name>
28+
<description>Verify that the launcher script does not expand ${...} placeholders in CLI arguments.</description>
29+
30+
<properties>
31+
<pom.placeholder>-${test.placeholder}-</pom.placeholder>
32+
</properties>
33+
34+
<build>
35+
<plugins>
36+
<plugin>
37+
<groupId>org.apache.maven.its.plugins</groupId>
38+
<artifactId>maven-it-plugin-expression</artifactId>
39+
<version>2.1-SNAPSHOT</version>
40+
<executions>
41+
<execution>
42+
<id>test</id>
43+
<goals>
44+
<goal>eval</goal>
45+
</goals>
46+
<phase>validate</phase>
47+
<configuration>
48+
<outputFile>target/pom.properties</outputFile>
49+
<expressions>
50+
<expression>project/properties</expression>
51+
</expressions>
52+
</configuration>
53+
</execution>
54+
</executions>
55+
</plugin>
56+
</plugins>
57+
</build>
58+
</project>

0 commit comments

Comments
 (0)