Skip to content

Commit 637e04e

Browse files
committed
extra security check - check db for saved file names
1 parent 51bf9f6 commit 637e04e

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

app/controllers/uploads_controller.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
@@ -47,9 +48,33 @@ def parse_upload_path
4748

4849
model_key = path_parts[0].downcase
4950
@model_name = MODELS_OVERRIDES[model_key] || model_key
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+
model_class = @model_name.classify.constantize
60+
record = model_class.find(@record_id)
61+
uploader = record.public_send(@uploader_name)
62+
db_filenames = [uploader.file.file, *uploader.versions.values.map { |v| v.file.file }].map { |f| File.basename(f) }
63+
64+
unless db_filenames.include?(File.basename(@sanitized_filepath))
65+
raise_not_found_exception
66+
end
67+
rescue NameError, ActiveRecord::RecordNotFound
68+
raise_not_found_exception
69+
end
70+
71+
def allowed_models
72+
Dir.entries(Rails.root.join("uploads"))
73+
.select { |entry| File.directory?(Rails.root.join("uploads", entry)) }
74+
.reject { |entry| entry.start_with?(".") || entry == "tmp" }
75+
.map { |entry| ALLOWED_MODELS_OVERRIDES[entry.downcase] || entry }
76+
end
77+
5378
def track_download
5479
TrackFileDownloadJob.perform_later(client_id, request.url, @filename, @model_name)
5580
end

spec/factories/contributors.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
priority { rand(0..10) }
2222
logo { Rack::Test::UploadedFile.new(File.join(Rails.root, "spec", "support", "files", "image.png")) }
2323

24-
factory :partner do
24+
factory :partner, class: Partner do
2525
type { "Partner" }
2626
end
2727

28-
factory :donor do
28+
factory :donor, class: Donor do
2929
type { "Donor" }
3030
end
3131
end

spec/integration/uploads_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@
4040

4141
expect(response).to have_http_status(:not_found)
4242
end
43+
44+
it "returns 404 for existing files but not in db" do
45+
new_file = File.join(File.dirname(@document_file.attachment.file.file), "newfile.txt")
46+
File.write(new_file, "test")
47+
48+
get "/uploads/operator_document_file/attachment/#{@document_file.id}/newfile.txt"
49+
50+
expect(response).to have_http_status(:not_found)
51+
end
4352
end
4453

4554
context "invalid path handling" do

0 commit comments

Comments
 (0)