diff options
author | George Claghorn <george@basecamp.com> | 2018-05-28 16:28:46 -0400 |
---|---|---|
committer | George Claghorn <george@basecamp.com> | 2018-05-28 16:28:46 -0400 |
commit | 1bdaccc0b8cf8aac0dc833d6db14b9a902593ff5 (patch) | |
tree | 47c7fe84ded16a785feb86f42faa225175d4a8c4 /activestorage | |
parent | 34cc301f03aea2e579d6687a9ea9782afc1089a0 (diff) | |
download | rails-1bdaccc0b8cf8aac0dc833d6db14b9a902593ff5.tar.gz rails-1bdaccc0b8cf8aac0dc833d6db14b9a902593ff5.tar.bz2 rails-1bdaccc0b8cf8aac0dc833d6db14b9a902593ff5.zip |
Verify integrity after chunked download
Diffstat (limited to 'activestorage')
-rw-r--r-- | activestorage/app/models/active_storage/blob.rb | 2 | ||||
-rw-r--r-- | activestorage/lib/active_storage/downloader.rb | 7 | ||||
-rw-r--r-- | activestorage/lib/active_storage/errors.rb | 4 | ||||
-rw-r--r-- | activestorage/lib/active_storage/service.rb | 2 | ||||
-rw-r--r-- | activestorage/test/models/blob_test.rb | 12 |
5 files changed, 24 insertions, 3 deletions
diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 134d3bb2d9..d36c951292 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -167,6 +167,8 @@ class ActiveStorage::Blob < ActiveRecord::Base end # Downloads the blob to a tempfile on disk. Yields the tempfile. + # + # Raises ActiveStorage::IntegrityError if the downloaded data does not match the blob's checksum. def open(tempdir: nil, &block) ActiveStorage::Downloader.new(self, tempdir: tempdir).download_blob_to_tempfile(&block) end diff --git a/activestorage/lib/active_storage/downloader.rb b/activestorage/lib/active_storage/downloader.rb index 0e7039e104..5953fd08fb 100644 --- a/activestorage/lib/active_storage/downloader.rb +++ b/activestorage/lib/active_storage/downloader.rb @@ -10,6 +10,7 @@ module ActiveStorage def download_blob_to_tempfile open_tempfile do |file| download_blob_to file + verify_integrity_of file yield file end end @@ -34,6 +35,12 @@ module ActiveStorage file.rewind end + def verify_integrity_of(file) + unless Digest::MD5.file(file).base64digest == checksum + raise ActiveStorage::IntegrityError + end + end + def tempfile_extension_with_delimiter blob.filename.extension_with_delimiter end diff --git a/activestorage/lib/active_storage/errors.rb b/activestorage/lib/active_storage/errors.rb index f099b13f5b..bedcd080c4 100644 --- a/activestorage/lib/active_storage/errors.rb +++ b/activestorage/lib/active_storage/errors.rb @@ -4,4 +4,8 @@ module ActiveStorage class InvariableError < StandardError; end class UnpreviewableError < StandardError; end class UnrepresentableError < StandardError; end + + # Raised when uploaded or downloaded data does not match a precomputed checksum. + # Indicates that a network error or a software bug caused data corruption. + class IntegrityError < StandardError; end end diff --git a/activestorage/lib/active_storage/service.rb b/activestorage/lib/active_storage/service.rb index 949969fc95..da1af4f745 100644 --- a/activestorage/lib/active_storage/service.rb +++ b/activestorage/lib/active_storage/service.rb @@ -3,8 +3,6 @@ require "active_storage/log_subscriber" module ActiveStorage - class IntegrityError < StandardError; end - # Abstract class serving as an interface for concrete services. # # The available services are: diff --git a/activestorage/test/models/blob_test.rb b/activestorage/test/models/blob_test.rb index a013b7a924..2d1857041d 100644 --- a/activestorage/test/models/blob_test.rb +++ b/activestorage/test/models/blob_test.rb @@ -84,7 +84,7 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase assert_equal "a" * 64.kilobytes, chunks.second end - test "open" do + test "open with integrity" do create_file_blob(filename: "racecar.jpg").open do |file| assert file.binmode? assert_equal 0, file.pos @@ -93,6 +93,16 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase end end + test "open without integrity" do + create_blob(data: "Hello, world!").tap do |blob| + blob.update! checksum: Digest::MD5.base64digest("Goodbye, world!") + + assert_raises ActiveStorage::IntegrityError do + blob.open { |file| flunk "Expected integrity check to fail" } + end + end + end + test "open in a custom tempdir" do tempdir = Dir.mktmpdir |