Skip to content

Commit ba24874

Browse files
author
Daniel Mikusa
authored
Relax escaping of values to enable limited access to the shell for AppDynamnics config (#911)
[Previous PRs escaped configuration values for AppD](#870). This was done to support things like names with spaces and other characters that, if used, would result in a broken start command. The PR broke a use case which was documented for the node or tier name. In some cases, you may want to set the node or tier name to a dynamic value that is loaded at runtime so that you can incorporate things like the application instance index. For example `DCX:$(echo $VCAP_APPLICATION | jq -r '.application_name'):$(echo $VCAP_APPLICATION | jq -r '.instance_index')`. This new PR will use `Shellwords.escape(..)` on the value for all properties 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 but 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: - `DCX:$(echo $VCAP_APPLICATION | jq -r '.application_name'):$(echo $VCAP_APPLICATION | jq -r '.instance_index')` - `$(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 837e45e commit ba24874

File tree

3 files changed

+58
-10
lines changed

3 files changed

+58
-10
lines changed

docs/framework-app_dynamics_agent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ When binding AppDynamics using a user-provided service, it must have name or tag
2626
| `node-name` | (Optional) the application's node name
2727
| `tier-name` | (Optional) the application's tier name
2828

29-
To provide more complex values such as the `tier-name`, using the interactive mode when creating a user-provided service will manage the character escaping automatically. For example, the default `tier-name` could be set with a value of `Tier-$(expr "$VCAP_APPLICATION" : '.*instance_index[": ]*\([[:digit:]]*\).*')` to calculate a value from the Cloud Foundry instance index.
29+
To provide more complex values such as the `tier-name`, using the interactive mode when creating a user-provided service will manage the character escaping automatically. For example, the default `tier-name` could be set with a value of `Tier-$(expr "${VCAP_APPLICATION}" : '.*instance_index[": ]*\([[:digit:]]*\).*')` to calculate a value from the Cloud Foundry instance index.
3030

3131
**Note:** Some credentials were previously marked as "(Optional)" as requirements have changed across versions of the AppDynamics agent. Please see the [AppDynamics Java Agent Configuration Properties][] for the version of the agent used by your application for more details.
3232

lib/java_buildpack/framework/app_dynamics_agent.rb

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,35 +86,35 @@ def supports?
8686
private_constant :FILTER
8787

8888
def application_name(java_opts, credentials)
89-
name = Shellwords.escape(@application.details['application_name'])
89+
name = escape(@application.details['application_name'])
9090
name = @configuration['default_application_name'] if @configuration['default_application_name']
91-
name = Shellwords.escape(credentials['application-name']) if credentials['application-name']
91+
name = escape(credentials['application-name']) if credentials['application-name']
9292

9393
java_opts.add_system_property('appdynamics.agent.applicationName', name.to_s)
9494
end
9595

9696
def account_access_key(java_opts, credentials)
9797
account_access_key = credentials['account-access-key'] || credentials.dig('account-access-secret', 'secret')
98-
account_access_key = Shellwords.escape(account_access_key)
98+
account_access_key = escape(account_access_key)
9999

100100
java_opts.add_system_property 'appdynamics.agent.accountAccessKey', account_access_key if account_access_key
101101
end
102102

103103
def account_name(java_opts, credentials)
104104
account_name = credentials['account-name']
105-
java_opts.add_system_property 'appdynamics.agent.accountName', Shellwords.escape(account_name) if account_name
105+
java_opts.add_system_property 'appdynamics.agent.accountName', escape(account_name) if account_name
106106
end
107107

108108
def host_name(java_opts, credentials)
109109
host_name = credentials['host-name']
110110
raise "'host-name' credential must be set" unless host_name
111111

112-
java_opts.add_system_property 'appdynamics.controller.hostName', Shellwords.escape(host_name)
112+
java_opts.add_system_property 'appdynamics.controller.hostName', escape(host_name)
113113
end
114114

115115
def node_name(java_opts, credentials)
116116
name = @configuration['default_node_name']
117-
name = Shellwords.escape(credentials['node-name']) if credentials['node-name']
117+
name = escape(credentials['node-name']) if credentials['node-name']
118118

119119
java_opts.add_system_property('appdynamics.agent.nodeName', name.to_s)
120120
end
@@ -130,15 +130,15 @@ def ssl_enabled(java_opts, credentials)
130130
end
131131

132132
def tier_name(java_opts, credentials)
133-
name = Shellwords.escape(@application.details['application_name'])
133+
name = escape(@application.details['application_name'])
134134
name = @configuration['default_tier_name'] if @configuration['default_tier_name']
135-
name = Shellwords.escape(credentials['tier-name']) if credentials['tier-name']
135+
name = escape(credentials['tier-name']) if credentials['tier-name']
136136

137137
java_opts.add_system_property('appdynamics.agent.tierName', name.to_s)
138138
end
139139

140140
def unique_host_name(java_opts)
141-
name = Shellwords.escape(@application.details['application_name'])
141+
name = escape(@application.details['application_name'])
142142
name = @configuration['default_unique_host_name'] if @configuration['default_unique_host_name']
143143

144144
java_opts.add_system_property('appdynamics.agent.uniqueHostId', name.to_s)
@@ -180,6 +180,14 @@ def save_cfg_file(file, conf_file)
180180
FileUtils.cp_r file, target_directory + '/conf/' + conf_file
181181
end
182182
end
183+
184+
def escape(value)
185+
if /\$[\(\{][^\)\}]+[\)\}]/ =~ value
186+
"\\\"#{value}\\\""
187+
else
188+
Shellwords.escape(value)
189+
end
190+
end
183191
end
184192
end
185193
end

spec/java_buildpack/framework/app_dynamics_agent_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@
9292
end
9393
end
9494

95+
context do
96+
let(:credentials) { super().merge 'tier-name' => '--> ${SOME_VAR} <--' }
97+
98+
it 'adds tier_name from credentials with shell variable in it to JAVA_OPTS if specified' do
99+
component.release
100+
101+
expect(java_opts).to include('-Dappdynamics.agent.tierName=\"--> ${SOME_VAR} <--\"')
102+
end
103+
end
104+
105+
context do
106+
let(:credentials) { super().merge 'tier-name' => '$(echo \'Hello World!\') and stuff' }
107+
108+
it 'adds tier_name from credentials with subshell in it to JAVA_OPTS if specified' do
109+
component.release
110+
111+
expect(java_opts).to include('-Dappdynamics.agent.tierName=\"$(echo \'Hello World!\') and stuff\"')
112+
end
113+
end
114+
95115
context do
96116
let(:credentials) { super().merge 'application-name' => 'another-test application-name' }
97117

@@ -102,6 +122,26 @@
102122
end
103123
end
104124

125+
context do
126+
let(:credentials) { super().merge 'application-name' => '$(echo \'Hello World!\') and stuff' }
127+
128+
it 'adds application_name from credentials with subshell in value to JAVA_OPTS if specified' do
129+
component.release
130+
131+
expect(java_opts).to include('-Dappdynamics.agent.applicationName=\"$(echo \'Hello World!\') and stuff\"')
132+
end
133+
end
134+
135+
context do
136+
let(:credentials) { super().merge 'application-name' => 'Name ${MY_APP_NAME}' }
137+
138+
it 'adds application_name from credentials with env variable in value to JAVA_OPTS if specified' do
139+
component.release
140+
141+
expect(java_opts).to include('-Dappdynamics.agent.applicationName=\"Name ${MY_APP_NAME}\"')
142+
end
143+
end
144+
105145
context do
106146
let(:configuration) do
107147
{ 'default_tier_name' => nil,

0 commit comments

Comments
 (0)