Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to send commands containing apostrophe characters #69

Closed
kashook opened this issue Jun 13, 2014 · 8 comments
Closed

Unable to send commands containing apostrophe characters #69

kashook opened this issue Jun 13, 2014 · 8 comments

Comments

@kashook
Copy link

kashook commented Jun 13, 2014

It doesn't appear to be possible to use the winrm gem to send any commands that contain an apostrophe character.

For example, this script:

require 'winrm'

endpoint = 'http://localhost:57985/wsman'
opts = { :user => 'vagrant', :pass => 'vagrant', :operation_timeout => 120, :basic_auth_only => true }

client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, opts)

client.cmd(%q{echo 'hello world'}) do |stdout, stderr|
  STDOUT.print stdout
  STDERR.print stderr
end

prints the following output:

'#39' is not recognized as an internal or external command,
ECHO is on.
operable program or batch file.
'#39' is not recognized as an internal or external command,
operable program or batch file.

(Note that this command is just a sample to demonstrate the problem. The echo command doesn't need the single quotes, but there are commands that do, such as a for loop that is processing the output of a command).

If you directly run the same command in a command prompt, or if you use winrs to send the command to the server, then the command works fine.

For example:

C:\>winrs -r:http://localhost:57985 -u:vagrant -p:vagrant "echo 'hello world'"

Produces:

'hello world'

I did a little bit of digging, and the problem appears to be that the winrm web service doesn't like the ' (despite the fact that it is perfectly valid xml). To prove it, I first enabled the analytic log as described here. In the log, you can see the xml payload that was received by the server. Here is a snippet of one of the log messages that shows what the server actually received from the example script:

<rsp:Command>&quot;echo &#39;hello world&#39;&quot;</rsp:Command>

If you enter the command echo &#39;hello world&#39; at a command prompt in windows, you get the exact same output that was displayed by the above ruby script.

When looking at the analytic log after using winrs to send the same command, I noticed that it escaped the apostrophe characters by using &apos; instead of &#39;. If I modify this line in my locally installed copy of the winrm gem to replace any &#39; characters with &apos; and then run the sample script again, I get the expected output. (I considered just sending a pull request that simply does the replace on that line, but I'm not sure it's the best solution to the problem. If someone were legitimately trying to send the text &#39; as part of some command, it would be replaced with &apos;).

I noticed that there is a "working" branch in this repository that appears to have pretty drastically changed how the soap messages get built. I repeated the test using the code from the working branch, but it appears that it also sends &#39; instead of &apos;.

The server I was sending winrm commands to was running Windows Server 2008 R2 SP1.

@rinat-io
Copy link

Try this: 'echo \'Hello World!\'' or "echo \'Hello World!\'"
Both worked for me just fine.

In some cases to run Windows DOS and Powershell commands using WInRM can get really complicated, but it works great for all the cases I have tried so far.

@kashook
Copy link
Author

kashook commented Jun 16, 2014

@rinat-io, The command syntax can indeed get tricky! :) I have no problem when using winrs, but only with the winrm gem. The event log output in my original post shows that the xml that is received by the server contains &#39; when the commands are sent using the winrm gem, and the output I receive from running the commands shows that the &#39; is being interpreted literally by the server instead of as an apostrophe. Furthermore, if I hack the winrm gem to send &apos; instead of &#39, then it suddenly works just fine. No amount of "escaping" the apostrophe characters seems to work, because the command is literally being executed with &#39; as part of the command text.

I'm curious, what version of Windows did you send your example commands to? Also, would you mind posting a small snippet of your ruby script where you got this to work? I may have misinterpreted something in your post, but I cannot get the commands you posted to work for me in any form, whether I directly enter them at a command prompt, send them to a server with winrs, or send them to a server with the winrm gem. The only form that did execute actually echoed the slash characters, which is not what I want.

@rinat-io
Copy link

@keiths-osc , yes it is very tricky and needs some time to get use to it.
I'm using WinRM very extensively for my daily work and I also developed "WinRM R&D" module that I use to try things out before adding them into the mainstream libraries. I cannot post the whole "WinRM R&D" module here just because does too many other things I work on and reader will need to spend time to get an idea on the logic, so I put together this snippet to help you out and that can hopefully give you some idea:

$ cat test.rb 
require 'winrm'

myuser = 'my_user'
mypass = 'my_password'
hostname = '172.16.0.14'
endpoint = 'http://' + hostname + ':5985/wsman'

winrm = WinRM::WinRMWebService.new(endpoint, :plaintext, :user => myuser, :pass => mypass, :basic_auth_only => true)
command = "echo \'Hello World!\'"

result = winrm.cmd(command)
if result[:exitcode] != 0
  result[:data].each do |output_line|
    if output_line.has_key?(:stderr)
      STDOUT.print output_line[:stderr]
    end
  end
else
  result[:data].each do |output_line|
    if output_line.has_key?(:stdout)
      STDOUT.print output_line[:stdout]
    end
  end
end

Result

$ ruby test.rb 
'Hello World!'

I run framework that has WinRM gem integrated into it with both Windows Server 2008 & Windows Server 2012 and it works fine. There were some minor differences between those two OS types, but I have added some exceptions handling in my libraries and have forgotten when I did RDP to my hosts for those tasks that have been automated :)
There is certainly space for improvements in WinRM, but those can be worked around through other means if needed. When I started integration of this package into our test automation framework there were cases that looked very odd, but with a little bit for persistence and experiment I got though most of my use cases just fine.
It is in my plan to put some details together for this module, but first I need to make some time for that.

@kashook
Copy link
Author

kashook commented Jun 16, 2014

@rinat-io, Thanks for posting your example. When I run your example (with the correct hostname, port, etc), I get the exact same error that I originally posted. I believe that the command text in your example is identical to my original example. The only difference between your example and mine is a bit of ruby syntax.

For example, this script:

# These are all equivalent
a = "echo \'hello world\'"
b = %q{echo 'hello world'}
c = "echo 'hello world'"

puts "a = " + a
puts "b = " + b
puts "c = " + c

if a == b && b == c
  puts "They are equal!"
else
  puts "They are not equal"
end

produces this output:

a = echo 'hello world'
b = echo 'hello world'
c = echo 'hello world'
They are equal!

The slash characters are not actually in the string you are sending to the server, and if you remove them from your example, you should find that it makes no difference.

There are two possibilities I see for why you aren't getting the same error as me: 1) You are using a different version of the winrm gem or one of its dependencies which actually sends &apos; instead of &#39; , or 2) there is a windows update that actually fixes the issue in windows so that it correctly interprets the &#39; (although my server has all current windows updates installed and I still get the same error). Would you mind running gem list and pasting the output? (Feel free to remove any proprietary libraries from the list). Another piece of information that would be really helpful if you are willing to do it would be to enable the windows remote management analytic log (as described here) and see if the command text contains &apos; or &#39;. Thanks again!

@rinat-io
Copy link

@keiths-osc it is very interesting, to say the least :)
Here is WinRM gem information that I have been using:

$ gem list winrm
*** LOCAL GEMS ***
winrm (1.1.3)

And I'm running it with Ruby 1.9.3 on Linux Ubuntu 12.04 LTS

$ ruby --version
ruby 1.9.3p374 (2013-01-15 revision 38858) [x86_64-linux]

I just run the same snippet with Windows Server 2012 and here how that command looks like in analytic log:

SOAP [listener receiving index 2 of 2 total chunks (530 bytes)] n></w:OptionSet><w:SelectorSet><w:Selector Name="ShellId">6D3BD597-B92A-42BB-B989-7EF39ABF0773</w:Selector></w:SelectorSet></env:Header><env:Body><rsp:CommandLine><rsp:Command>&quot;echo 'Hello World!'&quot;</rsp:Command></rsp:CommandLine></env:Body></env:Envelope>

I hope that helps.

@kashook
Copy link
Author

kashook commented Jun 17, 2014

@rinat-io, that was extremely helpful! The analytic log shows that the apostrophe characters were not encoded at all. I think have determined what's going on. The winrm gem uses gyoku (via savon) to build the xml strings that ultimately get sent to the server. Gyoko, on this line, uses ruby's CGI.escapeHTML function to encode the special characters. You are running Ruby 1.9.3, and I am running 2.0. It turns out that the behavior of the CGI.escapeHTML function changed from Ruby 1.9.3 to Ruby 2.0. Here is an example.

This script:

require 'cgi'
string = %q{<>&"'}
puts "Ruby Version: #{RUBY_VERSION}"
puts "Original: #{string}"
puts "Encoded: #{CGI.escapeHTML(string)}"

produces this output on Ruby 1.9.3:

Ruby Version: 1.9.3
Original: <>&"'
Encoded: &lt;&gt;&amp;&quot;'

but this output on Ruby 2.0:

Ruby Version: 2.1.1
Original: <>&"'
Encoded: &lt;&gt;&amp;&quot;&#39;

So, it appears that this issue will only manifest itself on Ruby 2.0 or higher.

@rinat-io
Copy link

You are welcome @keiths-osc and thank you very much for figuring out all the differences.

@sneal
Copy link
Member

sneal commented Jul 8, 2014

Fixed by #73

@sneal sneal closed this as completed Jul 8, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants