Skip to content

Commit 0703271

Browse files
committed
Add check to allow only whitelisted methods
1 parent 7d7d2e7 commit 0703271

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

lib/bitcoin/rpc/http_server.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,22 @@ class HttpServer < EM::Connection
99
include EM::HttpServer
1010
include RequestHandler
1111

12+
SUPPORTED_COMMANDS = %w[
13+
getblockchaininfo
14+
stop
15+
getblockheader
16+
getpeerinfo
17+
sendrawtransaction
18+
decoderawtransaction
19+
decodescript
20+
createwallet
21+
listwallets
22+
getwalletinfo
23+
listaccounts
24+
encryptwallet
25+
getnewaddress
26+
].freeze
27+
1228
attr_reader :node
1329
attr_accessor :logger
1430

@@ -31,6 +47,9 @@ def process_http_request
3147
operation = proc {
3248
command, args = parse_json_params
3349
logger.debug("process http request. command = #{command}")
50+
unless SUPPORTED_COMMANDS.include?(command)
51+
raise ArgumentError, "Unsupported method: #{command}"
52+
end
3453
begin
3554
send(command, *args).to_json
3655
rescue Exception => e
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
require 'spec_helper'
2+
3+
RSpec.describe Bitcoin::RPC::HttpServer do
4+
describe 'SUPPORTED_COMMANDS' do
5+
subject { described_class::SUPPORTED_COMMANDS }
6+
7+
it 'is frozen' do
8+
expect(subject).to be_frozen
9+
end
10+
11+
it 'includes all RequestHandler methods' do
12+
expected_commands = %w[
13+
getblockchaininfo
14+
stop
15+
getblockheader
16+
getpeerinfo
17+
sendrawtransaction
18+
decoderawtransaction
19+
decodescript
20+
createwallet
21+
listwallets
22+
getwalletinfo
23+
listaccounts
24+
encryptwallet
25+
getnewaddress
26+
]
27+
expect(subject).to match_array(expected_commands)
28+
end
29+
30+
it 'does not include undefined methods' do
31+
undefined_methods = %w[
32+
eval
33+
instance_eval
34+
class_eval
35+
module_eval
36+
exec
37+
system
38+
`
39+
spawn
40+
fork
41+
send
42+
__send__
43+
public_send
44+
method
45+
define_method
46+
remove_method
47+
undef_method
48+
instance_variable_set
49+
instance_variable_get
50+
const_set
51+
const_get
52+
]
53+
undefined_methods.each do |method|
54+
expect(subject).not_to include(method), "SUPPORTED_COMMANDS should not include '#{method}'"
55+
end
56+
end
57+
end
58+
59+
describe 'command validation' do
60+
# Test command validation logic without EventMachine
61+
def validate_command(command)
62+
unless Bitcoin::RPC::HttpServer::SUPPORTED_COMMANDS.include?(command)
63+
raise ArgumentError, "Unsupported method: #{command}"
64+
end
65+
command
66+
end
67+
68+
describe 'with allowed commands' do
69+
%w[getblockchaininfo stop getblockheader decoderawtransaction].each do |cmd|
70+
it "allows '#{cmd}'" do
71+
expect { validate_command(cmd) }.not_to raise_error
72+
end
73+
end
74+
end
75+
76+
describe 'with undefined commands' do
77+
# These are the attack vectors from the security advisory GHSA-q66h-m87m-j2q6
78+
%w[eval system exec instance_eval class_eval].each do |cmd|
79+
it "rejects '#{cmd}' to prevent remote code execution" do
80+
expect { validate_command(cmd) }.to raise_error(
81+
ArgumentError, "Unsupported method: #{cmd}"
82+
)
83+
end
84+
end
85+
end
86+
87+
describe 'with arbitrary unknown commands' do
88+
%w[unknown_method foo bar __send__ public_send].each do |cmd|
89+
it "rejects '#{cmd}'" do
90+
expect { validate_command(cmd) }.to raise_error(
91+
ArgumentError, "Unsupported method: #{cmd}"
92+
)
93+
end
94+
end
95+
end
96+
end
97+
end

0 commit comments

Comments
 (0)