WinRb / WinRM

Ruby library for Windows Remote Management
Apache License 2.0
412 stars 117 forks source link

Undefined method `elements' for nil:NilClass on 'Invoke-Command' #279

Closed gettalong closed 4 years ago

gettalong commented 6 years ago

Thanks for developing this library!

I was just testing it whether it would be of use in our environment and found the following bug:

Given the following script:

require 'winrm'
conn = WinRM::Connection.new(endpoint: 'http://HOST:5985/wsman', user: 'USERNAME', password: 'PASSWORD')
conn.shell(:powershell) {|shell| shell.run('Invoke-Command HOST {Get-Service LanManServer}') {|so, se| p [so, se]}}

The following Ruby error is thrown:

NoMethodError: undefined method `elements' for nil:NilClass
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/message_data/error_record.rb:53:in `property_hash'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/message_data/error_record.rb:31:in `invocation_info'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/powershell_output_decoder.rb:106:in `render_exception'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/powershell_output_decoder.rb:87:in `decode_error_record'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/powershell_output_decoder.rb:39:in `decode'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/receive_response_reader.rb:63:in `block (2 levels) in read_output'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/receive_response_reader.rb:45:in `block in read_message'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/wsmv/receive_response_reader.rb:66:in `block in read_response'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/wsmv/receive_response_reader.rb:121:in `block in read_streams'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/wsmv/receive_response_reader.rb:119:in `each'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/wsmv/receive_response_reader.rb:119:in `read_streams'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/wsmv/receive_response_reader.rb:65:in `read_response'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/receive_response_reader.rb:41:in `read_message'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/receive_response_reader.rb:60:in `block in read_output'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/wsmv/receive_response_reader.rb:75:in `with_output'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/psrp/receive_response_reader.rb:59:in `read_output'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/shells/base.rb:82:in `block in run'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/shells/base.rb:132:in `with_command_shell'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/shells/base.rb:81:in `run'
        from (irb):1:in `block in irb_binding'
        from /home/thomas/.rvm/gems/ruby-2.4.2/gems/winrm-2.2.3/lib/winrm/connection.rb:44:in `shell'
        from (irb):1
        from /home/thomas/.rvm/rubies/ruby-2.4.2/bin/irb:11:in `<main>'

On investigating I found that WinRM::PSRP::MessageData::ErrorRecord#doc has the following value:

<?xml version="1.0"?>
<Obj RefId="0">
  <TN RefId="0">
    <T>System.Management.Automation.ErrorRecord</T>
    <T>System.Object</T>
  </TN>
  <ToString>[HOST] Connecting to remote server HOST failed with the following error message : Zugriff verweigert For more information, see the about_Remote_Troubleshooting Help topic.</ToString>
  <MS>
    <B N="writeErrorStream">true</B>
    <Obj N="Exception" RefId="1">
      <TN RefId="1">
        <T>System.Management.Automation.Remoting.PSRemotingTransportException</T>
        <T>System.Management.Automation.RuntimeException</T>
        <T>System.SystemException</T>
        <T>System.Exception</T>
        <T>System.Object</T>
      </TN>
      <ToString>System.Management.Automation.Remoting.PSRemotingTransportException: Connecting to remote server HOST failed with the following error message : Zugriff verweigert For more information, see the about_Remote_Troubleshooting Help topic.</ToString>
      <Props>
        <I32 N="ErrorCode">5</I32>
        <S N="TransportMessage">Zugriff verweigert _x0000_</S>
        <S N="ErrorRecord">Connecting to remote server HOST failed with the following error message : Zugriff verweigert For more information, see the about_Remote_Troubleshooting Help topic.</S>
        <B N="WasThrownFromThrowStatement">false</B>
        <S N="Message">Connecting to remote server HOST failed with the following error message : Zugriff verweigert For more information, see the about_Remote_Troubleshooting Help topic.</S>
        <Obj N="Data" RefId="2">
          <TN RefId="2">
            <T>System.Collections.ListDictionaryInternal</T>
            <T>System.Object</T>
          </TN>
          <DCT/>
        </Obj>
        <Nil N="InnerException"/>
        <Nil N="TargetSite"/>
        <Nil N="StackTrace"/>
        <Nil N="HelpLink"/>
        <Nil N="Source"/>
        <I32 N="HResult">-2146233087</I32>
      </Props>
    </Obj>
    <S N="TargetObject">HOST</S>
    <S N="FullyQualifiedErrorId">AccessDenied,PSSessionStateBroken</S>
    <Nil N="InvocationInfo"/>
    <I32 N="ErrorCategory_Category">1</I32>
    <S N="ErrorCategory_Activity"/>
    <S N="ErrorCategory_Reason">PSRemotingTransportException</S>
    <S N="ErrorCategory_TargetName">HOST</S>
    <S N="ErrorCategory_TargetType">String</S>
    <S N="ErrorCategory_Message">OpenError: (HOST:String) [], PSRemotingTransportException</S>
    <S N="ErrorDetails_Message">[HOST] Connecting to remote server HOST failed with the following error message : Zugriff verweigert For more information, see the about_Remote_Troubleshooting Help topic.</S>
    <S N="ErrorDetails_RecommendedAction"/>
    <B N="SerializeExtendedInfo">false</B>
    <Nil N="PSMessageDetails"/>
  </MS>
</Obj>

It seems that the ErrorRecord class can't currently handle this kind of situation and raises an exception. However, even if the access is denied, it shouldn't result in an exception.

mwrock commented 6 years ago

Yeah definitely looks like you hit a scenario where the error record parser cant handle the PSRP message its passed.

However an access denied will certainly produce an exception. It just needs to be parsed correctly so that the user gets a legible message. We probably just need to do a nil check on the Invocation Info and handle cases where that is empty.

To work around this, you might try ommiting the HOST arg and just pass the script block since you are invoking the command on the same host.

gettalong commented 6 years ago

Thanks for getting back and for the work around!

donoghuc commented 4 years ago

This appears to have been fixed in https://github.com/WinRb/WinRM/commit/d7ecaccdf956e998b8fc61d077cd9bdd44f8f898 . But the patch is not included in a release.

mwrock commented 4 years ago

That commit is now released in v2.3.4