winrm 1.8.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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