carrierwave-virus_free 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 53ff921bee314e66dc7b2c62718af922b265b4cc
4
- data.tar.gz: 8ce52e4ec315c7293a67ace9fe1cf61cb4760a20
3
+ metadata.gz: ee02200813acfb4ff5961f083501393455a85ef7
4
+ data.tar.gz: 01a9f1759272d7ca857484a33d5d1ef0d78296c0
5
5
  SHA512:
6
- metadata.gz: 285d309ed1f62b5f289bfdd3a7155ed5c64688b1f72efc2dc5f436712cea1cac31e3e41065a9d34b15c5fe7031f6b9f6aed27801fcf0b2d59cc5a21f220cee88
7
- data.tar.gz: 469e6e5c8f2e1684f2c64cd8bc9d4d9ff92e1ae2a9b9d485ef60e0861b6cccbf0b4745fdda6ee9bc1392d0e244d68c1741fc70096d83fcd2173f0ab4dc657758
6
+ metadata.gz: 071763af5b7df8061b5200e1139b8dede35b092e8b875eda20b84675ed8caf4c3b49e45aecfa6fecc7028902f11f5be327cfbd43af70ba8f07dc04bfdc79ecf1
7
+ data.tar.gz: e22c0e0f4080c3db77e221bb4a03c9ade87c127e7d6b0fc2a888a7082eb9d289d27e88ca359033f4efd7b0578a558204310bc3875983c9f9b26688c1e1af08ea
data/.gitignore CHANGED
@@ -21,3 +21,5 @@ tmp
21
21
  *.a
22
22
  mkmf.log
23
23
  spec/data/
24
+ .vagrant/
25
+ spec/internal/db/*.sqlite
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --color
2
- --warnings
3
2
  --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## Unreleased
2
+
3
+ * Near-rewrite
4
+ * Use combustion for testing
5
+ * Don't re-scan a file that hasn't changed
6
+ * I18n railtie (closes [#1](https://github.com/jschroeder9000/carrierwave-virus_free/issues/1))
7
+ * Differentiate between error and unsafe (closes [#2](https://github.com/jschroeder9000/carrierwave-virus_free/issues/1))
data/Gemfile CHANGED
@@ -2,5 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in carrierwave-virus_free_validator.gemspec
4
4
  gemspec
5
-
6
- gem 'clam_scan', path: '/home/jschroeder/rails/clam_scan'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Carrierwave::VirusFreeValidator
1
+ # Carrierwave::VirusFree
2
2
 
3
3
  Validate carrierwave uploads are virus free with clamav.
4
4
 
@@ -36,9 +36,23 @@ end
36
36
 
37
37
  It's that easy.
38
38
 
39
+ ## I18n
40
+
41
+ Validation error messages are processed through I18n. See [lib/carrierwave/virus_free/locale/en.yml](https://github.com/jschroeder9000/carrierwave-virus_free/blob/master/lib/carrierwave/virus_free/locale/en.yml) for default messages and relevant keys to override.
42
+
43
+ ## Lazy validation
44
+
45
+ As of 0.1.0, validation is lazy in that it does nothing if the model attribute with the uploader mounted has not changed. This increases compatibility in cases where the final storage location of a file is not local, but also implies that a file _could_ be changed to something malicious by an external process and would not be caught by further validation. E.g.:
46
+
47
+ * User uploads safe file which gets saved somewhere
48
+ * Some external process overwrites this file with something malicious
49
+ * You call `model.valid?` and it returns true because your model is not aware of the file changing (`model.file_changed?` is false) even though the file is now something that clamav would catch as malicious
50
+
51
+ Please be aware of this caveat. You can protect against this by preventing external processes from modifying you application's files and/or you can use [clam_scan](https://github.com/jschroeder9000/clam_scan) directly.
52
+
39
53
  ## Contributing
40
54
 
41
- 1. Fork it ( https://github.com/[my-github-username]/carrierwave-virus_free_validator/fork )
55
+ 1. Fork it ( https://github.com/jschroeder9000/carrierwave-virus_free_validator/fork )
42
56
  2. Create your feature branch (`git checkout -b my-new-feature`)
43
57
  3. Commit your changes (`git commit -am 'Add some feature'`)
44
58
  4. Push to the branch (`git push origin my-new-feature`)
data/Rakefile CHANGED
@@ -1,2 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ task :console do
4
+ exec 'pry -r carrierwave/virus_free -I ./lib'
5
+ end
data/Vagrantfile ADDED
@@ -0,0 +1,44 @@
1
+ Vagrant.configure(2) do |config|
2
+ config.vm.box = 'ubuntu/trusty64'
3
+
4
+ config.vm.network :private_network, type: 'dhcp'
5
+
6
+ config.vm.provider 'virtualbox' do |v|
7
+ v.cpus = ENV['VAGRANT_CPUS'] || 4
8
+ v.memory = ENV['VAGRANT_MEMORY'] || 1024
9
+ end
10
+
11
+ if ENV['VAGRANT_NFS']
12
+ config.vm.synced_folder '.', '/vagrant', type: 'nfs'
13
+ elsif ENV['VAGRANT_RSYNC']
14
+ config.vm.synced_folder '.', '/vagrant', type: 'rsync'
15
+ end
16
+
17
+ # install packages
18
+ config.vm.provision 'shell', inline: <<-SCRIPT
19
+ apt-get update &&
20
+ apt-get -y install \
21
+ clamav-daemon \
22
+ git \
23
+ libgmp-dev
24
+ SCRIPT
25
+
26
+ # disable clamav apparmor
27
+ config.vm.provision 'shell', inline: <<-SCRIPT
28
+ ln -s /etc/apparmor.d/local/usr.sbin.clamd /etc/apparmor.d/disable &&
29
+ service apparmor restart
30
+ SCRIPT
31
+
32
+ # install ruby/rvm
33
+ config.vm.provision 'shell', privileged: false, inline: <<-SCRIPT
34
+ curl -sSL https://rvm.io/mpapis.asc | gpg --import - &&
35
+ curl -sSL https://get.rvm.io | bash -s stable --ruby=2.2.4
36
+ SCRIPT
37
+
38
+ # install gems
39
+ config.vm.provision 'shell', privileged: false, inline: <<-SCRIPT
40
+ cd /vagrant &&
41
+ gem install bundler &&
42
+ bundle install
43
+ SCRIPT
44
+ end
@@ -22,7 +22,11 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency "carrierwave"
23
23
  spec.add_dependency "clam_scan"
24
24
 
25
+ spec.add_development_dependency "activerecord"
25
26
  spec.add_development_dependency "bundler", "~> 1.6"
27
+ spec.add_development_dependency "combustion", "~> 0.5.3"
28
+ spec.add_development_dependency "pry-byebug"
26
29
  spec.add_development_dependency "rake"
27
- spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "rspec-rails", "~> 3.4.0"
31
+ spec.add_development_dependency "sqlite3"
28
32
  end
data/config.ru ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize! :all
7
+ run Combustion::Application
@@ -1,14 +1,12 @@
1
+ require 'active_model'
1
2
  require 'clam_scan'
2
3
 
4
+ require 'carrierwave/virus_free/railtie'
5
+ require 'carrierwave/virus_free/version'
6
+ require 'carrierwave/virus_free/virus_free_validator'
7
+
3
8
  module CarrierWave
4
9
  module VirusFree
5
- class VirusFreeValidator < ActiveModel::EachValidator
6
- def validate_each (record, attribute, value)
7
- if value.present? && !ClamScan::Client.scan(location: value.url).safe?
8
- record.errors.add(attribute, 'That file can not be accepted')
9
- end
10
- end
11
- end
12
10
  end
13
11
  end
14
12
 
@@ -0,0 +1,5 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ virus_free_error: 'could not be processed'
5
+ virus_free_unsafe: 'could not be accepted'
@@ -0,0 +1,25 @@
1
+ # heavily borrowed from https://github.com/carrierwaveuploader/carrierwave/blob/ecf57fe5fed17657b258d708a59feeec9fac74cd/lib/carrierwave.rb
2
+
3
+ module CarrierWave
4
+ module VirusFree
5
+ if defined? Rails
6
+ class Railtie < Rails::Railtie
7
+ initializer "carrierwave_virus_free.setup_paths" do |app|
8
+ pattern = CarrierWave::VirusFree::Railtie.locales_pattern_from app.config.i18n.available_locales
9
+
10
+ files = Dir[File.join(File.dirname(__FILE__), 'locale', "#{pattern}.yml")]
11
+ # Loads the Carrierwave locale files before the Rails application locales
12
+ # letting the Rails application overrite the carrierwave locale defaults
13
+ I18n.load_path = files.concat I18n.load_path
14
+ end
15
+
16
+ private
17
+
18
+ def self.locales_pattern_from(args)
19
+ array = Array(args || [])
20
+ array.blank? ? '*' : "{#{array.join ','}}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  module Carrierwave
2
2
  module VirusFree
3
- VERSION = "0.0.3"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -0,0 +1,16 @@
1
+ module CarrierWave
2
+ module VirusFree
3
+ class VirusFreeValidator < ActiveModel::EachValidator
4
+ def validate_each(record, attribute, value)
5
+ return unless record.send :"#{attribute}_changed?"
6
+
7
+ response = ClamScan::Client.scan(location: value.path)
8
+ if response.error? || response.unknown?
9
+ record.errors.add attribute, :virus_free_error
10
+ elsif !response.safe?
11
+ record.errors.add attribute, :virus_free_unsafe
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ class User < ActiveRecord::Base
2
+ mount_uploader :avatar, CarrierWave::Uploader::Base
3
+ validates :avatar, virus_free: true
4
+ end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/combustion_test.sqlite
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -0,0 +1,5 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :users, force: true do |t|
3
+ t.string :avatar
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe User do
4
+ describe 'i18n' do
5
+ context 'error' do
6
+ before do
7
+ mock_clam_scan_response 2
8
+ subject.avatar = File.open safe_path
9
+ subject.valid?
10
+ end
11
+
12
+ it 'uses i18n' do
13
+ expect(subject.errors.full_messages).to include('Avatar could not be processed')
14
+ end
15
+ end
16
+
17
+ context 'unknown' do
18
+ before do
19
+ mock_clam_scan_response 4
20
+ subject.avatar = File.open safe_path
21
+ subject.valid?
22
+ end
23
+
24
+ it 'uses i18n' do
25
+ expect(subject.errors.full_messages).to include('Avatar could not be processed')
26
+ end
27
+ end
28
+
29
+ context 'unsafe' do
30
+ before do
31
+ subject.avatar = File.open eicar_path
32
+ subject.valid?
33
+ end
34
+
35
+ it 'uses i18n' do
36
+ expect(subject.errors.full_messages).to include('Avatar could not be accepted')
37
+ end
38
+ end
39
+ end
40
+
41
+ describe 'validation' do
42
+ context 'error scanning' do
43
+ before do
44
+ mock_clam_scan_response 2
45
+ subject.avatar = File.open safe_path
46
+ end
47
+
48
+ it 'is not valid' do
49
+ expect(subject).not_to be_valid
50
+ end
51
+ end
52
+
53
+ context 'nothing is uploaded' do
54
+ it 'is valid' do
55
+ expect(subject).to be_valid
56
+ end
57
+ end
58
+
59
+ context 'safe file is uploaded' do
60
+ before do
61
+ subject.avatar = File.open safe_path
62
+ end
63
+
64
+ it 'is valid' do
65
+ expect(subject).to be_valid
66
+ end
67
+ end
68
+
69
+ context 'unknown clamav response' do
70
+ before do
71
+ mock_clam_scan_response 4
72
+ subject.avatar = File.open safe_path
73
+ end
74
+
75
+ it 'is not valid' do
76
+ expect(subject).not_to be_valid
77
+ end
78
+ end
79
+
80
+ context 'unsafe file is uploaded' do
81
+ before do
82
+ subject.avatar = File.open eicar_path
83
+ end
84
+
85
+ it 'is not valid' do
86
+ expect(subject).not_to be_valid
87
+ end
88
+ end
89
+ end
90
+ end
data/spec/spec_helper.rb CHANGED
@@ -17,9 +17,17 @@
17
17
 
18
18
  # end initial stuff
19
19
 
20
- require 'active_model'
20
+ require 'combustion'
21
+
22
+ require 'active_record'
23
+ require 'carrierwave'
21
24
  require 'carrierwave/virus_free'
22
- require 'clam_scan'
25
+ require 'pry'
26
+ require 'sqlite3'
27
+
28
+ Combustion.initialize! :active_record
29
+
30
+ require 'rspec/rails'
23
31
 
24
32
  Dir[File.expand_path(File.join('..', 'support', '**', '*.rb'), __FILE__)].each { |f| require f }
25
33
 
@@ -85,7 +93,13 @@ RSpec.configure do |config|
85
93
 
86
94
  # end default stuff
87
95
 
88
- config.before(:suite) do
96
+ config.include DataHelpers
97
+ config.include MockHelpers
98
+
99
+ # this should really be before :suite
100
+ # but it doesn't work and i'm not exactly sure why
101
+ # http://stackoverflow.com/q/34403095/4283486
102
+ config.before(:context) do
89
103
  # make data dir if it doesn't exist
90
104
  unless File.directory? data_path
91
105
  require 'fileutils'
@@ -0,0 +1,13 @@
1
+ module DataHelpers
2
+ def data_path
3
+ File.expand_path(File.join('..', '..', 'data'), __FILE__)
4
+ end
5
+
6
+ def eicar_path
7
+ File.join(data_path, 'eicar.com')
8
+ end
9
+
10
+ def safe_path
11
+ File.join(data_path, 'foo.txt')
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ module MockHelpers
2
+ def mock_clam_scan_response(exit_status)
3
+ process_status = instance_double('Process::Status', exitstatus: exit_status)
4
+ allow(ClamScan::Client).to receive(:scan).and_return(ClamScan::Response.new(process_status, ''))
5
+ end
6
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carrierwave-virus_free
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Schroeder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-30 00:00:00.000000000 Z
11
+ date: 2015-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,34 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '1.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: combustion
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.5.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.5.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: rake
71
113
  requirement: !ruby/object:Gem::Requirement
@@ -81,7 +123,21 @@ dependencies:
81
123
  - !ruby/object:Gem::Version
82
124
  version: '0'
83
125
  - !ruby/object:Gem::Dependency
84
- name: rspec
126
+ name: rspec-rails
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 3.4.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 3.4.0
139
+ - !ruby/object:Gem::Dependency
140
+ name: sqlite3
85
141
  requirement: !ruby/object:Gem::Requirement
86
142
  requirements:
87
143
  - - ">="
@@ -103,17 +159,29 @@ extra_rdoc_files: []
103
159
  files:
104
160
  - ".gitignore"
105
161
  - ".rspec"
162
+ - CHANGELOG.md
106
163
  - Gemfile
107
164
  - LICENSE.txt
108
165
  - README.md
109
166
  - Rakefile
167
+ - Vagrantfile
110
168
  - carrierwave-virus_free.gemspec
169
+ - config.ru
111
170
  - lib/carrierwave/virus_free.rb
171
+ - lib/carrierwave/virus_free/locale/en.yml
172
+ - lib/carrierwave/virus_free/railtie.rb
112
173
  - lib/carrierwave/virus_free/version.rb
174
+ - lib/carrierwave/virus_free/virus_free_validator.rb
175
+ - spec/internal/app/models/user.rb
176
+ - spec/internal/config/database.yml
177
+ - spec/internal/config/routes.rb
178
+ - spec/internal/db/schema.rb
179
+ - spec/internal/log/.gitignore
180
+ - spec/internal/public/favicon.ico
181
+ - spec/models/user_spec.rb
113
182
  - spec/spec_helper.rb
114
- - spec/support/data.rb
115
- - spec/support/mock.rb
116
- - spec/virus_free_validator_spec.rb
183
+ - spec/support/data_helpers.rb
184
+ - spec/support/mock_helpers.rb
117
185
  homepage: https://github.com/jschroeder9000/carrierwave-virus_free
118
186
  licenses:
119
187
  - MIT
@@ -134,12 +202,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
202
  version: '0'
135
203
  requirements: []
136
204
  rubyforge_project:
137
- rubygems_version: 2.2.2
205
+ rubygems_version: 2.4.8
138
206
  signing_key:
139
207
  specification_version: 4
140
208
  summary: Validate carrierwave uploads are virus free with clamav.
141
209
  test_files:
210
+ - spec/internal/app/models/user.rb
211
+ - spec/internal/config/database.yml
212
+ - spec/internal/config/routes.rb
213
+ - spec/internal/db/schema.rb
214
+ - spec/internal/log/.gitignore
215
+ - spec/internal/public/favicon.ico
216
+ - spec/models/user_spec.rb
142
217
  - spec/spec_helper.rb
143
- - spec/support/data.rb
144
- - spec/support/mock.rb
145
- - spec/virus_free_validator_spec.rb
218
+ - spec/support/data_helpers.rb
219
+ - spec/support/mock_helpers.rb
data/spec/support/data.rb DELETED
@@ -1,11 +0,0 @@
1
- def data_path
2
- File.expand_path(File.join('..', '..', 'data'), __FILE__)
3
- end
4
-
5
- def eicar_path
6
- File.join(data_path, 'eicar.com')
7
- end
8
-
9
- def safe_path
10
- File.join(data_path, 'foo.txt')
11
- end
data/spec/support/mock.rb DELETED
@@ -1,29 +0,0 @@
1
- # okay... this isn't exactly great
2
- # but after spending too much time looking for the perfect way to test this, it's good enough for now
3
- # something better is welcome
4
-
5
- class MyModel
6
- include ActiveModel::Validations
7
-
8
- attr_accessor :file
9
- validates :file, :virus_free => true
10
-
11
- def initialize (present, virus)
12
- @file = MyFile.new(present, virus)
13
- end
14
- end
15
-
16
- class MyFile
17
- def initialize (present, virus)
18
- @present = present
19
- @virus = virus
20
- end
21
-
22
- def present?
23
- @present
24
- end
25
-
26
- def url
27
- @virus ? eicar_path : safe_path
28
- end
29
- end
@@ -1,15 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe CarrierWave::VirusFree do
4
- it 'is not valid when a virus is uploaded' do
5
- expect(MyModel.new(true, true).valid?).to be_falsey
6
- end
7
-
8
- it 'is valid when something not infected is uploaded' do
9
- expect(MyModel.new(true, false).valid?).to be_truthy
10
- end
11
-
12
- it 'is valid when nothing is uploaded' do
13
- expect(MyModel.new(false, false).valid?).to be_truthy
14
- end
15
- end