mihari 5.1.1 → 5.1.2
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/.gitmodules +0 -3
- data/.rubocop.yml +6 -0
- data/README.md +0 -1
- data/lib/mihari/analyzers/base.rb +32 -27
- data/lib/mihari/analyzers/binaryedge.rb +8 -2
- data/lib/mihari/analyzers/censys.rb +7 -49
- data/lib/mihari/analyzers/circl.rb +5 -2
- data/lib/mihari/analyzers/crtsh.rb +6 -0
- data/lib/mihari/analyzers/dnstwister.rb +4 -2
- data/lib/mihari/analyzers/feed.rb +21 -0
- data/lib/mihari/analyzers/greynoise.rb +5 -28
- data/lib/mihari/analyzers/onyphe.rb +8 -33
- data/lib/mihari/analyzers/otx.rb +3 -0
- data/lib/mihari/analyzers/passivetotal.rb +3 -0
- data/lib/mihari/analyzers/pulsedive.rb +3 -0
- data/lib/mihari/analyzers/rule.rb +0 -1
- data/lib/mihari/analyzers/securitytrails.rb +8 -10
- data/lib/mihari/analyzers/shodan.rb +13 -81
- data/lib/mihari/analyzers/urlscan.rb +9 -0
- data/lib/mihari/analyzers/virustotal.rb +4 -0
- data/lib/mihari/analyzers/virustotal_intelligence.rb +8 -2
- data/lib/mihari/analyzers/zoomeye.rb +9 -0
- data/lib/mihari/clients/binaryedge.rb +5 -0
- data/lib/mihari/clients/censys.rb +4 -4
- data/lib/mihari/clients/circl.rb +3 -3
- data/lib/mihari/clients/greynoise.rb +6 -1
- data/lib/mihari/clients/misp.rb +6 -1
- data/lib/mihari/clients/onyphe.rb +13 -1
- data/lib/mihari/clients/otx.rb +20 -0
- data/lib/mihari/clients/passivetotal.rb +6 -2
- data/lib/mihari/clients/publsedive.rb +18 -1
- data/lib/mihari/clients/securitytrails.rb +94 -0
- data/lib/mihari/clients/shodan.rb +14 -3
- data/lib/mihari/clients/the_hive.rb +6 -1
- data/lib/mihari/clients/urlscan.rb +3 -1
- data/lib/mihari/clients/virustotal.rb +9 -3
- data/lib/mihari/clients/zoomeye.rb +7 -1
- data/lib/mihari/commands/database.rb +1 -6
- data/lib/mihari/commands/searcher.rb +1 -2
- data/lib/mihari/database.rb +9 -0
- data/lib/mihari/structs/censys.rb +62 -0
- data/lib/mihari/structs/greynoise.rb +43 -0
- data/lib/mihari/structs/onyphe.rb +45 -0
- data/lib/mihari/structs/shodan.rb +83 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/middleware/connection_adapter.rb +1 -3
- data/lib/mihari/web/public/assets/{index-63900d73.js → index-7d0fb8c4.js} +2 -2
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +2 -2
- data/lib/mihari.rb +1 -3
- data/mihari.gemspec +2 -3
- metadata +9 -25
- data/lib/mihari/analyzers/dnpedia.rb +0 -33
- data/lib/mihari/clients/dnpedia.rb +0 -64
- data/lib/mihari/mixins/database.rb +0 -16
| @@ -10,6 +10,12 @@ module Mihari | |
| 10 10 | 
             
                  # @return [String, nil]
         | 
| 11 11 | 
             
                  attr_reader :api_key
         | 
| 12 12 |  | 
| 13 | 
            +
                  # @return [Integer]
         | 
| 14 | 
            +
                  attr_reader :interval
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # @return [String]
         | 
| 17 | 
            +
                  attr_reader :query
         | 
| 18 | 
            +
             | 
| 13 19 | 
             
                  def initialize(*args, **kwargs)
         | 
| 14 20 | 
             
                    super(*args, **kwargs)
         | 
| 15 21 |  | 
| @@ -18,13 +24,9 @@ module Mihari | |
| 18 24 |  | 
| 19 25 | 
             
                  def artifacts
         | 
| 20 26 | 
             
                    results = search
         | 
| 21 | 
            -
                    return []  | 
| 22 | 
            -
             | 
| 23 | 
            -
                    results = results.map { |result| Structs::Shodan::Result.from_dynamic!(result) }
         | 
| 24 | 
            -
                    matches = results.map { |result| result.matches || [] }.flatten
         | 
| 27 | 
            +
                    return [] if results.empty?
         | 
| 25 28 |  | 
| 26 | 
            -
                     | 
| 27 | 
            -
                    uniq_matches.map { |match| build_artifact(match, matches) }
         | 
| 29 | 
            +
                    results.map { |result| result.to_artifacts(source) }.flatten.uniq(&:data)
         | 
| 28 30 | 
             
                  end
         | 
| 29 31 |  | 
| 30 32 | 
             
                  private
         | 
| @@ -42,29 +44,25 @@ module Mihari | |
| 42 44 | 
             
                  #
         | 
| 43 45 | 
             
                  # Search with pagination
         | 
| 44 46 | 
             
                  #
         | 
| 45 | 
            -
                  # @param [String] query
         | 
| 46 47 | 
             
                  # @param [Integer] page
         | 
| 47 48 | 
             
                  #
         | 
| 48 | 
            -
                  # @return [ | 
| 49 | 
            +
                  # @return [Structs::Shodan::Result]
         | 
| 49 50 | 
             
                  #
         | 
| 50 | 
            -
                  def search_with_page( | 
| 51 | 
            +
                  def search_with_page(page: 1)
         | 
| 51 52 | 
             
                    client.search(query, page: page)
         | 
| 52 53 | 
             
                  end
         | 
| 53 54 |  | 
| 54 55 | 
             
                  #
         | 
| 55 56 | 
             
                  # Search
         | 
| 56 57 | 
             
                  #
         | 
| 57 | 
            -
                  # @return [Array< | 
| 58 | 
            +
                  # @return [Array<Structs::Shodan::Result>]
         | 
| 58 59 | 
             
                  #
         | 
| 59 60 | 
             
                  def search
         | 
| 60 61 | 
             
                    responses = []
         | 
| 61 62 | 
             
                    (1..Float::INFINITY).each do |page|
         | 
| 62 | 
            -
                      res = search_with_page( | 
| 63 | 
            -
             | 
| 64 | 
            -
                      break unless res
         | 
| 65 | 
            -
             | 
| 63 | 
            +
                      res = search_with_page(page: page)
         | 
| 66 64 | 
             
                      responses << res
         | 
| 67 | 
            -
                      break if res | 
| 65 | 
            +
                      break if res.total <= page * PAGE_SIZE
         | 
| 68 66 |  | 
| 69 67 | 
             
                      # sleep #{interval} seconds to avoid the rate limitation (if it is set)
         | 
| 70 68 | 
             
                      sleep interval
         | 
| @@ -76,42 +74,6 @@ module Mihari | |
| 76 74 | 
             
                    responses
         | 
| 77 75 | 
             
                  end
         | 
| 78 76 |  | 
| 79 | 
            -
                  #
         | 
| 80 | 
            -
                  # Collect metadata from matches
         | 
| 81 | 
            -
                  #
         | 
| 82 | 
            -
                  # @param [Array<Structs::Shodan::Match>] matches
         | 
| 83 | 
            -
                  # @param [String] ip
         | 
| 84 | 
            -
                  #
         | 
| 85 | 
            -
                  # @return [Array<Hash>]
         | 
| 86 | 
            -
                  #
         | 
| 87 | 
            -
                  def collect_metadata_by_ip(matches, ip)
         | 
| 88 | 
            -
                    matches.select { |match| match.ip_str == ip }.map(&:metadata)
         | 
| 89 | 
            -
                  end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                  #
         | 
| 92 | 
            -
                  # Collect ports from matches
         | 
| 93 | 
            -
                  #
         | 
| 94 | 
            -
                  # @param [Array<Structs::Shodan::Match>] matches
         | 
| 95 | 
            -
                  # @param [String] ip
         | 
| 96 | 
            -
                  #
         | 
| 97 | 
            -
                  # @return [Array<String>]
         | 
| 98 | 
            -
                  #
         | 
| 99 | 
            -
                  def collect_ports_by_ip(matches, ip)
         | 
| 100 | 
            -
                    matches.select { |match| match.ip_str == ip }.map(&:port)
         | 
| 101 | 
            -
                  end
         | 
| 102 | 
            -
             | 
| 103 | 
            -
                  #
         | 
| 104 | 
            -
                  # Collect hostnames from matches
         | 
| 105 | 
            -
                  #
         | 
| 106 | 
            -
                  # @param [Array<Structs::Shodan::Match>] matches
         | 
| 107 | 
            -
                  # @param [String] ip
         | 
| 108 | 
            -
                  #
         | 
| 109 | 
            -
                  # @return [Array<String>]
         | 
| 110 | 
            -
                  #
         | 
| 111 | 
            -
                  def collect_hostnames_by_ip(matches, ip)
         | 
| 112 | 
            -
                    matches.select { |match| match.ip_str == ip }.map(&:hostnames).flatten.uniq
         | 
| 113 | 
            -
                  end
         | 
| 114 | 
            -
             | 
| 115 77 | 
             
                  #
         | 
| 116 78 | 
             
                  # Build an artifact from a Shodan search API response
         | 
| 117 79 | 
             
                  #
         | 
| @@ -121,36 +83,6 @@ module Mihari | |
| 121 83 | 
             
                  # @return [Artifact]
         | 
| 122 84 | 
             
                  #
         | 
| 123 85 | 
             
                  def build_artifact(match, matches)
         | 
| 124 | 
            -
                    as = nil
         | 
| 125 | 
            -
                    as = AutonomousSystem.new(asn: normalize_asn(match.asn)) unless match.asn.nil?
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                    geolocation = nil
         | 
| 128 | 
            -
                    if !match.location.country_name.nil? && !match.location.country_code.nil?
         | 
| 129 | 
            -
                      geolocation = Geolocation.new(
         | 
| 130 | 
            -
                        country: match.location.country_name,
         | 
| 131 | 
            -
                        country_code: match.location.country_code
         | 
| 132 | 
            -
                      )
         | 
| 133 | 
            -
                    end
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                    metadata = collect_metadata_by_ip(matches, match.ip_str)
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                    ports = collect_ports_by_ip(matches, match.ip_str).map do |port|
         | 
| 138 | 
            -
                      Port.new(port: port)
         | 
| 139 | 
            -
                    end
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                    reverse_dns_names = collect_hostnames_by_ip(matches, match.ip_str).map do |name|
         | 
| 142 | 
            -
                      ReverseDnsName.new(name: name)
         | 
| 143 | 
            -
                    end
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                    Artifact.new(
         | 
| 146 | 
            -
                      data: match.ip_str,
         | 
| 147 | 
            -
                      source: source,
         | 
| 148 | 
            -
                      metadata: metadata,
         | 
| 149 | 
            -
                      autonomous_system: as,
         | 
| 150 | 
            -
                      geolocation: geolocation,
         | 
| 151 | 
            -
                      ports: ports,
         | 
| 152 | 
            -
                      reverse_dns_names: reverse_dns_names
         | 
| 153 | 
            -
                    )
         | 
| 154 86 | 
             
                  end
         | 
| 155 87 | 
             
                end
         | 
| 156 88 | 
             
              end
         | 
| @@ -15,6 +15,15 @@ module Mihari | |
| 15 15 | 
             
                  # @return [String, nil]
         | 
| 16 16 | 
             
                  attr_reader :api_key
         | 
| 17 17 |  | 
| 18 | 
            +
                  # @return [String]
         | 
| 19 | 
            +
                  attr_reader :query
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  # @return [Integer]
         | 
| 22 | 
            +
                  attr_reader :interval
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  # @return [String]
         | 
| 25 | 
            +
                  attr_reader :allowed_data_types
         | 
| 26 | 
            +
             | 
| 18 27 | 
             
                  def initialize(*args, **kwargs)
         | 
| 19 28 | 
             
                    super
         | 
| 20 29 |  | 
| @@ -10,6 +10,12 @@ module Mihari | |
| 10 10 | 
             
                  # @return [String, nil]
         | 
| 11 11 | 
             
                  attr_reader :api_key
         | 
| 12 12 |  | 
| 13 | 
            +
                  # @return [String]
         | 
| 14 | 
            +
                  attr_reader :query
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  # @return [Integer]
         | 
| 17 | 
            +
                  attr_reader :interval
         | 
| 18 | 
            +
             | 
| 13 19 | 
             
                  def initialize(*args, **kwargs)
         | 
| 14 20 | 
             
                    super
         | 
| 15 21 |  | 
| @@ -19,7 +25,7 @@ module Mihari | |
| 19 25 | 
             
                  end
         | 
| 20 26 |  | 
| 21 27 | 
             
                  def artifacts
         | 
| 22 | 
            -
                    responses =  | 
| 28 | 
            +
                    responses = search_with_cursor
         | 
| 23 29 | 
             
                    responses.map do |response|
         | 
| 24 30 | 
             
                      response.data.map do |datum|
         | 
| 25 31 | 
             
                        Artifact.new(data: datum.value, source: source, metadata: datum.metadata)
         | 
| @@ -47,7 +53,7 @@ module Mihari | |
| 47 53 | 
             
                  #
         | 
| 48 54 | 
             
                  # @return [Array<Structs::VirusTotalIntelligence::Response>]
         | 
| 49 55 | 
             
                  #
         | 
| 50 | 
            -
                  def  | 
| 56 | 
            +
                  def search_with_cursor
         | 
| 51 57 | 
             
                    cursor = nil
         | 
| 52 58 | 
             
                    responses = []
         | 
| 53 59 |  | 
| @@ -12,6 +12,15 @@ module Mihari | |
| 12 12 | 
             
                  # @return [String, nil]
         | 
| 13 13 | 
             
                  attr_reader :api_key
         | 
| 14 14 |  | 
| 15 | 
            +
                  # @return [String]
         | 
| 16 | 
            +
                  attr_reader :query
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # @return [String]
         | 
| 19 | 
            +
                  attr_reader :type
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  # @return [Integer]
         | 
| 22 | 
            +
                  attr_reader :interval
         | 
| 23 | 
            +
             | 
| 15 24 | 
             
                  def initialize(*args, **kwargs)
         | 
| 16 25 | 
             
                    super(*args, **kwargs)
         | 
| 17 26 |  | 
| @@ -3,6 +3,11 @@ | |
| 3 3 | 
             
            module Mihari
         | 
| 4 4 | 
             
              module Clients
         | 
| 5 5 | 
             
                class BinaryEdge < Base
         | 
| 6 | 
            +
                  #
         | 
| 7 | 
            +
                  # @param [String] base_url
         | 
| 8 | 
            +
                  # @param [String, nil] api_key
         | 
| 9 | 
            +
                  # @param [Hash] headers
         | 
| 10 | 
            +
                  #
         | 
| 6 11 | 
             
                  def initialize(base_url = "https://api.binaryedge.io/v2", api_key:, headers: {})
         | 
| 7 12 | 
             
                    raise(ArgumentError, "'api_key' argument is required") unless api_key
         | 
| 8 13 |  | 
| @@ -7,8 +7,8 @@ module Mihari | |
| 7 7 | 
             
                class Censys < Base
         | 
| 8 8 | 
             
                  #
         | 
| 9 9 | 
             
                  # @param [String] base_url
         | 
| 10 | 
            -
                  # @param [String] id
         | 
| 11 | 
            -
                  # @param [String] secret
         | 
| 10 | 
            +
                  # @param [String, nil] id
         | 
| 11 | 
            +
                  # @param [String, nil] secret
         | 
| 12 12 | 
             
                  # @param [Hash] headers
         | 
| 13 13 | 
             
                  #
         | 
| 14 14 | 
             
                  def initialize(base_url = "https://search.censys.io", id:, secret:, headers: {})
         | 
| @@ -30,12 +30,12 @@ module Mihari | |
| 30 30 | 
             
                  # @params [Integer, nil] per_page the number of results to be returned for each page.
         | 
| 31 31 | 
             
                  # @params [Integer, nil] cursor the cursor of the desired result set.
         | 
| 32 32 | 
             
                  #
         | 
| 33 | 
            -
                  # @return [ | 
| 33 | 
            +
                  # @return [Structs::Censys::Response]
         | 
| 34 34 | 
             
                  #
         | 
| 35 35 | 
             
                  def search(query, per_page: nil, cursor: nil)
         | 
| 36 36 | 
             
                    params = { q: query, per_page: per_page, cursor: cursor }.compact
         | 
| 37 37 | 
             
                    res = get("/api/v2/hosts/search", params: params)
         | 
| 38 | 
            -
                    JSON.parse(res.body.to_s)
         | 
| 38 | 
            +
                    Structs::Censys::Response.from_dynamic! JSON.parse(res.body.to_s)
         | 
| 39 39 | 
             
                  end
         | 
| 40 40 | 
             
                end
         | 
| 41 41 | 
             
              end
         | 
    
        data/lib/mihari/clients/circl.rb
    CHANGED
    
    | @@ -7,8 +7,8 @@ module Mihari | |
| 7 7 | 
             
                class CIRCL < Base
         | 
| 8 8 | 
             
                  #
         | 
| 9 9 | 
             
                  # @param [String] base_url
         | 
| 10 | 
            -
                  # @param [String] username
         | 
| 11 | 
            -
                  # @param [String] password
         | 
| 10 | 
            +
                  # @param [String, nil] username
         | 
| 11 | 
            +
                  # @param [String, nil] password
         | 
| 12 12 | 
             
                  # @param [Hash] headers
         | 
| 13 13 | 
             
                  #
         | 
| 14 14 | 
             
                  def initialize(base_url = "https://www.circl.lu", username:, password:, headers: {})
         | 
| @@ -43,7 +43,7 @@ module Mihari | |
| 43 43 | 
             
                  #
         | 
| 44 44 | 
             
                  #
         | 
| 45 45 | 
             
                  # @param [String] path
         | 
| 46 | 
            -
                  # @param [ | 
| 46 | 
            +
                  # @param [Hash] params
         | 
| 47 47 | 
             
                  #
         | 
| 48 48 | 
             
                  def _get(path, params: {})
         | 
| 49 49 | 
             
                    res = get(path, params: params)
         | 
| @@ -3,6 +3,11 @@ | |
| 3 3 | 
             
            module Mihari
         | 
| 4 4 | 
             
              module Clients
         | 
| 5 5 | 
             
                class GreyNoise < Base
         | 
| 6 | 
            +
                  #
         | 
| 7 | 
            +
                  # @param [String] base_url
         | 
| 8 | 
            +
                  # @param [String, nil] api_key
         | 
| 9 | 
            +
                  # @param [Hash] headers
         | 
| 10 | 
            +
                  #
         | 
| 6 11 | 
             
                  def initialize(base_url = "https://api.greynoise.io", api_key:, headers: {})
         | 
| 7 12 | 
             
                    raise(ArgumentError, "'api_key' argument is required") unless api_key
         | 
| 8 13 |  | 
| @@ -22,7 +27,7 @@ module Mihari | |
| 22 27 | 
             
                  def gnql_search(query, size: nil, scroll: nil)
         | 
| 23 28 | 
             
                    params = { query: query, size: size, scroll: scroll }.compact
         | 
| 24 29 | 
             
                    res = get("/v2/experimental/gnql", params: params)
         | 
| 25 | 
            -
                    JSON.parse | 
| 30 | 
            +
                    Structs::GreyNoise::Response.from_dynamic! JSON.parse(res.body.to_s)
         | 
| 26 31 | 
             
                  end
         | 
| 27 32 | 
             
                end
         | 
| 28 33 | 
             
              end
         | 
    
        data/lib/mihari/clients/misp.rb
    CHANGED
    
    | @@ -5,7 +5,7 @@ module Mihari | |
| 5 5 | 
             
                class MISP < Base
         | 
| 6 6 | 
             
                  #
         | 
| 7 7 | 
             
                  # @param [String] base_url
         | 
| 8 | 
            -
                  # @param [String] api_key
         | 
| 8 | 
            +
                  # @param [String, nil] api_key
         | 
| 9 9 | 
             
                  # @param [Hash] headers
         | 
| 10 10 | 
             
                  #
         | 
| 11 11 | 
             
                  def initialize(base_url, api_key:, headers: {})
         | 
| @@ -15,6 +15,11 @@ module Mihari | |
| 15 15 | 
             
                    super(base_url, headers: headers)
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # @param [Hash] payload
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # @return [Hash]
         | 
| 22 | 
            +
                  #
         | 
| 18 23 | 
             
                  def create_event(payload)
         | 
| 19 24 | 
             
                    res = post("/events/add", json: payload)
         | 
| 20 25 | 
             
                    JSON.parse(res.body.to_s)
         | 
| @@ -3,8 +3,14 @@ | |
| 3 3 | 
             
            module Mihari
         | 
| 4 4 | 
             
              module Clients
         | 
| 5 5 | 
             
                class Onyphe < Base
         | 
| 6 | 
            +
                  # @return [String]
         | 
| 6 7 | 
             
                  attr_reader :api_key
         | 
| 7 8 |  | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # @param [String] base_url
         | 
| 11 | 
            +
                  # @param [String, nil] api_key
         | 
| 12 | 
            +
                  # @param [Hash] headers
         | 
| 13 | 
            +
                  #
         | 
| 8 14 | 
             
                  def initialize(base_url = "https://www.onyphe.io", api_key:, headers: {})
         | 
| 9 15 | 
             
                    raise(ArgumentError, "'api_key' argument is required") if api_key.nil?
         | 
| 10 16 |  | 
| @@ -13,10 +19,16 @@ module Mihari | |
| 13 19 | 
             
                    @api_key = api_key
         | 
| 14 20 | 
             
                  end
         | 
| 15 21 |  | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # @param [String] query
         | 
| 24 | 
            +
                  # @param [Integer] page
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # @return [Hash]
         | 
| 27 | 
            +
                  #
         | 
| 16 28 | 
             
                  def datascan(query, page: 1)
         | 
| 17 29 | 
             
                    params = { page: page, apikey: api_key }
         | 
| 18 30 | 
             
                    res = get("/api/v2/simple/datascan/#{query}", params: params)
         | 
| 19 | 
            -
                    JSON.parse(res.body.to_s)
         | 
| 31 | 
            +
                    Structs::Onyphe::Response.from_dynamic! JSON.parse(res.body.to_s)
         | 
| 20 32 | 
             
                  end
         | 
| 21 33 | 
             
                end
         | 
| 22 34 | 
             
              end
         | 
    
        data/lib/mihari/clients/otx.rb
    CHANGED
    
    | @@ -3,6 +3,11 @@ | |
| 3 3 | 
             
            module Mihari
         | 
| 4 4 | 
             
              module Clients
         | 
| 5 5 | 
             
                class OTX < Base
         | 
| 6 | 
            +
                  #
         | 
| 7 | 
            +
                  # @param [String] base_url
         | 
| 8 | 
            +
                  # @param [String, nil] api_key
         | 
| 9 | 
            +
                  # @param [Hash] headers
         | 
| 10 | 
            +
                  #
         | 
| 6 11 | 
             
                  def initialize(base_url = "https://otx.alienvault.com", api_key:, headers: {})
         | 
| 7 12 | 
             
                    raise(ArgumentError, "'api_key' argument is required") unless api_key
         | 
| 8 13 |  | 
| @@ -10,16 +15,31 @@ module Mihari | |
| 10 15 | 
             
                    super(base_url, headers: headers)
         | 
| 11 16 | 
             
                  end
         | 
| 12 17 |  | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # @param [String] ip
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # @return [Hash]
         | 
| 22 | 
            +
                  #
         | 
| 13 23 | 
             
                  def query_by_ip(ip)
         | 
| 14 24 | 
             
                    _get "/api/v1/indicators/IPv4/#{ip}/passive_dns"
         | 
| 15 25 | 
             
                  end
         | 
| 16 26 |  | 
| 27 | 
            +
                  #
         | 
| 28 | 
            +
                  # @param [String] domain
         | 
| 29 | 
            +
                  #
         | 
| 30 | 
            +
                  # @return [Hash]
         | 
| 31 | 
            +
                  #
         | 
| 17 32 | 
             
                  def query_by_domain(domain)
         | 
| 18 33 | 
             
                    _get "/api/v1/indicators/domain/#{domain}/passive_dns"
         | 
| 19 34 | 
             
                  end
         | 
| 20 35 |  | 
| 21 36 | 
             
                  private
         | 
| 22 37 |  | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # @param [String] path
         | 
| 40 | 
            +
                  #
         | 
| 41 | 
            +
                  # @return [Hash]
         | 
| 42 | 
            +
                  #
         | 
| 23 43 | 
             
                  def _get(path)
         | 
| 24 44 | 
             
                    res = get(path)
         | 
| 25 45 | 
             
                    JSON.parse(res.body.to_s)
         | 
| @@ -7,8 +7,8 @@ module Mihari | |
| 7 7 | 
             
                class PassiveTotal < Base
         | 
| 8 8 | 
             
                  #
         | 
| 9 9 | 
             
                  # @param [String] base_url
         | 
| 10 | 
            -
                  # @param [String] username
         | 
| 11 | 
            -
                  # @param [String] api_key
         | 
| 10 | 
            +
                  # @param [String, nil] username
         | 
| 11 | 
            +
                  # @param [String, nil] api_key
         | 
| 12 12 | 
             
                  # @param [Hash] headers
         | 
| 13 13 | 
             
                  #
         | 
| 14 14 | 
             
                  def initialize(base_url = "https://api.passivetotal.org", username:, api_key:, headers: {})
         | 
| @@ -31,6 +31,8 @@ module Mihari | |
| 31 31 | 
             
                  #
         | 
| 32 32 | 
             
                  # @param [String] query
         | 
| 33 33 | 
             
                  #
         | 
| 34 | 
            +
                  # @return [Hash]
         | 
| 35 | 
            +
                  #
         | 
| 34 36 | 
             
                  def passive_dns_search(query)
         | 
| 35 37 | 
             
                    params = { query: query }
         | 
| 36 38 | 
             
                    _get("/v2/dns/passive/unique", params: params)
         | 
| @@ -56,6 +58,8 @@ module Mihari | |
| 56 58 | 
             
                  # @param [String] path
         | 
| 57 59 | 
             
                  # @param [Hash] params
         | 
| 58 60 | 
             
                  #
         | 
| 61 | 
            +
                  # @return [Hash]
         | 
| 62 | 
            +
                  #
         | 
| 59 63 | 
             
                  def _get(path, params: {})
         | 
| 60 64 | 
             
                    res = get(path, params: params)
         | 
| 61 65 | 
             
                    JSON.parse(res.body.to_s)
         | 
| @@ -3,8 +3,14 @@ | |
| 3 3 | 
             
            module Mihari
         | 
| 4 4 | 
             
              module Clients
         | 
| 5 5 | 
             
                class PulseDive < Base
         | 
| 6 | 
            +
                  # @return [String]
         | 
| 6 7 | 
             
                  attr_reader :api_key
         | 
| 7 8 |  | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # @param [String] base_url
         | 
| 11 | 
            +
                  # @param [String, nil] api_key
         | 
| 12 | 
            +
                  # @param [Hash] headers
         | 
| 13 | 
            +
                  #
         | 
| 8 14 | 
             
                  def initialize(base_url = "https://pulsedive.com", api_key:, headers: {})
         | 
| 9 15 | 
             
                    super(base_url, headers: headers)
         | 
| 10 16 |  | 
| @@ -13,21 +19,32 @@ module Mihari | |
| 13 19 | 
             
                    raise(ArgumentError, "'api_key' argument is required") unless api_key
         | 
| 14 20 | 
             
                  end
         | 
| 15 21 |  | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # @param [String] indicator_id
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @return [Hash]
         | 
| 26 | 
            +
                  #
         | 
| 16 27 | 
             
                  def get_indicator(ip_or_domain)
         | 
| 17 28 | 
             
                    _get "/api/info.php", params: { indicator: ip_or_domain }
         | 
| 18 29 | 
             
                  end
         | 
| 19 30 |  | 
| 31 | 
            +
                  #
         | 
| 32 | 
            +
                  # @param [String] indicator_id
         | 
| 33 | 
            +
                  #
         | 
| 34 | 
            +
                  # @return [Hash]
         | 
| 35 | 
            +
                  #
         | 
| 20 36 | 
             
                  def get_properties(indicator_id)
         | 
| 21 37 | 
             
                    _get "/api/info.php", params: { iid: indicator_id, get: "properties" }
         | 
| 22 38 | 
             
                  end
         | 
| 23 39 |  | 
| 24 40 | 
             
                  private
         | 
| 25 41 |  | 
| 26 | 
            -
                  #
         | 
| 27 42 | 
             
                  #
         | 
| 28 43 | 
             
                  # @param [String] path
         | 
| 29 44 | 
             
                  # @param [Hash] params
         | 
| 30 45 | 
             
                  #
         | 
| 46 | 
            +
                  # @return [Hash]
         | 
| 47 | 
            +
                  #
         | 
| 31 48 | 
             
                  def _get(path, params: {})
         | 
| 32 49 | 
             
                    params["key"] = api_key
         | 
| 33 50 |  | 
| @@ -0,0 +1,94 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Mihari
         | 
| 4 | 
            +
              module Clients
         | 
| 5 | 
            +
                class SecurityTrails < Base
         | 
| 6 | 
            +
                  #
         | 
| 7 | 
            +
                  # @param [String] base_url
         | 
| 8 | 
            +
                  # @param [String, nil] api_key
         | 
| 9 | 
            +
                  # @param [Hash] headers
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  def initialize(base_url = "https://api.securitytrails.com", api_key:, headers: {})
         | 
| 12 | 
            +
                    raise(ArgumentError, "'api_key' argument is required") unless api_key
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    headers["apikey"] = api_key
         | 
| 15 | 
            +
                    super(base_url, headers: headers)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # @param [String] mail
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # @return [Array<Hash>]
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  def search_by_mail(mail)
         | 
| 24 | 
            +
                    res = _post "/v1/domains/list", json: { filter: { whois_email: mail } }
         | 
| 25 | 
            +
                    res["records"] || []
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # @param [String] ip
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  # @return [Array<Hash>]
         | 
| 32 | 
            +
                  #
         | 
| 33 | 
            +
                  def search_by_ip(ip)
         | 
| 34 | 
            +
                    res = _post "/v1/domains/list", json: { filter: { ipv4: ip } }
         | 
| 35 | 
            +
                    res["records"] || []
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # @param [String] domain
         | 
| 40 | 
            +
                  # @param [String] type
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  # @return [Array<Hash>]
         | 
| 43 | 
            +
                  #
         | 
| 44 | 
            +
                  def get_all_dns_history(domain, type:)
         | 
| 45 | 
            +
                    first_page = get_dns_history(domain, type: type, page: 1)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    pages = first_page["pages"].to_i
         | 
| 48 | 
            +
                    records = first_page["records"] || []
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    (2..pages).each do |page_idx|
         | 
| 51 | 
            +
                      next_page = get_dns_history(domain, type: type, page: page_idx)
         | 
| 52 | 
            +
                      records << next_page["records"]
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    records.flatten
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  private
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  #
         | 
| 61 | 
            +
                  # @param [String] domain
         | 
| 62 | 
            +
                  # @param [String] type
         | 
| 63 | 
            +
                  # @param [Integer] page
         | 
| 64 | 
            +
                  #
         | 
| 65 | 
            +
                  # @return [Array<Hash>]
         | 
| 66 | 
            +
                  #
         | 
| 67 | 
            +
                  def get_dns_history(domain, type:, page:)
         | 
| 68 | 
            +
                    _get "/v1/history/#{domain}/dns/#{type}", params: { page: page }
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  #
         | 
| 72 | 
            +
                  # @param [String] path
         | 
| 73 | 
            +
                  # @param [Hash, nil] params
         | 
| 74 | 
            +
                  #
         | 
| 75 | 
            +
                  # @return [Hash]
         | 
| 76 | 
            +
                  #
         | 
| 77 | 
            +
                  def _get(path, params:)
         | 
| 78 | 
            +
                    res = get(path, params: params)
         | 
| 79 | 
            +
                    JSON.parse(res.body.to_s)
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  #
         | 
| 83 | 
            +
                  # @param [String] path
         | 
| 84 | 
            +
                  # @param [Hash, nil] json
         | 
| 85 | 
            +
                  #
         | 
| 86 | 
            +
                  # @return [Hash]
         | 
| 87 | 
            +
                  #
         | 
| 88 | 
            +
                  def _post(path, json:)
         | 
| 89 | 
            +
                    res = post(path, json: json)
         | 
| 90 | 
            +
                    JSON.parse(res.body.to_s)
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
            end
         | 
| @@ -3,8 +3,14 @@ | |
| 3 3 | 
             
            module Mihari
         | 
| 4 4 | 
             
              module Clients
         | 
| 5 5 | 
             
                class Shodan < Base
         | 
| 6 | 
            +
                  # @return [String]
         | 
| 6 7 | 
             
                  attr_reader :api_key
         | 
| 7 8 |  | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # @param [String] base_url
         | 
| 11 | 
            +
                  # @param [String, nil] api_key
         | 
| 12 | 
            +
                  # @param [Hash] headers
         | 
| 13 | 
            +
                  #
         | 
| 8 14 | 
             
                  def initialize(base_url = "https://api.shodan.io", api_key:, headers: {})
         | 
| 9 15 | 
             
                    raise(ArgumentError, "'api_key' argument is required") unless api_key
         | 
| 10 16 |  | 
| @@ -13,8 +19,13 @@ module Mihari | |
| 13 19 | 
             
                    @api_key = api_key
         | 
| 14 20 | 
             
                  end
         | 
| 15 21 |  | 
| 16 | 
            -
                  # | 
| 17 | 
            -
                  #  | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # @param [String] query
         | 
| 24 | 
            +
                  # @param [Integer] page
         | 
| 25 | 
            +
                  # @param [Boolean] minify
         | 
| 26 | 
            +
                  #
         | 
| 27 | 
            +
                  # @return [Structs::Shodan::Result]
         | 
| 28 | 
            +
                  #
         | 
| 18 29 | 
             
                  def search(query, page: 1, minify: true)
         | 
| 19 30 | 
             
                    params = {
         | 
| 20 31 | 
             
                      query: query,
         | 
| @@ -23,7 +34,7 @@ module Mihari | |
| 23 34 | 
             
                      key: api_key
         | 
| 24 35 | 
             
                    }
         | 
| 25 36 | 
             
                    res = get("/shodan/host/search", params: params)
         | 
| 26 | 
            -
                    JSON.parse(res.body.to_s)
         | 
| 37 | 
            +
                    Structs::Shodan::Result.from_dynamic! JSON.parse(res.body.to_s)
         | 
| 27 38 | 
             
                  end
         | 
| 28 39 | 
             
                end
         | 
| 29 40 | 
             
              end
         | 
| @@ -5,7 +5,7 @@ module Mihari | |
| 5 5 | 
             
                class TheHive < Base
         | 
| 6 6 | 
             
                  #
         | 
| 7 7 | 
             
                  # @param [String] base_url
         | 
| 8 | 
            -
                  # @param [String] api_key
         | 
| 8 | 
            +
                  # @param [String, nil] api_key
         | 
| 9 9 | 
             
                  # @param [String, nil] api_version
         | 
| 10 10 | 
             
                  # @param [Hash] headers
         | 
| 11 11 | 
             
                  #
         | 
| @@ -18,6 +18,11 @@ module Mihari | |
| 18 18 | 
             
                    super(base_url, headers: headers)
         | 
| 19 19 | 
             
                  end
         | 
| 20 20 |  | 
| 21 | 
            +
                  #
         | 
| 22 | 
            +
                  # @param [Hash] json
         | 
| 23 | 
            +
                  #
         | 
| 24 | 
            +
                  # @return [Hash]
         | 
| 25 | 
            +
                  #
         | 
| 21 26 | 
             
                  def alert(json)
         | 
| 22 27 | 
             
                    json = json.to_camelback_keys.compact
         | 
| 23 28 | 
             
                    res = post("/alert", json: json)
         | 
| @@ -5,7 +5,7 @@ module Mihari | |
| 5 5 | 
             
                class UrlScan < Base
         | 
| 6 6 | 
             
                  #
         | 
| 7 7 | 
             
                  # @param [String] base_url
         | 
| 8 | 
            -
                  # @param [String] api_key
         | 
| 8 | 
            +
                  # @param [String, nil] api_key
         | 
| 9 9 | 
             
                  # @param [Hash] headers
         | 
| 10 10 | 
             
                  #
         | 
| 11 11 | 
             
                  def initialize(base_url = "https://urlscan.io", api_key:, headers: {})
         | 
| @@ -21,6 +21,8 @@ module Mihari | |
| 21 21 | 
             
                  # @param [Integer] size
         | 
| 22 22 | 
             
                  # @param [String, nil] search_after
         | 
| 23 23 | 
             
                  #
         | 
| 24 | 
            +
                  # @return [Hash]
         | 
| 25 | 
            +
                  #
         | 
| 24 26 | 
             
                  def search(q, size: 100, search_after: nil)
         | 
| 25 27 | 
             
                    params = { q: q, size: size, search_after: search_after }.compact
         | 
| 26 28 | 
             
                    res = get("/api/v1/search/", params: params)
         |