euvd 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +131 -0
- data/lib/euvd/api/downloads.rb +28 -0
- data/lib/euvd/api/meta.rb +28 -0
- data/lib/euvd/api/observations.rb +50 -0
- data/lib/euvd/api/records.rb +33 -0
- data/lib/euvd/api/vulnerabilities.rb +60 -0
- data/lib/euvd/client.rb +121 -0
- data/lib/euvd/version.rb +5 -0
- data/lib/euvd.rb +9 -0
- metadata +95 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0ae86924284ac98695e0f9284b8a5ef19ec67aba878c5cfd5c230c1a52a51e96
|
|
4
|
+
data.tar.gz: 51e187f62bea1dc5d59e42e4992339d9561706dd932f5169360fa1d089be7b3b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 854e39f4ec23586462d1af360a55a8cbeed2104cf889d6601139216ab7faca15ce2fd03d97cc98e6459b2568d4b13126582508ad76ed2d640e9c8276fe77cc15
|
|
7
|
+
data.tar.gz: a9881bb353650ba25476686c3d17611ddafc9862cd0225510b0ab1f13c90783946d2b15aad72d19b38dfc237afc601c55bc31c5be3bbd91fca515c94af839779
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 noraj (Alexandre ZANNI)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# EUVD Ruby Gem
|
|
2
|
+
|
|
3
|
+
A Ruby wrapper library for the [European Union Vulnerability Database (EUVD)](https://euvd.enisa.europa.eu/apidoc) API. Uses the [Sawyer](https://github.com/lostisland/sawyer) HTTP agent framework.
|
|
4
|
+
|
|
5
|
+
All endpoints are **GET-only** and **require no authentication**.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'euvd'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
$ bundle install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install it yourself:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
$ gem install euvd
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
require 'euvd'
|
|
31
|
+
|
|
32
|
+
client = EUVD::Client.new
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Vulnerabilities
|
|
36
|
+
|
|
37
|
+
Returns `Sawyer::Resource` objects (or `Array` of them). Access JSON fields as methods:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
results = client.vulnerabilities.latest
|
|
41
|
+
# => Array<Sawyer::Resource>
|
|
42
|
+
# results.first.id, results.first.description, results.first.baseScore, ...
|
|
43
|
+
|
|
44
|
+
search = client.vulnerabilities.search(text: 'log4j', size: 10, page: 0)
|
|
45
|
+
# => Sawyer::Resource with .items (Array) and .total (Integer)
|
|
46
|
+
# search.items.first.id, search.total
|
|
47
|
+
|
|
48
|
+
client.vulnerabilities.exploited
|
|
49
|
+
client.vulnerabilities.critical
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Search filters: `text`, `fromScore` (0-10), `toScore`, `fromEpss` (0-100), `toEpss`, `fromDate` (YYYY-MM-DD), `toDate`, `fromUpdatedDate`, `toUpdatedDate`, `product`, `vendor`, `assigner`, `exploited` (true/false), `page` (starts at 0), `size` (default 10, max 100).
|
|
53
|
+
|
|
54
|
+
### Records
|
|
55
|
+
|
|
56
|
+
Returns `Sawyer::Resource` objects:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
record = client.records.find('EUVD-2025-20101')
|
|
60
|
+
# => Sawyer::Resource
|
|
61
|
+
# record.id, record.description, record.baseScore, record.assigner, ...
|
|
62
|
+
|
|
63
|
+
# Also works with CVE or GHSA IDs
|
|
64
|
+
client.records.find('CVE-2025-47228')
|
|
65
|
+
client.records.find('GHSA-pfxq-29cx-gm9c')
|
|
66
|
+
|
|
67
|
+
advisory = client.records.advisory('oxas-adv-2024-0002')
|
|
68
|
+
# => Sawyer::Resource
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Downloads
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
client.downloads.cve_euvd_mapping # returns CSV String
|
|
75
|
+
client.downloads.kev_dump # returns raw JSON String
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Meta
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
client.meta.assigners # returns Array of assigner names
|
|
82
|
+
client.meta.banner # returns Sawyer::Resource (enabled, message)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Observations
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
client.observations.honeypot_by_cve('CVE-2021-44228')
|
|
89
|
+
client.observations.honeypot_batch(%w[CVE-2021-44228 CVE-2021-45046])
|
|
90
|
+
client.observations.kev_by_cve('CVE-2021-44228')
|
|
91
|
+
client.observations.kev_batch('CVE-2021-44228')
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Error Handling
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
begin
|
|
98
|
+
record = client.records.find('EUVD-XXXX-XXXXX')
|
|
99
|
+
rescue EUVD::NotFoundError
|
|
100
|
+
puts 'Not found'
|
|
101
|
+
rescue EUVD::RateLimitError
|
|
102
|
+
puts 'Rate limited'
|
|
103
|
+
rescue EUVD::ServerError
|
|
104
|
+
puts 'Server error'
|
|
105
|
+
rescue EUVD::BadResponseError
|
|
106
|
+
puts 'Server returned non-JSON (likely down)'
|
|
107
|
+
rescue EUVD::Error => e
|
|
108
|
+
puts "Error: #{e.message}"
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Configuration
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
EUVD::Client.new(
|
|
116
|
+
base_url: 'https://euvdservices.enisa.europa.eu/api' # default
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
Run the tests:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
bundle install
|
|
126
|
+
bundle exec rspec
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EUVD
|
|
4
|
+
module API
|
|
5
|
+
# Access to bulk data download endpoints.
|
|
6
|
+
class Downloads
|
|
7
|
+
def initialize(client)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# GET /api/dump/cve-euvd-mapping
|
|
12
|
+
# Downloads the CVE-to-EUVD ID mapping as CSV.
|
|
13
|
+
#
|
|
14
|
+
# @return [String] Raw CSV data
|
|
15
|
+
def cve_euvd_mapping
|
|
16
|
+
@client.get('dump/cve-euvd-mapping')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# GET /api/kev/dump
|
|
20
|
+
# Downloads the full KEV dump as JSON.
|
|
21
|
+
#
|
|
22
|
+
# @return [String] Raw JSON data
|
|
23
|
+
def kev_dump
|
|
24
|
+
@client.get('kev/dump')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EUVD
|
|
4
|
+
module API
|
|
5
|
+
# Access to meta/utility endpoints (banner, assigners list).
|
|
6
|
+
class Meta
|
|
7
|
+
def initialize(client)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# GET /api/assigners/names
|
|
12
|
+
# Returns the list of assigner names.
|
|
13
|
+
#
|
|
14
|
+
# @return [Array<String>]
|
|
15
|
+
def assigners
|
|
16
|
+
@client.get('assigners/names')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# GET /api/banner
|
|
20
|
+
# Returns the banner status (enabled/message).
|
|
21
|
+
#
|
|
22
|
+
# @return [Sawyer::Resource]
|
|
23
|
+
def banner
|
|
24
|
+
@client.get('banner')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EUVD
|
|
4
|
+
module API
|
|
5
|
+
# Access to honeypot observation and KEV entry endpoints.
|
|
6
|
+
class Observations
|
|
7
|
+
def initialize(client)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# GET /api/honeypotObservations?cveId=X
|
|
12
|
+
# Returns honeypot observations for a specific CVE.
|
|
13
|
+
#
|
|
14
|
+
# @param cve_id [String] CVE identifier (e.g. 'CVE-2021-44228')
|
|
15
|
+
# @return [Array<Sawyer::Resource>]
|
|
16
|
+
def honeypot_by_cve(cve_id)
|
|
17
|
+
@client.get('honeypotObservations', cveId: cve_id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# GET /api/honeypotObservations/batch?ids=X,Y
|
|
21
|
+
# Returns honeypot observations for multiple CVEs.
|
|
22
|
+
#
|
|
23
|
+
# @param ids [Array<String>] CVE identifiers
|
|
24
|
+
# @return [Array<Sawyer::Resource>]
|
|
25
|
+
def honeypot_batch(ids)
|
|
26
|
+
ids = ids.join(',') if ids.is_a?(Array)
|
|
27
|
+
@client.get('honeypotObservations/batch', ids: ids)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# GET /api/kevEntries?cveId=X
|
|
31
|
+
# Returns KEV entries for a specific CVE.
|
|
32
|
+
#
|
|
33
|
+
# @param cve_id [String] CVE identifier
|
|
34
|
+
# @return [Array<Sawyer::Resource>]
|
|
35
|
+
def kev_by_cve(cve_id)
|
|
36
|
+
@client.get('kevEntries', cveId: cve_id)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# GET /api/kevEntries/batch?ids=X,Y
|
|
40
|
+
# Returns KEV entries for multiple CVEs.
|
|
41
|
+
#
|
|
42
|
+
# @param ids [Array<String>] CVE identifiers
|
|
43
|
+
# @return [Array<Sawyer::Resource>]
|
|
44
|
+
def kev_batch(ids)
|
|
45
|
+
ids = ids.join(',') if ids.is_a?(Array)
|
|
46
|
+
@client.get('kevEntries/batch', ids: ids)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EUVD
|
|
4
|
+
module API
|
|
5
|
+
# Access to specific resource endpoints: EUVD record by ID and advisory by ID.
|
|
6
|
+
class Records
|
|
7
|
+
def initialize(client)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# GET /api/enisaid?id=EUVD-XXXX-XXXXX
|
|
12
|
+
# Returns a single EUVD record by its EUVD ID.
|
|
13
|
+
#
|
|
14
|
+
# @param euvd_id [String] EUVD identifier (e.g. 'EUVD-2024-45012')
|
|
15
|
+
# @return [Sawyer::Resource]
|
|
16
|
+
def enisaid(euvd_id)
|
|
17
|
+
@client.get('enisaid', id: euvd_id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Alias for #enisaid.
|
|
21
|
+
alias find enisaid
|
|
22
|
+
|
|
23
|
+
# GET /api/advisory?id=XXXXX
|
|
24
|
+
# Returns a single advisory by its ID.
|
|
25
|
+
#
|
|
26
|
+
# @param advisory_id [String] Advisory identifier (e.g. 'oxas-adv-2024-0002')
|
|
27
|
+
# @return [Sawyer::Resource]
|
|
28
|
+
def advisory(advisory_id)
|
|
29
|
+
@client.get('advisory', id: advisory_id)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EUVD
|
|
4
|
+
module API
|
|
5
|
+
# Access to vulnerability endpoints: latest, critical, exploited, and search.
|
|
6
|
+
class Vulnerabilities
|
|
7
|
+
def initialize(client)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# GET /api/lastvulnerabilities
|
|
12
|
+
# Returns the latest vulnerabilities (maximum 8 records).
|
|
13
|
+
#
|
|
14
|
+
# @return [Array<Sawyer::Resource>]
|
|
15
|
+
def latest
|
|
16
|
+
@client.get('lastvulnerabilities')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# GET /api/exploitedvulnerabilities
|
|
20
|
+
# Returns the latest exploited vulnerabilities (maximum 8 records).
|
|
21
|
+
#
|
|
22
|
+
# @return [Array<Sawyer::Resource>]
|
|
23
|
+
def exploited
|
|
24
|
+
@client.get('exploitedvulnerabilities')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# GET /api/criticalvulnerabilities
|
|
28
|
+
# Returns the latest critical vulnerabilities (maximum 8 records).
|
|
29
|
+
#
|
|
30
|
+
# @return [Array<Sawyer::Resource>]
|
|
31
|
+
def critical
|
|
32
|
+
@client.get('criticalvulnerabilities')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# GET /api/search
|
|
36
|
+
# Query vulnerabilities with flexible filters.
|
|
37
|
+
#
|
|
38
|
+
# @param params [Hash] Query parameters
|
|
39
|
+
# @option params [String] :text Keywords (searches across description, EUVD ID, aliases, product, vendor)
|
|
40
|
+
# @option params [Float] :fromScore Minimum CVSS score (0-10)
|
|
41
|
+
# @option params [Float] :toScore Maximum CVSS score (0-10)
|
|
42
|
+
# @option params [Integer] :fromEpss Minimum EPSS score (0-100)
|
|
43
|
+
# @option params [Integer] :toEpss Maximum EPSS score (0-100)
|
|
44
|
+
# @option params [String] :fromDate Minimum published date (YYYY-MM-DD)
|
|
45
|
+
# @option params [String] :toDate Maximum published date (YYYY-MM-DD)
|
|
46
|
+
# @option params [String] :fromUpdatedDate Minimum updated date (YYYY-MM-DD)
|
|
47
|
+
# @option params [String] :toUpdatedDate Maximum updated date (YYYY-MM-DD)
|
|
48
|
+
# @option params [String] :product Product name
|
|
49
|
+
# @option params [String] :vendor Vendor name
|
|
50
|
+
# @option params [String] :assigner Assigner name
|
|
51
|
+
# @option params [Boolean] :exploited Filter by exploited status
|
|
52
|
+
# @option params [Integer] :page Page number (starts at 0)
|
|
53
|
+
# @option params [Integer] :size Page size (default 10, max 100)
|
|
54
|
+
# @return [Sawyer::Resource] Search results with +.items+ (Array) and +.total+ (Integer)
|
|
55
|
+
def search(params = {})
|
|
56
|
+
@client.get('search', params)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/euvd/client.rb
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'sawyer'
|
|
4
|
+
|
|
5
|
+
module EUVD
|
|
6
|
+
# Error base class for all EUVD client errors.
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
|
|
9
|
+
# Raised when the API returns a non-JSON response (e.g. HTML error page).
|
|
10
|
+
class BadResponseError < Error; end
|
|
11
|
+
|
|
12
|
+
# Raised when a resource is not found (HTTP 404).
|
|
13
|
+
class NotFoundError < Error; end
|
|
14
|
+
|
|
15
|
+
# Raised when the API rate limit is exceeded (HTTP 429).
|
|
16
|
+
class RateLimitError < Error; end
|
|
17
|
+
|
|
18
|
+
# Raised when the API returns a server error (HTTP 5xx).
|
|
19
|
+
class ServerError < Error; end
|
|
20
|
+
|
|
21
|
+
# Client for the EUVD API.
|
|
22
|
+
#
|
|
23
|
+
# All endpoints are GET-only and require no authentication.
|
|
24
|
+
# JSON responses are returned as rich +Sawyer::Resource+ objects
|
|
25
|
+
# (or Array of them), so you can access fields as methods
|
|
26
|
+
# instead of hash keys.
|
|
27
|
+
class Client
|
|
28
|
+
BASE_URL = 'https://euvdservices.enisa.europa.eu/api'
|
|
29
|
+
|
|
30
|
+
attr_reader :base_url, :agent
|
|
31
|
+
|
|
32
|
+
# @param options [Hash]
|
|
33
|
+
# @option options [String] :base_url API base URL (default: EUVD::Client::BASE_URL)
|
|
34
|
+
def initialize(options = {})
|
|
35
|
+
@base_url = options[:base_url] || BASE_URL
|
|
36
|
+
@agent = build_agent
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Make a GET request and return parsed response data.
|
|
40
|
+
#
|
|
41
|
+
# @param path [String] API path (e.g. 'lastvulnerabilities')
|
|
42
|
+
# @param params [Hash] Query parameters
|
|
43
|
+
# @return [Sawyer::Resource, Array, String] parsed data
|
|
44
|
+
def get(path, params = {})
|
|
45
|
+
response = @agent.call(:get, path, nil, query: params)
|
|
46
|
+
response = handle_status(response)
|
|
47
|
+
validate_content_type!(response)
|
|
48
|
+
response.data
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [EUVD::API::Vulnerabilities]
|
|
52
|
+
def vulnerabilities
|
|
53
|
+
API::Vulnerabilities.new(self)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [EUVD::API::Records]
|
|
57
|
+
def records
|
|
58
|
+
API::Records.new(self)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [EUVD::API::Downloads]
|
|
62
|
+
def downloads
|
|
63
|
+
API::Downloads.new(self)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [EUVD::API::Meta]
|
|
67
|
+
def meta
|
|
68
|
+
API::Meta.new(self)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [EUVD::API::Observations]
|
|
72
|
+
def observations
|
|
73
|
+
API::Observations.new(self)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def inspect
|
|
77
|
+
format('#<%<class_name>s:0x%<object_id>x %<url>s>',
|
|
78
|
+
class_name: self.class,
|
|
79
|
+
object_id: object_id * 2,
|
|
80
|
+
url: @base_url)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def build_agent
|
|
86
|
+
conn = Faraday.new(url: @base_url) do |b|
|
|
87
|
+
b.request :url_encoded
|
|
88
|
+
b.adapter Faraday.default_adapter
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
Sawyer::Agent.new(@base_url, faraday: conn) do |http|
|
|
92
|
+
http.headers['Accept'] = 'application/json'
|
|
93
|
+
http.headers['User-Agent'] = "euvd-ruby/#{EUVD::VERSION}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def handle_status(response)
|
|
98
|
+
case response.status
|
|
99
|
+
when 200..299
|
|
100
|
+
response
|
|
101
|
+
when 404
|
|
102
|
+
raise NotFoundError, "Resource not found at #{response.env.url}"
|
|
103
|
+
when 429
|
|
104
|
+
raise RateLimitError, 'API rate limit exceeded'
|
|
105
|
+
when 500..599
|
|
106
|
+
raise ServerError, "Server error (#{response.status})"
|
|
107
|
+
else
|
|
108
|
+
raise Error, "Unexpected response (#{response.status})"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def validate_content_type!(response)
|
|
113
|
+
ct = response.headers['content-type'].to_s
|
|
114
|
+
allowed = %w[application/json application/problem+json text/csv text/plain]
|
|
115
|
+
return if allowed.any? { |t| ct.include?(t) }
|
|
116
|
+
|
|
117
|
+
snippet = response.body.to_s.gsub(/\s+/, ' ').strip[0..120]
|
|
118
|
+
raise BadResponseError, "Expected JSON/text but got '#{ct}' (HTTP #{response.status}): #{snippet.inspect}"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/euvd/version.rb
ADDED
data/lib/euvd.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'euvd/version'
|
|
4
|
+
require_relative 'euvd/client'
|
|
5
|
+
require_relative 'euvd/api/vulnerabilities'
|
|
6
|
+
require_relative 'euvd/api/records'
|
|
7
|
+
require_relative 'euvd/api/downloads'
|
|
8
|
+
require_relative 'euvd/api/meta'
|
|
9
|
+
require_relative 'euvd/api/observations'
|
metadata
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: euvd
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Alexandre ZANNI
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: faraday
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '3'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '1.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '3'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: sawyer
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - "~>"
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0.9'
|
|
39
|
+
type: :runtime
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - "~>"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0.9'
|
|
46
|
+
description: A Ruby client library for the EUVD (European Union Vulnerability Database)
|
|
47
|
+
API. Provides a convenient interface to search and retrieve vulnerability information,
|
|
48
|
+
CVEs, CPEs, CWEs, CAPECs, and advisories.
|
|
49
|
+
email: alexandre.zanni@europe.com
|
|
50
|
+
executables: []
|
|
51
|
+
extensions: []
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- LICENSE
|
|
55
|
+
- README.md
|
|
56
|
+
- lib/euvd.rb
|
|
57
|
+
- lib/euvd/api/downloads.rb
|
|
58
|
+
- lib/euvd/api/meta.rb
|
|
59
|
+
- lib/euvd/api/observations.rb
|
|
60
|
+
- lib/euvd/api/records.rb
|
|
61
|
+
- lib/euvd/api/vulnerabilities.rb
|
|
62
|
+
- lib/euvd/client.rb
|
|
63
|
+
- lib/euvd/version.rb
|
|
64
|
+
homepage: https://github.com/noraj/euvd
|
|
65
|
+
licenses:
|
|
66
|
+
- MIT
|
|
67
|
+
metadata:
|
|
68
|
+
yard.run: yard
|
|
69
|
+
bug_tracker_uri: https://github.com/noraj/euvd/issues
|
|
70
|
+
changelog_uri: https://github.com/noraj/euvd/releases
|
|
71
|
+
documentation_uri: https://noraj.github.io/euvd/
|
|
72
|
+
homepage_uri: https://github.com/noraj/euvd
|
|
73
|
+
source_code_uri: https://github.com/noraj/euvd/
|
|
74
|
+
rubygems_mfa_required: 'true'
|
|
75
|
+
rdoc_options: []
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 3.3.0
|
|
83
|
+
- - "<"
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '5.0'
|
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
requirements: []
|
|
92
|
+
rubygems_version: 4.0.10
|
|
93
|
+
specification_version: 4
|
|
94
|
+
summary: Ruby wrapper for the European Union Vulnerability Database (EUVD) API
|
|
95
|
+
test_files: []
|