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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.github/workflows/ruby.yml +31 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +39 -0
- data/Gemfile +27 -0
- data/README.md +143 -0
- data/Rakefile +72 -0
- data/db/migrate/0001_create_ronin_ip_address_mac_addresses_table.rb +43 -0
- data/db/migrate/0002_create_ronin_vulnerabilities_table.rb +61 -0
- data/db/migrate/0003_create_ronin_url_schemes_table.rb +32 -0
- data/db/migrate/0004_create_ronin_url_query_param_names_table.rb +32 -0
- data/db/migrate/0005_create_ronin_user_names_table.rb +33 -0
- data/db/migrate/0006_create_ronin_software_vendors_table.rb +32 -0
- data/db/migrate/0007_create_ronin_advisories_table.rb +42 -0
- data/db/migrate/0008_create_ronin_host_name_ip_addresses_table.rb +43 -0
- data/db/migrate/0009_create_ronin_host_names_table.rb +34 -0
- data/db/migrate/0010_create_ronin_arches_table.rb +37 -0
- data/db/migrate/0011_create_ronin_email_addresses_table.rb +44 -0
- data/db/migrate/0012_create_ronin_oses_table.rb +36 -0
- data/db/migrate/0013_create_ronin_organizations_table.rb +31 -0
- data/db/migrate/0014_create_ronin_ip_addresses_table.rb +35 -0
- data/db/migrate/0015_create_ronin_os_guesses_table.rb +40 -0
- data/db/migrate/0016_create_ronin_url_query_params_table.rb +42 -0
- data/db/migrate/0017_create_ronin_passwords_table.rb +32 -0
- data/db/migrate/0018_create_ronin_open_ports_table.rb +46 -0
- data/db/migrate/0019_create_ronin_urls_table.rb +50 -0
- data/db/migrate/0020_create_ronin_softwares_table.rb +39 -0
- data/db/migrate/0021_create_ronin_mac_addresses_table.rb +33 -0
- data/db/migrate/0022_create_ronin_countries_table.rb +34 -0
- data/db/migrate/0023_create_ronin_services_table.rb +32 -0
- data/db/migrate/0024_create_ronin_credentials_table.rb +44 -0
- data/db/migrate/0025_create_ronin_ports_table.rb +33 -0
- data/db/migrate/0026_create_ronin_asns_table.rb +44 -0
- data/db/migrate/0027_create_ronin_http_query_param_names_table.rb +32 -0
- data/db/migrate/0028_create_ronin_http_query_params_table.rb +42 -0
- data/db/migrate/0029_create_ronin_http_header_names_table.rb +31 -0
- data/db/migrate/0030_create_ronin_http_request_headers_table.rb +41 -0
- data/db/migrate/0031_create_ronin_http_response_headers_table.rb +41 -0
- data/db/migrate/0032_create_ronin_http_requests_table.rb +41 -0
- data/db/migrate/0033_create_ronin_http_responses_table.rb +36 -0
- data/db/migrate/0034_create_ronin_service_credentials_table.rb +41 -0
- data/db/migrate/0035_create_ronin_web_credentials_table.rb +41 -0
- data/gemspec.yml +28 -0
- data/lib/ronin/db/address.rb +105 -0
- data/lib/ronin/db/advisory.rb +169 -0
- data/lib/ronin/db/arch.rb +160 -0
- data/lib/ronin/db/asn.rb +212 -0
- data/lib/ronin/db/credential.rb +248 -0
- data/lib/ronin/db/email_address.rb +225 -0
- data/lib/ronin/db/host_name.rb +224 -0
- data/lib/ronin/db/host_name_ip_address.rb +65 -0
- data/lib/ronin/db/http_header_name.rb +75 -0
- data/lib/ronin/db/http_query_param.rb +79 -0
- data/lib/ronin/db/http_query_param_name.rb +76 -0
- data/lib/ronin/db/http_request.rb +120 -0
- data/lib/ronin/db/http_request_header.rb +78 -0
- data/lib/ronin/db/http_response.rb +91 -0
- data/lib/ronin/db/http_response_header.rb +78 -0
- data/lib/ronin/db/ip_address.rb +351 -0
- data/lib/ronin/db/ip_address_mac_address.rb +62 -0
- data/lib/ronin/db/mac_address.rb +91 -0
- data/lib/ronin/db/migrations.rb +137 -0
- data/lib/ronin/db/model/has_name.rb +102 -0
- data/lib/ronin/db/model/has_unique_name.rb +82 -0
- data/lib/ronin/db/model/importable.rb +85 -0
- data/lib/ronin/db/model/last_scanned_at.rb +48 -0
- data/lib/ronin/db/model.rb +37 -0
- data/lib/ronin/db/models.rb +108 -0
- data/lib/ronin/db/open_port.rb +148 -0
- data/lib/ronin/db/organization.rb +50 -0
- data/lib/ronin/db/os.rb +183 -0
- data/lib/ronin/db/os_guess.rb +67 -0
- data/lib/ronin/db/password.rb +167 -0
- data/lib/ronin/db/port.rb +123 -0
- data/lib/ronin/db/root.rb +28 -0
- data/lib/ronin/db/schema_migration.rb +34 -0
- data/lib/ronin/db/service.rb +48 -0
- data/lib/ronin/db/service_credential.rb +66 -0
- data/lib/ronin/db/software.rb +85 -0
- data/lib/ronin/db/software_vendor.rb +42 -0
- data/lib/ronin/db/url.rb +497 -0
- data/lib/ronin/db/url_query_param.rb +79 -0
- data/lib/ronin/db/url_query_param_name.rb +76 -0
- data/lib/ronin/db/url_scheme.rb +80 -0
- data/lib/ronin/db/user_name.rb +96 -0
- data/lib/ronin/db/vulnerability.rb +81 -0
- data/lib/ronin/db/web_credential.rb +69 -0
- data/ronin-db-activerecord.gemspec +61 -0
- data/spec/advisory_spec.rb +277 -0
- data/spec/arch_spec.rb +228 -0
- data/spec/asn_spec.rb +504 -0
- data/spec/credential_spec.rb +362 -0
- data/spec/email_address_spec.rb +372 -0
- data/spec/host_name_ip_address_spec.rb +8 -0
- data/spec/host_name_spec.rb +207 -0
- data/spec/http_header_name_spec.rb +25 -0
- data/spec/http_query_param_name_spec.rb +25 -0
- data/spec/http_query_param_spec.rb +104 -0
- data/spec/http_request_header_spec.rb +72 -0
- data/spec/http_request_spec.rb +168 -0
- data/spec/http_response_header_spec.rb +74 -0
- data/spec/http_response_spec.rb +103 -0
- data/spec/ip_address_mac_addresses_spec.rb +8 -0
- data/spec/ip_address_spec.rb +386 -0
- data/spec/mac_address_spec.rb +67 -0
- data/spec/migrations_spec.rb +122 -0
- data/spec/model/has_name_spec.rb +65 -0
- data/spec/model/has_unique_name_spec.rb +61 -0
- data/spec/model/importable_spec.rb +105 -0
- data/spec/models_spec.rb +60 -0
- data/spec/open_port_spec.rb +87 -0
- data/spec/organization_spec.rb +10 -0
- data/spec/os_guess_spec.rb +43 -0
- data/spec/os_spec.rb +114 -0
- data/spec/password_spec.rb +81 -0
- data/spec/port_spec.rb +102 -0
- data/spec/schema_migration_spec.rb +8 -0
- data/spec/service_credential_spec.rb +43 -0
- data/spec/service_spec.rb +39 -0
- data/spec/software_spec.rb +76 -0
- data/spec/software_vendor_spec.rb +33 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/url_query_param_name_spec.rb +25 -0
- data/spec/url_query_param_spec.rb +110 -0
- data/spec/url_scheme_spec.rb +39 -0
- data/spec/url_spec.rb +951 -0
- data/spec/user_name_spec.rb +54 -0
- data/spec/vulnerability_spec.rb +8 -0
- data/spec/web_credential_spec.rb +72 -0
- 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'
|