diff --git a/Gemfile.lock b/Gemfile.lock index 1922b38e..d16da6bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -112,7 +112,7 @@ GEM activerecord (>= 5.2, < 7.2) activesupport (>= 5.2, < 7.2) aws-eventstream (1.3.0) - aws-partitions (1.940.0) + aws-partitions (1.943.0) aws-sdk-core (3.197.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) @@ -121,7 +121,7 @@ GEM aws-sdk-kms (1.83.0) aws-sdk-core (~> 3, >= 3.197.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.152.0) + aws-sdk-s3 (1.152.1) aws-sdk-core (~> 3, >= 3.197.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) @@ -160,7 +160,7 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) cocoon (1.2.15) - concurrent-ruby (1.3.1) + concurrent-ruby (1.3.3) connection_pool (2.4.1) crass (1.0.6) cssbundling-rails (1.4.0) @@ -304,11 +304,11 @@ GEM racc (~> 1.4) nokogiri (1.16.5-x86_64-linux) racc (~> 1.4) - oj (3.16.3) + oj (3.16.4) bigdecimal (>= 3.0) orm_adapter (0.5.0) pagy (8.4.4) - parallel (1.24.0) + parallel (1.25.1) parser (3.3.2.0) ast (~> 2.4.1) racc @@ -323,7 +323,7 @@ GEM nio4r (~> 2.0) raabro (1.4.0) racc (1.8.0) - rack (3.0.11) + rack (3.1.2) rack-mini-profiler (3.3.1) rack (>= 1.2.0) rack-session (2.0.0) @@ -377,8 +377,8 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.0) + strscan rspec-core (3.13.0) rspec-support (~> 3.13.0) rspec-expectations (3.13.0) @@ -426,10 +426,8 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.31.3) parser (>= 3.3.1.0) - rubocop-capybara (2.20.0) - rubocop (~> 1.41) - rubocop-factory_bot (2.25.1) - rubocop (~> 1.41) + rubocop-factory_bot (2.26.1) + rubocop (~> 1.61) rubocop-minitest (0.35.0) rubocop (>= 1.61, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) @@ -450,13 +448,8 @@ GEM rubocop-rails rubocop-rake (0.6.0) rubocop (~> 1.0) - rubocop-rspec (2.30.0) - rubocop (~> 1.40) - rubocop-capybara (~> 2.17) - rubocop-factory_bot (~> 2.22) - rubocop-rspec_rails (~> 2.28) - rubocop-rspec_rails (2.28.3) - rubocop (~> 1.40) + rubocop-rspec (3.0.1) + rubocop (~> 1.61) rubocop-thread_safety (0.5.1) rubocop (>= 0.90.0) ruby-next-core (1.0.3) @@ -509,7 +502,7 @@ GEM sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) - sprockets-rails (3.5.0) + sprockets-rails (3.5.1) actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) @@ -659,4 +652,4 @@ RUBY VERSION ruby 3.2.2p53 BUNDLED WITH - 2.5.10 + 2.5.11 diff --git a/app/controllers/api/v1/client/agents_controller.rb b/app/controllers/api/v1/client/agents_controller.rb index dbc415bd..2db173cb 100644 --- a/app/controllers/api/v1/client/agents_controller.rb +++ b/app/controllers/api/v1/client/agents_controller.rb @@ -56,8 +56,10 @@ def submit_benchmark benchmark_record.runtime = benchmark[:runtime].to_i records.append(benchmark_record) end + @agent.hashcat_benchmarks.destroy_all if @agent.hashcat_benchmarks.append(records) - return @agent.benchmarked + @agent.benchmarked + return end render json: { errors: @agent.errors }, status: :unprocessable_entity end diff --git a/app/controllers/hash_lists_controller.rb b/app/controllers/hash_lists_controller.rb index 90bc68c1..e8cf72b9 100644 --- a/app/controllers/hash_lists_controller.rb +++ b/app/controllers/hash_lists_controller.rb @@ -12,9 +12,7 @@ def index def show; end # GET /hash_lists/new - def new - @hash_list = HashList.new - end + def new; end # GET /hash_lists/1/edit def edit; end @@ -28,7 +26,7 @@ def create format.html { redirect_to hash_list_url(@hash_list), notice: "Hash list was successfully created." } format.json { render :show, status: :created, location: @hash_list } else - format.html { render :new, status: :unprocessable_entity } + format.html { render :new, status: :unprocessable_entity, error: "Hash list could not be created." } format.json { render json: @hash_list.errors, status: :unprocessable_entity } end end @@ -61,7 +59,7 @@ def destroy # Only allow a list of trusted parameters through. def hash_list_params - params.require(:hash_list).permit(:name, :description, :file, :line_count, :sensitive, :hash_mode) + params.require(:hash_list).permit(:name, :description, :file, :line_count, :sensitive, :project_id, :hash_type_id) end # Use callbacks to share common setup or constraints between actions. diff --git a/app/controllers/rule_lists_controller.rb b/app/controllers/rule_lists_controller.rb index 299cd3d9..2eac4216 100644 --- a/app/controllers/rule_lists_controller.rb +++ b/app/controllers/rule_lists_controller.rb @@ -62,7 +62,7 @@ def destroy # Only allow a list of trusted parameters through. def rule_list_params - params.require(:rule_list).permit(:name, :description, :file, :line_count, :sensitive) + params.require(:rule_list).permit(:name, :description, :file, :line_count, :sensitive, project_ids: []) end # Use callbacks to share common setup or constraints between actions. diff --git a/app/controllers/word_lists_controller.rb b/app/controllers/word_lists_controller.rb index a4fe376b..e604b201 100644 --- a/app/controllers/word_lists_controller.rb +++ b/app/controllers/word_lists_controller.rb @@ -87,6 +87,6 @@ def set_word_list # Only allow a list of trusted parameters through. def word_list_params - params.require(:word_list).permit(:name, :description, :file, :line_count, :sensitive) + params.require(:word_list).permit(:name, :description, :file, :line_count, :sensitive, project_ids: []) end end diff --git a/app/models/agent.rb b/app/models/agent.rb index 4cc4095d..1f2c98cc 100644 --- a/app/models/agent.rb +++ b/app/models/agent.rb @@ -68,6 +68,7 @@ class Agent < ApplicationRecord event :benchmarked do transition pending: :active + transition any => same end event :deactivate do @@ -85,17 +86,18 @@ class Agent < ApplicationRecord end event :check_benchmark_age do + transition active: same transition active: :pending if ->(agent) { agent.hashcat_benchmarks.empty? } - transition active: :pending if ->(agent) { agent.last_benchmark_date >= ApplicationConfig.max_benchmark_age.ago } + # transition active: :pending if ->(agent) { agent.last_benchmark_date <= ApplicationConfig.max_benchmark_age.ago } transition any => same end event :heartbeat do # If the agent has been offline for more than 12 hours, we'll transition it to pending. # This will require the agent to benchmark again. - transition offline: :pending if ->(agent) { agent.last_seen_at > ApplicationConfig.max_offline_time.ago } + transition offline: :pending if ->(agent) { agent.last_seen_at < ApplicationConfig.max_offline_time.ago } # If the agent has only been offline for less than 12 hours, we'll keep it active. - transition offline: :active if ->(agent) { agent.last_seen_at <= ApplicationConfig.max_offline_time.ago } + transition offline: :active if ->(agent) { agent.last_seen_at >= ApplicationConfig.max_offline_time.ago } transition any => same end @@ -139,6 +141,17 @@ def last_benchmark_date end end + # Returns the last benchmarks recorded for the agent. + # + # If there are no benchmarks available, it returns nil. + # + # @return [ActiveRecord::Relation, nil] The last benchmarks recorded for the agent, or nil if there are no benchmarks. + def last_benchmarks + return nil if hashcat_benchmarks.empty? + max = hashcat_benchmarks.maximum(:benchmark_date) + hashcat_benchmarks.where(benchmark_date: (max.all_day)).order(hash_type: :asc) + end + # Public: Finds or creates a new task for the agent. # # This method is responsible for assigning a new task to the agent. It follows a specific logic to determine which task to assign. @@ -155,7 +168,9 @@ def new_task # first we assign any tasks that are assigned to the agent and are incomplete. if tasks.incomplete.any? && tasks.incomplete.where(agent_id: id).any? incomplete_task = tasks.incomplete.where(agent_id: id).first - return incomplete_task if incomplete_task.present? + + # If the task is incomplete and there are no errors for the task, we'll return the task. + return incomplete_task if incomplete_task.present? && !agent_errors.where([task_id: incomplete_task.id, severity: AgentError.severities[:fatal]]).any? end # Ok, so there's no existing tasks already assigned to the agent. diff --git a/app/models/hash_list.rb b/app/models/hash_list.rb index e17ac19d..867aa067 100644 --- a/app/models/hash_list.rb +++ b/app/models/hash_list.rb @@ -43,7 +43,7 @@ class HashList < ApplicationRecord validates :name, length: { maximum: 255 } validates :separator, length: { is: 1, allow_blank: true } validates :metadata_fields_count, numericality: { greater_than_or_equal_to: 0, only_integer: true } - validates :file, content_type: %w[text/plain], attached: ->(record) { record.processed? || record.file.attached? } + validates :file, attached: ->(record) { record.processed? || record.file.attached? } broadcasts_refreshes unless Rails.env.test? diff --git a/app/models/hash_type.rb b/app/models/hash_type.rb index c8d4c685..c37fd96e 100644 --- a/app/models/hash_type.rb +++ b/app/models/hash_type.rb @@ -57,4 +57,8 @@ class HashType < ApplicationRecord instant_messaging: 20, cryptocurrency: 21 } + + def to_s + "#{hash_type.hashcat_mode} (#{hash_type.name})" + end end diff --git a/app/views/agents/show.html.erb b/app/views/agents/show.html.erb index 7967cfb0..1a0e1169 100644 --- a/app/views/agents/show.html.erb +++ b/app/views/agents/show.html.erb @@ -3,10 +3,7 @@ <%= show_for @agent do |s| %> <%= turbo_stream_from @agent %> <%= s.attribute :client_signature %> - <%= s.attribute :command_parameters %> - <%= s.attribute :cpu_only %> - <%= s.attribute :ignore_errors %> - <%= s.attribute :active %> + <%= s.attribute :state %> <%= s.attribute :trusted %> <%= s.attribute :last_ipaddress %> <%= s.attribute :last_seen_at, format: :short %> diff --git a/app/views/hash_lists/_form.html.erb b/app/views/hash_lists/_form.html.erb index 464a033e..bf758082 100644 --- a/app/views/hash_lists/_form.html.erb +++ b/app/views/hash_lists/_form.html.erb @@ -1,5 +1,4 @@ <%= simple_form_for(@hash_list) do |f| %> - <%= f.error_notification %> <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>