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,277 @@
1
+ require 'spec_helper'
2
+ require 'ronin/db/advisory'
3
+
4
+ describe Ronin::DB::Advisory do
5
+ describe described_class::ID do
6
+ describe ".parse" do
7
+ subject { described_class }
8
+
9
+ context "when given a CVE ID" do
10
+ let(:year) { 2020 }
11
+ let(:identifier) { '1234' }
12
+ let(:id) { "CVE-#{year}-#{identifier}" }
13
+
14
+ it "must return a Hash containing the ID, 'CVE' prefix, the year, and the identifier" do
15
+ expect(subject.parse(id)).to eq(
16
+ {
17
+ id: id,
18
+ prefix: 'CVE',
19
+ year: year,
20
+ identifier: identifier
21
+ }
22
+ )
23
+ end
24
+ end
25
+
26
+ context "when given a MS ID" do
27
+ let(:year) { 2017 }
28
+ let(:identifier) { '010' }
29
+ let(:id) { "MS#{year - 2000}-#{identifier}" }
30
+
31
+ it "must return a Hash containing the ID, 'MS' prefix, the full year, and the identifier" do
32
+ expect(subject.parse(id)).to eq(
33
+ {
34
+ id: id,
35
+ prefix: 'MS',
36
+ year: year,
37
+ identifier: identifier
38
+ }
39
+ )
40
+ end
41
+ end
42
+
43
+ context "when given a RHSA ID" do
44
+ let(:year) { 2022 }
45
+ let(:identifier) { '6187' }
46
+ let(:id) { "RHSA-#{year}:#{identifier}" }
47
+
48
+ it "must return a Hash containing the ID, 'RHSA' prefix, the year, and the identifier" do
49
+ expect(subject.parse(id)).to eq(
50
+ {
51
+ id: id,
52
+ prefix: 'RHSA',
53
+ year: year,
54
+ identifier: identifier
55
+ }
56
+ )
57
+ end
58
+ end
59
+
60
+ context "when given a GHSA ID" do
61
+ let(:identifier) { '3hhc-qp5v-9p2j' }
62
+ let(:id) { "GHSA-#{identifier}" }
63
+
64
+ it "must return a Hash containing the ID and the identifier" do
65
+ expect(subject.parse(id)).to eq(
66
+ {
67
+ id: id,
68
+ prefix: 'GHSA',
69
+ identifier: identifier
70
+ }
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "validations" do
78
+ describe "prefix" do
79
+ subject do
80
+ end
81
+
82
+ it "must require a preifx" do
83
+ advisory = described_class.new(
84
+ id: 'CVE-2022-1234',
85
+ year: 2022,
86
+ identifier: '2022-1234'
87
+ )
88
+ expect(advisory).to_not be_valid
89
+ expect(advisory.errors[:prefix]).to eq(["can't be blank"])
90
+
91
+ advisory = described_class.new(
92
+ id: 'CVE-2022-1234',
93
+ prefix: 'CVE',
94
+ year: 2022,
95
+ identifier: '2022-1234'
96
+ )
97
+ expect(advisory).to be_valid
98
+ end
99
+ end
100
+
101
+ describe "year" do
102
+ it "must accept a nil value" do
103
+ advisory = described_class.new(
104
+ id: 'GHSA-3hhc-qp5v-9p2j',
105
+ prefix: 'GHSA',
106
+ year: nil,
107
+ identifier: '3hhc-qp5v-9p2j',
108
+ )
109
+
110
+ advisory.valid?
111
+ p advisory.errors
112
+ expect(advisory).to be_valid
113
+ end
114
+
115
+ it "must accept a numberic value" do
116
+ advisory = described_class.new(
117
+ id: 'CVE-2022-1234',
118
+ prefix: 'CVE',
119
+ year: 2022,
120
+ identifier: '2022-1234'
121
+ )
122
+
123
+ expect(advisory).to be_valid
124
+ end
125
+ end
126
+
127
+ describe "identifier" do
128
+ it "must require a preifx" do
129
+ advisory = described_class.new(
130
+ id: 'CVE-2022-1234',
131
+ prefix: 'CVE',
132
+ year: 2022
133
+ )
134
+ expect(advisory).to_not be_valid
135
+ expect(advisory.errors[:identifier]).to eq(["can't be blank"])
136
+
137
+ advisory = described_class.new(
138
+ id: 'CVE-2022-1234',
139
+ prefix: 'CVE',
140
+ year: 2022,
141
+ identifier: '2022-1234'
142
+ )
143
+ expect(advisory).to be_valid
144
+ end
145
+ end
146
+ end
147
+
148
+ let(:prefix) { 'CVE' }
149
+ let(:year) { 2022 }
150
+ let(:identifier) { '1234' }
151
+ let(:id) { "#{prefix}-#{year}-#{identifier}" }
152
+
153
+ describe ".lookup" do
154
+ let(:id) { 'CVE-2022-1234' }
155
+
156
+ before do
157
+ described_class.create(
158
+ id: 'CVE-2000-1234',
159
+ prefix: 'CVE',
160
+ year: 2000,
161
+ identifier: '2000-1234'
162
+ )
163
+ described_class.create(
164
+ id: id,
165
+ prefix: prefix,
166
+ year: year,
167
+ identifier: identifier
168
+ )
169
+ described_class.create(
170
+ id: 'CVE-2000-5678',
171
+ prefix: 'CVE',
172
+ year: 2000,
173
+ identifier: '2000-5678'
174
+ )
175
+ end
176
+
177
+ it "must query the #{described_class} with the matching ID" do
178
+ advisory = described_class.lookup(id)
179
+
180
+ expect(advisory).to be_kind_of(described_class)
181
+ expect(advisory.id).to eq(id)
182
+ end
183
+
184
+ after { described_class.destroy_all }
185
+ end
186
+
187
+ describe ".import" do
188
+ let(:id) { 'CVE-2022-1234' }
189
+ let(:parsed_id) { described_class::ID.parse(id) }
190
+
191
+ subject { described_class.import(id) }
192
+
193
+ it "must parse and import the advisory ID and return a new #{described_class}" do
194
+ expect(subject.id).to eq(id)
195
+ expect(subject.prefix).to eq(parsed_id[:prefix])
196
+ expect(subject.year).to eq(parsed_id[:year])
197
+ expect(subject.identifier).to eq(parsed_id[:identifier])
198
+ end
199
+
200
+ after { described_class.destroy_all }
201
+ end
202
+
203
+ subject do
204
+ described_class.new(
205
+ id: id,
206
+ prefix: prefix,
207
+ year: year,
208
+ identifier: identifier
209
+ )
210
+ end
211
+
212
+ describe "#url" do
213
+ context "when #prefix is 'CVE'" do
214
+ let(:prefix) { 'CVE' }
215
+ let(:year) { 2022 }
216
+ let(:identifier) { '1234' }
217
+ let(:id) { "#{prefix}-#{year}-#{identifier}" }
218
+
219
+ subject do
220
+ described_class.new(
221
+ id: id,
222
+ prefix: prefix,
223
+ year: year,
224
+ identifier: identifier
225
+ )
226
+ end
227
+
228
+ it "must return 'https://nvd.nist.gov/vuln/detail/CVE-YYYY-NNNN'" do
229
+ expect(subject.url).to eq("https://nvd.nist.gov/vuln/detail/#{id}")
230
+ end
231
+ end
232
+
233
+ context "when #prefix is 'RHSA'" do
234
+ let(:prefix) { 'RHSA' }
235
+ let(:year) { 2022 }
236
+ let(:identifier) { '6187' }
237
+ let(:id) { "#{prefix}-#{year}:#{identifier}" }
238
+
239
+ subject do
240
+ described_class.new(
241
+ id: id,
242
+ prefix: prefix,
243
+ year: year,
244
+ identifier: identifier
245
+ )
246
+ end
247
+
248
+ it "must return 'https://access.redhat.com/errata/RHSA-YYYY-NNNN'" do
249
+ expect(subject.url).to eq("https://access.redhat.com/errata/#{id}")
250
+ end
251
+ end
252
+
253
+ context "when #prefix is 'GHSA'" do
254
+ let(:prefix) { 'GHSA' }
255
+ let(:identifier) { '3hhc-qp5v-9p2j' }
256
+ let(:id) { "#{prefix}-#{identifier}" }
257
+
258
+ subject do
259
+ described_class.new(
260
+ id: id,
261
+ prefix: prefix,
262
+ identifier: identifier
263
+ )
264
+ end
265
+
266
+ it "must return 'https://github.com/advisories/GHSA-...'" do
267
+ expect(subject.url).to eq("https://github.com/advisories/#{id}")
268
+ end
269
+ end
270
+ end
271
+
272
+ describe "#to_s" do
273
+ it "must return the #id" do
274
+ expect(subject.to_s).to eq(id)
275
+ end
276
+ end
277
+ end
data/spec/arch_spec.rb ADDED
@@ -0,0 +1,228 @@
1
+ require 'spec_helper'
2
+ require 'ronin/db/arch'
3
+
4
+ describe Ronin::DB::Arch do
5
+ it "must use the 'ronin_arches' table" do
6
+ expect(described_class.table_name).to eq('ronin_arches')
7
+ end
8
+
9
+ let(:name) { 'cats' }
10
+ let(:endian) { :little }
11
+ let(:word_size) { 4 }
12
+
13
+ it "must include Ronin::DB::Model" do
14
+ expect(described_class).to include(Ronin::DB::Model)
15
+ end
16
+
17
+ it "must include Ronin::DB::Model::HasUniqueName" do
18
+ expect(described_class).to include(Ronin::DB::Model::HasUniqueName)
19
+ end
20
+
21
+ describe "validations" do
22
+ describe "name" do
23
+ it "must requier a name" do
24
+ arch = described_class.new(endian: endian, word_size: word_size)
25
+ expect(arch).to_not be_valid
26
+ expect(arch.errors[:name]).to eq(
27
+ ["can't be blank"]
28
+ )
29
+
30
+ arch = described_class.new(
31
+ name: name,
32
+ endian: endian,
33
+ word_size: word_size
34
+ )
35
+ expect(arch).to be_valid
36
+ end
37
+ end
38
+
39
+ describe "endian" do
40
+ subject do
41
+ described_class.new(
42
+ name: name,
43
+ word_size: word_size
44
+ )
45
+ end
46
+
47
+ it "must accept :little" do
48
+ arch = described_class.new(
49
+ name: name,
50
+ endian: :little,
51
+ word_size: word_size
52
+ )
53
+ expect(arch).to be_valid
54
+ end
55
+
56
+ it "must accept :big" do
57
+ arch = described_class.new(
58
+ name: name,
59
+ endian: :big,
60
+ word_size: word_size
61
+ )
62
+ expect(arch).to be_valid
63
+ end
64
+
65
+ context "otherwise" do
66
+ let(:endian) { :foo }
67
+
68
+ it do
69
+ expect {
70
+ described_class.new(
71
+ name: name,
72
+ endian: endian,
73
+ word_size: word_size
74
+ )
75
+ }.to raise_error(ArgumentError,"'#{endian}' is not a valid endian")
76
+ end
77
+ end
78
+ end
79
+
80
+ describe "word_size" do
81
+ it "must require a word_size" do
82
+ arch = described_class.new(name: name, endian: endian)
83
+ expect(arch).to_not be_valid
84
+ expect(arch.errors[:word_size]).to include("can't be blank")
85
+
86
+ arch = described_class.new(
87
+ name: name,
88
+ endian: endian,
89
+ word_size: word_size
90
+ )
91
+ expect(arch).to be_valid
92
+ end
93
+
94
+ it "must accept a word_size of 4" do
95
+ arch = described_class.new(
96
+ name: name,
97
+ endian: endian,
98
+ word_size: 4
99
+ )
100
+ expect(arch).to be_valid
101
+ end
102
+
103
+ it "must accept a word_size of 8" do
104
+ arch = described_class.new(
105
+ name: name,
106
+ endian: endian,
107
+ word_size: 8
108
+ )
109
+ expect(arch).to be_valid
110
+ end
111
+
112
+ context "otherwise" do
113
+ let(:word_size) { 3 }
114
+
115
+ it do
116
+ arch = described_class.new(
117
+ name: name,
118
+ endian: endian,
119
+ word_size: word_size
120
+ )
121
+ expect(arch).to_not be_valid
122
+ expect(arch.errors[:word_size]).to eq(
123
+ ['is not included in the list']
124
+ )
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ describe ".x86" do
131
+ subject { described_class.x86 }
132
+
133
+ it "should return the x86 architecture" do
134
+ expect(subject).to be_kind_of(described_class)
135
+ expect(subject.name).to eq('x86')
136
+ end
137
+ end
138
+
139
+ describe ".i686" do
140
+ subject { described_class.i686 }
141
+
142
+ it "should return the i686 architecture" do
143
+ expect(subject).to be_kind_of(described_class)
144
+ expect(subject.name).to eq('i686')
145
+ end
146
+ end
147
+
148
+ describe ".x86_64" do
149
+ subject { described_class.x86_64 }
150
+
151
+ it "should return the x86-64 architecture" do
152
+ expect(subject).to be_kind_of(described_class)
153
+ expect(subject.name).to eq('x86-64')
154
+ end
155
+ end
156
+
157
+ describe ".ppc" do
158
+ subject { described_class.ppc }
159
+
160
+ it "should return the PPC architecture" do
161
+ expect(subject).to be_kind_of(described_class)
162
+ expect(subject.name).to eq('PPC')
163
+ end
164
+ end
165
+
166
+ describe ".ppc64" do
167
+ subject { described_class.ppc64 }
168
+
169
+ it "should return the PPC64 architecture" do
170
+ expect(subject).to be_kind_of(described_class)
171
+ expect(subject.name).to eq('PPC64')
172
+ end
173
+ end
174
+
175
+ describe ".mips" do
176
+ subject { described_class.mips }
177
+
178
+ it "should return the MIPS architecture" do
179
+ expect(subject).to be_kind_of(described_class)
180
+ expect(subject.name).to eq('MIPS')
181
+ end
182
+ end
183
+
184
+ describe ".mips_le" do
185
+ subject { described_class.mips_le }
186
+
187
+ it "should return the MIPS (Little-Endian) architecture" do
188
+ expect(subject).to be_kind_of(described_class)
189
+ expect(subject.name).to eq('MIPS (Little-Endian)')
190
+ end
191
+ end
192
+
193
+ describe ".mips_be" do
194
+ subject { described_class.mips_be }
195
+
196
+ it "should return the MIPS (Big-Endian) architecture" do
197
+ expect(subject).to be_kind_of(described_class)
198
+ expect(subject.name).to eq('MIPS (Big-Endian)')
199
+ end
200
+ end
201
+
202
+ describe ".arm" do
203
+ subject { described_class.arm }
204
+
205
+ it "should return the ARM architecture" do
206
+ expect(subject).to be_kind_of(described_class)
207
+ expect(subject.name).to eq('ARM')
208
+ end
209
+ end
210
+
211
+ describe ".arm_le" do
212
+ subject { described_class.arm_le }
213
+
214
+ it "should return the ARM (Little-Endian) architecture" do
215
+ expect(subject).to be_kind_of(described_class)
216
+ expect(subject.name).to eq('ARM (Little-Endian)')
217
+ end
218
+ end
219
+
220
+ describe ".arm_be" do
221
+ subject { described_class.arm_be }
222
+
223
+ it "should return the ARM (Big-Endian) architecture" do
224
+ expect(subject).to be_kind_of(described_class)
225
+ expect(subject.name).to eq('ARM (Big-Endian)')
226
+ end
227
+ end
228
+ end