Skip to content

Commit 75d663b

Browse files
author
Daniel Mikusa
authored
Shell escape the value of Elastic APM custom properties (#908)
The current behavior is for no escaping to occur. This if you use a value like `"object_name[java.lang:type=Memory] attribute[HeapMemoryUsage:metric_name=test_heap_metric]"` (as we see in #786), the literal value is inserted into a system property and the space (among other characters) breaks the start command. This change will use `Shellwords.escape(..)` the value for every custom property unless that property value contains what looks like a subshell `$(..)` or environment variable `${..}` reference. If it looks like a subshell or env variable is being referenced, we will not escape just wrap the value in escaped quotes. We wrap it in escaped quotes in case the shell variable or subshell returns something which includes spaces. This is not perfect though, and you need to be careful if using subshell/env variables, you should ensure the output is properly escaped. For example: - `object_name[java.lang:type=Memory] attribute[HeapMemoryUsage:metric_name=test_heap_metric]` becomes `object_name\[java.lang:type\=Memory\]\ attribute\[HeapMemoryUsage:metric_name\=test_heap_metric\]` - `$(echo 'Hello world!') and stuff` becomes `\"$(echo 'Hello world!') and stuff\"` - `--> ${SOME_VAR} <--` becomes `\"--> ${SOME_VAR} <--\"` Signed-off-by: Daniel Mikusa <[email protected]>
1 parent b5bfccb commit 75d663b

File tree

3 files changed

+19
-3
lines changed

3 files changed

+19
-3
lines changed

docs/framework-elastic_apm_agent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ When binding Elastic APM using a user-provided service, it must have name or tag
2121
| ---- | -----------
2222
| `server_urls` | The URLs for the Elastic APM Server. They must be fully qualified, including protocol (http or https) and port.
2323
| `secret_token` (Optional)| This string is used to ensure that only your agents can send data to your APM server. Both the agents and the APM server have to be configured with the same secret token. Use if APM Server requires a token.
24-
| `***` (Optional) | Any additional entries will be applied as a system property appended to `-Delastic.apm.` to allow full configuration of the agent. See [Configuration of Elastic Agent][].
24+
| `***` (Optional) | Any additional entries will be applied as a system property appended to `-Delastic.apm.` to allow full configuration of the agent. See [Configuration of Elastic Agent][]. Values are shell-escaped by default, but do have limited support, use with caution, for incorporating subshells (i.e. `$(some-cmd)`) and accessing environment variables (i.e. `${SOME_VAR}`).
2525

2626

2727
### Creating an Elastic APM USer Provided Service

lib/java_buildpack/framework/elastic_apm_agent.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# See the License for the specific language governing permissions and
1616
# limitations under the License.
1717

18+
require 'shellwords'
1819
require 'java_buildpack/component/versioned_dependency_component'
1920
require 'java_buildpack/framework'
2021

@@ -89,7 +90,12 @@ def apply_user_configuration(credentials, configuration)
8990

9091
def write_java_opts(java_opts, configuration)
9192
configuration.each do |key, value|
92-
java_opts.add_system_property("elastic.apm.#{key}", value)
93+
if /\$[\(\{][^\)\}]+[\)\}]/ =~ value
94+
# we need \" because this is a system property which ends up inside `JAVA_OPTS` which is already quoted
95+
java_opts.add_system_property("elastic.apm.#{key}", "\\\"#{value}\\\"")
96+
else
97+
java_opts.add_system_property("elastic.apm.#{key}", Shellwords.escape(value))
98+
end
9399
end
94100
end
95101

spec/java_buildpack/framework/elastic_apm_agent_spec.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,27 @@
6060
end
6161

6262
it 'updates JAVA_OPTS with additional options' do
63+
val = 'object_name[java.lang:type=Memory] attribute[HeapMemoryUsage:metric_name=test_heap_metric]'
64+
shell = '$(echo \'Hello world!\') and stuff'
65+
var = '--> ${SOME_VAR} <--'
6366
allow(services).to receive(:find_service).and_return('credentials' => { 'secret_token' => 'test-secret_token',
6467
'server_urls' => 'different-serverurl',
6568
'service_name' => 'different-name',
66-
'foo' => 'bar' })
69+
'foo' => 'bar',
70+
'capture_jmx_metrics' => val,
71+
'sub' => shell,
72+
'var' => var })
6773

6874
component.release
6975

7076
expect(java_opts).to include('-Delastic.apm.secret_token=test-secret_token')
7177
expect(java_opts).to include('-Delastic.apm.server_urls=different-serverurl')
7278
expect(java_opts).to include('-Delastic.apm.service_name=different-name')
7379
expect(java_opts).to include('-Delastic.apm.foo=bar')
80+
escaped = 'object_name\[java.lang:type\=Memory\]\ attribute\[HeapMemoryUsage:metric_name\=test_heap_metric\]'
81+
expect(java_opts).to include("-Delastic.apm.capture_jmx_metrics=#{escaped}")
82+
expect(java_opts).to include('-Delastic.apm.sub=\"$(echo \'Hello world!\') and stuff\"')
83+
expect(java_opts).to include('-Delastic.apm.var=\"--> ${SOME_VAR} <--\"')
7484
end
7585

7686
end

0 commit comments

Comments
 (0)