hachi 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 789e04c496071bb676e874312b47929b538a1d6a028ca5042ac18b85364d2ac7
4
- data.tar.gz: 439eccca76345fb3b74aacc2a9749ef4875652b5ba1ab15f171b7b47a9f0d45b
3
+ metadata.gz: f58d7b1ed785abfc7d69d44f261074333b779f60f21317bf6070c79bf9ea67cd
4
+ data.tar.gz: 55b4a69a5aa240fc7b81e81f576ecf2afc9ae6a58598a136dc0cfa398cd7bc31
5
5
  SHA512:
6
- metadata.gz: ba111b9284560325d00bcbe3f09240130c75428072a85fe596db524e35f9437c603a0788ca008ae309b179f475aac0ab11c0872455b6233f178da687b1c01996
7
- data.tar.gz: c9c720cf508b16cc10170d52732b6a2cdb0d0b39899810a5f3b559d1a561574cea3298b5a64911cc2addb574f117e5afd8870f32445ef0ca5f90f3dc42ac0ab4
6
+ metadata.gz: e127d0b1f0e49334cce18a4ee0fe1d56de814a52862ed9048e1810e851f1030a85d181d442661d42b7f13b9cdc4a71c39f4816b835d9b2d2f2646808eb9f852b
7
+ data.tar.gz: c100c3044222c254321f9de7675b9417ee16c147cfe72349c803204c6ab6798528bfb1612fc36f6dd0b287b4520905efc4a8de4357b86ce3552d868916f04c99
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/hachi.svg)](https://badge.fury.io/rb/hachi)
4
4
  [![Build Status](https://travis-ci.org/ninoseki/hachi.svg?branch=master)](https://travis-ci.org/ninoseki/hachi)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/hachi/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/hachi?branch=master)
6
+ [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/hachi/badge)](https://www.codefactor.io/repository/github/ninoseki/hachi)
6
7
 
7
8
  Hachi(`蜂`) is a dead simple [TheHive](https://github.com/TheHive-Project/TheHive) API wrapper for Ruby.
8
9
 
@@ -27,6 +28,8 @@ api.alert.list
27
28
 
28
29
  # search atrifacts
29
30
  api.artifact.search(data: "1.1.1.1", data_type: "ip")
31
+ # you can do a bulk search by giving an array as an input
32
+ api.artifact.search(data: %w(1.1.1.1 8.8.8.8 github.com))
30
33
  ```
31
34
 
32
35
  See `samples` for more.
@@ -38,7 +41,7 @@ See `samples` for more.
38
41
  | HTTP Method | URI | Action | API method |
39
42
  |-------------|-----------------------------------|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
40
43
  | GET | /api/alert | List alerts | `#api.alert.list` |
41
- | POST | /api/alert/_search | Find alerts | N/A |
44
+ | POST | /api/alert/_search | Find alerts | `#api.alert.search(attributes:, range: "all")` |
42
45
  | PATCH | /api/alert/_bulk | Update alerts in bulk | N/A |
43
46
  | POST | /api/alert/_stats | Compute stats on alerts | N/A |
44
47
  | POST | /api/alert | Create an alert | `#api.alert.create(title:, description:, severity: nil, date: nil, tags: nil, tlp: nil, status: nil, type:, source:, source_ref: nil, artifacts: nil, follow: nil)` |
@@ -56,13 +59,13 @@ See `samples` for more.
56
59
 
57
60
  | HTTP Method | URI | Action | API method |
58
61
  |-------------|----------------------------------------|---------------------------------|---------------------------------------------------------------------------------------|
59
- | POST | /api/case/artifact/_search | Find observables | `#api.artifact.search(data:, date_type:)` |
62
+ | POST | /api/case/artifact/_search | Find observables | `#api.artifact.search(attributes, range: "all")` |
60
63
  | POST | /api/case/artifact/_stats | Compute stats on observables | N/A |
61
64
  | POST | /api/case/:caseId/artifact | Create an observable | `#api.artifact.create(case_id, data:, data_type:, message: nil, tlp: nil, tags: nil)` |
62
65
  | GET | /api/case/artifact/:artifactId | Get an observable | `#api.artifact.get_by_id(id)` |
63
66
  | DELETE | /api/case/artifact/:artifactId | Remove an observable | `#api.artifact.delete_by_id(id)` |
64
67
  | PATCH | /api/case/artifact/:artifactId | Update an observable | N/A |
65
- | GET | /api/case/artifact/:artifactId/similar | Get list of similar observables | N/A |
68
+ | GET | /api/case/artifact/:artifactId/similar | Get list of similar observables | `#api.artifact.similar(id)` |
66
69
  | PATCH | /api/case/artifact/_bulk | Update observables in bulk | N/A |
67
70
 
68
71
  ### Case
@@ -70,14 +73,14 @@ See `samples` for more.
70
73
  | HTTP Method | URI | Action | API method |
71
74
  |-------------|------------------------------------|---------------------------------------|----------------------------------------------------------------------------------------------------------------------|
72
75
  | GET | /api/case | List cases | `#api.case.list` |
73
- | POST | /api/case/_search | Find cases | `#api.case.search(query)` |
76
+ | POST | /api/case/_search | Find cases | `#api.case.search(attributes, range: "all")` |
74
77
  | PATCH | /api/case/_bulk | Update cases in bulk | N/A |
75
78
  | POST | /api/case/_stats | Compute stats on cases | N/A |
76
79
  | POST | /api/case | Create a case | `#api.case.create(title:, description:, severity: nil, start_date: nil, owner: nil, flag: nil, tlp: nil, tags: nil)` |
77
80
  | GET | /api/case/:caseId | Get a case | `#api.case.get_by_id(id)` |
78
81
  | PATCH | /api/case/:caseId | Update a case | N/A |
79
82
  | DELETE | /api/case/:caseId | Remove a case | `#api.case.delete_by_id(id)` |
80
- | GET | /api/case/:caseId/links | Get list of cases linked to this case | N/A |
83
+ | GET | /api/case/:caseId/links | Get list of cases linked to this case | `#api.case.links(id)` |
81
84
  | POST | /api/case/:caseId1/_merge/:caseId2 | Merge two cases | N/A |
82
85
 
83
86
  ## License
@@ -1,33 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "hachi/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = "hachi"
9
- spec.version = Hachi::VERSION
10
- spec.authors = ["Manabu Niseki"]
11
- spec.email = ["manabu.niseki@gmail.com"]
8
+ spec.name = "hachi"
9
+ spec.version = Hachi::VERSION
10
+ spec.authors = ["Manabu Niseki"]
11
+ spec.email = ["manabu.niseki@gmail.com"]
12
12
 
13
- spec.summary = "A dead simple TheHive API wrapper."
14
- spec.description = "A dead simple TheHive API wrapper."
15
- spec.homepage = "https://github.com/ninoseki/hachi"
16
- spec.license = "MIT"
13
+ spec.summary = "A dead simple TheHive API wrapper."
14
+ spec.description = "A dead simple TheHive API wrapper."
15
+ spec.homepage = "https://github.com/ninoseki/hachi"
16
+ spec.license = "MIT"
17
17
 
18
18
  # Specify which files should be added to the gem when it is released.
19
19
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
20
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
21
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
22
  end
23
- spec.bindir = "exe"
24
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
 
27
27
  spec.add_development_dependency "bundler", "~> 2.0"
28
28
  spec.add_development_dependency "coveralls", "~> 0.8"
29
29
  spec.add_development_dependency "rake", "~> 12.3"
30
30
  spec.add_development_dependency "rspec", "~> 3.8"
31
- spec.add_development_dependency "vcr", "~> 4.0"
32
- spec.add_development_dependency "webmock", "~> 3.5"
31
+ spec.add_development_dependency "vcr", "~> 5.0"
32
+ spec.add_development_dependency "webmock", "~> 3.6"
33
33
  end
@@ -31,10 +31,14 @@ module Hachi
31
31
  source: source,
32
32
  source_ref: source_ref,
33
33
  artifacts: artifacts,
34
- follow: follow
34
+ follow: follow,
35
35
  )
36
36
  post("/api/alert", alert.payload) { |json| json }
37
37
  end
38
+
39
+ def search(attributes:, range: "all")
40
+ _search("/api/alert/_search", attributes: attributes, range: range) { |json| json }
41
+ end
38
42
  end
39
43
  end
40
44
  end
@@ -9,7 +9,7 @@ module Hachi
9
9
  data_type: data_type,
10
10
  message: message,
11
11
  tlp: tlp,
12
- tags: tags
12
+ tags: tags,
13
13
  )
14
14
 
15
15
  post("/api/case/#{case_id}/artifact", artifact.payload) { |json| json }
@@ -23,27 +23,12 @@ module Hachi
23
23
  delete("/api/case/artifact/#{id}") { |json| json }
24
24
  end
25
25
 
26
- def search(data:, data_type:, range: "all")
27
- validate_range range
28
-
29
- artifact = Models::Artifact.new(data: data, data_type: data_type)
30
- payload = {
31
- query: {
32
- _and:
33
- [
34
- { _field: "data", _value: artifact.data },
35
- { _field: "dataType", _value: artifact.data_type },
36
- { _and:
37
- [
38
- { _not: { status: "Deleted" } },
39
- { _not:
40
- { _in: { _field: "_type", _values: ["dashboard", "data", "user", "analyzer", "caseTemplate", "reportTemplate", "action"] } } }
41
- ] }
42
- ]
43
- }
44
- }
26
+ def search(attributes, range: "all")
27
+ _search("/api/case/artifact/_search", attributes: attributes, range: range) { |json| json }
28
+ end
45
29
 
46
- post("/api/case/artifact/_search?range=#{range}", payload) { |json| json }
30
+ def similar(id)
31
+ get("/api/case/artifact/#{id}/similar") { |json| json }
47
32
  end
48
33
  end
49
34
  end
@@ -33,7 +33,7 @@ module Hachi
33
33
  proxy_address: uri.hostname,
34
34
  proxy_port: uri.port,
35
35
  proxy_from_env: false,
36
- use_ssl: true
36
+ use_ssl: true,
37
37
  }
38
38
  else
39
39
  { use_ssl: true }
@@ -55,7 +55,7 @@ module Hachi
55
55
 
56
56
  def parse_body(body)
57
57
  JSON.parse body.to_s
58
- rescue JSON::ParserError => _
58
+ rescue JSON::ParserError => _e
59
59
  body.to_s
60
60
  end
61
61
 
@@ -64,7 +64,7 @@ module Hachi
64
64
  response = http.request(req)
65
65
  json = parse_body(response.body)
66
66
 
67
- raise(Error, "Unsupported response code returned: #{response.code} (#{json&.dig('message')})" ) unless response.code.start_with? "20"
67
+ raise(Error, "Unsupported response code returned: #{response.code} (#{json&.dig('message')})") unless response.code.start_with? "20"
68
68
 
69
69
  yield json
70
70
  end
@@ -109,6 +109,31 @@ module Hachi
109
109
 
110
110
  raise ArgumentError, "from should be smaller than to"
111
111
  end
112
+
113
+ def _search(path, attributes:, range: "all")
114
+ validate_range range
115
+
116
+ conditions = attributes.map do |key, value|
117
+ if value.is_a?(Array)
118
+ { _string: value.join(", ") }
119
+ else
120
+ { _string: "#{key}:#{value}" }
121
+ end
122
+ end
123
+
124
+ default_conditions = {
125
+ _and: [
126
+ { _not: { status: "Deleted" } },
127
+ { _not: { _in: { _field: "_type", _values: ["dashboard", "data", "user", "analyzer", "caseTemplate", "reportTemplate", "action"] } } },
128
+ ],
129
+ }
130
+
131
+ query = {
132
+ _and: [conditions, default_conditions].flatten,
133
+ }
134
+
135
+ post("#{path}?range=#{range}", query: query) { |json| json }
136
+ end
112
137
  end
113
138
  end
114
139
  end
@@ -24,31 +24,18 @@ module Hachi
24
24
  owner: owner,
25
25
  flag: flag,
26
26
  tlp: tlp,
27
- tags: tags
27
+ tags: tags,
28
28
  )
29
29
 
30
30
  post("/api/case", kase.payload) { |json| json }
31
31
  end
32
32
 
33
- def search(query, range: "all")
34
- validate_range range
35
-
36
- payload = {
37
- query: {
38
- _and:
39
- [
40
- { string: query },
41
- { _and:
42
- [
43
- { _not: { status: "Deleted" } },
44
- { _not:
45
- { _in: { _field: "_type", _values: ["dashboard", "data", "user", "analyzer", "caseTemplate", "reportTemplate", "action"] } } }
46
- ] }
47
- ]
48
- }
49
- }
33
+ def search(attributes, range: "all")
34
+ _search("/api/case/_search", attributes: attributes, range: range) { |json| json }
35
+ end
50
36
 
51
- post("/api/case/_search?range=#{range}", payload) { |json| json }
37
+ def links(id)
38
+ get("/api/case/#{id}/links") { |json| json }
52
39
  end
53
40
  end
54
41
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hachi
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hachi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.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-04-20 00:00:00.000000000 Z
11
+ date: 2019-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,28 +72,28 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '4.0'
75
+ version: '5.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '4.0'
82
+ version: '5.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: webmock
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '3.5'
89
+ version: '3.6'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '3.5'
96
+ version: '3.6'
97
97
  description: A dead simple TheHive API wrapper.
98
98
  email:
99
99
  - manabu.niseki@gmail.com