lib/ddr/antivirus/adapters/clamd_scanner_adapter.rb in ddr-antivirus-1.3.3 vs lib/ddr/antivirus/adapters/clamd_scanner_adapter.rb in ddr-antivirus-2.0.0.rc1
- old
+ new
@@ -1,69 +1,84 @@
+require "open3"
require "fileutils"
-require_relative "scanner_adapter"
-require_relative "scan_result"
+require "shellwords"
-module Ddr
- module Antivirus
- module Adapters
- #
- # Adapter for clamd client (clamdscan)
- #
- class ClamdScannerAdapter < ScannerAdapter
+module Ddr::Antivirus
+ #
+ # Adapter for clamd client (clamdscan)
+ #
+ class ClamdScannerAdapter < ScannerAdapter
- def scan(path)
- raw = clamdscan(path)
- ClamdScanResult.new(raw, path)
- end
+ SCANNER = "clamdscan".freeze
- def clamdscan(path)
- original_mode = File.stat(path).mode
- FileUtils.chmod("a+r", path) unless File.world_readable?(path)
- result = command(path)
- FileUtils.chmod(original_mode, path) if File.stat(path).mode != original_mode
- result
- end
+ def scan(path)
+ output, status = clamdscan(path)
+ result = ScanResult.new(path, output, version: version, scanned_at: Time.now.utc)
+ case status.exitstatus
+ when 0
+ result
+ when 1
+ raise VirusFoundError, result.to_s
+ when 2
+ raise ScannerError, result.to_s
+ end
+ end
- private
-
- def command(path)
- `clamdscan --no-summary "#{path}"`.strip
- end
-
+ def clamdscan(path)
+ make_readable(path) do
+ command(path)
end
+ end
- #
- # Result of a scan with the ClamdScannerAdapter
- #
- class ClamdScanResult < ScanResult
+ def version
+ out, err, status = Open3.capture3(SCANNER, "-V")
+ out.strip
+ end
- def virus_found
- if m = /: ([^\s]+) FOUND$/.match(raw)
- m[1]
- end
- end
+ private
- def has_virus?
- raw =~ / FOUND$/
- end
+ def command(path)
+ safe_path = Shellwords.shellescape(path)
+ Open3.capture2e(SCANNER, safe_path)
+ end
- def error?
- raw =~ / ERROR$/
- end
+ def make_readable(path)
+ changed = false
+ original = File.stat(path).mode # raises Errno::ENOENT
+ if !File.world_readable?(path)
+ changed = FileUtils.chmod("a+r", path)
+ logger.info "File #{path} made world-readable for virus scanning."
+ end
+ result = yield
+ if changed
+ FileUtils.chmod(original, path)
+ logger.info "Mode reset to original #{original} on file #{path}."
+ end
+ result
+ end
- def ok?
- raw =~ / OK$/
- end
+ end
- def to_s
- "#{raw} (#{version})"
- end
+ # Result of a scan with the ClamdScannerAdapter
+ # @api private
+ class ClamdScanResult < ScanResult
- def default_version
- `sigtool --version`.strip
- end
-
+ def virus_found
+ if m = /: ([^\s]+) FOUND$/.match(output)
+ m[1]
end
+ end
+ def ok?
+ status.exitstatus == 0
end
+
+ def has_virus?
+ status.exitstatus == 1
+ end
+
+ def error?
+ status.exitstatus == 2
+ end
+
end
end