winrm 1.8.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -11
  3. data/.rubocop.yml +26 -22
  4. data/.travis.yml +11 -12
  5. data/Gemfile +3 -9
  6. data/LICENSE +202 -202
  7. data/README.md +232 -215
  8. data/Rakefile +34 -36
  9. data/Vagrantfile +6 -9
  10. data/WinrmAppveyor.psm1 +31 -31
  11. data/appveyor.yml +51 -51
  12. data/bin/rwinrm +97 -97
  13. data/changelog.md +86 -86
  14. data/lib/winrm.rb +39 -42
  15. data/lib/winrm/connection.rb +82 -0
  16. data/lib/winrm/connection_opts.rb +87 -0
  17. data/lib/winrm/{exceptions/exceptions.rb → exceptions.rb} +76 -57
  18. data/lib/winrm/http/response_handler.rb +96 -82
  19. data/lib/winrm/http/transport.rb +424 -435
  20. data/lib/winrm/http/transport_factory.rb +68 -0
  21. data/lib/winrm/output.rb +59 -43
  22. data/lib/winrm/psrp/create_pipeline.xml.erb +167 -0
  23. data/lib/winrm/psrp/fragment.rb +70 -0
  24. data/lib/winrm/psrp/init_runspace_pool.xml.erb +224 -0
  25. data/lib/winrm/psrp/message.rb +130 -0
  26. data/lib/winrm/psrp/message_data.rb +41 -0
  27. data/lib/winrm/psrp/message_data/base.rb +49 -0
  28. data/lib/winrm/psrp/message_data/error_record.rb +68 -0
  29. data/lib/winrm/psrp/message_data/pipeline_host_call.rb +32 -0
  30. data/lib/winrm/psrp/message_data/pipeline_output.rb +49 -0
  31. data/lib/winrm/psrp/message_data/runspacepool_host_call.rb +32 -0
  32. data/lib/winrm/psrp/message_data/runspacepool_state.rb +39 -0
  33. data/lib/winrm/psrp/message_data/session_capability.rb +36 -0
  34. data/lib/winrm/psrp/message_defragmenter.rb +62 -0
  35. data/lib/winrm/psrp/message_factory.rb +75 -0
  36. data/lib/winrm/psrp/message_fragmenter.rb +60 -0
  37. data/lib/winrm/psrp/powershell_output_decoder.rb +120 -0
  38. data/lib/winrm/psrp/receive_response_reader.rb +93 -0
  39. data/lib/winrm/psrp/session_capability.xml.erb +7 -0
  40. data/lib/winrm/psrp/uuid.rb +40 -0
  41. data/lib/winrm/shells/base.rb +175 -0
  42. data/lib/winrm/shells/cmd.rb +65 -0
  43. data/lib/winrm/shells/power_shell.rb +201 -0
  44. data/lib/winrm/shells/retryable.rb +45 -0
  45. data/lib/winrm/shells/shell_factory.rb +50 -0
  46. data/lib/winrm/version.rb +7 -7
  47. data/lib/winrm/wsmv/base.rb +59 -0
  48. data/lib/winrm/wsmv/cleanup_command.rb +61 -0
  49. data/lib/winrm/wsmv/close_shell.rb +50 -0
  50. data/lib/winrm/wsmv/command.rb +101 -0
  51. data/lib/winrm/wsmv/command_output.rb +76 -0
  52. data/lib/winrm/wsmv/command_output_decoder.rb +55 -0
  53. data/lib/winrm/wsmv/configuration.rb +46 -0
  54. data/lib/winrm/wsmv/create_pipeline.rb +66 -0
  55. data/lib/winrm/wsmv/create_shell.rb +119 -0
  56. data/lib/winrm/wsmv/header.rb +203 -0
  57. data/lib/winrm/wsmv/init_runspace_pool.rb +95 -0
  58. data/lib/winrm/wsmv/iso8601_duration.rb +60 -0
  59. data/lib/winrm/wsmv/keep_alive.rb +68 -0
  60. data/lib/winrm/wsmv/receive_response_reader.rb +128 -0
  61. data/lib/winrm/wsmv/send_data.rb +68 -0
  62. data/lib/winrm/wsmv/soap.rb +51 -0
  63. data/lib/winrm/wsmv/wql_query.rb +79 -0
  64. data/lib/winrm/wsmv/write_stdin.rb +88 -0
  65. data/preamble +17 -17
  66. data/{spec → tests/integration}/auth_timeout_spec.rb +18 -16
  67. data/{spec → tests/integration}/cmd_spec.rb +104 -102
  68. data/{spec → tests/integration}/config-example.yml +16 -19
  69. data/{spec → tests/integration}/issue_59_spec.rb +26 -23
  70. data/tests/integration/powershell_spec.rb +154 -0
  71. data/{spec → tests/integration}/spec_helper.rb +65 -73
  72. data/{spec → tests/integration}/transport_spec.rb +99 -139
  73. data/{spec → tests/integration}/wql_spec.rb +16 -14
  74. data/{spec → tests}/matchers.rb +60 -74
  75. data/tests/spec/configuration_spec.rb +93 -0
  76. data/tests/spec/connection_spec.rb +39 -0
  77. data/{spec → tests/spec}/exception_spec.rb +50 -50
  78. data/tests/spec/http/transport_factory_spec.rb +68 -0
  79. data/tests/spec/http/transport_spec.rb +44 -0
  80. data/{spec → tests/spec}/output_spec.rb +127 -110
  81. data/tests/spec/psrp/fragment_spec.rb +62 -0
  82. data/tests/spec/psrp/message_data/base_spec.rb +13 -0
  83. data/tests/spec/psrp/message_data/error_record_spec.rb +41 -0
  84. data/tests/spec/psrp/message_data/pipeline_host_call_spec.rb +25 -0
  85. data/tests/spec/psrp/message_data/pipeline_output_spec.rb +32 -0
  86. data/tests/spec/psrp/message_data/runspace_pool_host_call_spec.rb +25 -0
  87. data/tests/spec/psrp/message_data/runspacepool_state_spec.rb +16 -0
  88. data/tests/spec/psrp/message_data/session_capability_spec.rb +30 -0
  89. data/tests/spec/psrp/message_data_spec.rb +35 -0
  90. data/tests/spec/psrp/message_defragmenter_spec.rb +47 -0
  91. data/tests/spec/psrp/message_fragmenter_spec.rb +105 -0
  92. data/tests/spec/psrp/powershell_output_decoder_spec.rb +84 -0
  93. data/tests/spec/psrp/psrp_message_spec.rb +70 -0
  94. data/tests/spec/psrp/recieve_response_reader_spec.rb +154 -0
  95. data/tests/spec/psrp/uuid_spec.rb +28 -0
  96. data/{spec → tests/spec}/response_handler_spec.rb +61 -61
  97. data/tests/spec/shells/base_spec.rb +202 -0
  98. data/tests/spec/shells/cmd_spec.rb +75 -0
  99. data/tests/spec/shells/powershell_spec.rb +175 -0
  100. data/tests/spec/spec_helper.rb +47 -0
  101. data/tests/spec/stubs/clixml/error_record.xml.erb +84 -0
  102. data/{spec → tests/spec}/stubs/responses/get_command_output_response.xml.erb +13 -13
  103. data/tests/spec/stubs/responses/get_command_output_response_not_done.xml.erb +10 -0
  104. data/tests/spec/stubs/responses/get_powershell_keepalive_response.xml.erb +10 -0
  105. data/tests/spec/stubs/responses/get_powershell_output_response.xml.erb +12 -0
  106. data/tests/spec/stubs/responses/get_powershell_output_response_not_done.xml.erb +9 -0
  107. data/{spec → tests/spec}/stubs/responses/open_shell_v1.xml +19 -19
  108. data/{spec → tests/spec}/stubs/responses/open_shell_v2.xml +20 -20
  109. data/{spec → tests/spec}/stubs/responses/soap_fault_v1.xml +36 -36
  110. data/{spec → tests/spec}/stubs/responses/soap_fault_v2.xml +42 -42
  111. data/{spec → tests/spec}/stubs/responses/wmi_error_v2.xml +41 -41
  112. data/tests/spec/wsmv/cleanup_command_spec.rb +22 -0
  113. data/tests/spec/wsmv/close_shell_spec.rb +17 -0
  114. data/{spec → tests/spec/wsmv}/command_output_decoder_spec.rb +37 -37
  115. data/tests/spec/wsmv/command_output_spec.rb +45 -0
  116. data/tests/spec/wsmv/command_spec.rb +19 -0
  117. data/tests/spec/wsmv/configuration_spec.rb +17 -0
  118. data/tests/spec/wsmv/create_pipeline_spec.rb +31 -0
  119. data/tests/spec/wsmv/create_shell_spec.rb +38 -0
  120. data/tests/spec/wsmv/init_runspace_pool_spec.rb +36 -0
  121. data/tests/spec/wsmv/keep_alive_spec.rb +21 -0
  122. data/tests/spec/wsmv/receive_response_reader_spec.rb +123 -0
  123. data/tests/spec/wsmv/send_data_spec.rb +30 -0
  124. data/tests/spec/wsmv/wql_query_spec.rb +13 -0
  125. data/tests/spec/wsmv/write_stdin_spec.rb +22 -0
  126. data/winrm.gemspec +42 -40
  127. metadata +140 -38
  128. data/.rspec +0 -3
  129. data/lib/winrm/command_executor.rb +0 -243
  130. data/lib/winrm/command_output_decoder.rb +0 -53
  131. data/lib/winrm/helpers/iso8601_duration.rb +0 -58
  132. data/lib/winrm/helpers/powershell_script.rb +0 -42
  133. data/lib/winrm/soap_provider.rb +0 -39
  134. data/lib/winrm/winrm_service.rb +0 -550
  135. data/spec/command_executor_spec.rb +0 -475
  136. data/spec/issue_184_spec.rb +0 -67
  137. data/spec/powershell_spec.rb +0 -97
  138. data/spec/winrm_options_spec.rb +0 -76
  139. data/spec/winrm_primitives_spec.rb +0 -51
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/message_data/base'
4
+ require 'winrm/psrp/message_data/runspacepool_host_call'
5
+
6
+ describe WinRM::PSRP::MessageData::RunspacepoolHostCall do
7
+ let(:raw_data) do
8
+ "\xEF\xBB\xBF<Obj RefId=\"0\"><MS><I64 N=\"ci\">-100</I64><Obj N=\"mi\" RefId=\"1\">"\
9
+ '<TN RefId="0"><T>System.Management.Automation.Remoting.RemoteHostMethodId</T>'\
10
+ '<T>System.Enum</T><T>System.ValueType</T><T>System.Object</T></TN>'\
11
+ '<ToString>WriteLine3</ToString><I32>17</I32></Obj><Obj N="mp" RefId="2">'\
12
+ '<TN RefId="1"><T>System.Collections.ArrayList</T><T>System.Object</T></TN><LST>'\
13
+ '<I32>7</I32><I32>0</I32><S>hello</S></LST></Obj></MS></Obj>'
14
+ end
15
+
16
+ subject { described_class.new(raw_data) }
17
+
18
+ it 'parses method identifier' do
19
+ expect(subject.method_identifier).to eq('WriteLine3')
20
+ end
21
+
22
+ it 'parses method parameters' do
23
+ expect(subject.method_parameters[:s]).to eq('hello')
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/message_data/base'
4
+ require 'winrm/psrp/message_data/runspacepool_state'
5
+
6
+ describe WinRM::PSRP::MessageData::RunspacepoolState do
7
+ let(:raw_data) do
8
+ "\xEF\xBB\xBF<Obj RefId=\"0\"><MS><I32 N=\"RunspaceState\">2</I32></MS></Obj>"
9
+ end
10
+
11
+ subject { described_class.new(raw_data) }
12
+
13
+ it 'parses runspace state' do
14
+ expect(subject.runspace_state).to eq(WinRM::PSRP::MessageData::RunspacepoolState::OPENED)
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/message_data/base'
4
+ require 'winrm/psrp/message_data/session_capability'
5
+
6
+ describe WinRM::PSRP::MessageData::SessionCapability do
7
+ let(:protocol_version) { '2.2' }
8
+ let(:ps_version) { '2.0' }
9
+ let(:serialization_version) { '1.1.0.1' }
10
+ let(:raw_data) do
11
+ "\xEF\xBB\xBF<Obj RefId=\"0\"><MS>"\
12
+ "<Version N=\"protocolversion\">#{protocol_version}</Version>"\
13
+ "<Version N=\"PSVersion\">#{ps_version}</Version>"\
14
+ "<Version N=\"SerializationVersion\">#{serialization_version}</Version></MS></Obj>"
15
+ end
16
+
17
+ subject { described_class.new(raw_data) }
18
+
19
+ it 'parses protocol version' do
20
+ expect(subject.protocol_version).to eq(protocol_version)
21
+ end
22
+
23
+ it 'parses ps version' do
24
+ expect(subject.ps_version).to eq(ps_version)
25
+ end
26
+
27
+ it 'parses serialization version' do
28
+ expect(subject.serialization_version).to eq(serialization_version)
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/message'
4
+ require 'winrm/psrp/message_data'
5
+
6
+ describe WinRM::PSRP::MessageData do
7
+ describe '#parse' do
8
+ let(:raw_data) { 'raw_data' }
9
+ let(:message) do
10
+ WinRM::PSRP::Message.new(
11
+ '00000000-0000-0000-0000-000000000000',
12
+ message_type,
13
+ raw_data
14
+ )
15
+ end
16
+
17
+ subject { WinRM::PSRP::MessageData.parse(message) }
18
+
19
+ context 'defined message type' do
20
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output] }
21
+
22
+ it 'creates correct message data type' do
23
+ expect(subject).to be_a(WinRM::PSRP::MessageData::PipelineOutput)
24
+ end
25
+ end
26
+
27
+ context 'undefined message type' do
28
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_input] }
29
+
30
+ it 'returns nill' do
31
+ expect(subject).to be nil
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/message_defragmenter'
4
+
5
+ describe WinRM::PSRP::MessageDefragmenter do
6
+ context 'a real life fragment' do
7
+ let(:bytes) do
8
+ "\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00I\x01"\
9
+ "\x00\x00\x00\x04\x10\x04\x00Kk/=Z\xD3-E\x81v\xA0+6\xB1\xD3\x88\n\xED\x90\x9Cj\xE7PG"\
10
+ "\x9F\xA2\xB2\xC99to9\xEF\xBB\xBF<S>some data_x000D__x000A_</S>".to_byte_string
11
+ end
12
+ subject { described_class.new.defragment(Base64.encode64(bytes)) }
13
+
14
+ it 'parses the data' do
15
+ expect(subject.data).to eq("\xEF\xBB\xBF<S>some data_x000D__x000A_</S>".to_byte_string)
16
+ end
17
+
18
+ it 'parses the destination' do
19
+ expect(subject.destination).to eq(1)
20
+ end
21
+
22
+ it 'parses the message type' do
23
+ expect(subject.type).to eq(WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output])
24
+ end
25
+ end
26
+
27
+ context 'multiple fragments' do
28
+ let(:blob) do
29
+ WinRM::PSRP::Message.new(
30
+ 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16',
31
+ WinRM::PSRP::Message::MESSAGE_TYPES[:session_capability],
32
+ 'This is a fragmented message'
33
+ )
34
+ end
35
+ let(:fragment1) { WinRM::PSRP::Fragment.new(1, blob.bytes[0..5], 0, true, false) }
36
+ let(:fragment2) { WinRM::PSRP::Fragment.new(1, blob.bytes[6..10], 1, false, false) }
37
+ let(:fragment3) { WinRM::PSRP::Fragment.new(1, blob.bytes[11..-1], 2, false, true) }
38
+
39
+ it 'pieces the message together' do
40
+ subject.defragment(Base64.strict_encode64(fragment1.bytes.pack('C*')))
41
+ subject.defragment(Base64.strict_encode64(fragment2.bytes.pack('C*')))
42
+ message = subject.defragment(Base64.strict_encode64(fragment3.bytes.pack('C*')))
43
+
44
+ expect(message.data[3..-1]).to eq(blob.data)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,105 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/message'
4
+ require 'winrm/psrp/message_fragmenter'
5
+
6
+ describe WinRM::PSRP::MessageFragmenter do
7
+ let(:message) do
8
+ WinRM::PSRP::Message.new(
9
+ 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16',
10
+ WinRM::PSRP::Message::MESSAGE_TYPES[:session_capability],
11
+ data
12
+ )
13
+ end
14
+
15
+ subject do
16
+ fragmenter = described_class.new(45)
17
+ fragments = []
18
+ fragmenter.fragment(message) do |fragment|
19
+ fragments.push(fragment)
20
+ end
21
+ fragments
22
+ end
23
+
24
+ context 'one fragment' do
25
+ let(:data) { 'th' }
26
+
27
+ it 'returns 1 fragment' do
28
+ expect(subject.length).to eq(1)
29
+ end
30
+
31
+ it 'has blob data equal to the message bytes' do
32
+ expect(subject[0].blob.length).to eq(message.bytes.length)
33
+ end
34
+
35
+ it 'identifies the fragment as start and end' do
36
+ expect(subject[0].start_fragment).to eq(true)
37
+ expect(subject[0].end_fragment).to eq(true)
38
+ end
39
+
40
+ it 'assigns fragment id correctly' do
41
+ expect(subject[0].fragment_id).to eq(0)
42
+ end
43
+ end
44
+
45
+ context 'two fragments' do
46
+ let(:data) { 'This is a fragmented message' }
47
+
48
+ it 'splits the message' do
49
+ expect(subject.length).to eq(2)
50
+ end
51
+
52
+ it 'has a sum of blob data equal to the message bytes' do
53
+ expect(subject[0].blob.length + subject[1].blob.length).to eq(message.bytes.length)
54
+ end
55
+
56
+ it 'identifies the first fragment as start and not end' do
57
+ expect(subject[0].start_fragment).to eq(true)
58
+ expect(subject[0].end_fragment).to eq(false)
59
+ end
60
+
61
+ it 'identifies the first fragment as start and not end' do
62
+ expect(subject[1].start_fragment).to eq(false)
63
+ expect(subject[1].end_fragment).to eq(true)
64
+ end
65
+
66
+ it 'assigns incementing fragment ids' do
67
+ expect(subject[0].fragment_id).to eq(0)
68
+ expect(subject[1].fragment_id).to eq(1)
69
+ end
70
+ end
71
+
72
+ context 'three fragments' do
73
+ let(:data) { 'This is a fragmented message because framents are lovely' }
74
+
75
+ it 'splits the message' do
76
+ expect(subject.length).to eq(3)
77
+ end
78
+
79
+ it 'has a sum of blob data equal to the message bytes' do
80
+ expect(subject[0].blob.length + subject[1].blob.length + subject[2].blob.length)
81
+ .to eq(message.bytes.length)
82
+ end
83
+
84
+ it 'identifies the first fragment as start and not end' do
85
+ expect(subject[0].start_fragment).to eq(true)
86
+ expect(subject[0].end_fragment).to eq(false)
87
+ end
88
+
89
+ it 'identifies the first fragment as start and not end' do
90
+ expect(subject[1].start_fragment).to eq(false)
91
+ expect(subject[1].end_fragment).to eq(false)
92
+ end
93
+
94
+ it 'identifies the third fragment as not start and end' do
95
+ expect(subject[2].start_fragment).to eq(false)
96
+ expect(subject[2].end_fragment).to eq(true)
97
+ end
98
+
99
+ it 'assigns incementing fragment ids' do
100
+ expect(subject[0].fragment_id).to eq(0)
101
+ expect(subject[1].fragment_id).to eq(1)
102
+ expect(subject[2].fragment_id).to eq(2)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,84 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/powershell_output_decoder'
4
+
5
+ describe WinRM::PSRP::PowershellOutputDecoder do
6
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:error_record] }
7
+ let(:data) { 'blah' }
8
+ let(:message) do
9
+ WinRM::PSRP::Message.new(
10
+ 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16',
11
+ message_type,
12
+ data,
13
+ '4218a578-0f18-4b19-82c3-46b433319126'
14
+ )
15
+ end
16
+
17
+ subject { described_class.new.decode(message) }
18
+
19
+ context 'undecodable message type' do
20
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_state] }
21
+
22
+ it 'ignores message' do
23
+ expect(subject).to be nil
24
+ end
25
+ end
26
+
27
+ context 'undecodable method identifier' do
28
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_host_call] }
29
+ let(:data) do
30
+ "\xEF\xBB\xBF<Obj RefId=\"0\"><MS><I64 N=\"ci\">-100</I64><Obj N=\"mi\" RefId=\"1\">"\
31
+ '<TN RefId="0"><T>System.Management.Automation.Remoting.RemoteHostMethodId</T>'\
32
+ '<T>System.Enum</T><T>System.ValueType</T><T>System.Object</T></TN>'\
33
+ '<ToString>WriteProgress</ToString><I32>17</I32></Obj><Obj N="mp" RefId="2">'\
34
+ '<TN RefId="1"><T>System.Collections.ArrayList</T><T>System.Object</T></TN><LST>'\
35
+ '<I32>7</I32><I32>0</I32><S>some_x000D__x000A_data</S></LST></Obj></MS></Obj>'
36
+ end
37
+
38
+ it 'ignores message' do
39
+ expect(subject).to be nil
40
+ end
41
+ end
42
+
43
+ context 'receiving pipeline output' do
44
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output] }
45
+ let(:data) { '<obj><S>some data</S></obj>' }
46
+
47
+ it 'decodes output' do
48
+ expect(subject).to eq("some data\r\n")
49
+ end
50
+ end
51
+
52
+ context 'writeline with new line in middle' do
53
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_host_call] }
54
+ let(:data) do
55
+ "\xEF\xBB\xBF<Obj RefId=\"0\"><MS><I64 N=\"ci\">-100</I64><Obj N=\"mi\" RefId=\"1\">"\
56
+ '<TN RefId="0"><T>System.Management.Automation.Remoting.RemoteHostMethodId</T>'\
57
+ '<T>System.Enum</T><T>System.ValueType</T><T>System.Object</T></TN>'\
58
+ '<ToString>WriteLine3</ToString><I32>17</I32></Obj><Obj N="mp" RefId="2">'\
59
+ '<TN RefId="1"><T>System.Collections.ArrayList</T><T>System.Object</T></TN><LST>'\
60
+ '<I32>7</I32><I32>0</I32><S>some_x000D__x000A_data</S></LST></Obj></MS></Obj>'
61
+ end
62
+
63
+ it 'decodes and replaces newline' do
64
+ expect(subject).to eq("some\r\ndata\r\n")
65
+ end
66
+ end
67
+
68
+ context 'receiving error record' do
69
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:error_record] }
70
+ let(:test_data_error_xml_template) do
71
+ ERB.new(stubbed_clixml('error_record.xml.erb'))
72
+ end
73
+ let(:error_message) { 'an error' }
74
+ let(:script_root) { 'script_root' }
75
+ let(:category_message) { 'category message' }
76
+ let(:stack_trace) { 'stack trace' }
77
+ let(:error_id) { 'Microsoft.PowerShell.Commands.WriteErrorException' }
78
+ let(:data) { test_data_error_xml_template.result(binding) }
79
+
80
+ it 'decodes error record' do
81
+ expect(subject).to match(/#{error_message}/)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/message'
4
+
5
+ describe WinRM::PSRP::Message do
6
+ context 'all fields provided' do
7
+ let(:payload) { 'this is my payload' }
8
+ subject do
9
+ described_class.new(
10
+ 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16',
11
+ WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output],
12
+ payload,
13
+ '4218a578-0f18-4b19-82c3-46b433319126')
14
+ end
15
+
16
+ it 'sets the destination to server LE' do
17
+ expect(subject.bytes[0..3]).to eq([2, 0, 0, 0])
18
+ end
19
+ it 'sets the message type LE' do
20
+ expect(subject.bytes[4..7]).to eq([4, 16, 4, 0])
21
+ end
22
+ it 'sets the runspace pool id' do
23
+ expect(subject.bytes[8..23]).to eq(
24
+ [186, 251, 27, 188, 21, 130, 4, 74, 178, 223, 122, 58, 192, 49, 14, 22])
25
+ end
26
+ it 'sets the pipeline id' do
27
+ expect(subject.bytes[24..39]).to eq(
28
+ [120, 165, 24, 66, 24, 15, 25, 75, 130, 195, 70, 180, 51, 49, 145, 38])
29
+ end
30
+ it 'prefixes the blob with BOM' do
31
+ expect(subject.bytes[40..42]).to eq([239, 187, 191])
32
+ end
33
+ it 'contains at least the first 8 bytes of the XML payload' do
34
+ expect(subject.bytes[43..-1]).to eq(payload.bytes)
35
+ end
36
+ it 'parses the data' do
37
+ expect(subject.parsed_data).to be_a(WinRM::PSRP::MessageData::PipelineOutput)
38
+ end
39
+ end
40
+
41
+ context 'create' do
42
+ it 'raises error when message type is not valid' do
43
+ expect do
44
+ WinRM::PSRP::Message.new(
45
+ 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16',
46
+ 0x00000000,
47
+ %(<Obj RefId="0"/>),
48
+ '4218a578-0f18-4b19-82c3-46b433319126')
49
+ end.to raise_error(RuntimeError)
50
+ end
51
+ end
52
+
53
+ context 'no command id' do
54
+ subject(:msg) do
55
+ payload = <<-HERE.unindent
56
+ <Obj RefId="0"><MS><Version N="protocolversion">2.3</Version>
57
+ <Version N="PSVersion">2.0</Version><Version N="SerializationVersion">1.1.0.1</Version></MS>
58
+ </Obj>
59
+ HERE
60
+ WinRM::PSRP::Message.new(
61
+ 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16',
62
+ WinRM::PSRP::Message::MESSAGE_TYPES[:session_capability],
63
+ payload)
64
+ end
65
+
66
+ it 'sets the pipeline id to empty' do
67
+ expect(msg.bytes[24..39]).to eq([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,154 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/psrp/receive_response_reader'
4
+
5
+ describe WinRM::PSRP::ReceiveResponseReader do
6
+ let(:shell_id) { 'F4A2622B-B842-4EB8-8A78-0225C8A993DF' }
7
+ let(:command_id) { 'A2A2622B-B842-4EB8-8A78-0225C8A993DF' }
8
+ let(:output_message) { double('output_message', build: 'output_message') }
9
+ let(:test_data_xml_template) do
10
+ ERB.new(stubbed_response('get_powershell_output_response.xml.erb'))
11
+ end
12
+ let(:test_data_xml_template_not_done) do
13
+ ERB.new(stubbed_response('get_powershell_output_response_not_done.xml.erb'))
14
+ end
15
+ let(:test_data_text) { 'some data' }
16
+ let(:test_data) { "<obj><S>#{test_data_text}</S></obj>" }
17
+ let(:message) do
18
+ WinRM::PSRP::Message.new(
19
+ shell_id,
20
+ message_type,
21
+ test_data,
22
+ command_id
23
+ )
24
+ end
25
+ let(:fragment) { WinRM::PSRP::Fragment.new(1, message.bytes) }
26
+ let(:test_data_stdout) { Base64.strict_encode64(fragment.bytes.pack('C*')) }
27
+ let(:transport) { {} }
28
+
29
+ before do
30
+ allow(transport).to receive(:send_request).and_return(
31
+ REXML::Document.new(test_data_xml_template.result(binding))
32
+ )
33
+ end
34
+
35
+ subject do
36
+ described_class.new(
37
+ transport,
38
+ Logging.logger['test']
39
+ )
40
+ end
41
+
42
+ describe '#read_output' do
43
+ context 'response doc stdout with pipeline output' do
44
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output] }
45
+
46
+ it 'outputs to stdout' do
47
+ expect(
48
+ subject.read_output(output_message).stdout
49
+ ).to eq("#{test_data_text}\r\n")
50
+ end
51
+ end
52
+
53
+ context 'response doc stdout error record' do
54
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:error_record] }
55
+ let(:test_data_error_xml_template) do
56
+ ERB.new(stubbed_clixml('error_record.xml.erb'))
57
+ end
58
+ let(:error_message) { 'an error' }
59
+ let(:script_root) { 'script_root' }
60
+ let(:category_message) { 'category message' }
61
+ let(:stack_trace) { 'stack trace' }
62
+ let(:error_id) { 'Microsoft.PowerShell.Commands.WriteErrorException' }
63
+ let(:test_data) { test_data_error_xml_template.result(binding) }
64
+
65
+ it 'outputs to stderr' do
66
+ expect(
67
+ subject.read_output(output_message).stderr
68
+ ).to match(/#{error_message}/)
69
+ end
70
+ end
71
+
72
+ context 'response doc writing error to host' do
73
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_host_call] }
74
+ let(:test_data) do
75
+ "<Obj RefId='0'><MS><I64 N='ci'>-100</I64><Obj N='mi' RefId='1'><TN RefId='0'>" \
76
+ '<T>System.Management.Automation.Remoting.RemoteHostMethodId</T><T>System.Enum</T>' \
77
+ '<T>System.ValueType</T><T>System.Object</T></TN><ToString>WriteErrorLine</ToString>' \
78
+ "<I32>18</I32></Obj><Obj N='mp' RefId='2'><TN RefId='1'>" \
79
+ '<T>System.Collections.ArrayList</T><T>System.Object</T></TN><LST><S>errors</S></LST>' \
80
+ '</Obj></MS></Obj>'
81
+ end
82
+
83
+ it 'outputs to stderr' do
84
+ expect(
85
+ subject.read_output(output_message).stderr
86
+ ).to eq("errors\r\n")
87
+ end
88
+ end
89
+
90
+ context 'response doc writing output to host' do
91
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_host_call] }
92
+ let(:test_data) do
93
+ "<Obj RefId='0'><MS><I64 N='ci'>-100</I64><Obj N='mi' RefId='1'><TN RefId='0'>" \
94
+ '<T>System.Management.Automation.Remoting.RemoteHostMethodId</T><T>System.Enum</T>' \
95
+ '<T>System.ValueType</T><T>System.Object</T></TN><ToString>WriteLine</ToString>' \
96
+ "<I32>18</I32></Obj><Obj N='mp' RefId='2'><TN RefId='1'>" \
97
+ '<T>System.Collections.ArrayList</T><T>System.Object</T></TN><LST><S>output</S></LST>' \
98
+ '</Obj></MS></Obj>'
99
+ end
100
+
101
+ it 'outputs to stdout' do
102
+ expect(
103
+ subject.read_output(output_message).stdout
104
+ ).to eq("output\r\n")
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '#read_message' do
110
+ context 'do not wait for done state' do
111
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output] }
112
+
113
+ it 'outputs the message in an array' do
114
+ expect(subject.read_message(output_message).length).to eq(1)
115
+ expect(subject.read_message(output_message)[0].data).to end_with(message.data)
116
+ end
117
+ end
118
+
119
+ context 'read in a block' do
120
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output] }
121
+
122
+ it 'outputs the message in an array' do
123
+ subject.read_message(output_message) do |msg|
124
+ expect(msg.data).to end_with(message.data)
125
+ end
126
+ end
127
+ end
128
+
129
+ context 'wait for done state' do
130
+ let(:message_type) { WinRM::PSRP::Message::MESSAGE_TYPES[:pipeline_output] }
131
+ let(:test_data_xml_not_done) { test_data_xml_template_not_done.result(binding) }
132
+ let(:test_data_xml_done) { test_data_xml_template.result(binding) }
133
+
134
+ before do
135
+ allow(transport).to receive(:send_request).and_return(
136
+ REXML::Document.new(test_data_xml_not_done),
137
+ REXML::Document.new(test_data_xml_done)
138
+ )
139
+ end
140
+
141
+ it 'outputs two messages' do
142
+ expect(subject.read_message(output_message, true).length).to eq(2)
143
+ end
144
+
145
+ it 'outputs the first message' do
146
+ expect(subject.read_message(output_message, true)[0].data).to end_with(message.data)
147
+ end
148
+
149
+ it 'outputs the second message' do
150
+ expect(subject.read_message(output_message, true)[1].data).to end_with(message.data)
151
+ end
152
+ end
153
+ end
154
+ end