unclesp1d3r/CipherSwarm

View on GitHub
app/jobs/process_hash_list_job.rb

Summary

Maintainability
A
3 hrs
Test Coverage
A
90%
# frozen_string_literal: true

# SPDX-FileCopyrightText:  2024 UncleSp1d3r
# SPDX-License-Identifier: MPL-2.0

#
# The ProcessHashListJob class is responsible for processing a HashList identified by a given ID.
# It retrieves the HashList object, checks if it has already been processed, and if not, processes
# each line in the associated file. For each line, it creates a HashItem, validates it, and adds it
# to the HashList. Additionally, it checks if the hash value has already been cracked and updates
# the HashItem accordingly.
#
# The job is configured to retry on specific errors:
# - ActiveStorage::FileNotFoundError: Retries with a polynomially increasing wait time, up to 10 attempts.
# - ActiveRecord::RecordNotFound: Retries with a polynomially increasing wait time, up to 3 attempts.
#
# @param id [Integer] the ID of the HashList to be processed
# @return [void]
# @raise [StandardError] if there is an error during file processing
class ProcessHashListJob < ApplicationJob
  queue_as :ingest
  retry_on ActiveStorage::FileNotFoundError, wait: :polynomially_longer, attempts: 10
  retry_on ActiveRecord::RecordNotFound, wait: :polynomially_longer, attempts: 3

  # Performs the processing of a HashList identified by the given ID.
  #
  # This method retrieves the HashList object, checks if it has already been processed,
  # and if not, processes each line in the associated file. For each line, it creates
  # a HashItem, validates it, and adds it to the HashList. Additionally, it checks if
  # the hash value has already been cracked and updates the HashItem accordingly.
  #
  # @param id [Integer] the ID of the HashList to be processed
  # @return [void]
  # @raise [StandardError] if there is an error during file processing
  def perform(id)
    list = HashList.find(id)
    return if list.processed?

    HashList.transaction do
      list.file.open do |file|
        file.each_line do |line|
          next if line.blank?

          line.strip!
          hi = HashItem.build(hash_value: line, metadata: {}, hash_list: list)
          list.hash_items << hi if hi.valid?

          cracked_hash = HashItem.includes(:hash_list)
                                 .where(hash_value: line, cracked: true, hash_list: { hash_type_id: list.hash_type_id })
                                 .first
          if cracked_hash
            cracked = hi.update(plain_text: cracked_hash.plain_text, cracked: true, cracked_time: Time.zone.now, attack: cracked_hash.attack)
            Rails.logger.error("Found a cracked hash: #{cracked_hash.hash_value}, but failed to update hash item") unless cracked
          end
        end
      end

      list.processed = true if list.hash_items.any?
      Rails.logger.error("Failed to ingest hash items") unless list.save
    end
  end
end