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,248 @@
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
+
24
+ require 'active_record'
25
+
26
+ module Ronin
27
+ module DB
28
+ #
29
+ # Represents Credentials used to access services or websites.
30
+ #
31
+ class Credential < ActiveRecord::Base
32
+
33
+ include Model
34
+ include Model::Importable
35
+
36
+ # @!attribute [rw] id
37
+ # Primary key of the credential.
38
+ #
39
+ # @return [Integer]
40
+ attribute :id, :integer
41
+
42
+ # @!attribute [rw] user_name
43
+ # User name of the credential.
44
+ #
45
+ # @return [UserName, nil]
46
+ belongs_to :user_name, optional: true
47
+ validates :user_name, presence: true,
48
+ if: ->(cred) { cred.email_address.nil? }
49
+
50
+ # @!attribute [rw] email_address
51
+ # The optional email address associated with the Credential
52
+ #
53
+ # @return [EmailAddress, nil]
54
+ belongs_to :email_address, optional: true
55
+ validates :email_address, presence: true,
56
+ if: ->(cred) { cred.user_name.nil? }
57
+
58
+ # @!attribute [rw] password
59
+ # Password of the credential.
60
+ #
61
+ # @return [Password]
62
+ belongs_to :password, required: true
63
+
64
+ # @!attribute [rw] service_credentials
65
+ # The service credentials.
66
+ #
67
+ # @return [Array<ServiceCredential>]
68
+ has_many :service_credentials, dependent: :destroy
69
+
70
+ # @!attribute [rw] open_ports
71
+ # The open ports that accept this credential pair.
72
+ #
73
+ # @return [Array<OpenPort>]
74
+ has_many :open_ports, through: :service_credentials
75
+
76
+ # @!attribute [rw] web_credentials
77
+ # The Web credentials.
78
+ #
79
+ # @return [Array<WebCredential>]
80
+ has_many :web_credentials, dependent: :destroy
81
+
82
+ # @!attribute [rw] urls
83
+ # The URLs that accept this credential pair.
84
+ #
85
+ # @return [Array<URL>]
86
+ has_many :urls, through: :web_credentials
87
+
88
+ #
89
+ # Searches for all credentials for a specific user.
90
+ #
91
+ # @param [String] name
92
+ # The name of the user.
93
+ #
94
+ # @return [Array<Credential>]
95
+ # The credentials for the user.
96
+ #
97
+ # @api public
98
+ #
99
+ def self.for_user(name)
100
+ joins(:user_name).where(user_name: {name: name})
101
+ end
102
+
103
+ #
104
+ # Searches all web credentials that are associated with an
105
+ # email address.
106
+ #
107
+ # @param [String] email
108
+ # The email address to search for.
109
+ #
110
+ # @return [Array<WebCredential>]
111
+ # The web credentials associated with the email address.
112
+ #
113
+ # @raise [ArgumentError]
114
+ # The given email address was not a valid email address.
115
+ #
116
+ # @api public
117
+ #
118
+ def self.with_email_address(email)
119
+ unless email.include?('@')
120
+ raise(ArgumentError,"invalid email address #{email.inspect}")
121
+ end
122
+
123
+ user, domain = email.split('@',2)
124
+
125
+ return joins(email_address: [:user_name, :host_name]).where(
126
+ email_address: {
127
+ ronin_user_names: {name: user},
128
+ ronin_host_names: {name: domain}
129
+ }
130
+ )
131
+ end
132
+
133
+ #
134
+ # Searches for all credentials with a common password.
135
+ #
136
+ # @param [String] password
137
+ # The password to search for.
138
+ #
139
+ # @return [Array<Credential>]
140
+ # The credentials with the common password.
141
+ #
142
+ # @api public
143
+ #
144
+ def self.with_password(password)
145
+ joins(:password).where(password: {plain_text: password})
146
+ end
147
+
148
+ #
149
+ # Looks up the given credential.
150
+ #
151
+ # @param [String] cred
152
+ # The credential String
153
+ # (ex: `user:password` or `user@example.com:password`).
154
+ #
155
+ # @return [Credential, nil]
156
+ # The found credential.
157
+ #
158
+ def self.lookup(cred)
159
+ unless cred.include?(':')
160
+ raise(ArgumentError,"credential must be of the form user:password or email:password: #{cred.inspect}")
161
+ end
162
+
163
+ user_or_email, password = cred.split(':',2)
164
+
165
+ query = if user_or_email.include?('@')
166
+ with_email_address(user_or_email)
167
+ else
168
+ for_user(user_or_email)
169
+ end
170
+ query.with_password(password)
171
+ return query.first
172
+ end
173
+
174
+ #
175
+ # Imports the given credential.
176
+ #
177
+ # @param [String] cred
178
+ # The credential String
179
+ # (ex: `user:password` or `user@example.com:password`).
180
+ #
181
+ # @return [Credential]
182
+ # The imported credential.
183
+ #
184
+ def self.import(cred)
185
+ unless cred.include?(':')
186
+ raise(ArgumentError,"credential must be of the form user:password or email:password: #{cred.inspect}")
187
+ end
188
+
189
+ user_or_email, password = cred.split(':',2)
190
+
191
+ if user_or_email.include?('@')
192
+ create(
193
+ email_address: EmailAddress.find_or_import(user_or_email),
194
+ password: Password.find_or_import(password)
195
+ )
196
+ else
197
+ create(
198
+ user_name: UserName.find_or_import(user_or_email),
199
+ password: Password.find_or_import(password)
200
+ )
201
+ end
202
+ end
203
+
204
+ #
205
+ # The user the credential belongs to.
206
+ #
207
+ # @return [String]
208
+ # The user name.
209
+ #
210
+ # @api public
211
+ #
212
+ def user
213
+ self.user_name.name if self.user_name
214
+ end
215
+
216
+ #
217
+ # The clear-text password of the credential.
218
+ #
219
+ # @return [String]
220
+ # The clear-text password.
221
+ #
222
+ # @api public
223
+ #
224
+ def plain_text
225
+ self.password.plain_text if self.password
226
+ end
227
+
228
+ #
229
+ # Converts the credentials to a String.
230
+ #
231
+ # @return [String]
232
+ # The user name and the password.
233
+ #
234
+ # @api public
235
+ #
236
+ def to_s
237
+ "#{self.user_name}:#{self.password}"
238
+ end
239
+
240
+ end
241
+ end
242
+ end
243
+
244
+ require 'ronin/db/user_name'
245
+ require 'ronin/db/email_address'
246
+ require 'ronin/db/password'
247
+ require 'ronin/db/service_credential'
248
+ require 'ronin/db/web_credential'
@@ -0,0 +1,225 @@
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
+
24
+ require 'active_record'
25
+ require 'uri/mailto'
26
+
27
+ module Ronin
28
+ module DB
29
+ #
30
+ # Represents email addresses and their associated {UserName user names} and
31
+ # {HostName host names}.
32
+ #
33
+ class EmailAddress < ActiveRecord::Base
34
+
35
+ include Model
36
+ include Model::Importable
37
+
38
+ # @!attribute [rw] id
39
+ # The primary key of the email address.
40
+ #
41
+ # @return [Integer]
42
+ attribute :id, :integer
43
+
44
+ # @!attribute [rw] address
45
+ # The raw string of the email address.
46
+ #
47
+ # @return [String]
48
+ attribute :address, :string
49
+ validates :address, presence: true,
50
+ uniqueness: true,
51
+ length: {maximum: 320},
52
+ format: {
53
+ with: URI::MailTo::EMAIL_REGEXP,
54
+ message: 'Must be a valid email address'
55
+ }
56
+
57
+ # @!attribute [rw] user_name
58
+ # The user-name component of the email address.
59
+ #
60
+ # @return [UserName]
61
+ belongs_to :user_name, required: true
62
+ validates :user_name, uniqueness: {scope: [:host_name_id]}
63
+
64
+ # @!attribute [rw] host_name
65
+ # The host-name component of the email address.
66
+ #
67
+ # @return [HostName]
68
+ belongs_to :host_name, required: true
69
+
70
+ # @!attribute [rw] ip_addresses
71
+ # Any IP addresses associated with the host name.
72
+ #
73
+ # @return [Array<IPAddress>]
74
+ has_many :ip_addresses, through: :host_name,
75
+ class_name: 'IPAddress'
76
+
77
+ # @!attribute [rw] credentials
78
+ # Any web credentials that are associated with the email address.
79
+ #
80
+ # @return [Array<Credential>]
81
+ has_many :credentials, dependent: :destroy
82
+
83
+ # @!attribute [rw] created_at
84
+ # Tracks when the email address was created at.
85
+ #
86
+ # @return [Time]
87
+ attribute :created_at, :time
88
+
89
+ #
90
+ # Searches for email addresses associated with the given host name(s).
91
+ #
92
+ # @param [Array<String>, String] name
93
+ # The host name(s) to search for.
94
+ #
95
+ # @return [Array<EmailAddress>]
96
+ # The matching email addresses.
97
+ #
98
+ # @api public
99
+ #
100
+ def self.with_host_name(name)
101
+ joins(:host_name).where(host_name: {name: name})
102
+ end
103
+
104
+ #
105
+ # Searches for email addresses associated with the given IP address(es).
106
+ #
107
+ # @param [Array<String>, String] ip
108
+ # The IP address(es) to search for.
109
+ #
110
+ # @return [Array<EmailAddress>]
111
+ # The matching email addresses.
112
+ #
113
+ # @api public
114
+ #
115
+ def self.with_ip_address(ip)
116
+ joins(:ip_addresses).where(ip_addresses: {address: ip})
117
+ end
118
+
119
+ #
120
+ # Searches for email addresses associated with the given user name(s).
121
+ #
122
+ # @param [Array<String>, String] name
123
+ # The user name(s) to search for.
124
+ #
125
+ # @return [Array<EmailAddress>]
126
+ # The matching email addresses.
127
+ #
128
+ # @api public
129
+ #
130
+ def self.with_user_name(name)
131
+ joins(:user_name).where(user_name: {name: name})
132
+ end
133
+
134
+ #
135
+ # Looks up the email address.
136
+ #
137
+ # @param [String] email
138
+ # The raw email address string.
139
+ #
140
+ # @return [EmailAddress, nil]
141
+ # The found email address.
142
+ #
143
+ def self.lookup(email)
144
+ find_by(address: email)
145
+ end
146
+
147
+ #
148
+ # Imports an email address.
149
+ #
150
+ # @param [String] email
151
+ # The email address to parse.
152
+ #
153
+ # @return [EmailAddress]
154
+ # A new or previously saved email address resource.
155
+ #
156
+ # @raise [ArgumentError]
157
+ # The email address did not have a user name or a host name.
158
+ #
159
+ # @api public
160
+ #
161
+ def self.import(email)
162
+ if email =~ /\s/
163
+ raise(ArgumentError,"email address #{email.inspect} must not contain spaces")
164
+ end
165
+
166
+ normalized_email = email.downcase
167
+ user, host = normalized_email.split('@',2)
168
+
169
+ if user.empty?
170
+ raise(ArgumentError,"email address #{email.inspect} must have a user name")
171
+ end
172
+
173
+ if host.empty?
174
+ raise(ArgumentError,"email address #{email.inspect} must have a host name")
175
+ end
176
+
177
+ return create(
178
+ address: normalized_email,
179
+ user_name: UserName.find_or_import(user),
180
+ host_name: HostName.find_or_import(host)
181
+ )
182
+ end
183
+
184
+ #
185
+ # The user of the email address.
186
+ #
187
+ # @return [String]
188
+ # The user name.
189
+ #
190
+ # @api public
191
+ #
192
+ def user
193
+ self.user_name.name if self.user_name
194
+ end
195
+
196
+ #
197
+ # The host of the email address.
198
+ #
199
+ # @return [String]
200
+ # The host name.
201
+ #
202
+ # @api public
203
+ #
204
+ def host
205
+ self.host_name.name if self.host_name
206
+ end
207
+
208
+ #
209
+ # Converts the email address into a String.
210
+ #
211
+ # @return [String]
212
+ # The raw email address.
213
+ #
214
+ # @api public
215
+ #
216
+ def to_s
217
+ "#{self.user_name}@#{self.host_name}"
218
+ end
219
+
220
+ end
221
+ end
222
+ end
223
+
224
+ require 'ronin/db/user_name'
225
+ require 'ronin/db/host_name'
@@ -0,0 +1,224 @@
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/rfc2396_parser'
27
+ require 'strscan'
28
+
29
+ module Ronin
30
+ module DB
31
+ #
32
+ # Represents host names and their associated {IPAddress IP addresses}.
33
+ #
34
+ class HostName < ActiveRecord::Base
35
+
36
+ include Model
37
+ include Model::Importable
38
+ include Model::LastScannedAt
39
+
40
+ # @!attribute [rw] id
41
+ # The primary ID of the host nmae.
42
+ #
43
+ # @return [Integer]
44
+ attribute :id, :integer
45
+
46
+ # @!attribute [rw] name
47
+ # The address of the host name.
48
+ #
49
+ # @return [String]
50
+ attribute :name, :string
51
+ validates :name, presence: true,
52
+ uniqueness: true,
53
+ length: {maximum: 255},
54
+ format: {
55
+ with: /\A#{URI::RFC2396_REGEXP::PATTERN::HOSTNAME}\z/,
56
+ message: 'Must be a valid host-name'
57
+ }
58
+
59
+ # @!attribute [rw] created_at
60
+ # When the host name was first created.
61
+ #
62
+ # @return [Time]
63
+ attribute :created_at, :time
64
+
65
+ # @!attribute [rw] host_name_ip_addresses
66
+ # The IP Address associations.
67
+ #
68
+ # @return [Array<HostNameIPAddress>]
69
+ has_many :host_name_ip_addresses, dependent: :destroy,
70
+ class_name: 'HostNameIPAddress'
71
+
72
+ # @!attribute [rw] ip_addresses
73
+ # The IP Addresses that host the host name.
74
+ #
75
+ # @return [Array<IPAddress>]
76
+ has_many :ip_addresses, through: :host_name_ip_addresses,
77
+ class_name: 'IPAddress'
78
+
79
+ # @!attribute [rw] open_ports
80
+ # The open ports of the host.
81
+ #
82
+ # @return [Array<OpenPort>]
83
+ has_many :open_ports, through: :ip_addresses
84
+
85
+ # @!attribute [rw] ports
86
+ # The ports of the host.
87
+ #
88
+ # @return [Array<Port>]
89
+ has_many :ports, through: :ip_addresses
90
+
91
+ # @!attribute [rw] email_addresses
92
+ # The email addresses that are associated with the host-name.
93
+ #
94
+ # @return [Array<EmailAddress>]
95
+ has_many :email_addresses
96
+
97
+ # @!attribute [rw] urls
98
+ # The URLs that point to this host name.
99
+ #
100
+ # @return [Array<URL>]
101
+ has_many :urls, class_name: 'URL'
102
+
103
+ #
104
+ # Looks up the host name.
105
+ #
106
+ # @param [String] name
107
+ # The raw host name.
108
+ #
109
+ # @return [HostName, nil]
110
+ # The found host name.
111
+ #
112
+ def self.lookup(name)
113
+ find_by(name: name)
114
+ end
115
+
116
+ #
117
+ # Creates a new host name.
118
+ #
119
+ # @param [String] name
120
+ # The host name.
121
+ #
122
+ # @return [HostName]
123
+ # The created host name record.
124
+ #
125
+ def self.import(name)
126
+ create(name: name)
127
+ end
128
+
129
+ #
130
+ # Searches for host names associated with the given IP address(es).
131
+ #
132
+ # @param [Array<String>, String] ip
133
+ # The IP address(es) to search for.
134
+ #
135
+ # @return [Array<HostName>]
136
+ # The matching host names.
137
+ #
138
+ # @api public
139
+ #
140
+ def self.with_ip_address(ip)
141
+ joins(:ip_addresses).where(ip_addresses: {address: ip})
142
+ end
143
+
144
+ #
145
+ # Searches for host names with the given open port(s).
146
+ #
147
+ # @param [Array<Integer>, Integer] number
148
+ # The open port(s) to search for.
149
+ #
150
+ # @return [Array<HostName>]
151
+ # The matching host names.
152
+ #
153
+ # @api public
154
+ #
155
+ def self.with_port_number(number)
156
+ joins(:ports).where(ports: {number: number})
157
+ end
158
+
159
+ #
160
+ # Searches for all host names under the Top-Level Domain (TLD).
161
+ #
162
+ # @param [String] name
163
+ # The Top-Level Domain (TLD).
164
+ #
165
+ # @return [Array<HostName>]
166
+ # The matching host names.
167
+ #
168
+ # @api public
169
+ #
170
+ def self.with_tld(name)
171
+ name_column = self.arel_table[:name]
172
+
173
+ where(name_column.matches("%.#{sanitize_sql_like(name)}"))
174
+ end
175
+
176
+ #
177
+ # Searches for all host names sharing a canonical domain name.
178
+ #
179
+ # @param [String] name
180
+ # The canonical domain name to search for.
181
+ #
182
+ # @return [Array<HostName>]
183
+ # The matching host names.
184
+ #
185
+ # @api public
186
+ #
187
+ def self.with_domain(name)
188
+ name_column = self.arel_table[:name]
189
+
190
+ name = sanitize_sql_like(name)
191
+
192
+ where(name: name).or(where(name_column.matches("%.#{name}")))
193
+ end
194
+
195
+ #
196
+ # The IP Address that was most recently used by the host name.
197
+ #
198
+ # @return [IPAddress]
199
+ # The IP Address that most recently used by the host name.
200
+ #
201
+ # @api public
202
+ #
203
+ def recent_ip_address
204
+ self.host_name_ip_addresses.order('created_at DESC').ip_addresses.first
205
+ end
206
+
207
+ #
208
+ # Converts the host name to a String.
209
+ #
210
+ # @return [String]
211
+ # The host name.
212
+ #
213
+ # @api public
214
+ #
215
+ def to_s
216
+ self.name.to_s
217
+ end
218
+
219
+ end
220
+ end
221
+ end
222
+
223
+ require 'ronin/db/host_name_ip_address'
224
+ require 'ronin/db/ip_address'