ronin-db-activerecord 0.1.0.beta1

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 (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'