winrm 2.0.3 → 2.1.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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -10
  3. data/.rubocop.yml +26 -26
  4. data/.travis.yml +11 -11
  5. data/Gemfile +3 -3
  6. data/README.md +260 -233
  7. data/Rakefile +34 -34
  8. data/Vagrantfile +6 -6
  9. data/WinrmAppveyor.psm1 +31 -31
  10. data/appveyor.yml +51 -51
  11. data/changelog.md +104 -101
  12. data/lib/winrm.rb +39 -39
  13. data/lib/winrm/connection.rb +83 -82
  14. data/lib/winrm/connection_opts.rb +91 -91
  15. data/lib/winrm/exceptions.rb +76 -76
  16. data/lib/winrm/http/response_handler.rb +96 -96
  17. data/lib/winrm/http/transport.rb +424 -424
  18. data/lib/winrm/http/transport_factory.rb +68 -68
  19. data/lib/winrm/output.rb +59 -59
  20. data/lib/winrm/psrp/create_pipeline.xml.erb +167 -167
  21. data/lib/winrm/psrp/fragment.rb +70 -70
  22. data/lib/winrm/psrp/init_runspace_pool.xml.erb +224 -224
  23. data/lib/winrm/psrp/message.rb +130 -130
  24. data/lib/winrm/psrp/message_data.rb +42 -42
  25. data/lib/winrm/psrp/message_data/base.rb +49 -49
  26. data/lib/winrm/psrp/message_data/error_record.rb +68 -68
  27. data/lib/winrm/psrp/message_data/pipeline_host_call.rb +32 -32
  28. data/lib/winrm/psrp/message_data/pipeline_output.rb +49 -49
  29. data/lib/winrm/psrp/message_data/pipeline_state.rb +40 -40
  30. data/lib/winrm/psrp/message_data/runspacepool_host_call.rb +32 -32
  31. data/lib/winrm/psrp/message_data/runspacepool_state.rb +39 -39
  32. data/lib/winrm/psrp/message_data/session_capability.rb +36 -36
  33. data/lib/winrm/psrp/message_defragmenter.rb +62 -62
  34. data/lib/winrm/psrp/message_factory.rb +75 -75
  35. data/lib/winrm/psrp/message_fragmenter.rb +60 -60
  36. data/lib/winrm/psrp/powershell_output_decoder.rb +139 -139
  37. data/lib/winrm/psrp/receive_response_reader.rb +97 -97
  38. data/lib/winrm/psrp/session_capability.xml.erb +7 -7
  39. data/lib/winrm/psrp/uuid.rb +40 -40
  40. data/lib/winrm/shells/base.rb +180 -175
  41. data/lib/winrm/shells/cmd.rb +65 -65
  42. data/lib/winrm/shells/power_shell.rb +202 -202
  43. data/lib/winrm/shells/retryable.rb +45 -45
  44. data/lib/winrm/shells/shell_factory.rb +58 -50
  45. data/lib/winrm/version.rb +7 -7
  46. data/lib/winrm/wsmv/base.rb +59 -59
  47. data/lib/winrm/wsmv/cleanup_command.rb +61 -61
  48. data/lib/winrm/wsmv/close_shell.rb +50 -50
  49. data/lib/winrm/wsmv/command.rb +101 -101
  50. data/lib/winrm/wsmv/command_output.rb +76 -76
  51. data/lib/winrm/wsmv/command_output_decoder.rb +55 -55
  52. data/lib/winrm/wsmv/configuration.rb +46 -46
  53. data/lib/winrm/wsmv/create_pipeline.rb +66 -66
  54. data/lib/winrm/wsmv/create_shell.rb +119 -119
  55. data/lib/winrm/wsmv/header.rb +203 -203
  56. data/lib/winrm/wsmv/init_runspace_pool.rb +95 -95
  57. data/lib/winrm/wsmv/iso8601_duration.rb +60 -60
  58. data/lib/winrm/wsmv/keep_alive.rb +68 -68
  59. data/lib/winrm/wsmv/receive_response_reader.rb +126 -126
  60. data/lib/winrm/wsmv/send_data.rb +68 -68
  61. data/lib/winrm/wsmv/soap.rb +51 -51
  62. data/lib/winrm/wsmv/wql_query.rb +79 -79
  63. data/lib/winrm/wsmv/write_stdin.rb +88 -88
  64. data/tests/integration/auth_timeout_spec.rb +18 -18
  65. data/tests/integration/cmd_spec.rb +131 -110
  66. data/tests/integration/config-example.yml +16 -16
  67. data/tests/integration/issue_59_spec.rb +26 -26
  68. data/tests/integration/powershell_spec.rb +165 -165
  69. data/tests/integration/spec_helper.rb +65 -65
  70. data/tests/integration/transport_spec.rb +99 -99
  71. data/tests/integration/wql_spec.rb +16 -16
  72. data/tests/matchers.rb +60 -60
  73. data/tests/spec/configuration_spec.rb +184 -184
  74. data/tests/spec/connection_spec.rb +39 -39
  75. data/tests/spec/exception_spec.rb +50 -50
  76. data/tests/spec/http/transport_factory_spec.rb +68 -68
  77. data/tests/spec/http/transport_spec.rb +44 -44
  78. data/tests/spec/output_spec.rb +127 -127
  79. data/tests/spec/psrp/fragment_spec.rb +62 -62
  80. data/tests/spec/psrp/message_data/base_spec.rb +13 -13
  81. data/tests/spec/psrp/message_data/error_record_spec.rb +41 -41
  82. data/tests/spec/psrp/message_data/pipeline_host_call_spec.rb +25 -25
  83. data/tests/spec/psrp/message_data/pipeline_output_spec.rb +32 -32
  84. data/tests/spec/psrp/message_data/pipeline_state_spec.rb +40 -40
  85. data/tests/spec/psrp/message_data/runspace_pool_host_call_spec.rb +25 -25
  86. data/tests/spec/psrp/message_data/runspacepool_state_spec.rb +16 -16
  87. data/tests/spec/psrp/message_data/session_capability_spec.rb +30 -30
  88. data/tests/spec/psrp/message_data_spec.rb +35 -35
  89. data/tests/spec/psrp/message_defragmenter_spec.rb +47 -47
  90. data/tests/spec/psrp/message_fragmenter_spec.rb +105 -105
  91. data/tests/spec/psrp/powershell_output_decoder_spec.rb +100 -100
  92. data/tests/spec/psrp/psrp_message_spec.rb +70 -70
  93. data/tests/spec/psrp/recieve_response_reader_spec.rb +172 -172
  94. data/tests/spec/psrp/uuid_spec.rb +28 -28
  95. data/tests/spec/response_handler_spec.rb +61 -61
  96. data/tests/spec/shells/base_spec.rb +202 -202
  97. data/tests/spec/shells/cmd_spec.rb +75 -75
  98. data/tests/spec/shells/powershell_spec.rb +175 -175
  99. data/tests/spec/spec_helper.rb +47 -47
  100. data/tests/spec/stubs/clixml/error_record.xml.erb +84 -84
  101. data/tests/spec/stubs/clixml/pipeline_state.xml.erb +88 -88
  102. data/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 -10
  104. data/tests/spec/stubs/responses/get_powershell_keepalive_response.xml.erb +10 -10
  105. data/tests/spec/stubs/responses/get_powershell_output_response.xml.erb +12 -12
  106. data/tests/spec/stubs/responses/get_powershell_output_response_not_done.xml.erb +9 -9
  107. data/tests/spec/stubs/responses/open_shell_v1.xml +19 -19
  108. data/tests/spec/stubs/responses/open_shell_v2.xml +20 -20
  109. data/tests/spec/stubs/responses/soap_fault_v1.xml +36 -36
  110. data/tests/spec/stubs/responses/soap_fault_v2.xml +42 -42
  111. data/tests/spec/stubs/responses/wmi_error_v2.xml +41 -41
  112. data/tests/spec/wsmv/cleanup_command_spec.rb +22 -22
  113. data/tests/spec/wsmv/close_shell_spec.rb +17 -17
  114. data/tests/spec/wsmv/command_output_decoder_spec.rb +37 -37
  115. data/tests/spec/wsmv/command_output_spec.rb +45 -45
  116. data/tests/spec/wsmv/command_spec.rb +19 -19
  117. data/tests/spec/wsmv/configuration_spec.rb +17 -17
  118. data/tests/spec/wsmv/create_pipeline_spec.rb +31 -31
  119. data/tests/spec/wsmv/create_shell_spec.rb +38 -38
  120. data/tests/spec/wsmv/init_runspace_pool_spec.rb +36 -36
  121. data/tests/spec/wsmv/keep_alive_spec.rb +21 -21
  122. data/tests/spec/wsmv/receive_response_reader_spec.rb +123 -123
  123. data/tests/spec/wsmv/send_data_spec.rb +30 -30
  124. data/tests/spec/wsmv/wql_query_spec.rb +13 -13
  125. data/tests/spec/wsmv/write_stdin_spec.rb +22 -22
  126. data/winrm.gemspec +42 -42
  127. metadata +2 -2
@@ -1,75 +1,75 @@
1
- # encoding: UTF-8
2
-
3
- require 'winrm/shells/cmd'
4
-
5
- describe WinRM::Shells::Cmd do
6
- let(:retry_limit) { 1 }
7
- let(:shell_id) { 'shell_id' }
8
- let(:output) { 'output' }
9
- let(:create_shell_payload) { 'create_shell_payload' }
10
- let(:close_shell_payload) { 'close_shell_payload' }
11
- let(:cleanup_payload) { 'cleanup_payload' }
12
- let(:command) { 'run this command' }
13
- let(:arguments) { ['args'] }
14
- let(:command_response) { "<a xmlns:rsp='foo'><rsp:CommandId>command_id</rsp:CommandId></a>" }
15
- let(:connection_options) { { max_commands: 100, retry_limit: retry_limit, retry_delay: 0 } }
16
- let(:transport) { double('transport', send_request: nil) }
17
-
18
- before do
19
- allow_any_instance_of(WinRM::WSMV::CloseShell).to receive(:build)
20
- .and_return(close_shell_payload)
21
- allow_any_instance_of(WinRM::WSMV::CreateShell).to receive(:build)
22
- .and_return(create_shell_payload)
23
- allow_any_instance_of(WinRM::WSMV::CleanupCommand).to receive(:build)
24
- .and_return(cleanup_payload)
25
- allow_any_instance_of(WinRM::WSMV::ReceiveResponseReader).to receive(:read_output)
26
- .and_return(output)
27
- allow(transport).to receive(:send_request).with(create_shell_payload)
28
- .and_return(REXML::Document.new("<blah Name='ShellId'>#{shell_id}</blah>"))
29
- allow(transport).to receive(:send_request).with(/#{command}/)
30
- .and_return(REXML::Document.new(command_response))
31
- end
32
-
33
- subject { described_class.new(connection_options, transport, Logging.logger['test']) }
34
-
35
- describe '#run' do
36
- it 'opens a shell and gets shell id' do
37
- subject.run(command, arguments)
38
- expect(subject.shell_id).to eq shell_id
39
- end
40
-
41
- it 'sends create shell through transport' do
42
- expect(transport).to receive(:send_request).with(create_shell_payload)
43
- subject.run(command, arguments)
44
- end
45
-
46
- it 'returns output from generated command' do
47
- expect(subject.run(command, arguments)).to eq output
48
- end
49
-
50
- it 'sends command through transport' do
51
- expect(transport).to receive(:send_request).with(/#{command}/)
52
- subject.run(command, arguments)
53
- end
54
-
55
- it 'sends cleanup message through transport' do
56
- expect(transport).to receive(:send_request).with(cleanup_payload)
57
- subject.run(command, arguments)
58
- end
59
- end
60
-
61
- describe '#close' do
62
- it 'sends close shell through transport' do
63
- subject.run(command, arguments)
64
- expect(transport).to receive(:send_request).with(close_shell_payload)
65
- subject.close
66
- end
67
-
68
- it 'creates a shell closer with default shell uri' do
69
- allow(WinRM::WSMV::CloseShell).to receive(:new) do |_, opts|
70
- expect(opts[:shell_uri]).to be nil
71
- end.and_call_original
72
- subject.close
73
- end
74
- end
75
- end
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/shells/cmd'
4
+
5
+ describe WinRM::Shells::Cmd do
6
+ let(:retry_limit) { 1 }
7
+ let(:shell_id) { 'shell_id' }
8
+ let(:output) { 'output' }
9
+ let(:create_shell_payload) { 'create_shell_payload' }
10
+ let(:close_shell_payload) { 'close_shell_payload' }
11
+ let(:cleanup_payload) { 'cleanup_payload' }
12
+ let(:command) { 'run this command' }
13
+ let(:arguments) { ['args'] }
14
+ let(:command_response) { "<a xmlns:rsp='foo'><rsp:CommandId>command_id</rsp:CommandId></a>" }
15
+ let(:connection_options) { { max_commands: 100, retry_limit: retry_limit, retry_delay: 0 } }
16
+ let(:transport) { double('transport', send_request: nil) }
17
+
18
+ before do
19
+ allow_any_instance_of(WinRM::WSMV::CloseShell).to receive(:build)
20
+ .and_return(close_shell_payload)
21
+ allow_any_instance_of(WinRM::WSMV::CreateShell).to receive(:build)
22
+ .and_return(create_shell_payload)
23
+ allow_any_instance_of(WinRM::WSMV::CleanupCommand).to receive(:build)
24
+ .and_return(cleanup_payload)
25
+ allow_any_instance_of(WinRM::WSMV::ReceiveResponseReader).to receive(:read_output)
26
+ .and_return(output)
27
+ allow(transport).to receive(:send_request).with(create_shell_payload)
28
+ .and_return(REXML::Document.new("<blah Name='ShellId'>#{shell_id}</blah>"))
29
+ allow(transport).to receive(:send_request).with(/#{command}/)
30
+ .and_return(REXML::Document.new(command_response))
31
+ end
32
+
33
+ subject { described_class.new(connection_options, transport, Logging.logger['test']) }
34
+
35
+ describe '#run' do
36
+ it 'opens a shell and gets shell id' do
37
+ subject.run(command, arguments)
38
+ expect(subject.shell_id).to eq shell_id
39
+ end
40
+
41
+ it 'sends create shell through transport' do
42
+ expect(transport).to receive(:send_request).with(create_shell_payload)
43
+ subject.run(command, arguments)
44
+ end
45
+
46
+ it 'returns output from generated command' do
47
+ expect(subject.run(command, arguments)).to eq output
48
+ end
49
+
50
+ it 'sends command through transport' do
51
+ expect(transport).to receive(:send_request).with(/#{command}/)
52
+ subject.run(command, arguments)
53
+ end
54
+
55
+ it 'sends cleanup message through transport' do
56
+ expect(transport).to receive(:send_request).with(cleanup_payload)
57
+ subject.run(command, arguments)
58
+ end
59
+ end
60
+
61
+ describe '#close' do
62
+ it 'sends close shell through transport' do
63
+ subject.run(command, arguments)
64
+ expect(transport).to receive(:send_request).with(close_shell_payload)
65
+ subject.close
66
+ end
67
+
68
+ it 'creates a shell closer with default shell uri' do
69
+ allow(WinRM::WSMV::CloseShell).to receive(:new) do |_, opts|
70
+ expect(opts[:shell_uri]).to be nil
71
+ end.and_call_original
72
+ subject.close
73
+ end
74
+ end
75
+ end
@@ -1,175 +1,175 @@
1
- # encoding: UTF-8
2
-
3
- require 'winrm/shells/power_shell'
4
-
5
- describe WinRM::Shells::Powershell do
6
- let(:retry_limit) { 1 }
7
- let(:max_envelope_size_kb) { 150 }
8
- let(:shell_id) { 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16' }
9
- let(:output) { 'output' }
10
- let(:command_id) { '4218A578-0F18-4B19-82C3-46B433319126' }
11
- let(:keepalive_payload) { 'keepalive_payload' }
12
- let(:configuration_payload) { 'configuration_payload' }
13
- let(:command_payload) { 'command_payload' }
14
- let(:create_shell_payload) { 'create_shell_payload' }
15
- let(:close_shell_payload) { 'close_shell_payload' }
16
- let(:cleanup_payload) { 'cleanup_payload' }
17
- let(:command) { 'command' }
18
- let(:output_message) { double('output_message', build: 'output_message') }
19
- let(:command_response) { "<a xmlns:rsp='foo'><rsp:CommandId>#{command_id}</rsp:CommandId></a>" }
20
- let(:configuration_response) do
21
- "<a xmlns:cfg='foo'><cfg:MaxEnvelopeSizekb>#{max_envelope_size_kb}</cfg:MaxEnvelopeSizekb></a>"
22
- end
23
- let(:connection_options) { { max_commands: 100, retry_limit: retry_limit, retry_delay: 0 } }
24
- let(:transport) { double('transport', send_request: nil) }
25
- let(:test_data_xml_template) do
26
- ERB.new(stubbed_response('get_powershell_keepalive_response.xml.erb'))
27
- end
28
- let(:protocol_version) { 2.2 }
29
- let(:test_data1) do
30
- <<-EOH
31
- <Obj RefId="0">
32
- <MS>
33
- <Version N="protocolversion">#{protocol_version}</Version>
34
- <Version N="PSVersion">2.0</Version>
35
- <Version N="SerializationVersion">1.1.0.1</Version>
36
- </MS>
37
- </Obj>
38
- EOH
39
- end
40
- let(:test_data2) { '<Obj RefId="0"><MS><I32 N="RunspaceState">2</I32></MS></Obj>' }
41
- let(:message1) do
42
- WinRM::PSRP::Message.new(
43
- shell_id,
44
- WinRM::PSRP::Message::MESSAGE_TYPES[:session_capability],
45
- test_data1,
46
- command_id
47
- )
48
- end
49
- let(:message2) do
50
- WinRM::PSRP::Message.new(
51
- shell_id,
52
- WinRM::PSRP::Message::MESSAGE_TYPES[:runspacepool_state],
53
- test_data2,
54
- command_id
55
- )
56
- end
57
- let(:fragment1) { WinRM::PSRP::Fragment.new(1, message1.bytes) }
58
- let(:fragment2) { WinRM::PSRP::Fragment.new(1, message2.bytes) }
59
- let(:test_data_stdout1) { Base64.strict_encode64(fragment1.bytes.pack('C*')) }
60
- let(:test_data_stdout2) { Base64.strict_encode64(fragment2.bytes.pack('C*')) }
61
-
62
- before do
63
- allow(SecureRandom).to receive(:uuid).and_return(command_id)
64
- allow(subject).to receive(:command_output_message).with(shell_id, command_id)
65
- .and_return(output_message)
66
- allow_any_instance_of(WinRM::WSMV::CreatePipeline).to receive(:build)
67
- .and_return(command_payload)
68
- allow_any_instance_of(WinRM::WSMV::CloseShell).to receive(:build)
69
- .and_return(close_shell_payload)
70
- allow_any_instance_of(WinRM::WSMV::InitRunspacePool).to receive(:build)
71
- .and_return(create_shell_payload)
72
- allow_any_instance_of(WinRM::WSMV::Configuration).to receive(:build)
73
- .and_return(configuration_payload)
74
- allow_any_instance_of(WinRM::WSMV::CleanupCommand).to receive(:build)
75
- .and_return(cleanup_payload)
76
- allow_any_instance_of(WinRM::WSMV::KeepAlive).to receive(:build).and_return(keepalive_payload)
77
- allow_any_instance_of(WinRM::PSRP::ReceiveResponseReader).to receive(:read_output)
78
- .with(output_message).and_return(output)
79
- allow(transport).to receive(:send_request).with(configuration_payload).and_return(
80
- REXML::Document.new(configuration_response))
81
- allow(transport).to receive(:send_request).with(create_shell_payload)
82
- .and_return(REXML::Document.new("<blah Name='ShellId'>#{shell_id}</blah>"))
83
- allow(transport).to receive(:send_request).with(command_payload)
84
- .and_return(REXML::Document.new(command_response))
85
- allow(transport).to receive(:send_request).with(keepalive_payload)
86
- .and_return(REXML::Document.new(test_data_xml_template.result(binding)))
87
- end
88
-
89
- subject { described_class.new(connection_options, transport, Logging.logger['test']) }
90
-
91
- describe '#run' do
92
- it 'opens a shell and gets shell id' do
93
- subject.run(command)
94
- expect(subject.shell_id).to eq shell_id
95
- end
96
-
97
- it 'sends create shell through transport' do
98
- expect(transport).to receive(:send_request).with(create_shell_payload)
99
- subject.run(command)
100
- end
101
-
102
- it 'sends keepalive shell through transport' do
103
- expect(transport).to receive(:send_request).with(keepalive_payload)
104
- subject.run(command)
105
- end
106
-
107
- it 'returns output from generated command' do
108
- expect(subject.run(command)).to eq output
109
- end
110
-
111
- it 'sends command through transport' do
112
- expect(transport).to receive(:send_request).with(command_payload)
113
- subject.run(command)
114
- end
115
-
116
- it 'sends cleanup message through transport' do
117
- expect(transport).to receive(:send_request).with(cleanup_payload)
118
- subject.run(command)
119
- end
120
-
121
- context 'non admin user' do
122
- before do
123
- allow(transport).to receive(:send_request).with(configuration_payload)
124
- .and_raise(WinRM::WinRMWSManFault.new('no access for you', '5'))
125
- end
126
-
127
- context 'protocol version 2.1' do
128
- let(:protocol_version) { 2.1 }
129
-
130
- it 'sets the fragmenter max_blob_length' do
131
- expect_any_instance_of(WinRM::PSRP::MessageFragmenter).to receive(:max_blob_length=)
132
- .with(153600)
133
- subject.run(command)
134
- end
135
- end
136
-
137
- context 'protocol version 2.2' do
138
- let(:protocol_version) { 2.2 }
139
-
140
- it 'sets the fragmenter max_blob_length' do
141
- expect_any_instance_of(WinRM::PSRP::MessageFragmenter).to receive(:max_blob_length=)
142
- .with(512000)
143
- subject.run(command)
144
- end
145
- end
146
- end
147
-
148
- context 'fragment large command' do
149
- let(:command) { 'c' * 200000 }
150
-
151
- it 'fragments messages as large as max envelope size' do
152
- allow_any_instance_of(WinRM::WSMV::CreatePipeline).to receive(:build).and_call_original
153
- allow(transport).to receive(:send_request).with(/CommandLine/) do |payload|
154
- expect(payload.length).to be max_envelope_size_kb * 1024
155
- end.and_return(REXML::Document.new(command_response))
156
- subject.run(command)
157
- end
158
- end
159
- end
160
-
161
- describe '#close' do
162
- it 'sends close shell through transport' do
163
- subject.run(command)
164
- expect(transport).to receive(:send_request).with(close_shell_payload)
165
- subject.close
166
- end
167
-
168
- it 'creates a shell closer with powershell uri' do
169
- allow(WinRM::WSMV::CloseShell).to receive(:new) do |_, opts|
170
- expect(opts[:shell_uri]).to be WinRM::WSMV::Header::RESOURCE_URI_POWERSHELL
171
- end.and_call_original
172
- subject.close
173
- end
174
- end
175
- end
1
+ # encoding: UTF-8
2
+
3
+ require 'winrm/shells/power_shell'
4
+
5
+ describe WinRM::Shells::Powershell do
6
+ let(:retry_limit) { 1 }
7
+ let(:max_envelope_size_kb) { 150 }
8
+ let(:shell_id) { 'bc1bfbba-8215-4a04-b2df-7a3ac0310e16' }
9
+ let(:output) { 'output' }
10
+ let(:command_id) { '4218A578-0F18-4B19-82C3-46B433319126' }
11
+ let(:keepalive_payload) { 'keepalive_payload' }
12
+ let(:configuration_payload) { 'configuration_payload' }
13
+ let(:command_payload) { 'command_payload' }
14
+ let(:create_shell_payload) { 'create_shell_payload' }
15
+ let(:close_shell_payload) { 'close_shell_payload' }
16
+ let(:cleanup_payload) { 'cleanup_payload' }
17
+ let(:command) { 'command' }
18
+ let(:output_message) { double('output_message', build: 'output_message') }
19
+ let(:command_response) { "<a xmlns:rsp='foo'><rsp:CommandId>#{command_id}</rsp:CommandId></a>" }
20
+ let(:configuration_response) do
21
+ "<a xmlns:cfg='foo'><cfg:MaxEnvelopeSizekb>#{max_envelope_size_kb}</cfg:MaxEnvelopeSizekb></a>"
22
+ end
23
+ let(:connection_options) { { max_commands: 100, retry_limit: retry_limit, retry_delay: 0 } }
24
+ let(:transport) { double('transport', send_request: nil) }
25
+ let(:test_data_xml_template) do
26
+ ERB.new(stubbed_response('get_powershell_keepalive_response.xml.erb'))
27
+ end
28
+ let(:protocol_version) { 2.2 }
29
+ let(:test_data1) do
30
+ <<-EOH
31
+ <Obj RefId="0">
32
+ <MS>
33
+ <Version N="protocolversion">#{protocol_version}</Version>
34
+ <Version N="PSVersion">2.0</Version>
35
+ <Version N="SerializationVersion">1.1.0.1</Version>
36
+ </MS>
37
+ </Obj>
38
+ EOH
39
+ end
40
+ let(:test_data2) { '<Obj RefId="0"><MS><I32 N="RunspaceState">2</I32></MS></Obj>' }
41
+ let(:message1) do
42
+ WinRM::PSRP::Message.new(
43
+ shell_id,
44
+ WinRM::PSRP::Message::MESSAGE_TYPES[:session_capability],
45
+ test_data1,
46
+ command_id
47
+ )
48
+ end
49
+ let(:message2) do
50
+ WinRM::PSRP::Message.new(
51
+ shell_id,
52
+ WinRM::PSRP::Message::MESSAGE_TYPES[:runspacepool_state],
53
+ test_data2,
54
+ command_id
55
+ )
56
+ end
57
+ let(:fragment1) { WinRM::PSRP::Fragment.new(1, message1.bytes) }
58
+ let(:fragment2) { WinRM::PSRP::Fragment.new(1, message2.bytes) }
59
+ let(:test_data_stdout1) { Base64.strict_encode64(fragment1.bytes.pack('C*')) }
60
+ let(:test_data_stdout2) { Base64.strict_encode64(fragment2.bytes.pack('C*')) }
61
+
62
+ before do
63
+ allow(SecureRandom).to receive(:uuid).and_return(command_id)
64
+ allow(subject).to receive(:command_output_message).with(shell_id, command_id)
65
+ .and_return(output_message)
66
+ allow_any_instance_of(WinRM::WSMV::CreatePipeline).to receive(:build)
67
+ .and_return(command_payload)
68
+ allow_any_instance_of(WinRM::WSMV::CloseShell).to receive(:build)
69
+ .and_return(close_shell_payload)
70
+ allow_any_instance_of(WinRM::WSMV::InitRunspacePool).to receive(:build)
71
+ .and_return(create_shell_payload)
72
+ allow_any_instance_of(WinRM::WSMV::Configuration).to receive(:build)
73
+ .and_return(configuration_payload)
74
+ allow_any_instance_of(WinRM::WSMV::CleanupCommand).to receive(:build)
75
+ .and_return(cleanup_payload)
76
+ allow_any_instance_of(WinRM::WSMV::KeepAlive).to receive(:build).and_return(keepalive_payload)
77
+ allow_any_instance_of(WinRM::PSRP::ReceiveResponseReader).to receive(:read_output)
78
+ .with(output_message).and_return(output)
79
+ allow(transport).to receive(:send_request).with(configuration_payload).and_return(
80
+ REXML::Document.new(configuration_response))
81
+ allow(transport).to receive(:send_request).with(create_shell_payload)
82
+ .and_return(REXML::Document.new("<blah Name='ShellId'>#{shell_id}</blah>"))
83
+ allow(transport).to receive(:send_request).with(command_payload)
84
+ .and_return(REXML::Document.new(command_response))
85
+ allow(transport).to receive(:send_request).with(keepalive_payload)
86
+ .and_return(REXML::Document.new(test_data_xml_template.result(binding)))
87
+ end
88
+
89
+ subject { described_class.new(connection_options, transport, Logging.logger['test']) }
90
+
91
+ describe '#run' do
92
+ it 'opens a shell and gets shell id' do
93
+ subject.run(command)
94
+ expect(subject.shell_id).to eq shell_id
95
+ end
96
+
97
+ it 'sends create shell through transport' do
98
+ expect(transport).to receive(:send_request).with(create_shell_payload)
99
+ subject.run(command)
100
+ end
101
+
102
+ it 'sends keepalive shell through transport' do
103
+ expect(transport).to receive(:send_request).with(keepalive_payload)
104
+ subject.run(command)
105
+ end
106
+
107
+ it 'returns output from generated command' do
108
+ expect(subject.run(command)).to eq output
109
+ end
110
+
111
+ it 'sends command through transport' do
112
+ expect(transport).to receive(:send_request).with(command_payload)
113
+ subject.run(command)
114
+ end
115
+
116
+ it 'sends cleanup message through transport' do
117
+ expect(transport).to receive(:send_request).with(cleanup_payload)
118
+ subject.run(command)
119
+ end
120
+
121
+ context 'non admin user' do
122
+ before do
123
+ allow(transport).to receive(:send_request).with(configuration_payload)
124
+ .and_raise(WinRM::WinRMWSManFault.new('no access for you', '5'))
125
+ end
126
+
127
+ context 'protocol version 2.1' do
128
+ let(:protocol_version) { 2.1 }
129
+
130
+ it 'sets the fragmenter max_blob_length' do
131
+ expect_any_instance_of(WinRM::PSRP::MessageFragmenter).to receive(:max_blob_length=)
132
+ .with(153600)
133
+ subject.run(command)
134
+ end
135
+ end
136
+
137
+ context 'protocol version 2.2' do
138
+ let(:protocol_version) { 2.2 }
139
+
140
+ it 'sets the fragmenter max_blob_length' do
141
+ expect_any_instance_of(WinRM::PSRP::MessageFragmenter).to receive(:max_blob_length=)
142
+ .with(512000)
143
+ subject.run(command)
144
+ end
145
+ end
146
+ end
147
+
148
+ context 'fragment large command' do
149
+ let(:command) { 'c' * 200000 }
150
+
151
+ it 'fragments messages as large as max envelope size' do
152
+ allow_any_instance_of(WinRM::WSMV::CreatePipeline).to receive(:build).and_call_original
153
+ allow(transport).to receive(:send_request).with(/CommandLine/) do |payload|
154
+ expect(payload.length).to be max_envelope_size_kb * 1024
155
+ end.and_return(REXML::Document.new(command_response))
156
+ subject.run(command)
157
+ end
158
+ end
159
+ end
160
+
161
+ describe '#close' do
162
+ it 'sends close shell through transport' do
163
+ subject.run(command)
164
+ expect(transport).to receive(:send_request).with(close_shell_payload)
165
+ subject.close
166
+ end
167
+
168
+ it 'creates a shell closer with powershell uri' do
169
+ allow(WinRM::WSMV::CloseShell).to receive(:new) do |_, opts|
170
+ expect(opts[:shell_uri]).to be WinRM::WSMV::Header::RESOURCE_URI_POWERSHELL
171
+ end.and_call_original
172
+ subject.close
173
+ end
174
+ end
175
+ end