mihari 5.4.6 → 5.4.8

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +2 -0
  4. data/docs/analyzers/index.md +5 -0
  5. data/docs/emitters/misp.md +1 -1
  6. data/docs/requirements.md +8 -15
  7. data/frontend/package-lock.json +258 -249
  8. data/frontend/package.json +12 -12
  9. data/lib/mihari/analyzers/base.rb +8 -1
  10. data/lib/mihari/analyzers/binaryedge.rb +1 -1
  11. data/lib/mihari/analyzers/censys.rb +1 -1
  12. data/lib/mihari/analyzers/feed.rb +1 -0
  13. data/lib/mihari/analyzers/greynoise.rb +1 -1
  14. data/lib/mihari/analyzers/hunterhow.rb +1 -1
  15. data/lib/mihari/analyzers/onyphe.rb +1 -1
  16. data/lib/mihari/analyzers/shodan.rb +1 -1
  17. data/lib/mihari/analyzers/urlscan.rb +1 -1
  18. data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -1
  19. data/lib/mihari/analyzers/zoomeye.rb +1 -1
  20. data/lib/mihari/clients/base.rb +18 -5
  21. data/lib/mihari/clients/binaryedge.rb +4 -3
  22. data/lib/mihari/clients/censys.rb +3 -2
  23. data/lib/mihari/clients/greynoise.rb +3 -2
  24. data/lib/mihari/clients/hunterhow.rb +3 -2
  25. data/lib/mihari/clients/onyphe.rb +4 -2
  26. data/lib/mihari/clients/shodan.rb +3 -2
  27. data/lib/mihari/clients/urlscan.rb +3 -2
  28. data/lib/mihari/clients/virustotal.rb +3 -2
  29. data/lib/mihari/clients/zoomeye.rb +3 -2
  30. data/lib/mihari/config.rb +13 -0
  31. data/lib/mihari/database.rb +2 -2
  32. data/lib/mihari/emitters/webhook.rb +11 -11
  33. data/lib/mihari/enrichers/google_public_dns.rb +7 -1
  34. data/lib/mihari/enrichers/ipinfo.rb +13 -6
  35. data/lib/mihari/enrichers/shodan.rb +7 -1
  36. data/lib/mihari/errors.rb +0 -2
  37. data/lib/mihari/feed/reader.rb +15 -10
  38. data/lib/mihari/http.rb +26 -100
  39. data/lib/mihari/schemas/analyzer.rb +1 -0
  40. data/lib/mihari/version.rb +1 -1
  41. data/lib/mihari/web/public/assets/{index-0a5a47bf.js → index-a92abd57.js} +1 -1
  42. data/lib/mihari/web/public/index.html +1 -1
  43. data/lib/mihari/web/public/redoc-static.html +400 -400
  44. data/mihari.gemspec +8 -5
  45. data/requirements.txt +1 -1
  46. metadata +57 -15
@@ -17,9 +17,9 @@
17
17
  "@fortawesome/fontawesome-svg-core": "^6.4.2",
18
18
  "@fortawesome/free-solid-svg-icons": "^6.4.2",
19
19
  "@fortawesome/vue-fontawesome": "^3.0.3",
20
- "@vueuse/core": "^10.4.1",
21
- "@vueuse/router": "^10.4.1",
22
- "ace-builds": "^1.28.0",
20
+ "@vueuse/core": "^10.5.0",
21
+ "@vueuse/router": "^10.5.0",
22
+ "ace-builds": "^1.29.0",
23
23
  "axios": "^1.5.1",
24
24
  "bulma": "^0.9.4",
25
25
  "bulma-helpers": "^0.4.3",
@@ -38,21 +38,21 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@redocly/cli": "1.2.0",
41
- "@rushstack/eslint-patch": "^1.5.0",
41
+ "@rushstack/eslint-patch": "^1.5.1",
42
42
  "@tsconfig/node20": "^20.1.2",
43
43
  "@types/jsdom": "^21.1.3",
44
- "@types/node": "^20.7.2",
44
+ "@types/node": "^20.8.5",
45
45
  "@types/url-parse": "^1.4.9",
46
- "@typescript-eslint/eslint-plugin": "^6.7.3",
47
- "@typescript-eslint/parser": "^6.7.3",
48
- "@vitejs/plugin-vue": "^4.3.4",
46
+ "@typescript-eslint/eslint-plugin": "^6.7.5",
47
+ "@typescript-eslint/parser": "^6.7.5",
48
+ "@vitejs/plugin-vue": "^4.4.0",
49
49
  "@vue/eslint-config-prettier": "^8.0.0",
50
50
  "@vue/eslint-config-typescript": "^12.0.0",
51
51
  "@vue/test-utils": "2.4.1",
52
52
  "@vue/tsconfig": "^0.4.0",
53
- "eslint": "^8.50.0",
53
+ "eslint": "^8.51.0",
54
54
  "eslint-config-prettier": "^9.0.0",
55
- "eslint-plugin-prettier": "^5.0.0",
55
+ "eslint-plugin-prettier": "^5.0.1",
56
56
  "eslint-plugin-simple-import-sort": "^10.0.0",
57
57
  "eslint-plugin-vue": "^9.17.0",
58
58
  "husky": "^8.0.3",
@@ -60,8 +60,8 @@
60
60
  "npm-run-all": "^4.1.5",
61
61
  "prettier": "^3.0.3",
62
62
  "typescript": "~5.2.2",
63
- "vite": "^4.4.9",
63
+ "vite": "^4.4.11",
64
64
  "vitest": "^0.34.6",
65
- "vue-tsc": "^1.8.15"
65
+ "vue-tsc": "^1.8.19"
66
66
  }
67
67
  }
@@ -61,6 +61,13 @@ module Mihari
61
61
  Mihari.config.ignore_error
62
62
  end
63
63
 
64
+ #
65
+ # @return [Integer, nil]
66
+ #
67
+ def timeout
68
+ options[:timeout]
69
+ end
70
+
64
71
  # @return [Array<String>, Array<Mihari::Artifact>]
65
72
  def artifacts
66
73
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
@@ -97,7 +104,7 @@ module Mihari
97
104
  self.class.to_s.split("::").last
98
105
  end
99
106
 
100
- alias_method :source, :class_name
107
+ alias source class_name
101
108
 
102
109
  class << self
103
110
  #
@@ -32,7 +32,7 @@ module Mihari
32
32
  # @return [Mihari::Clients::BinaryEdge]
33
33
  #
34
34
  def client
35
- @client ||= Clients::BinaryEdge.new(api_key: api_key, interval: interval)
35
+ @client ||= Clients::BinaryEdge.new(api_key: api_key, interval: interval, timeout: timeout)
36
36
  end
37
37
  end
38
38
  end
@@ -52,7 +52,7 @@ module Mihari
52
52
  # @return [Mihari::Clients::Censys]
53
53
  #
54
54
  def client
55
- @client ||= Clients::Censys.new(id: id, secret: secret, interval: interval)
55
+ @client ||= Clients::Censys.new(id: id, secret: secret, interval: interval, timeout: timeout)
56
56
  end
57
57
 
58
58
  #
@@ -59,6 +59,7 @@ module Mihari
59
59
  query,
60
60
  method: method,
61
61
  headers: headers,
62
+ timeout: timeout,
62
63
  params: params,
63
64
  json: json,
64
65
  data: data
@@ -31,7 +31,7 @@ module Mihari
31
31
  private
32
32
 
33
33
  def client
34
- @client ||= Clients::GreyNoise.new(api_key: api_key, interval: interval)
34
+ @client ||= Clients::GreyNoise.new(api_key: api_key, interval: interval, timeout: timeout)
35
35
  end
36
36
  end
37
37
  end
@@ -46,7 +46,7 @@ module Mihari
46
46
  private
47
47
 
48
48
  def client
49
- @client ||= Clients::HunterHow.new(api_key: api_key, interval: interval)
49
+ @client ||= Clients::HunterHow.new(api_key: api_key, interval: interval, timeout: timeout)
50
50
  end
51
51
  end
52
52
  end
@@ -33,7 +33,7 @@ module Mihari
33
33
  private
34
34
 
35
35
  def client
36
- @client ||= Clients::Onyphe.new(api_key: api_key, interval: interval)
36
+ @client ||= Clients::Onyphe.new(api_key: api_key, interval: interval, timeout: timeout)
37
37
  end
38
38
  end
39
39
  end
@@ -34,7 +34,7 @@ module Mihari
34
34
  # @return [Clients::Shodan]
35
35
  #
36
36
  def client
37
- @client ||= Clients::Shodan.new(api_key: api_key, interval: interval)
37
+ @client ||= Clients::Shodan.new(api_key: api_key, interval: interval, timeout: timeout)
38
38
  end
39
39
  end
40
40
  end
@@ -44,7 +44,7 @@ module Mihari
44
44
  private
45
45
 
46
46
  def client
47
- @client ||= Clients::UrlScan.new(api_key: api_key, interval: interval)
47
+ @client ||= Clients::UrlScan.new(api_key: api_key, interval: interval, timeout: timeout)
48
48
  end
49
49
 
50
50
  #
@@ -33,7 +33,7 @@ module Mihari
33
33
  # @return [::VirusTotal::API]
34
34
  #
35
35
  def client
36
- @client = Clients::VirusTotal.new(api_key: api_key, interval: interval)
36
+ @client = Clients::VirusTotal.new(api_key: api_key, interval: interval, timeout: timeout)
37
37
  end
38
38
  end
39
39
  end
@@ -53,7 +53,7 @@ module Mihari
53
53
  end
54
54
 
55
55
  def client
56
- @client ||= Clients::ZoomEye.new(api_key: api_key, interval: interval)
56
+ @client ||= Clients::ZoomEye.new(api_key: api_key, interval: interval, timeout: timeout)
57
57
  end
58
58
 
59
59
  #
@@ -12,14 +12,20 @@ module Mihari
12
12
  # @return [Integer, nil]
13
13
  attr_reader :interval
14
14
 
15
+ # @return [Integer, nil]
16
+ attr_reader :timeout
17
+
15
18
  #
16
19
  # @param [String] base_url
17
20
  # @param [Hash] headers
21
+ # @param [Integer, nil] interval
22
+ # @param [Integer, nil] timeout
18
23
  #
19
- def initialize(base_url, headers: {}, interval: nil)
24
+ def initialize(base_url, headers: {}, interval: nil, timeout: nil)
20
25
  @base_url = base_url
21
26
  @headers = headers || {}
22
27
  @interval = interval
28
+ @timeout = timeout
23
29
  end
24
30
 
25
31
  private
@@ -28,6 +34,13 @@ module Mihari
28
34
  sleep(interval) if interval
29
35
  end
30
36
 
37
+ #
38
+ # @return [::HTTP::Client]
39
+ #
40
+ def http
41
+ HTTP::Factory.build headers: headers, timeout: timeout
42
+ end
43
+
31
44
  #
32
45
  # @param [String] path
33
46
  #
@@ -41,20 +54,20 @@ module Mihari
41
54
  # @param [String] path
42
55
  # @param [Hash, nil] params
43
56
  #
44
- # @return [String] <description>
57
+ # @return [::HTTP::Response]
45
58
  #
46
59
  def get(path, params: nil)
47
- HTTP.get(url_for(path), headers: headers, params: params)
60
+ http.get(url_for(path), params: params)
48
61
  end
49
62
 
50
63
  #
51
64
  # @param [String] path
52
65
  # @param [Hash, nil] json
53
66
  #
54
- # @return [String] <description>
67
+ # @return [::HTTP::Response]
55
68
  #
56
69
  def post(path, json: {})
57
- HTTP.post(url_for(path), headers: headers, json: json)
70
+ http.post(url_for(path), json: json)
58
71
  end
59
72
  end
60
73
  end
@@ -7,14 +7,15 @@ module Mihari
7
7
  # @param [String] base_url
8
8
  # @param [String, nil] api_key
9
9
  # @param [Hash] headers
10
- # @param [Integer.nil ] interval
10
+ # @param [Integer, nil] interval
11
+ # @param [Integer, nil] timeout
11
12
  #
12
- def initialize(base_url = "https://api.binaryedge.io/v2", api_key:, headers: {}, interval: nil)
13
+ def initialize(base_url = "https://api.binaryedge.io/v2", api_key:, headers: {}, interval: nil, timeout: nil)
13
14
  raise(ArgumentError, "'api_key' argument is required") unless api_key
14
15
 
15
16
  headers["x-key"] = api_key
16
17
 
17
- super(base_url, headers: headers, interval: interval)
18
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
18
19
  end
19
20
 
20
21
  #
@@ -11,14 +11,15 @@ module Mihari
11
11
  # @param [String, nil] secret
12
12
  # @param [Hash] headers
13
13
  # @param [Integer, nil] interval
14
+ # @param [Integer, nil] timeout
14
15
  #
15
- def initialize(base_url = "https://search.censys.io", id:, secret:, headers: {}, interval: nil)
16
+ def initialize(base_url = "https://search.censys.io", id:, secret:, headers: {}, interval: nil, timeout: nil)
16
17
  raise(ArgumentError, "'id' argument is required") if id.nil?
17
18
  raise(ArgumentError, "'secret' argument is required") if secret.nil?
18
19
 
19
20
  headers["authorization"] = "Basic #{Base64.strict_encode64("#{id}:#{secret}")}"
20
21
 
21
- super(base_url, headers: headers, interval: interval)
22
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
22
23
  end
23
24
 
24
25
  #
@@ -10,12 +10,13 @@ module Mihari
10
10
  # @param [String, nil] api_key
11
11
  # @param [Hash] headers
12
12
  # @param [Integer, nil] interval
13
+ # @param [Integer, nil] timeout
13
14
  #
14
- def initialize(base_url = "https://api.greynoise.io", api_key:, headers: {}, interval: nil)
15
+ def initialize(base_url = "https://api.greynoise.io", api_key:, headers: {}, interval: nil, timeout: nil)
15
16
  raise(ArgumentError, "'api_key' argument is required") unless api_key
16
17
 
17
18
  headers["key"] = api_key
18
- super(base_url, headers: headers, interval: interval)
19
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
19
20
  end
20
21
 
21
22
  #
@@ -15,11 +15,12 @@ module Mihari
15
15
  # @param [String, nil] api_key
16
16
  # @param [Hash] headers
17
17
  # @param [Integer, nil] interval
18
+ # @param [Integer, nil] timeout
18
19
  #
19
- def initialize(base_url = "https://api.hunter.how/", api_key:, headers: {}, interval: nil)
20
+ def initialize(base_url = "https://api.hunter.how/", api_key:, headers: {}, interval: nil, timeout: nil)
20
21
  raise(ArgumentError, "'api_key' argument is required") unless api_key
21
22
 
22
- super(base_url, headers: headers, interval: interval)
23
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
23
24
 
24
25
  @api_key = api_key
25
26
  end
@@ -12,11 +12,13 @@ module Mihari
12
12
  # @param [String] base_url
13
13
  # @param [String, nil] api_key
14
14
  # @param [Hash] headers
15
+ # @param [Integer, nil] interval
16
+ # @param [Integer, nil] timeout
15
17
  #
16
- def initialize(base_url = "https://www.onyphe.io", api_key:, headers: {}, interval: nil)
18
+ def initialize(base_url = "https://www.onyphe.io", api_key:, headers: {}, interval: nil, timeout: nil)
17
19
  raise(ArgumentError, "'api_key' argument is required") if api_key.nil?
18
20
 
19
- super(base_url, headers: headers, interval: interval)
21
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
20
22
 
21
23
  @api_key = api_key
22
24
  end
@@ -13,11 +13,12 @@ module Mihari
13
13
  # @param [String, nil] api_key
14
14
  # @param [Hash] headers
15
15
  # @param [Integer, nil] interval
16
+ # @param [Integer, nil] timeout
16
17
  #
17
- def initialize(base_url = "https://api.shodan.io", api_key:, headers: {}, interval: nil)
18
+ def initialize(base_url = "https://api.shodan.io", api_key:, headers: {}, interval: nil, timeout: nil)
18
19
  raise(ArgumentError, "'api_key' argument is required") unless api_key
19
20
 
20
- super(base_url, headers: headers, interval: interval)
21
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
21
22
 
22
23
  @api_key = api_key
23
24
  end
@@ -8,13 +8,14 @@ module Mihari
8
8
  # @param [String, nil] api_key
9
9
  # @param [Hash] headers
10
10
  # @param [Interval, nil] interval
11
+ # @param [Interval, nil] timeout
11
12
  #
12
- def initialize(base_url = "https://urlscan.io", api_key:, headers: {}, interval: nil)
13
+ def initialize(base_url = "https://urlscan.io", api_key:, headers: {}, interval: nil, timeout: nil)
13
14
  raise(ArgumentError, "'api_key' argument is required") if api_key.nil?
14
15
 
15
16
  headers["api-key"] = api_key
16
17
 
17
- super(base_url, headers: headers, interval: interval)
18
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
18
19
  end
19
20
 
20
21
  #
@@ -8,13 +8,14 @@ module Mihari
8
8
  # @param [String, nil] api_key
9
9
  # @param [Hash] headers
10
10
  # @param [Integer, nil] interval
11
+ # @param [Integer, nil] timeout
11
12
  #
12
- def initialize(base_url = "https://www.virustotal.com", api_key:, headers: {}, interval: nil)
13
+ def initialize(base_url = "https://www.virustotal.com", api_key:, headers: {}, interval: nil, timeout: nil)
13
14
  raise(ArgumentError, "'api_key' argument is required") if api_key.nil?
14
15
 
15
16
  headers["x-apikey"] = api_key
16
17
 
17
- super(base_url, headers: headers, interval: interval)
18
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
18
19
  end
19
20
 
20
21
  #
@@ -12,12 +12,13 @@ module Mihari
12
12
  # @param [String, nil] api_key
13
13
  # @param [Hash] headers
14
14
  # @param [Integer, nil] interval
15
+ # @param [Integer, nil] timeout
15
16
  #
16
- def initialize(base_url = "https://api.zoomeye.org", api_key:, headers: {}, interval: nil)
17
+ def initialize(base_url = "https://api.zoomeye.org", api_key:, headers: {}, interval: nil, timeout: nil)
17
18
  raise(ArgumentError, "'api_key' argument is required") unless api_key
18
19
 
19
20
  headers["api-key"] = api_key
20
- super(base_url, headers: headers, interval: interval)
21
+ super(base_url, headers: headers, interval: interval, timeout: timeout)
21
22
  end
22
23
 
23
24
  #
data/lib/mihari/config.rb CHANGED
@@ -97,6 +97,10 @@ module Mihari
97
97
  attr_reader :ignore_error
98
98
 
99
99
  def initialize
100
+ load
101
+ end
102
+
103
+ def load
100
104
  @binaryedge_api_key = ENV.fetch("BINARYEDGE_API_KEY", nil)
101
105
 
102
106
  @censys_id = ENV.fetch("CENSYS_ID", nil)
@@ -153,5 +157,14 @@ module Mihari
153
157
 
154
158
  @ignore_error = ENV.fetch("IGNORE_ERROR", false)
155
159
  end
160
+
161
+ #
162
+ # @return [Array<String>]
163
+ #
164
+ def keys
165
+ instance_variables.map do |key|
166
+ key[1..].to_s.upcase
167
+ end
168
+ end
156
169
  end
157
170
  end
@@ -13,7 +13,7 @@ def development_env?
13
13
  env == "development"
14
14
  end
15
15
 
16
- class V5Schema < ActiveRecord::Migration[7.0]
16
+ class V5Schema < ActiveRecord::Migration[7.1]
17
17
  def change
18
18
  create_table :rules, id: :string, if_not_exists: true do |t|
19
19
  t.string :title, null: false
@@ -162,7 +162,7 @@ module Mihari
162
162
  def close
163
163
  return unless ActiveRecord::Base.connected?
164
164
 
165
- ActiveRecord::Base.clear_active_connections!
165
+ ActiveRecord::Base.connection_handler.clear_active_connections!
166
166
  end
167
167
 
168
168
  def with_db_connection
@@ -72,17 +72,13 @@ module Mihari
72
72
  def emit
73
73
  return if artifacts.empty?
74
74
 
75
- client = Mihari::HTTP.new(url, headers: headers)
76
-
77
- res = nil
75
+ # returns body to prevent Parallel issue (Parallel fails to handle HTTP:Response object)
78
76
  case method
79
77
  when "GET"
80
- res = client.get
78
+ http.get(url).body.to_s
81
79
  when "POST"
82
- res = client.post(json: payload)
80
+ http.post(url, json: json).body.to_s
83
81
  end
84
-
85
- res
86
82
  end
87
83
 
88
84
  def valid?
@@ -93,12 +89,16 @@ module Mihari
93
89
 
94
90
  private
95
91
 
92
+ def http
93
+ HTTP::Factory.build headers: headers
94
+ end
95
+
96
96
  #
97
- # Convert payload into string
97
+ # Render template
98
98
  #
99
99
  # @return [String]
100
100
  #
101
- def payload_as_string
101
+ def rendered_template
102
102
  [].tap do |out|
103
103
  options = {}
104
104
  options[:template] = File.read(template) unless template.nil?
@@ -115,8 +115,8 @@ module Mihari
115
115
  #
116
116
  # @return [Hash]
117
117
  #
118
- def payload
119
- JSON.parse payload_as_string
118
+ def json
119
+ JSON.parse rendered_template
120
120
  end
121
121
  end
122
122
  end
@@ -37,7 +37,7 @@ module Mihari
37
37
  def query_by_type(name, resource_type)
38
38
  url = "https://dns.google/resolve"
39
39
  params = { name: name, type: resource_type }
40
- res = HTTP.get(url, params: params)
40
+ res = http.get(url, params: params)
41
41
 
42
42
  data = JSON.parse(res.body.to_s)
43
43
 
@@ -45,6 +45,12 @@ module Mihari
45
45
  rescue HTTPError
46
46
  nil
47
47
  end
48
+
49
+ private
50
+
51
+ def http
52
+ HTTP::Factory.build
53
+ end
48
54
  end
49
55
  end
50
56
  end
@@ -28,18 +28,25 @@ module Mihari
28
28
  # @return [Mihari::Structs::IPInfo::Response, nil]
29
29
  #
30
30
  def query(ip)
31
- headers = {}
32
-
33
- token = Mihari.config.ipinfo_api_key
34
- headers[:authorization] = "Bearer #{token}" unless token.nil?
35
-
36
31
  url = "https://ipinfo.io/#{ip}/json"
37
- res = HTTP.get(url, headers: headers)
32
+ res = http.get(url)
38
33
  data = JSON.parse(res.body.to_s)
39
34
 
40
35
  Structs::IPInfo::Response.from_dynamic! data
41
36
  end
42
37
  memoize :query
38
+
39
+ private
40
+
41
+ def headers
42
+ token = Mihari.config.ipinfo_api_key
43
+ authorization = token.nil? ? nil : "Bearer #{token}"
44
+ { authorization: authorization }.compact
45
+ end
46
+
47
+ def http
48
+ HTTP::Factory.build headers: headers
49
+ end
43
50
  end
44
51
  end
45
52
  end
@@ -23,12 +23,18 @@ module Mihari
23
23
  #
24
24
  def query(ip)
25
25
  url = "https://internetdb.shodan.io/#{ip}"
26
- res = HTTP.get(url)
26
+ res = http.get(url)
27
27
  data = JSON.parse(res.body.to_s)
28
28
 
29
29
  Structs::Shodan::InternetDBResponse.from_dynamic! data
30
30
  end
31
31
  memoize :query
32
+
33
+ private
34
+
35
+ def http
36
+ HTTP::Factory.build
37
+ end
32
38
  end
33
39
  end
34
40
  end
data/lib/mihari/errors.rb CHANGED
@@ -20,8 +20,6 @@ module Mihari
20
20
 
21
21
  class TimeoutError < HTTPError; end
22
22
 
23
- class SSLError < HTTPError; end
24
-
25
23
  class StatusCodeError < HTTPError
26
24
  # @return [Integer]
27
25
  attr_reader :status_code
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "csv"
4
- require "insensitive_hash"
5
4
 
6
5
  module Mihari
7
6
  module Feed
@@ -24,16 +23,18 @@ module Mihari
24
23
  # @return [String]
25
24
  attr_reader :method
26
25
 
27
- def initialize(url, headers: {}, method: "GET", params: nil, json: nil, data: nil)
26
+ # @return [Integer, nil]
27
+ attr_reader :timeout
28
+
29
+ def initialize(url, headers: {}, method: "GET", params: nil, json: nil, data: nil, timeout: nil)
28
30
  @url = Addressable::URI.parse(url)
29
- @headers = headers.insensitive
31
+ @headers = headers
30
32
  @method = method
33
+ @timeout = timeout
31
34
 
32
35
  @params = params
33
36
  @json = json
34
37
  @data = data
35
-
36
- headers["content-type"] = "application/json" unless json.nil?
37
38
  end
38
39
 
39
40
  #
@@ -43,14 +44,12 @@ module Mihari
43
44
  return read_file(url.path) if url.scheme == "file"
44
45
 
45
46
  res = nil
46
- client = HTTP.new(url, headers: headers)
47
-
48
- res = client.get(params: params) if method == "GET"
49
- res = client.post(params: params, json: json, data: data) if method == "POST"
47
+ res = http.get(url, params: params) if method == "GET"
48
+ res = http.post(url, params: params, json: json, form: data) if method == "POST"
50
49
 
51
50
  return [] if res.nil?
52
51
 
53
- body = res.body
52
+ body = res.body.to_s
54
53
  content_type = res["Content-Type"].to_s
55
54
  return convert_as_json(body) if content_type.include?("application/json")
56
55
 
@@ -98,6 +97,12 @@ module Mihari
98
97
 
99
98
  convert_as_csv text
100
99
  end
100
+
101
+ private
102
+
103
+ def http
104
+ HTTP::Factory.build headers: headers, timeout: timeout
105
+ end
101
106
  end
102
107
  end
103
108
  end