mihari 0.4.2 → 0.5.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/lib/mihari/alert_viewer.rb +41 -0
- data/lib/mihari/analyzers/base.rb +1 -1
- data/lib/mihari/artifact.rb +0 -2
- data/lib/mihari/cli.rb +11 -1
- data/lib/mihari/emitters/the_hive.rb +4 -4
- data/lib/mihari/the_hive/alert.rb +25 -0
- data/lib/mihari/the_hive/artifact.rb +33 -0
- data/lib/mihari/the_hive/base.rb +14 -0
- data/lib/mihari/the_hive.rb +11 -46
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari.rb +5 -0
- metadata +6 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 0a78aa76416da5dc139e1fe11ce2d2475a45deda3a82caa1fa21eecd6e291711
         | 
| 4 | 
            +
              data.tar.gz: 9958ca0873d55c20525c3fa9186b266bbfba68c957997ae14f73a1d136d2032e
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 4d80e8026601ca12ed90b45d21ef0eb88a53cd1312d6f29f910ded76d7fa76e72c3295051e59a91e159d88cc54b3abec627837bbb6e53eda8a75c4acc52edcb2
         | 
| 7 | 
            +
              data.tar.gz: f70660b7da90c4ae9a13cfba050d775283072fbd135d30b58df6d17ae41e6799532038bef16fe76a183e81a07ab188aada365f418a733540ee15436df0b6295f
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Mihari
         | 
| 4 | 
            +
              class AlertViewer
         | 
| 5 | 
            +
                attr_reader :limit
         | 
| 6 | 
            +
                attr_reader :the_hive
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                ALERT_KEYS = %w(title description artifacts tags createdAt status).freeze
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize(limit: 5)
         | 
| 11 | 
            +
                  @limit = limit
         | 
| 12 | 
            +
                  validate_limit
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  @the_hive = TheHive.new
         | 
| 15 | 
            +
                  raise Error, "Cannot connect to the TheHive instance" unless the_hive.valid?
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def list
         | 
| 19 | 
            +
                  range = limit == "all" ? "all" : "0-#{limit}"
         | 
| 20 | 
            +
                  alerts = the_hive.alert.list(range: range)
         | 
| 21 | 
            +
                  alerts.map { |alert| convert alert }
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def validate_limit
         | 
| 27 | 
            +
                  return true if limit == "all"
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  raise ArgumentError, "limit should be bigger than zero" unless limit.to_i.positive?
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def convert(alert)
         | 
| 33 | 
            +
                  attributes = alert.select { |k, _v| ALERT_KEYS.include? k }
         | 
| 34 | 
            +
                  attributes["createdAt"] = Time.at(attributes["createdAt"] / 1000).to_s
         | 
| 35 | 
            +
                  attributes["artifacts"] = (attributes.dig("artifacts") || []).map do |artifact|
         | 
| 36 | 
            +
                    artifact.dig("data")
         | 
| 37 | 
            +
                  end.sort
         | 
| 38 | 
            +
                  attributes
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
    
        data/lib/mihari/artifact.rb
    CHANGED
    
    
    
        data/lib/mihari/cli.rb
    CHANGED
    
    | @@ -62,10 +62,20 @@ module Mihari | |
| 62 62 | 
             
                  run_analyzer basic
         | 
| 63 63 | 
             
                end
         | 
| 64 64 |  | 
| 65 | 
            +
                desc "alerts", "Show the alerts on TheHive"
         | 
| 66 | 
            +
                method_option :limit, default: 5, desc: "Number of alerts to show (or 'all' to show all the alerts)"
         | 
| 67 | 
            +
                def alerts
         | 
| 68 | 
            +
                  with_error_handling do
         | 
| 69 | 
            +
                    viewer = AlertViewer.new(limit: options["limit"])
         | 
| 70 | 
            +
                    alerts = viewer.list
         | 
| 71 | 
            +
                    puts JSON.pretty_generate(alerts)
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 65 75 | 
             
                no_commands do
         | 
| 66 76 | 
             
                  def with_error_handling
         | 
| 67 77 | 
             
                    yield
         | 
| 68 | 
            -
                  rescue ArgumentError, Hachi::Error, Censys::ResponseError => e
         | 
| 78 | 
            +
                  rescue ArgumentError, Hachi::Error, Censys::ResponseError, Error => e
         | 
| 69 79 | 
             
                    puts "Warning: #{e}"
         | 
| 70 80 | 
             
                  rescue StandardError => e
         | 
| 71 81 | 
             
                    puts "Warning: #{e}"
         | 
| @@ -3,21 +3,21 @@ | |
| 3 3 | 
             
            module Mihari
         | 
| 4 4 | 
             
              module Emitters
         | 
| 5 5 | 
             
                class TheHive < Base
         | 
| 6 | 
            -
                  attr_reader : | 
| 6 | 
            +
                  attr_reader :the_hive
         | 
| 7 7 |  | 
| 8 8 | 
             
                  def initialize
         | 
| 9 | 
            -
                    @ | 
| 9 | 
            +
                    @the_hive = Mihari::TheHive.new
         | 
| 10 10 | 
             
                  end
         | 
| 11 11 |  | 
| 12 12 | 
             
                  # @return [true, false]
         | 
| 13 13 | 
             
                  def valid?
         | 
| 14 | 
            -
                     | 
| 14 | 
            +
                    the_hive.valid?
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 17 | 
             
                  def emit(title:, description:, artifacts:, tags: [])
         | 
| 18 18 | 
             
                    return if artifacts.empty?
         | 
| 19 19 |  | 
| 20 | 
            -
                     | 
| 20 | 
            +
                    the_hive.alert.create(
         | 
| 21 21 | 
             
                      title: title,
         | 
| 22 22 | 
             
                      description: description,
         | 
| 23 23 | 
             
                      artifacts: artifacts.map(&:to_h),
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Mihari
         | 
| 4 | 
            +
              class TheHive
         | 
| 5 | 
            +
                class Alert < Base
         | 
| 6 | 
            +
                  # @return [Array]
         | 
| 7 | 
            +
                  def list(range: "all", sort: "-date")
         | 
| 8 | 
            +
                    alerts = api.alert.search({ source: "mihari" }, range: range, sort: sort)
         | 
| 9 | 
            +
                    alerts.sort_by { |alert| -alert.dig("createdAt") }
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # @return [Hash]
         | 
| 13 | 
            +
                  def create(title:, description:, artifacts:, tags: [])
         | 
| 14 | 
            +
                    api.alert.create(
         | 
| 15 | 
            +
                      title: title,
         | 
| 16 | 
            +
                      description: description,
         | 
| 17 | 
            +
                      artifacts: artifacts,
         | 
| 18 | 
            +
                      tags: tags,
         | 
| 19 | 
            +
                      type: "external",
         | 
| 20 | 
            +
                      source: "mihari"
         | 
| 21 | 
            +
                    )
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Mihari
         | 
| 4 | 
            +
              class TheHive
         | 
| 5 | 
            +
                class Artifact < Base
         | 
| 6 | 
            +
                  # @return [Array]
         | 
| 7 | 
            +
                  def search(data:, data_type:, range: "all")
         | 
| 8 | 
            +
                    api.artifact.search({ data: data, data_type: data_type }, range: range)
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  # @return [Array]
         | 
| 12 | 
            +
                  def search_all(data:, range: "all")
         | 
| 13 | 
            +
                    api.artifact.search({ data: data }, range: range)
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # @return [true, false]
         | 
| 17 | 
            +
                  def exists?(data:, data_type:)
         | 
| 18 | 
            +
                    res = search(data: data, data_type: data_type, range: "0-1")
         | 
| 19 | 
            +
                    !res.empty?
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  # @return [Array<Mihari::Artifact>]
         | 
| 23 | 
            +
                  def find_non_existing_artifacts(artifacts)
         | 
| 24 | 
            +
                    data = artifacts.map(&:data)
         | 
| 25 | 
            +
                    results = search_all(data: data)
         | 
| 26 | 
            +
                    keys = results.map { |result| result.dig("data") }.compact.uniq
         | 
| 27 | 
            +
                    artifacts.reject do |artifact|
         | 
| 28 | 
            +
                      keys.include? artifact.data
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
    
        data/lib/mihari/the_hive.rb
    CHANGED
    
    | @@ -5,14 +5,12 @@ require "uri" | |
| 5 5 |  | 
| 6 6 | 
             
            module Mihari
         | 
| 7 7 | 
             
              class TheHive
         | 
| 8 | 
            -
                 | 
| 9 | 
            -
                 | 
| 10 | 
            -
                  ENV.key? "THEHIVE_API_ENDPOINT"
         | 
| 11 | 
            -
                end
         | 
| 8 | 
            +
                attr_reader :artifact
         | 
| 9 | 
            +
                attr_reader :alert
         | 
| 12 10 |  | 
| 13 | 
            -
                 | 
| 14 | 
            -
             | 
| 15 | 
            -
                   | 
| 11 | 
            +
                def initialize
         | 
| 12 | 
            +
                  @artifact = Artifact.new
         | 
| 13 | 
            +
                  @alert = Alert.new
         | 
| 16 14 | 
             
                end
         | 
| 17 15 |  | 
| 18 16 | 
             
                # @return [true, false]
         | 
| @@ -20,51 +18,18 @@ module Mihari | |
| 20 18 | 
             
                  api_endpont? && api_key? && ping?
         | 
| 21 19 | 
             
                end
         | 
| 22 20 |  | 
| 23 | 
            -
                 | 
| 24 | 
            -
                def api
         | 
| 25 | 
            -
                  @api ||= Hachi::API.new
         | 
| 26 | 
            -
                end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                # @return [Array]
         | 
| 29 | 
            -
                def search(data:, data_type:, range: "all")
         | 
| 30 | 
            -
                  api.artifact.search({ data: data, data_type: data_type }, range: range)
         | 
| 31 | 
            -
                end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                # @return [Array]
         | 
| 34 | 
            -
                def search_all(data:, range: "all")
         | 
| 35 | 
            -
                  api.artifact.search({ data: data }, range: range)
         | 
| 36 | 
            -
                end
         | 
| 21 | 
            +
                private
         | 
| 37 22 |  | 
| 38 23 | 
             
                # @return [true, false]
         | 
| 39 | 
            -
                def  | 
| 40 | 
            -
                   | 
| 41 | 
            -
                  !res.empty?
         | 
| 42 | 
            -
                end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                # @return [Array<Mihari::Artifact>]
         | 
| 45 | 
            -
                def find_non_existing_artifacts(artifacts)
         | 
| 46 | 
            -
                  data = artifacts.map(&:data)
         | 
| 47 | 
            -
                  results = search_all(data: data)
         | 
| 48 | 
            -
                  keys = results.map { |result| result.dig("data") }.compact.uniq
         | 
| 49 | 
            -
                  artifacts.reject do |artifact|
         | 
| 50 | 
            -
                    keys.include? artifact.data
         | 
| 51 | 
            -
                  end
         | 
| 24 | 
            +
                def api_endpont?
         | 
| 25 | 
            +
                  ENV.key? "THEHIVE_API_ENDPOINT"
         | 
| 52 26 | 
             
                end
         | 
| 53 27 |  | 
| 54 | 
            -
                # @return [ | 
| 55 | 
            -
                def  | 
| 56 | 
            -
                   | 
| 57 | 
            -
                    title: title,
         | 
| 58 | 
            -
                    description: description,
         | 
| 59 | 
            -
                    artifacts: artifacts,
         | 
| 60 | 
            -
                    tags: tags,
         | 
| 61 | 
            -
                    type: "external",
         | 
| 62 | 
            -
                    source: "mihari"
         | 
| 63 | 
            -
                  )
         | 
| 28 | 
            +
                # @return [true, false]
         | 
| 29 | 
            +
                def api_key?
         | 
| 30 | 
            +
                  ENV.key? "THEHIVE_API_KEY"
         | 
| 64 31 | 
             
                end
         | 
| 65 32 |  | 
| 66 | 
            -
                private
         | 
| 67 | 
            -
             | 
| 68 33 | 
             
                def ping?
         | 
| 69 34 | 
             
                  base_url = ENV.fetch("THEHIVE_API_ENDPOINT")
         | 
| 70 35 | 
             
                  base_url = base_url.end_with?("/") ? base_url[0..-2] : base_url
         | 
    
        data/lib/mihari/version.rb
    CHANGED
    
    
    
        data/lib/mihari.rb
    CHANGED
    
    | @@ -20,6 +20,9 @@ require "mihari/errors" | |
| 20 20 | 
             
            require "mihari/type_checker"
         | 
| 21 21 | 
             
            require "mihari/artifact"
         | 
| 22 22 |  | 
| 23 | 
            +
            require "mihari/the_hive/base"
         | 
| 24 | 
            +
            require "mihari/the_hive/alert"
         | 
| 25 | 
            +
            require "mihari/the_hive/artifact"
         | 
| 23 26 | 
             
            require "mihari/the_hive"
         | 
| 24 27 |  | 
| 25 28 | 
             
            require "mihari/analyzers/base"
         | 
| @@ -35,4 +38,6 @@ require "mihari/emitters/slack" | |
| 35 38 | 
             
            require "mihari/emitters/stdout"
         | 
| 36 39 | 
             
            require "mihari/emitters/the_hive"
         | 
| 37 40 |  | 
| 41 | 
            +
            require "mihari/alert_viewer"
         | 
| 42 | 
            +
             | 
| 38 43 | 
             
            require "mihari/cli"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: mihari
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.5.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Manabu Niseki
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019-08- | 
| 11 | 
            +
            date: 2019-08-12 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         | 
| @@ -297,6 +297,7 @@ files: | |
| 297 297 | 
             
            - examples/vt_passive_dns.rb
         | 
| 298 298 | 
             
            - exe/mihari
         | 
| 299 299 | 
             
            - lib/mihari.rb
         | 
| 300 | 
            +
            - lib/mihari/alert_viewer.rb
         | 
| 300 301 | 
             
            - lib/mihari/analyzers/base.rb
         | 
| 301 302 | 
             
            - lib/mihari/analyzers/basic.rb
         | 
| 302 303 | 
             
            - lib/mihari/analyzers/censys.rb
         | 
| @@ -312,6 +313,9 @@ files: | |
| 312 313 | 
             
            - lib/mihari/emitters/the_hive.rb
         | 
| 313 314 | 
             
            - lib/mihari/errors.rb
         | 
| 314 315 | 
             
            - lib/mihari/the_hive.rb
         | 
| 316 | 
            +
            - lib/mihari/the_hive/alert.rb
         | 
| 317 | 
            +
            - lib/mihari/the_hive/artifact.rb
         | 
| 318 | 
            +
            - lib/mihari/the_hive/base.rb
         | 
| 315 319 | 
             
            - lib/mihari/type_checker.rb
         | 
| 316 320 | 
             
            - lib/mihari/version.rb
         | 
| 317 321 | 
             
            - mihari.gemspec
         |