mihari 7.1.3 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +2 -2
- data/Rakefile +8 -1
- data/lefthook.yml +4 -1
- data/lib/mihari/actor.rb +16 -0
- data/lib/mihari/analyzers/base.rb +7 -25
- data/lib/mihari/analyzers/binaryedge.rb +0 -6
- data/lib/mihari/analyzers/censys.rb +0 -9
- data/lib/mihari/analyzers/circl.rb +0 -6
- data/lib/mihari/analyzers/fofa.rb +0 -6
- data/lib/mihari/analyzers/greynoise.rb +0 -6
- data/lib/mihari/analyzers/hunterhow.rb +0 -6
- data/lib/mihari/analyzers/onyphe.rb +0 -6
- data/lib/mihari/analyzers/otx.rb +0 -6
- data/lib/mihari/analyzers/passivetotal.rb +0 -4
- data/lib/mihari/analyzers/pulsedive.rb +0 -6
- data/lib/mihari/analyzers/securitytrails.rb +0 -4
- data/lib/mihari/analyzers/shodan.rb +0 -6
- data/lib/mihari/analyzers/urlscan.rb +0 -6
- data/lib/mihari/analyzers/virustotal.rb +0 -4
- data/lib/mihari/analyzers/virustotal_intelligence.rb +7 -6
- data/lib/mihari/analyzers/zoomeye.rb +0 -6
- data/lib/mihari/commands/web.rb +1 -1
- data/lib/mihari/concerns/falsepositive_normalizable.rb +30 -0
- data/lib/mihari/concerns/falsepositive_validatable.rb +1 -17
- data/lib/mihari/config.rb +1 -1
- data/lib/mihari/database.rb +18 -1
- data/lib/mihari/emitters/database.rb +0 -6
- data/lib/mihari/emitters/misp.rb +0 -6
- data/lib/mihari/emitters/slack.rb +5 -21
- data/lib/mihari/emitters/the_hive.rb +0 -6
- data/lib/mihari/enrichers/base.rb +54 -12
- data/lib/mihari/enrichers/google_public_dns.rb +28 -7
- data/lib/mihari/enrichers/mmdb.rb +25 -7
- data/lib/mihari/enrichers/shodan.rb +35 -4
- data/lib/mihari/enrichers/whois.rb +37 -31
- data/lib/mihari/entities/artifact.rb +6 -2
- data/lib/mihari/entities/autonomous_system.rb +1 -1
- data/lib/mihari/entities/cpe.rb +1 -1
- data/lib/mihari/entities/port.rb +1 -1
- data/lib/mihari/entities/vulnerability.rb +10 -0
- data/lib/mihari/errors.rb +2 -0
- data/lib/mihari/models/alert.rb +12 -0
- data/lib/mihari/models/artifact.rb +118 -159
- data/lib/mihari/models/rule.rb +21 -0
- data/lib/mihari/models/vulnerability.rb +12 -0
- data/lib/mihari/rule.rb +44 -29
- data/lib/mihari/schemas/alert.rb +3 -3
- data/lib/mihari/schemas/analyzer.rb +27 -27
- data/lib/mihari/schemas/emitter.rb +9 -9
- data/lib/mihari/schemas/macros.rb +2 -2
- data/lib/mihari/schemas/options.rb +2 -5
- data/lib/mihari/schemas/rule.rb +19 -12
- data/lib/mihari/services/builders.rb +0 -134
- data/lib/mihari/services/enrichers.rb +3 -1
- data/lib/mihari/services/feed.rb +2 -5
- data/lib/mihari/services/getters.rb +1 -1
- data/lib/mihari/services/proxies.rb +3 -3
- data/lib/mihari/structs/censys.rb +2 -2
- data/lib/mihari/structs/greynoise.rb +1 -1
- data/lib/mihari/structs/onyphe.rb +1 -1
- data/lib/mihari/structs/shodan.rb +59 -21
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/artifacts.rb +4 -2
- data/lib/mihari/web/endpoints/rules.rb +1 -1
- data/lib/mihari/web/public/assets/{index-TOeU8PE2.js → index-JHS0L8KZ.js} +47 -47
- data/lib/mihari/web/public/assets/{index-dVaNxqTC.css → index-ReF8ffd-.css} +1 -1
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari/web/public/redoc-static.html +17 -17
- data/lib/mihari.rb +3 -0
- data/mihari.gemspec +2 -2
- data/requirements.txt +1 -1
- metadata +11 -8
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 328a34edf637c36456cc7de39fccabdaaf75937b449b5e8a8e0434e71b9328c2
         | 
| 4 | 
            +
              data.tar.gz: 8483c669cfb3e715c86b4a3878961885e4cd4f93611e487c4360cb3030267c18
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 9858608c1ceb30f846a27b487e4bcbbad463aba56a401991fdade512bf6a7a7f028e5facecd764b200d94795a1da1a2b631b85540dfe1bbf002c756100b6627f
         | 
| 7 | 
            +
              data.tar.gz: c050fdafab0e7855eb610ad3d50762583ca931d31afeeb57fc654a2dc65ff381f20cb31eb28d0a61b5888c0a19df02320b09a529830fe332389b59f739721aba
         | 
    
        data/Dockerfile
    CHANGED
    
    | @@ -1,8 +1,8 @@ | |
| 1 | 
            -
            FROM ruby:3. | 
| 1 | 
            +
            FROM ruby:3.3.0-alpine3.19
         | 
| 2 2 |  | 
| 3 3 | 
             
            ARG MIHARI_VERSION=0.0.0
         | 
| 4 4 |  | 
| 5 | 
            -
            RUN apk --no-cache add build-base ruby-dev libpq-dev && \
         | 
| 5 | 
            +
            RUN apk --no-cache add build-base ruby-dev libpq-dev whois && \
         | 
| 6 6 | 
             
              echo 'gem: --no-document' >> /usr/local/etc/gemrc && \
         | 
| 7 7 | 
             
              gem install pg && \
         | 
| 8 8 | 
             
              gem install mihari -v ${MIHARI_VERSION} && \
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "time"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            require "rspec/core/rake_task"
         | 
| 4 6 | 
             
            require "standard/rake"
         | 
| 5 7 |  | 
| @@ -46,13 +48,18 @@ namespace :build do | |
| 46 48 | 
             
              desc "Build Swagger doc"
         | 
| 47 49 | 
             
              task :swagger, [:path] do |_t, args|
         | 
| 48 50 | 
             
                args.with_defaults(path: "./frontend/swagger.yaml")
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                started_at = Time.now
         | 
| 49 53 | 
             
                build_swagger_doc args.path
         | 
| 54 | 
            +
                elapsed = (Time.now - started_at).floor(2)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                puts "Swagger doc is built in #{elapsed}s"
         | 
| 50 57 | 
             
              end
         | 
| 51 58 | 
             
            end
         | 
| 52 59 |  | 
| 53 60 | 
             
            task :build do
         | 
| 54 | 
            -
              # Build Swagger dos
         | 
| 55 61 | 
             
              Rake::Task["build:swagger"].invoke
         | 
| 62 | 
            +
             | 
| 56 63 | 
             
              # Build ReDocs docs & frontend assets
         | 
| 57 64 | 
             
              sh "cd frontend && npm install && npm run docs && npm run build-only"
         | 
| 58 65 | 
             
              # Copy built assets into ./lib/web/public/
         | 
    
        data/lefthook.yml
    CHANGED
    
    | @@ -1,5 +1,4 @@ | |
| 1 1 | 
             
            pre-commit:
         | 
| 2 | 
            -
              parallel: true
         | 
| 3 2 | 
             
              commands:
         | 
| 4 3 | 
             
                standard:
         | 
| 5 4 | 
             
                  glob: "*.rb"
         | 
| @@ -15,6 +14,10 @@ pre-commit: | |
| 15 14 | 
             
                  glob: "*.{js,ts,vue}"
         | 
| 16 15 | 
             
                  run: npx prettier --write {staged_files}
         | 
| 17 16 | 
             
                  stage_fixed: true
         | 
| 17 | 
            +
                type-check:
         | 
| 18 | 
            +
                  root: "frontend/"
         | 
| 19 | 
            +
                  glob: "*.{js,ts,vue}"
         | 
| 20 | 
            +
                  run: npm run type-check
         | 
| 18 21 | 
             
                actionlint:
         | 
| 19 22 | 
             
                  glob: ".github/workflows/*.yaml"
         | 
| 20 23 | 
             
                  run: actionlint
         | 
    
        data/lib/mihari/actor.rb
    CHANGED
    
    | @@ -50,6 +50,13 @@ module Mihari | |
| 50 50 | 
             
                  options[:timeout]
         | 
| 51 51 | 
             
                end
         | 
| 52 52 |  | 
| 53 | 
            +
                #
         | 
| 54 | 
            +
                # @return [Boolean]
         | 
| 55 | 
            +
                #
         | 
| 56 | 
            +
                def parallel?
         | 
| 57 | 
            +
                  options[:parallel] || Mihari.config.parallel
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 53 60 | 
             
                def validate_configuration!
         | 
| 54 61 | 
             
                  return if configured?
         | 
| 55 62 |  | 
| @@ -93,6 +100,15 @@ module Mihari | |
| 93 100 | 
             
                    ([key] + [key_aliases]).flatten.compact.map(&:downcase)
         | 
| 94 101 | 
             
                  end
         | 
| 95 102 |  | 
| 103 | 
            +
                  def configuration_keys
         | 
| 104 | 
            +
                    # Automatically generate configuration keys based on key
         | 
| 105 | 
            +
                    # For example,
         | 
| 106 | 
            +
                    # - Shodan analyzer's key is "shodan"
         | 
| 107 | 
            +
                    # - Mihari.config has "shodan_api_key"
         | 
| 108 | 
            +
                    # - Select "shodan_api_key" by using "#{key}_" prefix
         | 
| 109 | 
            +
                    Mihari.config.keys.select { |config_key| config_key.start_with?("#{key}_") }
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
             | 
| 96 112 | 
             
                  def type
         | 
| 97 113 | 
             
                    return "analyzer" if ancestors.include?(Mihari::Analyzers::Base)
         | 
| 98 114 | 
             
                    return "emitter" if ancestors.include?(Mihari::Emitters::Base)
         | 
| @@ -40,13 +40,6 @@ module Mihari | |
| 40 40 | 
             
                    options[:ignore_error] || Mihari.config.ignore_error
         | 
| 41 41 | 
             
                  end
         | 
| 42 42 |  | 
| 43 | 
            -
                  #
         | 
| 44 | 
            -
                  # @return [Boolean]
         | 
| 45 | 
            -
                  #
         | 
| 46 | 
            -
                  def parallel?
         | 
| 47 | 
            -
                    options[:parallel] || Mihari.config.parallel
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
             | 
| 50 43 | 
             
                  # @return [Array<String>, Array<Mihari::Models::Artifact>]
         | 
| 51 44 | 
             
                  def artifacts
         | 
| 52 45 | 
             
                    raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| @@ -63,12 +56,10 @@ module Mihari | |
| 63 56 | 
             
                    artifacts.compact.sort.map do |artifact|
         | 
| 64 57 | 
             
                      # No need to set data_type manually
         | 
| 65 58 | 
             
                      # It is set automatically in #initialize
         | 
| 66 | 
            -
                      artifact | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
                       | 
| 70 | 
            -
             | 
| 71 | 
            -
                      artifact
         | 
| 59 | 
            +
                      (artifact.is_a?(Models::Artifact) ? artifact : Models::Artifact.new(data: artifact)).tap do |normalized|
         | 
| 60 | 
            +
                        normalized.source = self.class.key
         | 
| 61 | 
            +
                        normalized.query = query
         | 
| 62 | 
            +
                      end
         | 
| 72 63 | 
             
                    end.select(&:valid?).uniq(&:data)
         | 
| 73 64 | 
             
                  end
         | 
| 74 65 |  | 
| @@ -118,18 +109,9 @@ module Mihari | |
| 118 109 | 
             
                    #
         | 
| 119 110 | 
             
                    # @return [Mihari::Analyzers::Base]
         | 
| 120 111 | 
             
                    #
         | 
| 121 | 
            -
                    def  | 
| 122 | 
            -
                       | 
| 123 | 
            -
             | 
| 124 | 
            -
                      # convert params into arguments for initialization
         | 
| 125 | 
            -
                      query = copied[:query]
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                      # delete analyzer and query
         | 
| 128 | 
            -
                      %i[analyzer query].each { |key| copied.delete key }
         | 
| 129 | 
            -
             | 
| 130 | 
            -
                      copied[:options] = copied[:options] || nil
         | 
| 131 | 
            -
             | 
| 132 | 
            -
                      new(query, **copied)
         | 
| 112 | 
            +
                    def from_params(params)
         | 
| 113 | 
            +
                      query = params.delete(:query)
         | 
| 114 | 
            +
                      new(query, **params)
         | 
| 133 115 | 
             
                    end
         | 
| 134 116 |  | 
| 135 117 | 
             
                    def inherited(child)
         | 
    
        data/lib/mihari/analyzers/otx.rb
    CHANGED
    
    
| @@ -24,12 +24,6 @@ module Mihari | |
| 24 24 | 
             
                    client.intel_search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
         | 
| 25 25 | 
             
                  end
         | 
| 26 26 |  | 
| 27 | 
            -
                  class << self
         | 
| 28 | 
            -
                    def configuration_keys
         | 
| 29 | 
            -
                      %w[virustotal_api_key]
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
             | 
| 33 27 | 
             
                  class << self
         | 
| 34 28 | 
             
                    #
         | 
| 35 29 | 
             
                    # @return [String]
         | 
| @@ -44,6 +38,13 @@ module Mihari | |
| 44 38 | 
             
                    def key_aliases
         | 
| 45 39 | 
             
                      ["vt_intel"]
         | 
| 46 40 | 
             
                    end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    #
         | 
| 43 | 
            +
                    # @return [Array<String>]
         | 
| 44 | 
            +
                    #
         | 
| 45 | 
            +
                    def configuration_keys
         | 
| 46 | 
            +
                      %w[virustotal_api_key]
         | 
| 47 | 
            +
                    end
         | 
| 47 48 | 
             
                  end
         | 
| 48 49 |  | 
| 49 50 | 
             
                  private
         | 
    
        data/lib/mihari/commands/web.rb
    CHANGED
    
    | @@ -12,7 +12,7 @@ module Mihari | |
| 12 12 | 
             
                        desc "web", "Start the web app"
         | 
| 13 13 | 
             
                        method_option :port, type: :numeric, default: 9292, desc: "Port to listen on"
         | 
| 14 14 | 
             
                        method_option :host, type: :string, default: "localhost", desc: "Hostname to listen on"
         | 
| 15 | 
            -
                        method_option :threads, type: :string, default: "0: | 
| 15 | 
            +
                        method_option :threads, type: :string, default: "0:3", desc: "min:max threads to use"
         | 
| 16 16 | 
             
                        method_option :verbose, type: :boolean, default: false, desc: "Don't report each request"
         | 
| 17 17 | 
             
                        method_option :worker_timeout, type: :numeric, default: 60, desc: "Worker timeout value (in seconds)"
         | 
| 18 18 | 
             
                        method_option :open, type: :boolean, default: true, desc: "Whether to open the app in browser or not"
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Mihari
         | 
| 4 | 
            +
              module Concerns
         | 
| 5 | 
            +
                #
         | 
| 6 | 
            +
                # False positive normalizable concern
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                module FalsePositiveNormalizable
         | 
| 9 | 
            +
                  extend ActiveSupport::Concern
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  prepend MemoWise
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  #
         | 
| 14 | 
            +
                  # Normalize a falsepositive value
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # @param [String] value
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  # @return [String, Regexp]
         | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  def normalize_falsepositive(value)
         | 
| 21 | 
            +
                    return value if !value.start_with?("/") || !value.end_with?("/")
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    # if a value is surrounded by slashes, take it as a regexp
         | 
| 24 | 
            +
                    value_without_slashes = value[1..-2]
         | 
| 25 | 
            +
                    Regexp.compile value_without_slashes.to_s
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                  memo_wise :normalize_falsepositive
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -8,23 +8,7 @@ module Mihari | |
| 8 8 | 
             
                module FalsePositiveValidatable
         | 
| 9 9 | 
             
                  extend ActiveSupport::Concern
         | 
| 10 10 |  | 
| 11 | 
            -
                   | 
| 12 | 
            -
             | 
| 13 | 
            -
                  #
         | 
| 14 | 
            -
                  # Normalize a falsepositive value
         | 
| 15 | 
            -
                  #
         | 
| 16 | 
            -
                  # @param [String] value
         | 
| 17 | 
            -
                  #
         | 
| 18 | 
            -
                  # @return [String, Regexp]
         | 
| 19 | 
            -
                  #
         | 
| 20 | 
            -
                  def normalize_falsepositive(value)
         | 
| 21 | 
            -
                    return value if !value.start_with?("/") || !value.end_with?("/")
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                    # if a value is surrounded by slashes, take it as a regexp
         | 
| 24 | 
            -
                    value_without_slashes = value[1..-2]
         | 
| 25 | 
            -
                    Regexp.compile value_without_slashes.to_s
         | 
| 26 | 
            -
                  end
         | 
| 27 | 
            -
                  memo_wise :normalize_falsepositive
         | 
| 11 | 
            +
                  include FalsePositiveNormalizable
         | 
| 28 12 |  | 
| 29 13 | 
             
                  #
         | 
| 30 14 | 
             
                  # Check whether a value is valid format as a disallowed data value
         | 
    
        data/lib/mihari/config.rb
    CHANGED
    
    
    
        data/lib/mihari/database.rb
    CHANGED
    
    | @@ -104,11 +104,28 @@ class V7Schema < ActiveRecord::Migration[7.1] | |
| 104 104 | 
             
              end
         | 
| 105 105 | 
             
            end
         | 
| 106 106 |  | 
| 107 | 
            +
            class V72Schema < ActiveRecord::Migration[7.1]
         | 
| 108 | 
            +
              def change
         | 
| 109 | 
            +
                create_table :vulnerabilities, if_not_exists: true do |t|
         | 
| 110 | 
            +
                  t.string :name, null: false
         | 
| 111 | 
            +
                  t.datetime :created_at
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  t.belongs_to :artifact, foreign_key: true, null: false
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                rename_column :cpes, :cpe, :name if ActiveRecord::Base.connection.column_exists?(:cpes, :cpe)
         | 
| 117 | 
            +
                rename_column :autonomous_systems, :asn, :number if ActiveRecord::Base.connection.column_exists?(
         | 
| 118 | 
            +
                  :autonomous_systems, :asn
         | 
| 119 | 
            +
                )
         | 
| 120 | 
            +
                rename_column :ports, :port, :number if ActiveRecord::Base.connection.column_exists?(:ports, :port)
         | 
| 121 | 
            +
              end
         | 
| 122 | 
            +
            end
         | 
| 123 | 
            +
             | 
| 107 124 | 
             
            #
         | 
| 108 125 | 
             
            # @return [Array<ActiveRecord::Migration>] schemas
         | 
| 109 126 | 
             
            #
         | 
| 110 127 | 
             
            def schemas
         | 
| 111 | 
            -
              [V7Schema]
         | 
| 128 | 
            +
              [V7Schema, V72Schema]
         | 
| 112 129 | 
             
            end
         | 
| 113 130 |  | 
| 114 131 | 
             
            module Mihari
         | 
    
        data/lib/mihari/emitters/misp.rb
    CHANGED
    
    
| @@ -176,21 +176,11 @@ module Mihari | |
| 176 176 | 
             
                  # @return [::Slack::Notifier]
         | 
| 177 177 | 
             
                  #
         | 
| 178 178 | 
             
                  def notifier
         | 
| 179 | 
            -
                    @notifier ||=  | 
| 180 | 
            -
                       | 
| 181 | 
            -
             | 
| 182 | 
            -
             | 
| 183 | 
            -
             | 
| 184 | 
            -
                        )
         | 
| 185 | 
            -
                      else
         | 
| 186 | 
            -
                        ::Slack::Notifier.new(
         | 
| 187 | 
            -
                          webhook_url,
         | 
| 188 | 
            -
                          channel: channel,
         | 
| 189 | 
            -
                          username: username,
         | 
| 190 | 
            -
                          http_options: { timeout: timeout }
         | 
| 191 | 
            -
                        )
         | 
| 192 | 
            -
                      end
         | 
| 193 | 
            -
                    end.first
         | 
| 179 | 
            +
                    @notifier ||= lambda do
         | 
| 180 | 
            +
                      return ::Slack::Notifier.new(webhook_url, channel: channel, username: username) if timeout.nil?
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                      ::Slack::Notifier.new(webhook_url, channel: channel, username: username, http_options: { timeout: timeout })
         | 
| 183 | 
            +
                    end.call
         | 
| 194 184 | 
             
                  end
         | 
| 195 185 |  | 
| 196 186 | 
             
                  #
         | 
| @@ -227,12 +217,6 @@ module Mihari | |
| 227 217 |  | 
| 228 218 | 
             
                    notifier.post(text: text, attachments: attachments, mrkdwn: true)
         | 
| 229 219 | 
             
                  end
         | 
| 230 | 
            -
             | 
| 231 | 
            -
                  class << self
         | 
| 232 | 
            -
                    def configuration_keys
         | 
| 233 | 
            -
                      %w[slack_webhook_url slack_channel]
         | 
| 234 | 
            -
                    end
         | 
| 235 | 
            -
                  end
         | 
| 236 220 | 
             
                end
         | 
| 237 221 | 
             
              end
         | 
| 238 222 | 
             
            end
         | 
| @@ -6,46 +6,88 @@ module Mihari | |
| 6 6 | 
             
                # Base class for enrichers
         | 
| 7 7 | 
             
                #
         | 
| 8 8 | 
             
                class Base < Actor
         | 
| 9 | 
            -
                   | 
| 10 | 
            -
             | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # @param [Hash, nil] options
         | 
| 11 | 
            +
                  #
         | 
| 11 12 | 
             
                  def initialize(options: nil)
         | 
| 12 13 | 
             
                    super(options: options)
         | 
| 13 14 | 
             
                  end
         | 
| 14 15 |  | 
| 15 16 | 
             
                  #
         | 
| 16 | 
            -
                  #  | 
| 17 | 
            +
                  # Enrich an artifact
         | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # @param [Mihari::Models::Artifact] artifact
         | 
| 17 20 | 
             
                  #
         | 
| 18 | 
            -
                   | 
| 21 | 
            +
                  # @return [Mihari::Models::Artifact]
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  def call(artifact)
         | 
| 19 24 | 
             
                    raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 20 25 | 
             
                  end
         | 
| 21 26 |  | 
| 22 27 | 
             
                  #
         | 
| 23 | 
            -
                  # @param [Mihari::Models::Artifact]  | 
| 28 | 
            +
                  # @param [Mihari::Models::Artifact] artifact
         | 
| 24 29 | 
             
                  #
         | 
| 25 30 | 
             
                  # @return [Dry::Monads::Result::Success<Object>, Dry::Monads::Result::Failure]
         | 
| 26 31 | 
             
                  #
         | 
| 27 | 
            -
                  def result( | 
| 32 | 
            +
                  def result(artifact)
         | 
| 33 | 
            +
                    return unless callable?(artifact)
         | 
| 34 | 
            +
             | 
| 28 35 | 
             
                    result = Try[StandardError] do
         | 
| 29 | 
            -
                      retry_on_error(
         | 
| 30 | 
            -
                         | 
| 31 | 
            -
                         | 
| 32 | 
            -
             | 
| 33 | 
            -
                      ) { call value }
         | 
| 36 | 
            +
                      retry_on_error(times: retry_times, interval: retry_interval,
         | 
| 37 | 
            +
                        exponential_backoff: retry_exponential_backoff) do
         | 
| 38 | 
            +
                        call artifact
         | 
| 39 | 
            +
                      end
         | 
| 34 40 | 
             
                    end.to_result
         | 
| 35 41 |  | 
| 36 42 | 
             
                    if result.failure?
         | 
| 37 | 
            -
                      Mihari.logger.warn("Enricher:#{self.class.key} for #{ | 
| 43 | 
            +
                      Mihari.logger.warn("Enricher:#{self.class.key} for #{artifact.data.truncate(32)} failed: #{result.failure}")
         | 
| 38 44 | 
             
                    end
         | 
| 39 45 |  | 
| 40 46 | 
             
                    result
         | 
| 41 47 | 
             
                  end
         | 
| 42 48 |  | 
| 49 | 
            +
                  #
         | 
| 50 | 
            +
                  # @param [Mihari::Models::Artifact] artifact
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  # @return [Boolean]
         | 
| 53 | 
            +
                  #
         | 
| 54 | 
            +
                  def callable?(artifact)
         | 
| 55 | 
            +
                    callable_data_type?(artifact) && callable_relationships?(artifact)
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 43 58 | 
             
                  class << self
         | 
| 44 59 | 
             
                    def inherited(child)
         | 
| 45 60 | 
             
                      super
         | 
| 46 61 | 
             
                      Mihari.enrichers << child
         | 
| 47 62 | 
             
                    end
         | 
| 48 63 | 
             
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  private
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  #
         | 
| 68 | 
            +
                  # @param [Mihari::Models::Artifact] artifact
         | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
                  # @return [Boolean]
         | 
| 71 | 
            +
                  #
         | 
| 72 | 
            +
                  def callable_data_type?(artifact)
         | 
| 73 | 
            +
                    supported_data_types.include? artifact.data_type
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  #
         | 
| 77 | 
            +
                  # @param [Mihari::Models::Artifact] artifact
         | 
| 78 | 
            +
                  #
         | 
| 79 | 
            +
                  # @return [Boolean]
         | 
| 80 | 
            +
                  #
         | 
| 81 | 
            +
                  def callable_relationships?(artifact)
         | 
| 82 | 
            +
                    raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  #
         | 
| 86 | 
            +
                  # @return [Array<String>]
         | 
| 87 | 
            +
                  #
         | 
| 88 | 
            +
                  def supported_data_types
         | 
| 89 | 
            +
                    []
         | 
| 90 | 
            +
                  end
         | 
| 49 91 | 
             
                end
         | 
| 50 92 | 
             
              end
         | 
| 51 93 | 
             
            end
         |