ronin-db-activerecord 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.github/workflows/ruby.yml +31 -0
  4. data/.gitignore +13 -0
  5. data/.rspec +1 -0
  6. data/.ruby-version +1 -0
  7. data/.yardopts +1 -0
  8. data/COPYING.txt +165 -0
  9. data/ChangeLog.md +39 -0
  10. data/Gemfile +27 -0
  11. data/README.md +143 -0
  12. data/Rakefile +72 -0
  13. data/db/migrate/0001_create_ronin_ip_address_mac_addresses_table.rb +43 -0
  14. data/db/migrate/0002_create_ronin_vulnerabilities_table.rb +61 -0
  15. data/db/migrate/0003_create_ronin_url_schemes_table.rb +32 -0
  16. data/db/migrate/0004_create_ronin_url_query_param_names_table.rb +32 -0
  17. data/db/migrate/0005_create_ronin_user_names_table.rb +33 -0
  18. data/db/migrate/0006_create_ronin_software_vendors_table.rb +32 -0
  19. data/db/migrate/0007_create_ronin_advisories_table.rb +42 -0
  20. data/db/migrate/0008_create_ronin_host_name_ip_addresses_table.rb +43 -0
  21. data/db/migrate/0009_create_ronin_host_names_table.rb +34 -0
  22. data/db/migrate/0010_create_ronin_arches_table.rb +37 -0
  23. data/db/migrate/0011_create_ronin_email_addresses_table.rb +44 -0
  24. data/db/migrate/0012_create_ronin_oses_table.rb +36 -0
  25. data/db/migrate/0013_create_ronin_organizations_table.rb +31 -0
  26. data/db/migrate/0014_create_ronin_ip_addresses_table.rb +35 -0
  27. data/db/migrate/0015_create_ronin_os_guesses_table.rb +40 -0
  28. data/db/migrate/0016_create_ronin_url_query_params_table.rb +42 -0
  29. data/db/migrate/0017_create_ronin_passwords_table.rb +32 -0
  30. data/db/migrate/0018_create_ronin_open_ports_table.rb +46 -0
  31. data/db/migrate/0019_create_ronin_urls_table.rb +50 -0
  32. data/db/migrate/0020_create_ronin_softwares_table.rb +39 -0
  33. data/db/migrate/0021_create_ronin_mac_addresses_table.rb +33 -0
  34. data/db/migrate/0022_create_ronin_countries_table.rb +34 -0
  35. data/db/migrate/0023_create_ronin_services_table.rb +32 -0
  36. data/db/migrate/0024_create_ronin_credentials_table.rb +44 -0
  37. data/db/migrate/0025_create_ronin_ports_table.rb +33 -0
  38. data/db/migrate/0026_create_ronin_asns_table.rb +44 -0
  39. data/db/migrate/0027_create_ronin_http_query_param_names_table.rb +32 -0
  40. data/db/migrate/0028_create_ronin_http_query_params_table.rb +42 -0
  41. data/db/migrate/0029_create_ronin_http_header_names_table.rb +31 -0
  42. data/db/migrate/0030_create_ronin_http_request_headers_table.rb +41 -0
  43. data/db/migrate/0031_create_ronin_http_response_headers_table.rb +41 -0
  44. data/db/migrate/0032_create_ronin_http_requests_table.rb +41 -0
  45. data/db/migrate/0033_create_ronin_http_responses_table.rb +36 -0
  46. data/db/migrate/0034_create_ronin_service_credentials_table.rb +41 -0
  47. data/db/migrate/0035_create_ronin_web_credentials_table.rb +41 -0
  48. data/gemspec.yml +28 -0
  49. data/lib/ronin/db/address.rb +105 -0
  50. data/lib/ronin/db/advisory.rb +169 -0
  51. data/lib/ronin/db/arch.rb +160 -0
  52. data/lib/ronin/db/asn.rb +212 -0
  53. data/lib/ronin/db/credential.rb +248 -0
  54. data/lib/ronin/db/email_address.rb +225 -0
  55. data/lib/ronin/db/host_name.rb +224 -0
  56. data/lib/ronin/db/host_name_ip_address.rb +65 -0
  57. data/lib/ronin/db/http_header_name.rb +75 -0
  58. data/lib/ronin/db/http_query_param.rb +79 -0
  59. data/lib/ronin/db/http_query_param_name.rb +76 -0
  60. data/lib/ronin/db/http_request.rb +120 -0
  61. data/lib/ronin/db/http_request_header.rb +78 -0
  62. data/lib/ronin/db/http_response.rb +91 -0
  63. data/lib/ronin/db/http_response_header.rb +78 -0
  64. data/lib/ronin/db/ip_address.rb +351 -0
  65. data/lib/ronin/db/ip_address_mac_address.rb +62 -0
  66. data/lib/ronin/db/mac_address.rb +91 -0
  67. data/lib/ronin/db/migrations.rb +137 -0
  68. data/lib/ronin/db/model/has_name.rb +102 -0
  69. data/lib/ronin/db/model/has_unique_name.rb +82 -0
  70. data/lib/ronin/db/model/importable.rb +85 -0
  71. data/lib/ronin/db/model/last_scanned_at.rb +48 -0
  72. data/lib/ronin/db/model.rb +37 -0
  73. data/lib/ronin/db/models.rb +108 -0
  74. data/lib/ronin/db/open_port.rb +148 -0
  75. data/lib/ronin/db/organization.rb +50 -0
  76. data/lib/ronin/db/os.rb +183 -0
  77. data/lib/ronin/db/os_guess.rb +67 -0
  78. data/lib/ronin/db/password.rb +167 -0
  79. data/lib/ronin/db/port.rb +123 -0
  80. data/lib/ronin/db/root.rb +28 -0
  81. data/lib/ronin/db/schema_migration.rb +34 -0
  82. data/lib/ronin/db/service.rb +48 -0
  83. data/lib/ronin/db/service_credential.rb +66 -0
  84. data/lib/ronin/db/software.rb +85 -0
  85. data/lib/ronin/db/software_vendor.rb +42 -0
  86. data/lib/ronin/db/url.rb +497 -0
  87. data/lib/ronin/db/url_query_param.rb +79 -0
  88. data/lib/ronin/db/url_query_param_name.rb +76 -0
  89. data/lib/ronin/db/url_scheme.rb +80 -0
  90. data/lib/ronin/db/user_name.rb +96 -0
  91. data/lib/ronin/db/vulnerability.rb +81 -0
  92. data/lib/ronin/db/web_credential.rb +69 -0
  93. data/ronin-db-activerecord.gemspec +61 -0
  94. data/spec/advisory_spec.rb +277 -0
  95. data/spec/arch_spec.rb +228 -0
  96. data/spec/asn_spec.rb +504 -0
  97. data/spec/credential_spec.rb +362 -0
  98. data/spec/email_address_spec.rb +372 -0
  99. data/spec/host_name_ip_address_spec.rb +8 -0
  100. data/spec/host_name_spec.rb +207 -0
  101. data/spec/http_header_name_spec.rb +25 -0
  102. data/spec/http_query_param_name_spec.rb +25 -0
  103. data/spec/http_query_param_spec.rb +104 -0
  104. data/spec/http_request_header_spec.rb +72 -0
  105. data/spec/http_request_spec.rb +168 -0
  106. data/spec/http_response_header_spec.rb +74 -0
  107. data/spec/http_response_spec.rb +103 -0
  108. data/spec/ip_address_mac_addresses_spec.rb +8 -0
  109. data/spec/ip_address_spec.rb +386 -0
  110. data/spec/mac_address_spec.rb +67 -0
  111. data/spec/migrations_spec.rb +122 -0
  112. data/spec/model/has_name_spec.rb +65 -0
  113. data/spec/model/has_unique_name_spec.rb +61 -0
  114. data/spec/model/importable_spec.rb +105 -0
  115. data/spec/models_spec.rb +60 -0
  116. data/spec/open_port_spec.rb +87 -0
  117. data/spec/organization_spec.rb +10 -0
  118. data/spec/os_guess_spec.rb +43 -0
  119. data/spec/os_spec.rb +114 -0
  120. data/spec/password_spec.rb +81 -0
  121. data/spec/port_spec.rb +102 -0
  122. data/spec/schema_migration_spec.rb +8 -0
  123. data/spec/service_credential_spec.rb +43 -0
  124. data/spec/service_spec.rb +39 -0
  125. data/spec/software_spec.rb +76 -0
  126. data/spec/software_vendor_spec.rb +33 -0
  127. data/spec/spec_helper.rb +13 -0
  128. data/spec/url_query_param_name_spec.rb +25 -0
  129. data/spec/url_query_param_spec.rb +110 -0
  130. data/spec/url_scheme_spec.rb +39 -0
  131. data/spec/url_spec.rb +951 -0
  132. data/spec/user_name_spec.rb +54 -0
  133. data/spec/vulnerability_spec.rb +8 -0
  134. data/spec/web_credential_spec.rb +72 -0
  135. metadata +266 -0
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
4
+ #
5
+ # Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-db-activerecord is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-db-activerecord is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-db-activerecord. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/db/model'
22
+
23
+ require 'active_record'
24
+
25
+ module Ronin
26
+ module DB
27
+ #
28
+ # Represents Credentials used to access a TCP/UDP {Service}.
29
+ #
30
+ class ServiceCredential < ActiveRecord::Base
31
+
32
+ include Model
33
+
34
+ # @!attribute [rw] id
35
+ # Primary key of the service credential.
36
+ #
37
+ # @return [Integer]
38
+ attribute :id, :integer
39
+
40
+ # @!attribute [rw] credential
41
+ #
42
+ # @return [Credential]
43
+ belongs_to :credential
44
+
45
+ # @!attribute [rw] open_port
46
+ # The open port the credential belongs to.
47
+ #
48
+ # @return [OpenPort]
49
+ belongs_to :open_port
50
+
51
+ #
52
+ # Converts the service credential to a String.
53
+ #
54
+ # @return [String]
55
+ # The service credential string.
56
+ #
57
+ def to_s
58
+ "#{self.credential} (#{self.open_port})"
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+
65
+ require 'ronin/db/credential'
66
+ require 'ronin/db/open_port'
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
4
+ #
5
+ # Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-db-activerecord is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-db-activerecord is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-db-activerecord. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/db/model'
22
+
23
+ require 'active_record'
24
+
25
+ module Ronin
26
+ module DB
27
+ #
28
+ # Represents a Software product.
29
+ #
30
+ class Software < ActiveRecord::Base
31
+
32
+ include Model
33
+
34
+ # @!attribute [rw] id
35
+ # The primary key of the software.
36
+ #
37
+ # @return [Integer]
38
+ attribute :id, :integer
39
+
40
+ # @!attribute [rw] name
41
+ # The software's name.
42
+ #
43
+ # @return [String]
44
+ attribute :name, :string
45
+ validates :name, presence: true
46
+
47
+ # @!attribute [rw] version
48
+ # The software's Version.
49
+ #
50
+ # @return [String]
51
+ attribute :version, :string
52
+ validates :version, presence: true,
53
+ uniqueness: {scope: :name}
54
+
55
+ # @!attribute [rw] vendor
56
+ # The vendor of the software
57
+ #
58
+ # @return [SoftwareVendor, nil]
59
+ belongs_to :vendor, optional: true,
60
+ class_name: 'SoftwareVendor'
61
+
62
+ # @!attribute [rw] open_ports
63
+ # The open ports running the software
64
+ #
65
+ # @return [Array<OpenPort>]
66
+ has_many :open_ports
67
+
68
+ #
69
+ # Converts the software to a String.
70
+ #
71
+ # @return [String]
72
+ # The software vendor, name and version.
73
+ #
74
+ # @api public
75
+ #
76
+ def to_s
77
+ [self.vendor, self.name, self.version].compact.join(' ')
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+
84
+ require 'ronin/db/software_vendor'
85
+ require 'ronin/db/open_port'
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
4
+ #
5
+ # Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-db-activerecord is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-db-activerecord is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-db-activerecord. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/db/model'
22
+ require 'ronin/db/model/has_unique_name'
23
+
24
+ module Ronin
25
+ module DB
26
+ #
27
+ # Represents a {Software} vendor.
28
+ #
29
+ class SoftwareVendor < ActiveRecord::Base
30
+
31
+ include Model
32
+ include Model::HasUniqueName
33
+
34
+ # The primary-key of the vendor
35
+ attribute :id, :integer
36
+
37
+ # Products published by the vendor
38
+ has_many :software, class_name: 'Software'
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,497 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
4
+ #
5
+ # Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
6
+ #
7
+ # ronin-db-activerecord is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # ronin-db-activerecord is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with ronin-db-activerecord. If not, see <https://www.gnu.org/licenses/>.
19
+ #
20
+
21
+ require 'ronin/db/model'
22
+ require 'ronin/db/model/importable'
23
+ require 'ronin/db/model/last_scanned_at'
24
+
25
+ require 'active_record'
26
+ require 'uri/generic'
27
+ require 'uri/http'
28
+ require 'uri/https'
29
+ require 'uri/ftp'
30
+ require 'uri/query_params'
31
+
32
+ module Ronin
33
+ module DB
34
+ #
35
+ # Represents parsed URLs.
36
+ #
37
+ class URL < ActiveRecord::Base
38
+
39
+ include Model
40
+ include Model::Importable
41
+ include Model::LastScannedAt
42
+
43
+ # Mapping of URL Schemes and URI classes
44
+ SCHEMES = {
45
+ 'https' => ::URI::HTTPS,
46
+ 'http' => ::URI::HTTP,
47
+ 'ftp' => ::URI::FTP
48
+ }
49
+
50
+ # @!attribute [rw] id
51
+ # The primary key of the URL.
52
+ #
53
+ # @return [Integer]
54
+ attribute :id, :integer
55
+
56
+ # @!attribute [rw] scheme
57
+ # The scheme of the URL.
58
+ #
59
+ # @return [URLScheme]
60
+ belongs_to :scheme, required: true,
61
+ class_name: 'URLScheme'
62
+
63
+ # @!attribute [rw] host_name
64
+ # The host name of the URL
65
+ #
66
+ # @return [HostName]
67
+ belongs_to :host_name, required: true
68
+
69
+ # @!attribute [rw] port
70
+ # The port of the URL.
71
+ #
72
+ # @return [Port, nil]
73
+ belongs_to :port, optional: true,
74
+ class_name: 'Port'
75
+
76
+ # @!attribute [rw] path
77
+ # The path of the URL.
78
+ #
79
+ # @return [String]
80
+ attribute :path, :string
81
+
82
+ # @!attribute [rw] query
83
+ # The query string part of the URL.
84
+ #
85
+ # @return [String, nil]
86
+ attribute :query, :string
87
+
88
+ # @!attribute [rw] fragment
89
+ # The fragment of the URL.
90
+ #
91
+ # @return [String, nil]
92
+ attribute :fragment, :string
93
+
94
+ # @!attribute [r] created_at
95
+ # Defines the created_at timestamp
96
+ #
97
+ # @return [Time]
98
+ attribute :created_at, :time
99
+
100
+ # @!attribute [rw] query_params
101
+ # The query params of the URL.
102
+ #
103
+ # @return [Array<URLQueryParam>]
104
+ has_many :query_params, class_name: 'URLQueryParam',
105
+ dependent: :destroy
106
+
107
+ # @!attribute [rw] web_credentials
108
+ # Any credentials used with the URL.
109
+ #
110
+ # @return [Array<WebCredential>]
111
+ has_many :web_credentials, dependent: :destroy
112
+
113
+ # @!attribute [rw] credentials
114
+ # The credentials that will work with this URL.
115
+ #
116
+ # @return [Array<Credentials>]
117
+ has_many :credentials, through: :web_credentials
118
+
119
+ #
120
+ # Searches for all URLs using HTTP.
121
+ #
122
+ # @return [Array<URL>]
123
+ # The matching URLs.
124
+ #
125
+ # @api public
126
+ #
127
+ def self.http
128
+ joins(:scheme).where(scheme: {name: 'http'})
129
+ end
130
+
131
+ #
132
+ # Searches for all URLs using HTTPS.
133
+ #
134
+ # @return [Array<URL>]
135
+ # The matching URLs.
136
+ #
137
+ # @api public
138
+ #
139
+ def self.https
140
+ joins(:scheme).where(scheme: {name: 'https'})
141
+ end
142
+
143
+ #
144
+ # Searches for URLs with specific host name(s).
145
+ #
146
+ # @param [String, Array<String>] name
147
+ # The host name(s) to search for.
148
+ #
149
+ # @return [Array<URL>]
150
+ # The matching URLs.
151
+ #
152
+ # @api public
153
+ #
154
+ def self.with_host_name(name)
155
+ joins(:host_name).where(host_name: {name: name})
156
+ end
157
+
158
+ #
159
+ # Searches for URLs with the specific port number(s).
160
+ #
161
+ # @param [Integer, Array<Integer>] number
162
+ # The port number(s) to search for.
163
+ #
164
+ # @return [Array<URL>]
165
+ # The matching URLs.
166
+ #
167
+ # @api public
168
+ #
169
+ def self.with_port_number(number)
170
+ joins(:port).where(port: {number: number})
171
+ end
172
+
173
+ #
174
+ # Searches for all URLs with the exact path.
175
+ #
176
+ # @param [String] path
177
+ # The path to search for.
178
+ #
179
+ # @return [Array<URL>]
180
+ # The URL with the matching path.
181
+ #
182
+ # @api public
183
+ #
184
+ def self.with_path(path)
185
+ where(path: path)
186
+ end
187
+
188
+ #
189
+ # Searches for all URLs with the exact fragment.
190
+ #
191
+ # @param [String] fragment
192
+ # The fragment to search for.
193
+ #
194
+ # @return [Array<URL>]
195
+ # The URL with the matching fragment.
196
+ #
197
+ # @api public
198
+ #
199
+ def self.with_fragment(fragment)
200
+ where(fragment: fragment)
201
+ end
202
+
203
+ #
204
+ # Searches for all URLs sharing a common sub-directory.
205
+ #
206
+ # @param [String] root_dir
207
+ # The sub-directory to search for.
208
+ #
209
+ # @return [Array<URL>]
210
+ # The URL with the common sub-directory.
211
+ #
212
+ # @api public
213
+ #
214
+ def self.with_directory(root_dir)
215
+ path_column = self.arel_table[:path]
216
+
217
+ where(path: root_dir).or(where(path_column.matches("#{root_dir}/%")))
218
+ end
219
+
220
+ #
221
+ # Searches for all URLs sharing a common base name.
222
+ #
223
+ # @param [String] basename
224
+ # The base name to search for.
225
+ #
226
+ # @return [Array<URL>]
227
+ # The URL with the common base name.
228
+ #
229
+ # @api public
230
+ #
231
+ def self.with_basename(basename)
232
+ path_column = self.arel_table[:path]
233
+
234
+ where(path_column.matches("%/#{basename}"))
235
+ end
236
+
237
+ #
238
+ # Searches for all URLs with a common file-extension.
239
+ #
240
+ # @param [String] ext
241
+ # The file extension to search for.
242
+ #
243
+ # @return [Array<URL>]
244
+ # The URLs with the common file-extension.
245
+ #
246
+ # @api public
247
+ #
248
+ def self.with_file_ext(ext)
249
+ path_column = self.arel_table[:path]
250
+
251
+ where(path_column.matches("%.#{sanitize_sql_like(ext)}"))
252
+ end
253
+
254
+ #
255
+ # Searches for URLs with the given query param name and value.
256
+ #
257
+ # @param [String, Array<String>] name
258
+ # The query param name to search for.
259
+ #
260
+ # @param [String, Array<String>] value
261
+ # The query param value to search for.
262
+ #
263
+ # @return [Array<URL>]
264
+ # The URLs with the given query param.
265
+ #
266
+ # @api public
267
+ #
268
+ def self.with_query_param(name,value)
269
+ joins(query_params: :name).where(
270
+ query_params: {
271
+ ronin_url_query_param_names: {name: name},
272
+ value: value
273
+ }
274
+ )
275
+ end
276
+
277
+ #
278
+ # Search for all URLs with a given query param name.
279
+ #
280
+ # @param [Array<String>, String] name
281
+ # The query param name to search for.
282
+ #
283
+ # @return [Array<URL>]
284
+ # The URLs with the given query param name.
285
+ #
286
+ # @api public
287
+ #
288
+ def self.with_query_param_name(name)
289
+ joins(query_params: [:name]).where(
290
+ query_params: {
291
+ ronin_url_query_param_names: {name: name}
292
+ }
293
+ )
294
+ end
295
+
296
+ #
297
+ # Search for all URLs with a given query param value.
298
+ #
299
+ # @param [Array<String>, String] value
300
+ # The query param value to search for.
301
+ #
302
+ # @return [Array<URL>]
303
+ # The URLs with the given query param value.
304
+ #
305
+ # @api public
306
+ #
307
+ def self.with_query_param_value(value)
308
+ joins(:query_params).where(query_params: {value: value})
309
+ end
310
+
311
+ #
312
+ # Searches for a URL.
313
+ #
314
+ # @param [URI::HTTP, String] url
315
+ # The URL to search for.
316
+ #
317
+ # @return [URL, nil]
318
+ # The matching URL.
319
+ #
320
+ # @api public
321
+ #
322
+ def self.lookup(url)
323
+ uri = URI(url)
324
+
325
+ # create the initial query
326
+ query = joins(:scheme, :host_name).where(
327
+ scheme: {name: uri.scheme},
328
+ host_name: {name: uri.host},
329
+ path: normalized_path(uri),
330
+ query: uri.query,
331
+ fragment: uri.fragment
332
+ )
333
+
334
+ if uri.port
335
+ # query the port
336
+ query = query.joins(:port).where(port: {number: uri.port})
337
+ end
338
+
339
+ return query.first
340
+ end
341
+
342
+ #
343
+ # Creates a new URL.
344
+ #
345
+ # @param [String, URI::HTTP] uri
346
+ # The URI to create the URL from.
347
+ #
348
+ # @return [URL]
349
+ # The new URL.
350
+ #
351
+ # @api public
352
+ #
353
+ def self.import(uri)
354
+ uri = URI(uri)
355
+
356
+ # find or create the URL scheme, host_name and port
357
+ scheme = URLScheme.find_or_create_by(name: uri.scheme)
358
+ host_name = HostName.find_or_create_by(name: uri.host)
359
+ port = if uri.port
360
+ Port.find_or_create_by(
361
+ protocol: :tcp,
362
+ number: uri.port
363
+ )
364
+ end
365
+
366
+ path = normalized_path(uri)
367
+ query = uri.query
368
+ fragment = uri.fragment
369
+
370
+ query_params = []
371
+
372
+ if uri.respond_to?(:query_params)
373
+ # find or create the URL query params
374
+ uri.query_params.each do |name,value|
375
+ query_params << URLQueryParam.new(
376
+ name: URLQueryParamName.find_or_create_by(name: name),
377
+ value: value
378
+ )
379
+ end
380
+ end
381
+
382
+ # find or create the URL
383
+ return find_or_create_by(
384
+ scheme: scheme,
385
+ host_name: host_name,
386
+ port: port,
387
+ path: path,
388
+ query: query,
389
+ fragment: fragment,
390
+ query_params: query_params
391
+ )
392
+ end
393
+
394
+ #
395
+ # The host name of the URL.
396
+ #
397
+ # @return [String]
398
+ # The address of host name.
399
+ #
400
+ # @api public
401
+ #
402
+ def host
403
+ self.host_name.name
404
+ end
405
+
406
+ #
407
+ # The port number used by the URL.
408
+ #
409
+ # @return [Integer, nil]
410
+ # The port number.
411
+ #
412
+ # @api public
413
+ #
414
+ def port_number
415
+ self.port.number if self.port
416
+ end
417
+
418
+ #
419
+ # Builds a URI object from the URL.
420
+ #
421
+ # @return [URI::HTTP, URI::HTTPS]
422
+ # The URI object created from the URL attributes.
423
+ #
424
+ # @api public
425
+ #
426
+ def to_uri
427
+ # map the URL scheme to a URI class
428
+ url_class = SCHEMES.fetch(self.scheme.name,::URI::Generic)
429
+
430
+ scheme = if self.scheme
431
+ self.scheme.name
432
+ end
433
+ host = if self.host_name
434
+ self.host_name.name
435
+ end
436
+ port = if self.port
437
+ self.port.number
438
+ end
439
+
440
+ # build the URI
441
+ return url_class.build(
442
+ scheme: scheme,
443
+ host: host,
444
+ port: port,
445
+ path: self.path,
446
+ query: self.query,
447
+ fragment: self.fragment
448
+ )
449
+ end
450
+
451
+ #
452
+ # Converts the URL to a String.
453
+ #
454
+ # @return [String]
455
+ # The string form of the URL.
456
+ #
457
+ # @api public
458
+ #
459
+ def to_s
460
+ self.to_uri.to_s
461
+ end
462
+
463
+ protected
464
+
465
+ #
466
+ # Normalizes the path of a URI.
467
+ #
468
+ # @param [URI] uri
469
+ # The URI containing the path.
470
+ #
471
+ # @return [String, nil]
472
+ # The normalized path.
473
+ #
474
+ # @api private
475
+ #
476
+ def self.normalized_path(uri)
477
+ case uri
478
+ when ::URI::HTTP
479
+ # map empty HTTP paths to '/'
480
+ unless uri.path.empty? then uri.path
481
+ else '/'
482
+ end
483
+ else
484
+ uri.path
485
+ end
486
+ end
487
+
488
+ end
489
+ end
490
+ end
491
+
492
+ require 'ronin/db/host_name'
493
+ require 'ronin/db/port'
494
+ require 'ronin/db/url_scheme'
495
+ require 'ronin/db/url_query_param_name'
496
+ require 'ronin/db/url_query_param'
497
+ require 'ronin/db/web_credential'