class S3Antivirus::Scan

Public Class Methods

new(options={}) click to toggle source
# File lib/s3_antivirus/scan.rb, line 15
def initialize(options={})
  @options = options
end

Public Instance Methods

run() click to toggle source
# File lib/s3_antivirus/scan.rb, line 19
def run
  logger.info "Polling SQS queue for S3 antivirus findings. Started #{Time.now}..."
  return if @options[:noop]

  poller = Aws::SQS::QueuePoller.new(conf['queue'])
  poller.poll do |msg|
    begin
      body = JSON.parse(msg.body)

      logger.debug "body #{body}"
      if body.key?('Records')
        body['Records'].each do |record|
          # Instance variables change on each iteration
          @s3_record = S3Record.new(record)
          @notifier = Notifier.new(@s3_record)
          @tagger = Tagger.new(@s3_record)

          logger.info("Checking #{@s3_record.human_key}")
          within_size_limit = check_file_size
          next unless within_size_limit # continue to next file because oversized
          filename = "/tmp/#{SecureRandom.uuid}"
          success = download_file(filename)
          next unless success # continue to next file because unable to download
          scan_file(filename)
        end
      end
    rescue Exception => e
      logger.error "message failed: #{e.inspect} #{msg.inspect}"
      raise e
    end
    # Sometimes stdout doesnt get shown but the message is processed file.
    # Know this because the SNS message gets sent.
    # Even this $stdout.flush doesn't seem to be enough.
    $stdout.flush
  end
end

Private Instance Methods

check_file_size() click to toggle source
# File lib/s3_antivirus/scan.rb, line 124
def check_file_size
  within_size_limit = true
  if @s3_record.oversized?
    logger.info "#{@s3_record.human_key} is too large. Bigger than half of the EBS volume, skipping."
    if conf['tag_files']
      @tagger.tag("oversized")
    end
    @notifier.notify(status: "oversized", action: "no")
    within_size_limit = false
  end
  within_size_limit
end
clean_file_found() click to toggle source
# File lib/s3_antivirus/scan.rb, line 72
def clean_file_found
  if conf['tag_files']
    logger.info "#{human_key} is clean (tagging)"
    @tagger.tag("clean");
  else
    logger.info "#{human_key} is clean"
  end
  if conf['report_clean']
    @notifier.notify(status: "clean", action: "no");
  end
end
download_file(filename) click to toggle source
# File lib/s3_antivirus/scan.rb, line 106
def download_file(filename)
  logger.info "Downloading #{@s3_record.human_key} to #{filename}..."
  params = {
    response_target: filename,
    bucket: @s3_record.bucket,
    key: @s3_record.key,
  }
  params[:version_id] = @s3_record.version if @s3_record.version

  begin
    s3.get_object(params)
  rescue Aws::S3::Errors::NoSuchKey
    logger.info "#{@s3_record.human_key} no longer exist, skipping."
    return false
  end
  true # successful download
end
human_key() click to toggle source
# File lib/s3_antivirus/scan.rb, line 102
def human_key
  @s3_record.human_key
end
scan_file(filename) click to toggle source
# File lib/s3_antivirus/scan.rb, line 57
def scan_file(filename)
  logger.info "Scanning #{human_key}..."
  sh("clamdscan #{filename}")
  exitstatus = $?.exitstatus
  if exitstatus == 0 # No virus found.
    clean_file_found
  elsif exitstatus == 1 # Virus(es) found.
    virus_found
  else # An error occured.
    raise "#{human_key} could not be scanned, clamdscan exit status was #{exitstatus}, retry"
  end
ensure
  system("rm #{filename}")
end
sh(command) click to toggle source
# File lib/s3_antivirus/scan.rb, line 137
def sh(command)
  logger.info("=> #{command}")
  system(command)
end
virus_found() click to toggle source
# File lib/s3_antivirus/scan.rb, line 84
def virus_found
  if conf['delete']
    logger.info "#{human_key} is infected (deleting)"
    s3.delete_object(
      bucket: @s3_record.bucket,
      key: @s3_record.key,
    )
    @notifier.notify(status: "infected", action: "delete");
  elsif conf['tag_files']
    logger.info "#{human_key} is infected (tagging)"
    @tagger.tag("infected");
    @notifier.notify(status: "infected", action: "tag");
  else
    logger.info "#{human_key} is infected"
    @notifier.notify(status: "infected", action: "no");
  end
end