ClamScan
Ruby wrapper for ClamAV's clamscan/clamdscan.
Features
- Lightweight pure-ruby wrapper
- System call arguments always properly escaped with
IO.popen
- Scan a data stream before writing to disk
- Supports and validates all arguments supported by clamscan/clamdscan
Installation
Gem
Add this line to your application's Gemfile:
gem 'clam_scan'
And then execute:
$ bundle
Or install it yourself as:
$ gem install clam_scan
ClamAV
You will also need ClamAV (and optionally the ClamAV daemon) installed. On Ubuntu:
$ sudo apt-get install clamav
will install ClamAV and the clamscan
cli client.
$ sudo apt-get install clamav-daemon
will install the ClamAV daemon and the 'clamdscan' cli client.
Other *nix systems can do the equivalent from their respective package manager or install from source. Mac users should be able to something equivalent with homebrew. Windows... good luck.
If you really have an aversion to installing the daemon on your development machine, that's OK, but be aware that using the daemon can be literally thousands of orders of magnitude faster for scanning small files because it doesn't have to load the virus database every time a scan is initiated. You'll also need to set client_location
to point to clamscan (see below).
Usage
Configuration
ClamScan.configure do |config|
# provide default options to be passed to ClamScan::Client.scan
# options passed directly to a call to ClamScan::Client.scan will override these
# by merging the default options with the passed options
config.default_scan_options = {stdout: true} # default (request all output to be sent to STDOUT so it can be captured)
# path to clamscan/clamdscan client
# try `which clamdscan` or `which clamscan` in your shell to see where you should point this to
# recommended to set to an absolute path to clamdscan
config.client_location = '/usr/bin/clamdscan' # default
# if set to true, ClamScan will raise an exception
# unless a scan is successful and no viruses were found
config.raise_unless_safe = false # default
end
Example Code
# scan file on disk
options = {location: '/path/to/file'}
# scan data stream
options = {stream: some_binary_data}
# initiate scan - returns ClamScan::Response object
respone = ClamScan::Client.scan(options)
# check output from clamscan
puts response.body
# check response status
response.safe? # true if scan was successful and no virus was found
response.virus? # true if scan was successful an virus was found
response.error? # true if scan returned with a known error status
response.unknown? # true if scan returned with an unknown status
response.status # one of [:error, :safe, :unknown, :virus]
# short snippet appropriate for most situations
unless ClamScan::Client.scan(location: '/path/to/file').safe?
# do something
end
Advanced Usage
ClamScan supports and validates any options supported by the clamscan cli client. The convention when passing options to ClamScan is to remove the first two leading hyphens and chang the remaining hyphens to underscores.
# equivalent to `clamscan --recursive --max-recursion=5 /path/to/dir`
ClamScan::Client.scan(location: '/path/to/dir', recursive: true, max_recursion: 5)
You can also pass an array of custom options that will not be validated.
# equivalent to `clamscan --recursive --max-recursion=5 /path/to/dir`
args = ['--recursive', '--max-recursion=5', '/path/to'dir']
ClamScan::Client.scan(custom_args: args)
ClamScan should support and validate any arguments supported by ClamAV 0.98. See lib/clam_scan/request.rb
and ClamAV's man page.
Deleting infected files
ClamScan does not delete infected files by default, conforming with the defaults of clamscan/clamdscan. If you want that behaviour, you can do something like:
ClamScan.configure do |config|
# merge instead of re-assign so as to not blow away {stdout: true} default
config.default_scan_options.merge {remove: 'yes'}
end
Or you could arrange to have them moved or copied somewhere. See man clamscan
.
Exceptions
Any exception raised by ClamScan should be a subclass of ClamScan::Error
.
If an unrecognized option is passed to ClamScan::Client::scan
or an error occurs while making attempting to make the system call, a ClamScan::RequestError
is raised.
If config.raise_unless_safe
is true, a ClamScan::VirusDetected
, ClamScan::UnknownError
or ClamScan::ResponseError
could be raised. The first two are subclasses of the last and all have a response
attribute that contains a ClamScan::Response
object that can be further inspected.
Rails
ClamScan is a pure ruby wrapper and thus can be used in any ruby project, including one using rails.
# add virus-free validation with carrierwave
validate :virus_free
def virus_free
if self.file.present? && !ClamScan::Client.scan(location: self.file.url).safe?
File.delete(self.file.url)
errors.add(:file, 'That file can not be accepted')
end
end
Contributing
- Fork it ( https://github.com/jschroeder9000/clam_scan/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request