mihari 0.17.4 → 1.1.1
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/.gitignore +3 -0
- data/.rubocop.yml +155 -0
- data/.travis.yml +7 -1
- data/Gemfile +2 -0
- data/README.md +41 -72
- data/config/pre_commit.yml +3 -0
- data/docker/Dockerfile +1 -1
- data/lib/mihari.rb +12 -8
- data/lib/mihari/alert_viewer.rb +16 -34
- data/lib/mihari/analyzers/base.rb +7 -19
- data/lib/mihari/analyzers/basic.rb +3 -1
- data/lib/mihari/analyzers/binaryedge.rb +3 -3
- data/lib/mihari/analyzers/censys.rb +2 -2
- data/lib/mihari/analyzers/circl.rb +2 -2
- data/lib/mihari/analyzers/onyphe.rb +3 -3
- data/lib/mihari/analyzers/passivetotal.rb +2 -2
- data/lib/mihari/analyzers/pulsedive.rb +2 -2
- data/lib/mihari/analyzers/securitytrails.rb +2 -2
- data/lib/mihari/analyzers/securitytrails_domain_feed.rb +2 -2
- data/lib/mihari/analyzers/shodan.rb +2 -2
- data/lib/mihari/analyzers/virustotal.rb +2 -2
- data/lib/mihari/analyzers/zoomeye.rb +2 -2
- data/lib/mihari/cli.rb +13 -4
- data/lib/mihari/config.rb +68 -2
- data/lib/mihari/configurable.rb +1 -1
- data/lib/mihari/database.rb +68 -0
- data/lib/mihari/emitters/base.rb +1 -1
- data/lib/mihari/emitters/database.rb +29 -0
- data/lib/mihari/emitters/misp.rb +8 -1
- data/lib/mihari/emitters/slack.rb +4 -2
- data/lib/mihari/emitters/stdout.rb +2 -1
- data/lib/mihari/emitters/the_hive.rb +28 -14
- data/lib/mihari/models/alert.rb +11 -0
- data/lib/mihari/models/artifact.rb +27 -0
- data/lib/mihari/models/tag.rb +10 -0
- data/lib/mihari/models/tagging.rb +10 -0
- data/lib/mihari/notifiers/slack.rb +7 -4
- data/lib/mihari/serializers/alert.rb +12 -0
- data/lib/mihari/serializers/artifact.rb +9 -0
- data/lib/mihari/serializers/tag.rb +9 -0
- data/lib/mihari/slack_monkeypatch.rb +16 -0
- data/lib/mihari/status.rb +1 -1
- data/lib/mihari/type_checker.rb +1 -1
- data/lib/mihari/version.rb +1 -1
- data/mihari.gemspec +13 -6
- metadata +140 -36
- data/lib/mihari/artifact.rb +0 -36
- data/lib/mihari/cache.rb +0 -35
- data/lib/mihari/the_hive.rb +0 -42
- data/lib/mihari/the_hive/alert.rb +0 -25
- data/lib/mihari/the_hive/artifact.rb +0 -33
- data/lib/mihari/the_hive/base.rb +0 -14
| @@ -23,6 +23,10 @@ module Mihari | |
| 23 23 | 
             
                    raise NotImplementedError, "You must implement #{self.class}##{__method__}"
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 |  | 
| 26 | 
            +
                  def source
         | 
| 27 | 
            +
                    self.class.to_s.split("::").last
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 26 30 | 
             
                  # @return [Array<String>]
         | 
| 27 31 | 
             
                  def tags
         | 
| 28 32 | 
             
                    []
         | 
| @@ -37,7 +41,7 @@ module Mihari | |
| 37 41 | 
             
                  end
         | 
| 38 42 |  | 
| 39 43 | 
             
                  def run_emitter(emitter)
         | 
| 40 | 
            -
                    emitter.run(title: title, description: description, artifacts: unique_artifacts, tags: tags)
         | 
| 44 | 
            +
                    emitter.run(title: title, description: description, artifacts: unique_artifacts, source: source, tags: tags)
         | 
| 41 45 | 
             
                  rescue StandardError => e
         | 
| 42 46 | 
             
                    puts "Emission by #{emitter.class} is failed: #{e}"
         | 
| 43 47 | 
             
                  end
         | 
| @@ -48,32 +52,16 @@ module Mihari | |
| 48 52 |  | 
| 49 53 | 
             
                  private
         | 
| 50 54 |  | 
| 51 | 
            -
                  def the_hive
         | 
| 52 | 
            -
                    @the_hive ||= TheHive.new
         | 
| 53 | 
            -
                  end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                  def cache
         | 
| 56 | 
            -
                    @cache ||= Cache.new
         | 
| 57 | 
            -
                  end
         | 
| 58 | 
            -
             | 
| 59 55 | 
             
                  # @return [Array<Mihari::Artifact>]
         | 
| 60 56 | 
             
                  def normalized_artifacts
         | 
| 61 57 | 
             
                    @normalized_artifacts ||= artifacts.compact.uniq.sort.map do |artifact|
         | 
| 62 | 
            -
                      artifact.is_a?(Artifact) ? artifact : Artifact.new(artifact)
         | 
| 58 | 
            +
                      artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact)
         | 
| 63 59 | 
             
                    end.select(&:valid?)
         | 
| 64 60 | 
             
                  end
         | 
| 65 61 |  | 
| 66 | 
            -
                  def uncached_artifacts
         | 
| 67 | 
            -
                    @uncached_artifacts ||= normalized_artifacts.reject do |artifact|
         | 
| 68 | 
            -
                      cache.cached? artifact.data
         | 
| 69 | 
            -
                    end
         | 
| 70 | 
            -
                  end
         | 
| 71 | 
            -
             | 
| 72 62 | 
             
                  # @return [Array<Mihari::Artifact>]
         | 
| 73 63 | 
             
                  def unique_artifacts
         | 
| 74 | 
            -
                     | 
| 75 | 
            -
             | 
| 76 | 
            -
                    @unique_artifacts ||= the_hive.artifact.find_non_existing_artifacts(uncached_artifacts)
         | 
| 64 | 
            +
                    @unique_artifacts ||= normalized_artifacts.select(&:unique?)
         | 
| 77 65 | 
             
                  end
         | 
| 78 66 |  | 
| 79 67 | 
             
                  def set_unique_artifacts
         | 
| @@ -6,12 +6,14 @@ module Mihari | |
| 6 6 | 
             
                  attr_accessor :title
         | 
| 7 7 | 
             
                  attr_reader :description
         | 
| 8 8 | 
             
                  attr_reader :artifacts
         | 
| 9 | 
            +
                  attr_reader :source
         | 
| 9 10 | 
             
                  attr_reader :tags
         | 
| 10 11 |  | 
| 11 | 
            -
                  def initialize(title:, description:, artifacts:, tags: [])
         | 
| 12 | 
            +
                  def initialize(title:, description:, artifacts:, source:, tags: [])
         | 
| 12 13 | 
             
                    @title = title
         | 
| 13 14 | 
             
                    @description = description
         | 
| 14 15 | 
             
                    @artifacts = artifacts
         | 
| 16 | 
            +
                    @source = source
         | 
| 15 17 | 
             
                    @tags = tags
         | 
| 16 18 | 
             
                  end
         | 
| 17 19 | 
             
                end
         | 
| @@ -26,7 +26,7 @@ module Mihari | |
| 26 26 | 
             
                    results.map do |result|
         | 
| 27 27 | 
             
                      events = result.dig("events") || []
         | 
| 28 28 | 
             
                      events.map do |event|
         | 
| 29 | 
            -
                        event.dig " | 
| 29 | 
            +
                        event.dig "target", "ip"
         | 
| 30 30 | 
             
                      end.compact
         | 
| 31 31 | 
             
                    end.flatten.compact.uniq
         | 
| 32 32 | 
             
                  end
         | 
| @@ -52,11 +52,11 @@ module Mihari | |
| 52 52 | 
             
                  end
         | 
| 53 53 |  | 
| 54 54 | 
             
                  def config_keys
         | 
| 55 | 
            -
                    %w( | 
| 55 | 
            +
                    %w(binaryedge_api_key)
         | 
| 56 56 | 
             
                  end
         | 
| 57 57 |  | 
| 58 58 | 
             
                  def api
         | 
| 59 | 
            -
                    @api ||= ::BinaryEdge::API.new
         | 
| 59 | 
            +
                    @api ||= ::BinaryEdge::API.new(Mihari.config.binaryedge_api_key)
         | 
| 60 60 | 
             
                  end
         | 
| 61 61 | 
             
                end
         | 
| 62 62 | 
             
              end
         | 
| @@ -86,11 +86,11 @@ module Mihari | |
| 86 86 | 
             
                  end
         | 
| 87 87 |  | 
| 88 88 | 
             
                  def config_keys
         | 
| 89 | 
            -
                    %w( | 
| 89 | 
            +
                    %w(censys_id censys_secret)
         | 
| 90 90 | 
             
                  end
         | 
| 91 91 |  | 
| 92 92 | 
             
                  def api
         | 
| 93 | 
            -
                    @api ||= ::Censys::API.new
         | 
| 93 | 
            +
                    @api ||= ::Censys::API.new(Mihari.config.censys_id, Mihari.config.censys_secret)
         | 
| 94 94 | 
             
                  end
         | 
| 95 95 | 
             
                end
         | 
| 96 96 | 
             
              end
         | 
| @@ -27,11 +27,11 @@ module Mihari | |
| 27 27 | 
             
                  private
         | 
| 28 28 |  | 
| 29 29 | 
             
                  def config_keys
         | 
| 30 | 
            -
                    %w( | 
| 30 | 
            +
                    %w(circl_passive_password circl_passive_username)
         | 
| 31 31 | 
             
                  end
         | 
| 32 32 |  | 
| 33 33 | 
             
                  def api
         | 
| 34 | 
            -
                    @api ||= ::PassiveCIRCL::API.new
         | 
| 34 | 
            +
                    @api ||= ::PassiveCIRCL::API.new(username: Mihari.config.circl_passive_username, password: Mihari.config.circl_passive_password)
         | 
| 35 35 | 
             
                  end
         | 
| 36 36 |  | 
| 37 37 | 
             
                  def lookup
         | 
| @@ -35,15 +35,15 @@ module Mihari | |
| 35 35 | 
             
                  PAGE_SIZE = 10
         | 
| 36 36 |  | 
| 37 37 | 
             
                  def config_keys
         | 
| 38 | 
            -
                    %w( | 
| 38 | 
            +
                    %w(onyphe_api_key)
         | 
| 39 39 | 
             
                  end
         | 
| 40 40 |  | 
| 41 41 | 
             
                  def api
         | 
| 42 | 
            -
                    @api ||= ::Onyphe::API.new
         | 
| 42 | 
            +
                    @api ||= ::Onyphe::API.new(Mihari.config.onyphe_api_key)
         | 
| 43 43 | 
             
                  end
         | 
| 44 44 |  | 
| 45 45 | 
             
                  def search_with_page(query, page: 1)
         | 
| 46 | 
            -
                    api.datascan(query, page: page)
         | 
| 46 | 
            +
                    api.simple.datascan(query, page: page)
         | 
| 47 47 | 
             
                  end
         | 
| 48 48 |  | 
| 49 49 | 
             
                  def search
         | 
| @@ -30,11 +30,11 @@ module Mihari | |
| 30 30 | 
             
                  private
         | 
| 31 31 |  | 
| 32 32 | 
             
                  def config_keys
         | 
| 33 | 
            -
                    %w( | 
| 33 | 
            +
                    %w(passivetotal_username passivetotal_api_key)
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| 36 36 | 
             
                  def api
         | 
| 37 | 
            -
                    @api ||= ::PassiveTotal::API.new
         | 
| 37 | 
            +
                    @api ||= ::PassiveTotal::API.new(username: Mihari.config.passivetotal_username, api_key: Mihari.config.passivetotal_api_key)
         | 
| 38 38 | 
             
                  end
         | 
| 39 39 |  | 
| 40 40 | 
             
                  def valid_type?
         | 
| @@ -30,11 +30,11 @@ module Mihari | |
| 30 30 | 
             
                  private
         | 
| 31 31 |  | 
| 32 32 | 
             
                  def config_keys
         | 
| 33 | 
            -
                    %w( | 
| 33 | 
            +
                    %w(pulsedive_api_key)
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| 36 36 | 
             
                  def api
         | 
| 37 | 
            -
                    @api ||= ::Pulsedive::API.new
         | 
| 37 | 
            +
                    @api ||= ::Pulsedive::API.new(Mihari.config.pulsedive_api_key)
         | 
| 38 38 | 
             
                  end
         | 
| 39 39 |  | 
| 40 40 | 
             
                  def valid_type?
         | 
| @@ -30,11 +30,11 @@ module Mihari | |
| 30 30 | 
             
                  private
         | 
| 31 31 |  | 
| 32 32 | 
             
                  def config_keys
         | 
| 33 | 
            -
                    %w( | 
| 33 | 
            +
                    %w(securitytrails_api_key)
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| 36 36 | 
             
                  def api
         | 
| 37 | 
            -
                    @api ||= ::SecurityTrails::API.new
         | 
| 37 | 
            +
                    @api ||= ::SecurityTrails::API.new(Mihari.config.securitytrails_api_key)
         | 
| 38 38 | 
             
                  end
         | 
| 39 39 |  | 
| 40 40 | 
             
                  def valid_type?
         | 
| @@ -32,11 +32,11 @@ module Mihari | |
| 32 32 | 
             
                  private
         | 
| 33 33 |  | 
| 34 34 | 
             
                  def config_keys
         | 
| 35 | 
            -
                    %w( | 
| 35 | 
            +
                    %w(securitytrails_api_key)
         | 
| 36 36 | 
             
                  end
         | 
| 37 37 |  | 
| 38 38 | 
             
                  def api
         | 
| 39 | 
            -
                    @api ||= ::SecurityTrails::API.new
         | 
| 39 | 
            +
                    @api ||= ::SecurityTrails::API.new(Mihari.config.securitytrails_api_key)
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 42 | 
             
                  def valid_type?
         | 
| @@ -36,11 +36,11 @@ module Mihari | |
| 36 36 | 
             
                  PAGE_SIZE = 100
         | 
| 37 37 |  | 
| 38 38 | 
             
                  def config_keys
         | 
| 39 | 
            -
                    %w( | 
| 39 | 
            +
                    %w(shodan_api_key)
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 |  | 
| 42 42 | 
             
                  def api
         | 
| 43 | 
            -
                    @api ||= ::Shodan::API.new
         | 
| 43 | 
            +
                    @api ||= ::Shodan::API.new(key: Mihari.config.shodan_api_key)
         | 
| 44 44 | 
             
                  end
         | 
| 45 45 |  | 
| 46 46 | 
             
                  def search_with_page(query, page: 1)
         | 
| @@ -30,11 +30,11 @@ module Mihari | |
| 30 30 | 
             
                  private
         | 
| 31 31 |  | 
| 32 32 | 
             
                  def config_keys
         | 
| 33 | 
            -
                    %w( | 
| 33 | 
            +
                    %w(virustotal_api_key)
         | 
| 34 34 | 
             
                  end
         | 
| 35 35 |  | 
| 36 36 | 
             
                  def api
         | 
| 37 | 
            -
                    @api = ::VirusTotal::API.new
         | 
| 37 | 
            +
                    @api = ::VirusTotal::API.new(key: Mihari.config.virustotal_api_key)
         | 
| 38 38 | 
             
                  end
         | 
| 39 39 |  | 
| 40 40 | 
             
                  def valid_type?
         | 
| @@ -41,11 +41,11 @@ module Mihari | |
| 41 41 | 
             
                  end
         | 
| 42 42 |  | 
| 43 43 | 
             
                  def config_keys
         | 
| 44 | 
            -
                    %w( | 
| 44 | 
            +
                    %w(zoomeye_password zoomeye_username)
         | 
| 45 45 | 
             
                  end
         | 
| 46 46 |  | 
| 47 47 | 
             
                  def api
         | 
| 48 | 
            -
                    @api ||= ::ZoomEye::API.new
         | 
| 48 | 
            +
                    @api ||= ::ZoomEye::API.new(username: Mihari.config.zoomeye_username, password: Mihari.config.zoomeye_password)
         | 
| 49 49 | 
             
                  end
         | 
| 50 50 |  | 
| 51 51 | 
             
                  def convert_responses(responses)
         | 
    
        data/lib/mihari/cli.rb
    CHANGED
    
    | @@ -242,17 +242,22 @@ module Mihari | |
| 242 242 | 
             
                    artifacts = json.dig("artifacts")
         | 
| 243 243 | 
             
                    tags = json.dig("tags") || []
         | 
| 244 244 |  | 
| 245 | 
            -
                    basic = Analyzers::Basic.new(title: title, description: description, artifacts: artifacts, tags: tags)
         | 
| 245 | 
            +
                    basic = Analyzers::Basic.new(title: title, description: description, artifacts: artifacts, source: "json", tags: tags)
         | 
| 246 246 | 
             
                    basic.run
         | 
| 247 247 | 
             
                  end
         | 
| 248 248 | 
             
                end
         | 
| 249 249 |  | 
| 250 250 | 
             
                desc "alerts", "Show the alerts on TheHive"
         | 
| 251 251 | 
             
                method_option :limit, type: :string, default: "5", desc: "Number of alerts to show (or 'all' to show all the alerts)"
         | 
| 252 | 
            +
                method_option :title, type: :string, desc: "Title to filter"
         | 
| 253 | 
            +
                method_option :source, type: :string, desc: "Source to filter"
         | 
| 254 | 
            +
                method_option :tag, type: :string, desc: "Tag to filter"
         | 
| 252 255 | 
             
                def alerts
         | 
| 253 256 | 
             
                  with_error_handling do
         | 
| 254 | 
            -
                     | 
| 255 | 
            -
             | 
| 257 | 
            +
                    load_configuration
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                    viewer = AlertViewer.new
         | 
| 260 | 
            +
                    alerts = viewer.list(limit: options["limit"], title: options["title"], source: options["source"], tag: options[:tag])
         | 
| 256 261 | 
             
                    puts JSON.pretty_generate(alerts)
         | 
| 257 262 | 
             
                  end
         | 
| 258 263 | 
             
                end
         | 
| @@ -261,6 +266,7 @@ module Mihari | |
| 261 266 | 
             
                def status
         | 
| 262 267 | 
             
                  with_error_handling do
         | 
| 263 268 | 
             
                    load_configuration
         | 
| 269 | 
            +
             | 
| 264 270 | 
             
                    puts JSON.pretty_generate(Status.check)
         | 
| 265 271 | 
             
                  end
         | 
| 266 272 | 
             
                end
         | 
| @@ -286,7 +292,10 @@ module Mihari | |
| 286 292 |  | 
| 287 293 | 
             
                  def load_configuration
         | 
| 288 294 | 
             
                    config = options["config"]
         | 
| 289 | 
            -
                     | 
| 295 | 
            +
                    return unless config
         | 
| 296 | 
            +
             | 
| 297 | 
            +
                    Config.load_from_yaml(config)
         | 
| 298 | 
            +
                    Database.connect
         | 
| 290 299 | 
             
                  end
         | 
| 291 300 |  | 
| 292 301 | 
             
                  def run_analyzer(analyzer_class, query:, options:)
         | 
    
        data/lib/mihari/config.rb
    CHANGED
    
    | @@ -4,6 +4,58 @@ require "yaml" | |
| 4 4 |  | 
| 5 5 | 
             
            module Mihari
         | 
| 6 6 | 
             
              class Config
         | 
| 7 | 
            +
                attr_accessor :binaryedge_api_key
         | 
| 8 | 
            +
                attr_accessor :censys_id
         | 
| 9 | 
            +
                attr_accessor :censys_secret
         | 
| 10 | 
            +
                attr_accessor :circl_passive_password
         | 
| 11 | 
            +
                attr_accessor :circl_passive_username
         | 
| 12 | 
            +
                attr_accessor :misp_api_endpoint
         | 
| 13 | 
            +
                attr_accessor :misp_api_key
         | 
| 14 | 
            +
                attr_accessor :onyphe_api_key
         | 
| 15 | 
            +
                attr_accessor :passivetotal_api_key
         | 
| 16 | 
            +
                attr_accessor :passivetotal_username
         | 
| 17 | 
            +
                attr_accessor :pulsedive_api_key
         | 
| 18 | 
            +
                attr_accessor :securitytrails_api_key
         | 
| 19 | 
            +
                attr_accessor :shodan_api_key
         | 
| 20 | 
            +
                attr_accessor :slack_channel
         | 
| 21 | 
            +
                attr_accessor :slack_webhook_url
         | 
| 22 | 
            +
                attr_accessor :thehive_api_endpoint
         | 
| 23 | 
            +
                attr_accessor :thehive_api_key
         | 
| 24 | 
            +
                attr_accessor :virustotal_api_key
         | 
| 25 | 
            +
                attr_accessor :zoomeye_password
         | 
| 26 | 
            +
                attr_accessor :zoomeye_username
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                attr_accessor :database
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def initialize
         | 
| 31 | 
            +
                  load_from_env
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def load_from_env
         | 
| 35 | 
            +
                  @binaryedge_api_key = ENV["BINARYEDGE_API_KEY"]
         | 
| 36 | 
            +
                  @censys_id = ENV["CENSYS_ID"]
         | 
| 37 | 
            +
                  @censys_secret = ENV["CENSYS_SECRET"]
         | 
| 38 | 
            +
                  @circl_passive_password = ENV["CIRCL_PASSIVE_PASSWORD"]
         | 
| 39 | 
            +
                  @circl_passive_username = ENV["CIRCL_PASSIVE_USERNAME"]
         | 
| 40 | 
            +
                  @misp_api_endpoint = ENV["MISP_API_ENDPOINT"]
         | 
| 41 | 
            +
                  @misp_api_key = ENV["MISP_API_KEY"]
         | 
| 42 | 
            +
                  @onyphe_api_key = ENV["ONYPHE_API_KEY"]
         | 
| 43 | 
            +
                  @passivetotal_api_key = ENV["PASSIVETOTAL_API_KEY"]
         | 
| 44 | 
            +
                  @passivetotal_username = ENV["PASSIVETOTAL_USERNAME"]
         | 
| 45 | 
            +
                  @pulsedive_api_key = ENV["PULSEDIVE_API_KEY"]
         | 
| 46 | 
            +
                  @securitytrails_api_key = ENV["SECURITYTRAILS_API_KEY"]
         | 
| 47 | 
            +
                  @shodan_api_key = ENV["SHODAN_API_KEY"]
         | 
| 48 | 
            +
                  @slack_channel = ENV["SLACK_CHANNEL"]
         | 
| 49 | 
            +
                  @slack_webhook_url = ENV["SLACK_WEBHOOK_URL"]
         | 
| 50 | 
            +
                  @thehive_api_endpoint = ENV["THEHIVE_API_ENDPOINT"]
         | 
| 51 | 
            +
                  @thehive_api_key = ENV["THEHIVE_API_KEY"]
         | 
| 52 | 
            +
                  @virustotal_api_key = ENV["VIRUSTOTAL_API_KEY"]
         | 
| 53 | 
            +
                  @zoomeye_password = ENV["ZOOMEYE_PASSWORD"]
         | 
| 54 | 
            +
                  @zoomeye_username = ENV["ZOOMEYE_USERNAME"]
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  @database = ENV["DATABASE"] || "mihari.db"
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 7 59 | 
             
                class << self
         | 
| 8 60 | 
             
                  def load_from_yaml(path)
         | 
| 9 61 | 
             
                    raise ArgumentError, "#{path} does not exist." unless File.exist?(path)
         | 
| @@ -15,10 +67,24 @@ module Mihari | |
| 15 67 | 
             
                      return
         | 
| 16 68 | 
             
                    end
         | 
| 17 69 |  | 
| 18 | 
            -
                     | 
| 19 | 
            -
                       | 
| 70 | 
            +
                    Mihari.configure do |config|
         | 
| 71 | 
            +
                      yaml.each do |key, value|
         | 
| 72 | 
            +
                        config.send("#{key.downcase}=".to_sym, value)
         | 
| 73 | 
            +
                      end
         | 
| 20 74 | 
             
                    end
         | 
| 21 75 | 
             
                  end
         | 
| 22 76 | 
             
                end
         | 
| 23 77 | 
             
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              class << self
         | 
| 80 | 
            +
                def config
         | 
| 81 | 
            +
                  @config ||= Config.new
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                attr_writer :config
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def configure
         | 
| 87 | 
            +
                  yield config
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              end
         | 
| 24 90 | 
             
            end
         | 
    
        data/lib/mihari/configurable.rb
    CHANGED
    
    
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "active_record"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class InitialSchema < ActiveRecord::Migration[6.0]
         | 
| 6 | 
            +
              def change
         | 
| 7 | 
            +
                create_table :tags, if_not_exists: true do |t|
         | 
| 8 | 
            +
                  t.string :name, null: false
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                create_table :alerts, if_not_exists: true do |t|
         | 
| 12 | 
            +
                  t.string :title, null: false
         | 
| 13 | 
            +
                  t.string :description, null: true
         | 
| 14 | 
            +
                  t.string :source, null: false
         | 
| 15 | 
            +
                  t.timestamps
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                create_table :artifacts, if_not_exists: true do |t|
         | 
| 19 | 
            +
                  t.string :data, null: false
         | 
| 20 | 
            +
                  t.string :data_type, null: false
         | 
| 21 | 
            +
                  t.belongs_to :alert, foreign_key: true
         | 
| 22 | 
            +
                  t.timestamps
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                create_table :taggings, if_not_exists: true do |t|
         | 
| 26 | 
            +
                  t.integer :tag_id
         | 
| 27 | 
            +
                  t.integer :alert_id
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                add_index :taggings, :tag_id, if_not_exists: true
         | 
| 31 | 
            +
                add_index :taggings, [:tag_id, :alert_id], unique: true, if_not_exists: true
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            def adapter
         | 
| 36 | 
            +
              return "postgresql" if Mihari.config.database.start_with?("postgresql://", "postgres://")
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              "sqlite3"
         | 
| 39 | 
            +
            end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            module Mihari
         | 
| 42 | 
            +
              class Database
         | 
| 43 | 
            +
                class << self
         | 
| 44 | 
            +
                  def connect
         | 
| 45 | 
            +
                    case adapter
         | 
| 46 | 
            +
                    when "postgresql"
         | 
| 47 | 
            +
                      ActiveRecord::Base.establish_connection(Mihari.config.database)
         | 
| 48 | 
            +
                    else
         | 
| 49 | 
            +
                      ActiveRecord::Base.establish_connection(
         | 
| 50 | 
            +
                        adapter: adapter,
         | 
| 51 | 
            +
                        database: Mihari.config.database
         | 
| 52 | 
            +
                      )
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    ActiveRecord::Migration.verbose = false
         | 
| 56 | 
            +
                    InitialSchema.migrate(:up)
         | 
| 57 | 
            +
                  rescue StandardError
         | 
| 58 | 
            +
                    # Do nothing
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def destroy!
         | 
| 62 | 
            +
                    InitialSchema.migrate(:down) if ActiveRecord::Base.connected?
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
            end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            Mihari::Database.connect
         |