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 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
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EUVD
4
+ VERSION = '0.1.0'
5
+ end
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: []