diff --git a/lib/sequenceserver/blast.rb b/lib/sequenceserver/blast.rb index fa27a320..9a499302 100644 --- a/lib/sequenceserver/blast.rb +++ b/lib/sequenceserver/blast.rb @@ -5,5 +5,10 @@ module SequenceServer module BLAST VALID_SEQUENCE_ID = /\A[a-zA-Z0-9\-_.:*#|\[\]]+\z/ + + # Sequence range accepted by `blastdbcmd -range`: "start-stop" or "start-" + # (open ended). Anchored at both ends so that shell metacharacters cannot + # be smuggled in alongside an otherwise valid range. + VALID_SEQUENCE_RANGE = /\A[0-9]+-[0-9]*\z/ end end diff --git a/lib/sequenceserver/database.rb b/lib/sequenceserver/database.rb index 298a34e5..b74e0865 100644 --- a/lib/sequenceserver/database.rb +++ b/lib/sequenceserver/database.rb @@ -48,7 +48,7 @@ def retrieve(accession, coords = nil) fail( InvalidParameterError, "Invalid range coordinates: #{coords}" - ) unless coords =~ /[0-9]+-[0-9]*/ + ) unless coords =~ SequenceServer::BLAST::VALID_SEQUENCE_RANGE cmd << " -range #{coords}" end diff --git a/spec/database_spec.rb b/spec/database_spec.rb index f604db7c..b2ae308f 100644 --- a/spec/database_spec.rb +++ b/spec/database_spec.rb @@ -74,6 +74,24 @@ module SequenceServer Database.retrieve("SI2.2.0_06267:';hi") end.to raise_error(SequenceServer::InvalidParameterError) end + + it 'rejects a shell command appended to an otherwise valid range' do + expect do + Database.retrieve('SI2.2.0_06267:1-2;echo INJECTED') + end.to raise_error(SequenceServer::InvalidParameterError) + end + + it 'rejects a shell command hidden behind a newline in the range' do + expect do + Database.retrieve("SI2.2.0_06267:1-2\n;echo INJECTED") + end.to raise_error(SequenceServer::InvalidParameterError) + end + + it 'rejects a range that is only a prefix of a valid one' do + expect do + Database.retrieve('SI2.2.0_06267:x1-2') + end.to raise_error(SequenceServer::InvalidParameterError) + end end end end