Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/spec/integration/msfmcpd/rpc_availability_spec.rb
70330 views
1
# frozen_string_literal: true
2
3
require 'msf/core/mcp'
4
require 'stringio'
5
require 'tempfile'
6
7
RSpec.describe 'RPC Availability Integration' do
8
let(:output) { StringIO.new }
9
let(:file_fixtures_path) { File.join(Msf::Config.install_root, 'spec', 'file_fixtures') }
10
let(:valid_messagepack_path) { File.join(file_fixtures_path, 'config_files', 'msfmcpd', 'valid_messagepack.yaml') }
11
12
describe 'Application run with RPC already available but no credentials' do
13
it 'exits with RPC startup error when MessagePack credentials are missing' do
14
# Config with no credentials — validator allows this because auto-start
15
# could generate them, but RPC is already running so generation won't happen
16
config = {
17
msf_api: {
18
type: 'messagepack',
19
host: 'localhost',
20
port: 55553,
21
auto_start_rpc: true
22
}
23
}
24
25
config_file = Tempfile.new(['no_creds', '.yaml'])
26
config_file.write(YAML.dump(JSON.parse(config.to_json)))
27
config_file.flush
28
29
app = Msf::MCP::Application.new(['--config', config_file.path], output: output)
30
31
# Stub RPC as already available
32
allow_any_instance_of(Msf::MCP::RpcManager).to receive(:rpc_available?).and_return(true)
33
allow(Signal).to receive(:trap)
34
35
expect { app.run }.to raise_error(SystemExit) do |e|
36
expect(e.status).to eq(1)
37
end
38
39
expect(output.string).to include('RPC startup error')
40
expect(output.string).to include('no credentials')
41
42
config_file.close
43
config_file.unlink
44
end
45
46
it 'exits with RPC startup error when JSON-RPC token is missing' do
47
config = {
48
msf_api: {
49
type: 'json-rpc',
50
host: 'localhost',
51
port: 8081
52
}
53
}
54
55
config_file = Tempfile.new(['no_token', '.yaml'])
56
config_file.write(YAML.dump(JSON.parse(config.to_json)))
57
config_file.flush
58
59
app = Msf::MCP::Application.new(['--config', config_file.path], output: output)
60
61
# Stub RPC as already available
62
allow_any_instance_of(Msf::MCP::RpcManager).to receive(:rpc_available?).and_return(true)
63
allow(Signal).to receive(:trap)
64
65
expect { app.run }.to raise_error(SystemExit) do |e|
66
expect(e.status).to eq(1)
67
end
68
69
# The validator catches missing token before RpcManager runs
70
expect(output.string).to match(/token|Configuration validation failed/i)
71
72
config_file.close
73
config_file.unlink
74
end
75
76
it 'proceeds when RPC is available and credentials are provided' do
77
app = Msf::MCP::Application.new(['--config', valid_messagepack_path], output: output)
78
79
# Stub RPC as already available
80
allow_any_instance_of(Msf::MCP::RpcManager).to receive(:rpc_available?).and_return(true)
81
82
# Stub the rest of the startup sequence
83
mock_client = instance_double(Msf::MCP::Metasploit::Client)
84
allow(Msf::MCP::Metasploit::Client).to receive(:new).and_return(mock_client)
85
allow(mock_client).to receive(:authenticate)
86
mock_server = instance_double(Msf::MCP::Server)
87
allow(Msf::MCP::Server).to receive(:new).and_return(mock_server)
88
allow(mock_server).to receive(:start)
89
allow(Signal).to receive(:trap)
90
91
expect { app.run }.not_to raise_error
92
93
expect(output.string).to include('already running')
94
expect(output.string).to include('Authentication successful')
95
end
96
end
97
98
describe 'Application run with RPC not available' do
99
it 'exits with RPC startup error when auto-start is disabled' do
100
config = {
101
msf_api: {
102
type: 'messagepack',
103
host: 'localhost',
104
port: 55553,
105
user: 'msf',
106
password: 'pass',
107
auto_start_rpc: false
108
}
109
}
110
111
config_file = Tempfile.new(['no_autostart', '.yaml'])
112
config_file.write(YAML.dump(JSON.parse(config.to_json)))
113
config_file.flush
114
115
app = Msf::MCP::Application.new(['--config', config_file.path], output: output)
116
117
# Stub RPC as not available
118
allow_any_instance_of(Msf::MCP::RpcManager).to receive(:rpc_available?).and_return(false)
119
allow(Signal).to receive(:trap)
120
121
expect { app.run }.to raise_error(SystemExit) do |e|
122
expect(e.status).to eq(1)
123
end
124
125
expect(output.string).to include('RPC startup error')
126
expect(output.string).to include('auto-start is disabled')
127
128
config_file.close
129
config_file.unlink
130
end
131
132
it 'exits with RPC startup error on remote host' do
133
config = {
134
msf_api: {
135
type: 'messagepack',
136
host: '192.0.2.1',
137
port: 55553,
138
user: 'msf',
139
password: 'pass',
140
auto_start_rpc: true
141
}
142
}
143
144
config_file = Tempfile.new(['remote_host', '.yaml'])
145
config_file.write(YAML.dump(JSON.parse(config.to_json)))
146
config_file.flush
147
148
app = Msf::MCP::Application.new(['--config', config_file.path], output: output)
149
150
# Stub RPC as not available
151
allow_any_instance_of(Msf::MCP::RpcManager).to receive(:rpc_available?).and_return(false)
152
allow(Signal).to receive(:trap)
153
154
expect { app.run }.to raise_error(SystemExit) do |e|
155
expect(e.status).to eq(1)
156
end
157
158
expect(output.string).to include('RPC startup error')
159
expect(output.string).to include('192.0.2.1')
160
161
config_file.close
162
config_file.unlink
163
end
164
end
165
end
166
167