Skip to content

Commit d2b51d6

Browse files
authored
Merge pull request #1330 from stokpop/fix/java-opts-backslash-bash-compat
fix(java_opts): preserve backslashes in JAVA_OPTS on bash 4.x
2 parents 3bb9fa0 + 53ebe43 commit d2b51d6

3 files changed

Lines changed: 28 additions & 4 deletions

File tree

scripts/unit.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ function main() {
1414
local src
1515
src="$(find "${ROOTDIR}/src" -mindepth 1 -maxdepth 1 -type d )"
1616

17+
echo "bash: ${BASH_VERSION}"
18+
1719
util::tools::ginkgo::install --directory "${ROOTDIR}/.bin"
1820
util::tools::buildpack-packager::install --directory "${ROOTDIR}/.bin"
1921

src/java/frameworks/java_opts_writer.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,13 @@ case "$USER_JAVA_OPTS" in
141141
esac
142142
143143
# Escape replacement-special chars once; these values are loop-invariant.
144+
# USER_JAVA_OPTS is injected via string-split below (not ${//}) — bash 4.x and 5.x
145+
# treat '\\' in replacement strings differently, corrupting backslashes.
144146
_escaped_deps_dir="${DEPS_DIR//\\/\\\\}"
145147
_escaped_deps_dir="${_escaped_deps_dir//&/\\&}"
146148
_escaped_home="${HOME//\\/\\\\}"
147149
_escaped_home="${_escaped_home//&/\\&}"
148150
_user_java_opts_placeholder='__JAVA_OPTS_BUILDPACK_PLACEHOLDER__'
149-
_escaped_user_java_opts="${USER_JAVA_OPTS//\\/\\\\}"
150-
_escaped_user_java_opts="${_escaped_user_java_opts//&/\\&}"
151151
152152
if [ -d "$DEPS_DIR/%s/java_opts" ]; then
153153
for opts_file in "$DEPS_DIR/%s/java_opts"/*.opts; do
@@ -194,8 +194,21 @@ if [ -d "$DEPS_DIR/%s/java_opts" ]; then
194194
;;
195195
esac
196196
197-
# Now safely substitute JAVA_OPTS after expansion (preserves quotes, backslashes, and ampersands)
198-
opts_content="${opts_content//$_user_java_opts_placeholder/$_escaped_user_java_opts}"
197+
# Restore USER_JAVA_OPTS via string-split, not ${//}: bash 4.x and 5.x
198+
# treat '\\' in replacement strings differently, corrupting backslashes.
199+
# %%%% / # are pure string ops — no special-char interpretation, any bash.
200+
# Pre-count occurrences so the loop is bounded even if USER_JAVA_OPTS
201+
# itself contained the placeholder string (infinite-loop defence).
202+
# Counting uses empty replacement — no backslash in replacement, safe.
203+
# Handles multiple occurrences (e.g. user config "$JAVA_OPTS -Xmx512m"
204+
# with from_environment:true produces "$JAVA_OPTS -Xmx512m $JAVA_OPTS").
205+
_stripped="${opts_content//"$_user_java_opts_placeholder"/}"
206+
_placeholder_count=$(( (${#opts_content} - ${#_stripped}) / ${#_user_java_opts_placeholder} ))
207+
for (( _i=0; _i<_placeholder_count; _i++ )); do
208+
_before="${opts_content%%%%"$_user_java_opts_placeholder"*}"
209+
_after="${opts_content#*"$_user_java_opts_placeholder"}"
210+
opts_content="${_before}${USER_JAVA_OPTS}${_after}"
211+
done
199212
200213
if [ -n "$opts_content" ]; then
201214
JAVA_OPTS="$JAVA_OPTS $opts_content"

src/java/frameworks/java_opts_writer_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,15 @@ var _ = Describe("Java Opts Writer", func() {
370370
Expect(output).To(ContainSubstring(`-Dpath=C:\\double`))
371371
})
372372

373+
It("replaces all occurrences of $JAVA_OPTS when opts file contains it twice", func() {
374+
// Scenario: JBP_CONFIG_JAVA_OPTS: '{java_opts: "$JAVA_OPTS -Xmx512m", from_environment: true}'
375+
// produces opts file: "$JAVA_OPTS -Xmx512m $JAVA_OPTS" — two placeholders
376+
output, err := runScript(`-Dfoo=bar`, "$JAVA_OPTS -Xmx512m $JAVA_OPTS")
377+
Expect(err).NotTo(HaveOccurred(), "script failed with output: %s", output)
378+
Expect(strings.Count(output, "-Dfoo=bar")).To(Equal(2))
379+
Expect(output).NotTo(ContainSubstring("__JAVA_OPTS_BUILDPACK_PLACEHOLDER__"))
380+
})
381+
373382
// Full invocation cycle tests for issue #1301:
374383
// Verify that javaexec tokenizes $JAVA_OPTS without a shell, so glob chars,
375384
// pipes, and shell metacharacters are never expanded or executed.

0 commit comments

Comments
 (0)