Skip to content

Commit d95d940

Browse files
author
Christopher Frost
committed
Better escaping of Java options
This commit makes it possible to use both escaped and unescaped $ and \ characters in the value of a Java option. The $ character is required so users can include environment variables in Java options. The \ character is required so it's possible to include an escaped $ charecter in the value of a Java option. [#100119572]
1 parent 649b9f9 commit d95d940

File tree

3 files changed

+77
-10
lines changed

3 files changed

+77
-10
lines changed

docs/framework-java_opts.md

+31-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ The Java Options Framework contributes arbitrary Java options to the application
1414
</table>
1515
Tags are printed to standard output by the buildpack detect script
1616

17-
1817
## Configuration
1918
For general information on configuring the buildpack, including how to specify configuration values through environment variables, refer to [Configuration and Extension][].
2019

@@ -29,6 +28,37 @@ Any `JAVA_OPTS` from either the config file or environment variables that config
2928

3029
Any `JAVA_OPTS` from either the config file or environment variables will be specified in the start command after any Java Opts added by other frameworks.
3130

31+
## Escaping strings
32+
33+
Java options will have special characters escaped when used in the shell command that starts the Java application but the `$` and `\` characters will not be escaped. This is to allow Java options to include environment variables when the application starts.
34+
35+
```bash
36+
cf set-env my-application JAVA_OPTS '-Dexample.port=$PORT'
37+
```
38+
39+
If an escaped `$` or `\` character is needed in the Java options they will have to be escaped manually. For example, to obtain this output in the start command.
40+
41+
```bash
42+
-Dexample.other=something.\$dollar.\\slash
43+
```
44+
45+
From the command line use;
46+
```bash
47+
cf set-env my-application JAVA_OPTS '-Dexample.other=something.\\\\\$dollar.\\\\\\\slash'
48+
```
49+
50+
From the [`config/java_opts.yml`][] file use;
51+
```yaml
52+
from_environment: true
53+
java_opts: '-Dexample.other=something.\\$dollar.\\\\slash'
54+
```
55+
56+
Finally, from the applications manifest use;
57+
```yaml
58+
env:
59+
JAVA_OPTS: '-Dexample.other=something.\\\\\$dollar.\\\\\\\slash'
60+
```
61+
3262
## Example
3363
```yaml
3464
# JAVA_OPTS configuration

lib/java_buildpack/framework/java_opts.rb

+10-2
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,22 @@ def parsed_java_opts
6666
parsed_java_opts.concat ENV[ENVIRONMENT_VARIABLE].shellsplit if supports_environment?
6767

6868
parsed_java_opts.map do |java_opt|
69-
if /(?<key>.+)=(?<value>.+)/ =~ java_opt
70-
"#{key}=#{value.shellescape}"
69+
if /(?<key>.+?)=(?<value>.+)/ =~ java_opt
70+
"#{key}=#{parse_shell_string(value)}"
7171
else
7272
java_opt
7373
end
7474
end
7575
end
7676

77+
def parse_shell_string(str)
78+
return "''" if str.empty?
79+
str = str.dup
80+
str.gsub!(%r{([^A-Za-z0-9_\-.,:\/@\n$\\])}, '\\\\\\1')
81+
str.gsub!(/\n/, "'\n'")
82+
str
83+
end
84+
7785
def supports_configuration?
7886
@configuration.key?(CONFIGURATION_PROPERTY) && !@configuration[CONFIGURATION_PROPERTY].nil?
7987
end

spec/java_buildpack/framework/java_opts_spec.rb

+36-7
Original file line numberDiff line numberDiff line change
@@ -66,35 +66,64 @@
6666

6767
it 'adds split java_opts to context' do
6868
component.release
69-
7069
expect(java_opts).to include('-Xdebug')
7170
expect(java_opts).to include('-Xnoagent')
72-
expect(java_opts).to include('-Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=y')
71+
expect(java_opts).to include('-Xrunjdwp:transport=dt_socket,server\=y,address\=8000,suspend\=y')
7372
expect(java_opts).to include('-XX:OnOutOfMemoryError=kill\ -9\ \%p')
7473
end
7574
end
7675

7776
context do
7877
let(:configuration) do
79-
{ 'java_opts' => '-Dtest=!£$%^&*(){}<>[];~`' }
78+
{ 'java_opts' => '-Dtest=!£%^&*()<>[]{};~`' }
8079
end
8180

8281
it 'escapes special characters' do
8382
component.release
83+
expect(java_opts).to include('-Dtest=\!\£\%\^\&\*\(\)\<\>\[\]\{\}\;\~\`')
84+
end
85+
end
86+
87+
context do
88+
let(:configuration) do
89+
{ 'java_opts' => '-Dtest=$DOLLAR\\\SLASH' }
90+
end
91+
92+
it 'does not escape the shell variable character from configuration' do
93+
component.release
94+
expect(java_opts).to include('-Dtest=$DOLLAR\SLASH')
95+
end
96+
end
8497

85-
expect(java_opts).to include('-Dtest=\\!\\£\\$\\%\\^\\&\\*\\(\\)\\{\\}\\<\\>\\[\\]\\;\\~\\`')
98+
context do
99+
let(:configuration) { { 'from_environment' => true } }
100+
let(:environment) { { 'JAVA_OPTS' => '-Dtest=$dollar\\\slash' } }
101+
102+
it 'does not escape the shell variable character from environment' do
103+
component.release
104+
expect(java_opts).to include('-Dtest=$dollar\slash')
86105
end
87106
end
88107

89108
context do
90109
let(:configuration) do
91-
{ 'java_opts' => '-javaagent:agent.jar=port="\$PORT",host=localhost' }
110+
{ 'java_opts' => '-Dtest=something.\\\$dollar.\\\\\\\slash' }
92111
end
93112

94-
it 'allows escaped characters' do
113+
it 'can escape non-escaped characters ' do
95114
component.release
115+
expect(java_opts).to include('-Dtest=something.\\$dollar.\\\slash')
116+
end
117+
end
96118

97-
expect(java_opts).to include('-javaagent:agent.jar=port=$PORT,host=localhost')
119+
context do
120+
let(:configuration) do
121+
{ 'java_opts' => '-javaagent:agent.jar=port=$PORT,host=localhost' }
122+
end
123+
124+
it 'escapes equal signs after the first one' do
125+
component.release
126+
expect(java_opts).to include('-javaagent:agent.jar=port\\=$PORT,host\\=localhost')
98127
end
99128
end
100129

0 commit comments

Comments
 (0)