@@ -21,6 +21,7 @@ class UploadsController < ApplicationController
2121 def download
2222 sanitize_filepath
2323 parse_upload_path
24+ ensure_valid_db_record
2425 track_download if trackable_request?
2526 send_file @sanitized_filepath , disposition : :inline
2627 end
@@ -46,10 +47,38 @@ def parse_upload_path
4647 raise_not_found_exception if path_parts . length < 4
4748
4849 model_key = path_parts [ 0 ] . downcase
49- @model_name = MODELS_OVERRIDES [ model_key ] || model_key
50+ @model_name = ( MODELS_OVERRIDES [ model_key ] || model_key ) . downcase
51+ @uploader_name = path_parts [ 1 ] . downcase
52+ @record_id = path_parts [ 2 ] . to_i
5053 @filename = path_parts [ 3 ..] . join ( "/" )
5154 end
5255
56+ # extra security step, do not let download antything for removed records or not saved in DB
57+ # we already have sanitized file path that should be enough, but this also will ensure no protected file is downloaded
58+ def ensure_valid_db_record
59+ raise_not_found_exception unless allowed_models . include? ( @model_name )
60+
61+ model_class = @model_name . classify . constantize
62+ raise_not_found_exception unless model_class . uploaders . keys . map ( &:to_s ) . include ( @uploader_name ) # ensure valid uploader
63+
64+ record = model_class . find ( @record_id )
65+ uploader = record . public_send ( @uploader_name )
66+ db_filenames = [ uploader . file . file , *uploader . versions . values . map { |v | v . file . file } ] . map { |f | File . basename ( f ) }
67+
68+ unless db_filenames . include? ( File . basename ( @sanitized_filepath ) )
69+ raise_not_found_exception
70+ end
71+ rescue NameError , ActiveRecord ::RecordNotFound
72+ raise_not_found_exception
73+ end
74+
75+ def allowed_models
76+ Dir . entries ( Rails . root . join ( "uploads" ) )
77+ . select { |entry | File . directory? ( Rails . root . join ( "uploads" , entry ) ) }
78+ . reject { |entry | entry . start_with? ( "." ) || entry == "tmp" }
79+ . map { |entry | MODELS_OVERRIDES [ entry . downcase ] || entry }
80+ end
81+
5382 def track_download
5483 TrackFileDownloadJob . perform_later ( client_id , request . url , @filename , @model_name )
5584 end
0 commit comments