Skip to content

Commit 457e527

Browse files
authored
Merge pull request #759 from wurmlab/tt/escaping
Validate user inputs ore thoroughly
2 parents 4d2985f + 984c258 commit 457e527

File tree

6 files changed

+92
-5
lines changed

6 files changed

+92
-5
lines changed

lib/sequenceserver/api_errors.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,30 @@ def message
5252
end
5353
end
5454

55+
# InvalidParameterError is a more generic error class that can be
56+
# raised when the frontend sends a request with an invalid parameter
57+
class InvalidParameterError < ValidationError
58+
attr_reader :more_info
59+
60+
def initialize(more_info)
61+
super
62+
@more_info = more_info
63+
end
64+
65+
def http_status
66+
422
67+
end
68+
69+
def title
70+
'Invalid parameter'
71+
end
72+
73+
def message
74+
"The action you're trying to perform is not possible because \
75+
one of the provided parameters is invalid."
76+
end
77+
end
78+
5579
# API errors have an http status, title, message, and additional information
5680
# like stacktrace or information from program output.
5781
APIError = Class.new(Error)

lib/sequenceserver/blast.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
require_relative 'blast/job'
22
require_relative 'blast/report'
33
require_relative 'blast/constants'
4+
5+
module SequenceServer
6+
module BLAST
7+
VALID_SEQUENCE_ID = /\A[a-zA-Z0-9\-_.:*#|\[\]]+\z/
8+
end
9+
end

lib/sequenceserver/database.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,19 @@ def initialize(*args)
3737
alias path name
3838

3939
def retrieve(accession, coords = nil)
40+
fail(
41+
InvalidSequenceIdError,
42+
"Invalid sequence id: #{accession}"
43+
) unless accession =~ SequenceServer::BLAST::VALID_SEQUENCE_ID
44+
4045
cmd = "blastdbcmd -db #{name} -entry '#{accession}'"
46+
4147
if coords
48+
fail(
49+
InvalidParameterError,
50+
"Invalid range coordinates: #{coords}"
51+
) unless coords =~ /[0-9]+-[0-9]*/
52+
4253
cmd << " -range #{coords}"
4354
end
4455
out, = sys(cmd, path: config[:bin])
@@ -52,6 +63,8 @@ def retrieve(accession, coords = nil)
5263
# Returns true if the database contains the given sequence id.
5364
# Returns false otherwise.
5465
def include?(id)
66+
fail ArgumentError, "Invalid sequence id: #{id}" unless id =~ SequenceServer::BLAST::VALID_SEQUENCE_ID
67+
5568
cmd = "blastdbcmd -entry '#{id}' -db #{name}"
5669
sys(cmd, path: config[:bin]) rescue false
5770
end

lib/sequenceserver/sequence.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,8 @@ def validate
232232
)
233233
end
234234

235-
valid_id_regex = /\A[a-zA-Z0-9-_.:*#|\[\]]+\z/
236235
invalid_sequence_ids = sequence_ids.reject do |id|
237-
id =~ valid_id_regex
236+
id =~ SequenceServer::BLAST::VALID_SEQUENCE_ID
238237
end
239238

240239
unless invalid_sequence_ids.empty?

spec/database_spec.rb

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,53 @@ module SequenceServer
2727
Database[id].first
2828
end
2929

30-
it 'knows if a given accession is in the database or not' do
31-
solenopsis_protein_database.include?('SI2.2.0_06267').should be_truthy
30+
describe '#include?' do
31+
it 'knows if a given accession is present in the database' do
32+
expect(solenopsis_protein_database).to include('SI2.2.0_06267')
33+
end
34+
35+
it 'knows if a given accession is absent in the database' do
36+
expect(solenopsis_protein_database).not_to include('LOL.2.0_404')
37+
end
38+
39+
it 'validates the id' do
40+
expect do
41+
solenopsis_protein_database.include?("';hi")
42+
end.to raise_error(ArgumentError, "Invalid sequence id: ';hi")
43+
end
44+
end
45+
46+
describe '.retrieve' do
47+
it "retrieves the sequence for a given accession" do
48+
sequence = Database.retrieve("SI2.2.0_06267")
49+
expect(sequence).to include('SI2.2.0_06267')
50+
expect(sequence).to include('MNTLWLSLWDYPGKL') # start of fasta sequence
51+
end
52+
53+
it "retrieves an open sequence range for a given accession" do
54+
sequence = Database.retrieve("SI2.2.0_06267:10-")
55+
expect(sequence).to include('SI2.2.0_06267')
56+
expect(sequence).not_to include('MNTLWLSLWD') # excludes first 10 chars
57+
expect(sequence.lines[1]).to start_with('DYPGKLP') # start at an offset of 10
58+
end
59+
60+
it "retrieves a closed sequence range for a given accession" do
61+
sequence = Database.retrieve("SI2.2.0_06267:1-10")
62+
expect(sequence).to include('SI2.2.0_06267')
63+
expect(sequence.lines.last.size).to eq(10)
64+
end
65+
66+
it "validates the sequence id" do
67+
expect do
68+
Database.retrieve("';hi")
69+
end.to raise_error(SequenceServer::InvalidSequenceIdError)
70+
end
71+
72+
it "validates the range" do
73+
expect do
74+
Database.retrieve("SI2.2.0_06267:';hi")
75+
end.to raise_error(SequenceServer::InvalidParameterError)
76+
end
3277
end
3378
end
3479
end

spec/fixtures/38334a72-e8e7-4732-872b-24d3f8723563/expected_outputs/frontend.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)