carrierwave-virus_free 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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