Scan for embedded viruses/executables in uploaded files

This commit is contained in:
Kyle Drake 2014-08-10 18:46:28 -07:00
parent e756724fe7
commit cc64a57172
4 changed files with 43 additions and 5 deletions

View file

@ -18,6 +18,7 @@ gem 'tilt'
gem 'erubis'
gem 'stripe', :git => 'https://github.com/stripe/stripe-ruby'
gem 'screencap'
gem 'cocaine'
platform :mri do
gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic

View file

@ -10,6 +10,12 @@ GIT
GEM
remote: https://rubygems.org/
specs:
activesupport (4.1.4)
i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.1)
tzinfo (~> 1.1)
addressable (2.3.6)
ago (0.1.5)
ansi (1.4.3)
@ -30,7 +36,11 @@ GEM
minitest (>= 2)
celluloid (0.15.2)
timers (~> 1.1.0)
climate_control (0.0.3)
activesupport (>= 3.0)
cliver (0.3.2)
cocaine (0.5.4)
climate_control (>= 0.0.3, < 1.0)
coderay (1.1.0)
columnize (0.3.6)
connection_pool (2.0.0)
@ -168,11 +178,14 @@ GEM
sinatra-xsendfile (0.4.2)
sinatra (>= 0.9.1)
slop (3.5.0)
thread_safe (0.3.4)
tilt (1.4.1)
timers (1.1.0)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (1.2.2)
thread_safe (~> 0.1)
unicorn (4.8.2)
kgio (~> 2.6)
rack
@ -193,6 +206,7 @@ DEPENDENCIES
bcrypt
capybara
capybara_minitest_spec
cocaine
erubis
fabrication
faker

4
app.rb
View file

@ -636,7 +636,7 @@ post '/site_files/upload' do
end
if !Site.valid_file_type? file
file_upload_response "#{file[:filename]}: file type is not allowed on Neocities, upload cancelled."
file_upload_response "#{file[:filename]}: file type (or content in file) is not allowed on Neocities, upload cancelled."
end
end
@ -941,7 +941,7 @@ post '/api/upload' do
files.each do |file|
if !Site.valid_file_type?(file)
api_error 400, 'invalid_file_type', "#{file[:filename]} is not a valid file type, files have not been uploaded"
api_error 400, 'invalid_file_type', "#{file[:filename]} is not a valid file type (or contains not allowed content), files have not been uploaded"
end
end

View file

@ -60,6 +60,12 @@ class Site < Sequel::Model
SCREENSHOT_RESOLUTIONS = ['235x141', '105x63', '270x162', '37x37', '146x88', '302x182', '90x63', '82x62', '348x205']
THUMBNAIL_RESOLUTIONS = ['105x63', '90x63']
CLAMAV_THREAT_MATCHES = [
/^VBS/,
/^PUA.Win32/,
/^JS.Popupper/
]
BANNED_TIME = 2592000 # 30 days in seconds
TITLE_MAX = 100
@ -261,9 +267,26 @@ class Site < Sequel::Model
def self.valid_file_type?(uploaded_file)
mime_type = Magic.guess_file_mime_type uploaded_file[:tempfile].path
return true if (Site::VALID_MIME_TYPES.include?(mime_type) || mime_type =~ /text/) &&
Site::VALID_EXTENSIONS.include?(File.extname(uploaded_file[:filename]).sub(/^./, '').downcase)
false
return false unless (Site::VALID_MIME_TYPES.include?(mime_type) || mime_type =~ /text/) &&
Site::VALID_EXTENSIONS.include?(File.extname(uploaded_file[:filename]).sub(/^./, '').downcase)
File.chmod 0640, uploaded_file[:tempfile].path
line = Cocaine::CommandLine.new(
"clamdscan", "-i --remove=no --no-summary --stdout :path",
expected_outcodes: [0, 1]
)
output = line.run path: uploaded_file[:tempfile].path
return true if output == ''
threat = output.strip.match(/^.+: (.+) FOUND$/).captures.first
CLAMAV_THREAT_MATCHES.each do |threat_match|
return false if threat.match threat_match
end
true
end
def store_file(filename, uploaded)