ruby-masscan 0.2.3 → 0.3.0
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 +4 -4
- data/ChangeLog.md +10 -0
- data/lib/masscan/command.rb +385 -20
- data/lib/masscan/output_file.rb +2 -0
- data/lib/masscan/version.rb +1 -1
- data/spec/command_spec.rb +561 -1
- data/spec/output_file_spec.rb +2 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c7131e7fffe842982596ea49bdc4caafdcab24c9a2b1bd1dcc0b1372ca710ef
|
4
|
+
data.tar.gz: 50a2a06aca667ba92b82366f904420f6d42e30d05259ebab46a372ca7b4f4618
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7c91b8b36dcd52defb23ee391cc4f3f6a04fe47cb9d5495f8a737f10210e32b1c5929cbd6da138f617b3f0eb27727285eaec1dcd05cb245d7c95f3cbd97589d
|
7
|
+
data.tar.gz: a714178ff916f8c469df962a3cf6aa8905c537de2f9c39d0a71fda4487ba820f6862357a1c0028e93966e8c6276e5c796b07399eee0dd87d262d33aabc589e1a
|
data/ChangeLog.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
### 0.3.0 / 2024-06-23
|
2
|
+
|
3
|
+
* Include `Enumerable` into {Masscan::OutputFile}.
|
4
|
+
* Improvements to {Masscan::Command}:
|
5
|
+
* Added the `rotate` attribute for the `--rotate` option.
|
6
|
+
* Allow the `ports` attribute to accept a raw String value.
|
7
|
+
* Improve validation of String values passed to `ports`, `adapter_port`,
|
8
|
+
`range`, `shards`, and `ips` attributes.
|
9
|
+
* Correct the type used for the `exclude` attribute.
|
10
|
+
|
1
11
|
### 0.2.3 / 2024-01-27
|
2
12
|
|
3
13
|
* Switched to using `require_relative` to improve load-times.
|
data/lib/masscan/command.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'command_mapper/command'
|
4
4
|
|
5
|
+
require 'ipaddr'
|
6
|
+
|
5
7
|
module Masscan
|
6
8
|
#
|
7
9
|
# Provides an interface for invoking the `masscan` utility.
|
@@ -88,20 +90,98 @@ module Masscan
|
|
88
90
|
#
|
89
91
|
class Command < CommandMapper::Command
|
90
92
|
|
91
|
-
|
93
|
+
#
|
94
|
+
# Represents a port number.
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
#
|
98
|
+
# @since 0.3.0
|
99
|
+
#
|
100
|
+
class Port < CommandMapper::Types::Num
|
101
|
+
|
102
|
+
# Regular expression that validates a port number.
|
103
|
+
PORT_REGEXP = /[1-9][0-9]{0,3}|[1-5][0-9][0-9][0-9][0-9]|6[0-4][0-9][0-9][0-9]|65[0-4][0-9][0-9]|655[0-2][0-9]|6553[0-5]/
|
104
|
+
|
105
|
+
# Regular expression that validates either a port number or service name.
|
106
|
+
REGEXP = /\A#{PORT_REGEXP}\z/
|
107
|
+
|
108
|
+
#
|
109
|
+
# Initializes the port type.
|
110
|
+
#
|
111
|
+
def initialize
|
112
|
+
super(range: 1..65535)
|
113
|
+
end
|
92
114
|
|
115
|
+
#
|
116
|
+
# Validates the given value.
|
117
|
+
#
|
118
|
+
# @param [Object] value
|
119
|
+
# The value to validate.
|
120
|
+
#
|
121
|
+
# @return [true, (false, String)]
|
122
|
+
# Returns true if the value is valid, or `false` and a validation error
|
123
|
+
# message if the value is not compatible.
|
124
|
+
#
|
93
125
|
def validate(value)
|
94
126
|
case value
|
95
|
-
when
|
96
|
-
value
|
97
|
-
|
98
|
-
|
99
|
-
unless valid
|
100
|
-
return [valid, message]
|
101
|
-
end
|
127
|
+
when String
|
128
|
+
unless value =~ REGEXP
|
129
|
+
return [false, "must be a valid port number (#{value.inspect})"]
|
102
130
|
end
|
103
131
|
|
104
132
|
return true
|
133
|
+
else
|
134
|
+
super(value)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Formats the given port number.
|
140
|
+
#
|
141
|
+
# @param [Integer, String] value
|
142
|
+
# The given port number.
|
143
|
+
#
|
144
|
+
# @return [String]
|
145
|
+
# The formatted port number.
|
146
|
+
#
|
147
|
+
def format(value)
|
148
|
+
case value
|
149
|
+
when String
|
150
|
+
value
|
151
|
+
else
|
152
|
+
super(value)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
#
|
159
|
+
# Represents a port range.
|
160
|
+
#
|
161
|
+
# @api private
|
162
|
+
#
|
163
|
+
# @since 0.3.0
|
164
|
+
#
|
165
|
+
class PortRange < Port
|
166
|
+
|
167
|
+
# Regular expression to validate either a port or a port range.
|
168
|
+
PORT_RANGE_REGEXP = /#{PORT_REGEXP}-#{PORT_REGEXP}|#{PORT_REGEXP}/
|
169
|
+
|
170
|
+
# Regular expression to validate either a port or a port range.
|
171
|
+
REGEXP = /\A#{PORT_RANGE_REGEXP}\z/
|
172
|
+
|
173
|
+
#
|
174
|
+
# Validates the given port or port range value.
|
175
|
+
#
|
176
|
+
# @param [Object] value
|
177
|
+
# The port or port range value to validate.
|
178
|
+
#
|
179
|
+
# @return [true, (false, String)]
|
180
|
+
# Returns true if the value is valid, or `false` and a validation error
|
181
|
+
# message if the value is not compatible.
|
182
|
+
#
|
183
|
+
def validate(value)
|
184
|
+
case value
|
105
185
|
when Range
|
106
186
|
valid, message = super(value.begin)
|
107
187
|
|
@@ -115,16 +195,29 @@ module Masscan
|
|
115
195
|
return [valid, message]
|
116
196
|
end
|
117
197
|
|
198
|
+
return true
|
199
|
+
when String
|
200
|
+
unless value =~ REGEXP
|
201
|
+
return [false, "must be a valid port range or port number (#{value.inspect})"]
|
202
|
+
end
|
203
|
+
|
118
204
|
return true
|
119
205
|
else
|
120
206
|
super(value)
|
121
207
|
end
|
122
208
|
end
|
123
209
|
|
210
|
+
#
|
211
|
+
# Formats the given port or port range value.
|
212
|
+
#
|
213
|
+
# @param [Range, Integer, String] value
|
214
|
+
# The port or port range value to format.
|
215
|
+
#
|
216
|
+
# @return [String]
|
217
|
+
# The formatted port or port range.
|
218
|
+
#
|
124
219
|
def format(value)
|
125
220
|
case value
|
126
|
-
when Array
|
127
|
-
value.map(&method(:format)).join(',')
|
128
221
|
when Range
|
129
222
|
"#{value.begin}-#{value.end}"
|
130
223
|
else
|
@@ -134,21 +227,135 @@ module Masscan
|
|
134
227
|
|
135
228
|
end
|
136
229
|
|
230
|
+
#
|
231
|
+
# Represents the type for the `-p,--ports` option.
|
232
|
+
#
|
233
|
+
# @api private
|
234
|
+
#
|
235
|
+
class PortList < CommandMapper::Types::List
|
236
|
+
|
237
|
+
# Regular expression for validating a port or port range.
|
238
|
+
PORT_RANGE_REGEXP = PortRange::PORT_RANGE_REGEXP
|
239
|
+
|
240
|
+
# Regular expression that validates port list String values.
|
241
|
+
REGEXP = /\A(?:(?:U:)?#{PORT_RANGE_REGEXP})(?:,(?:U:)?#{PORT_RANGE_REGEXP})*\z/
|
242
|
+
|
243
|
+
#
|
244
|
+
# Initializes the port list type.
|
245
|
+
#
|
246
|
+
def initialize
|
247
|
+
super(type: PortRange.new)
|
248
|
+
end
|
249
|
+
|
250
|
+
#
|
251
|
+
# Validates a given value.
|
252
|
+
#
|
253
|
+
# @param [Array, Range, String, Object] value
|
254
|
+
# The port list value.
|
255
|
+
#
|
256
|
+
# @return [true, (false, String)]
|
257
|
+
# Returns true if the value is valid, or `false` and a validation error
|
258
|
+
# message if the value is not compatible.
|
259
|
+
#
|
260
|
+
def validate(value)
|
261
|
+
case value
|
262
|
+
when Range
|
263
|
+
@type.validate(value)
|
264
|
+
when String
|
265
|
+
unless value =~ REGEXP
|
266
|
+
return [false, "not a valid port list (#{value.inspect})"]
|
267
|
+
end
|
268
|
+
|
269
|
+
return true
|
270
|
+
else
|
271
|
+
super(value)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
#
|
276
|
+
# Formats a port list value into a String.
|
277
|
+
#
|
278
|
+
# @param [Array<String, Integer, Range>, Range<Integer,Integer>, String, #to_s] value
|
279
|
+
# The port list value to format.
|
280
|
+
#
|
281
|
+
# @return [String]
|
282
|
+
# The formatted port list string.
|
283
|
+
#
|
284
|
+
def format(value)
|
285
|
+
case value
|
286
|
+
when Range
|
287
|
+
# format an individual port range
|
288
|
+
@type.format(value)
|
289
|
+
when String
|
290
|
+
# pass strings directly through
|
291
|
+
value
|
292
|
+
else
|
293
|
+
super(value)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# Represents the type for the `--shards` option.
|
301
|
+
#
|
302
|
+
# @api private
|
303
|
+
#
|
137
304
|
class Shards < CommandMapper::Types::Str
|
138
305
|
|
306
|
+
# Regular expression for validating `--shards` values.
|
307
|
+
REGEXP = %r{\A\d+/\d+\z}
|
308
|
+
|
309
|
+
#
|
310
|
+
# Validates a shards value.
|
311
|
+
#
|
312
|
+
# @param [Array, Rational, String, #to_s] value
|
313
|
+
# The shards value to validate.
|
314
|
+
#
|
315
|
+
# @return [true, (false, String)]
|
316
|
+
# Returns true if the value is valid, or `false` and a validation error
|
317
|
+
# message if the value is not compatible.
|
318
|
+
#
|
139
319
|
def validate(value)
|
140
320
|
case value
|
141
321
|
when Array
|
142
|
-
|
143
|
-
return [false, "
|
322
|
+
unless value.length == 2
|
323
|
+
return [false, "must contain two elements (#{value.inspect})"]
|
324
|
+
end
|
325
|
+
|
326
|
+
unless (value[0].kind_of?(Integer) && value[1].kind_of?(Integer))
|
327
|
+
return [false, "shard values must be Integers (#{value.inspect})"]
|
144
328
|
end
|
145
329
|
|
330
|
+
return true
|
331
|
+
when Rational
|
146
332
|
return true
|
147
333
|
else
|
148
|
-
super(value)
|
334
|
+
valid, message = super(value)
|
335
|
+
|
336
|
+
unless valid
|
337
|
+
return [valid, message]
|
338
|
+
end
|
339
|
+
|
340
|
+
string = value.to_s
|
341
|
+
|
342
|
+
unless string =~ REGEXP
|
343
|
+
return [false, "invalid shards value (#{value.inspect})"]
|
344
|
+
end
|
345
|
+
|
346
|
+
return true
|
149
347
|
end
|
150
348
|
end
|
151
349
|
|
350
|
+
#
|
351
|
+
# Formats a shards value into a String.
|
352
|
+
#
|
353
|
+
# @param [(Integer, Integer), Rational, #to_s] value
|
354
|
+
# The shards value to format.
|
355
|
+
#
|
356
|
+
# @return [String]
|
357
|
+
# The formatted shards value.
|
358
|
+
#
|
152
359
|
def format(value)
|
153
360
|
case value
|
154
361
|
when Array
|
@@ -160,8 +367,166 @@ module Masscan
|
|
160
367
|
|
161
368
|
end
|
162
369
|
|
370
|
+
#
|
371
|
+
# Represents the type for the `--rotate` option.
|
372
|
+
#
|
373
|
+
# @api private
|
374
|
+
#
|
375
|
+
# @since 0.3.0
|
376
|
+
#
|
377
|
+
class RotateTime < CommandMapper::Types::Str
|
378
|
+
|
379
|
+
# Regular expression to validate the `--rotate` time value.
|
380
|
+
REGEXP = /\A(?:\d+|hourly|\d+hours|\d+min)\z/
|
381
|
+
|
382
|
+
#
|
383
|
+
# Validates a `--rotate` time value.
|
384
|
+
#
|
385
|
+
# @param [Integer, String, #to_s] value
|
386
|
+
# The time value to validate.
|
387
|
+
#
|
388
|
+
# @return [true, (false, String)]
|
389
|
+
# Returns true if the value is valid, or `false` and a validation error
|
390
|
+
# message if the value is not compatible.
|
391
|
+
#
|
392
|
+
def validate(value)
|
393
|
+
case value
|
394
|
+
when Integer
|
395
|
+
return true
|
396
|
+
else
|
397
|
+
valid, message = super(value)
|
398
|
+
|
399
|
+
unless valid
|
400
|
+
return [valid, message]
|
401
|
+
end
|
402
|
+
|
403
|
+
string = value.to_s
|
404
|
+
|
405
|
+
unless string =~ REGEXP
|
406
|
+
return [false, "invalid rotation time (#{value.inspect})"]
|
407
|
+
end
|
408
|
+
|
409
|
+
return true
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
414
|
+
|
415
|
+
#
|
416
|
+
# Represents the type for the `--adapter-mac` and `--router-mac` options.
|
417
|
+
#
|
418
|
+
# @api private
|
419
|
+
#
|
420
|
+
# @since 0.3.0
|
421
|
+
#
|
422
|
+
class MACAddress < CommandMapper::Types::Str
|
423
|
+
|
424
|
+
# Regular expression to validate a MAC address.
|
425
|
+
REGEXP = /\A[A-Fa-f0-9]{2}(?::[A-Fa-f0-9]{2}){5}\z/
|
426
|
+
|
427
|
+
#
|
428
|
+
# Validates a MAC address value.
|
429
|
+
#
|
430
|
+
# @param [String, #to_s] value
|
431
|
+
# The MAC address value to validate.
|
432
|
+
#
|
433
|
+
# @return [true, (false, String)]
|
434
|
+
# Returns true if the value is valid, or `false` and a validation error
|
435
|
+
# message if the value is not compatible.
|
436
|
+
#
|
437
|
+
def validate(value)
|
438
|
+
valid, message = super(value)
|
439
|
+
|
440
|
+
unless valid
|
441
|
+
return [valid, message]
|
442
|
+
end
|
443
|
+
|
444
|
+
string = value.to_s
|
445
|
+
|
446
|
+
unless string =~ REGEXP
|
447
|
+
return [false, "invalid MAC address (#{value.inspect})"]
|
448
|
+
end
|
449
|
+
|
450
|
+
return true
|
451
|
+
end
|
452
|
+
|
453
|
+
end
|
454
|
+
|
455
|
+
#
|
456
|
+
# Represents the type for the `--range` option and `ips` argument(s).
|
457
|
+
#
|
458
|
+
# @api private
|
459
|
+
#
|
460
|
+
# @since 0.3.0
|
461
|
+
#
|
462
|
+
class Target < CommandMapper::Types::Str
|
463
|
+
|
464
|
+
# Regular expression for validating decimal octets (0-255).
|
465
|
+
DECIMAL_OCTET_REGEXP = /(?<=[^\d]|^)(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])(?=[^\d]|$)/
|
466
|
+
|
467
|
+
# Regular expression for validating IPv4 addresses or CIDR ranges.
|
468
|
+
IPV4_REGEXP = %r{#{DECIMAL_OCTET_REGEXP}(?:\.#{DECIMAL_OCTET_REGEXP}){3}(?:/\d{1,2})?}
|
469
|
+
|
470
|
+
# Regular expression for validating IPv6 addresses or CIDR ranges.
|
471
|
+
IPV6_REGEXP = %r{
|
472
|
+
(?:[0-9a-f]{1,4}:){6}#{IPV4_REGEXP}|
|
473
|
+
(?:[0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:#{IPV4_REGEXP}|
|
474
|
+
(?:[0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:#{IPV4_REGEXP}|
|
475
|
+
(?:[0-9a-f]{1,4}:){1,1}(?::[0-9a-f]{1,4}){1,4}:#{IPV4_REGEXP}|
|
476
|
+
(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,3}:#{IPV4_REGEXP}|
|
477
|
+
(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,2}:#{IPV4_REGEXP}|
|
478
|
+
(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,1}:#{IPV4_REGEXP}|
|
479
|
+
:(?::[0-9a-f]{1,4}){1,5}:#{IPV4_REGEXP}|
|
480
|
+
(?:(?:[0-9a-f]{1,4}:){1,5}|:):#{IPV4_REGEXP}|
|
481
|
+
(?:[0-9a-f]{1,4}:){1,1}(?::[0-9a-f]{1,4}){1,6}(?:/\d{1,3})?|
|
482
|
+
(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}(?:/\d{1,3})?|
|
483
|
+
(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}(?:/\d{1,3})?|
|
484
|
+
(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}(?:/\d{1,3})?|
|
485
|
+
(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}(?:/\d{1,3})?|
|
486
|
+
(?:[0-9a-f]{1,4}:){1,6}(?::[0-9a-f]{1,4}){1,1}(?:/\d{1,3})?|
|
487
|
+
[0-9a-f]{1,4}(?::[0-9a-f]{1,4}){7}(?:/\d{1,3})?|
|
488
|
+
:(?::[0-9a-f]{1,4}){1,7}(?:/\d{1,3})?|
|
489
|
+
(?:(?:[0-9a-f]{1,4}:){1,7}|:):(?:/\d{1,3})?
|
490
|
+
}x
|
491
|
+
|
492
|
+
# Regular expression for validating masscan target IPs or IP ranges.
|
493
|
+
REGEXP = /\A(?:#{IPV4_REGEXP}|#{IPV6_REGEXP})\z/
|
494
|
+
|
495
|
+
#
|
496
|
+
# Validates a IP or IP range target value.
|
497
|
+
#
|
498
|
+
# @param [IPAddr, String, #to_s] value
|
499
|
+
# The IP or IP range value to validate.
|
500
|
+
#
|
501
|
+
# @return [true, (false, String)]
|
502
|
+
# Returns true if the value is valid, or `false` and a validation error
|
503
|
+
# message if the value is not compatible.
|
504
|
+
#
|
505
|
+
def validate(value)
|
506
|
+
case value
|
507
|
+
when IPAddr
|
508
|
+
return true
|
509
|
+
else
|
510
|
+
valid, message = super(value)
|
511
|
+
|
512
|
+
unless valid
|
513
|
+
return [valid, message]
|
514
|
+
end
|
515
|
+
|
516
|
+
string = value.to_s
|
517
|
+
|
518
|
+
unless string =~ REGEXP
|
519
|
+
return [false, "invalid IP or IP range (#{value.inspect})"]
|
520
|
+
end
|
521
|
+
|
522
|
+
return true
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
end
|
527
|
+
|
163
528
|
command "masscan" do
|
164
|
-
option '--range', name: :range, value:
|
529
|
+
option '--range', name: :range, value: {type: Target.new}, repeats: true
|
165
530
|
option '-p', name: :ports, value: {type: PortList.new}
|
166
531
|
option '--banners', name: :banners
|
167
532
|
option '--rate', name: :rate, value: {type: Num.new}
|
@@ -170,12 +535,12 @@ module Masscan
|
|
170
535
|
option '--echo', name: :echo, value: true
|
171
536
|
option '--adapter', name: :adapter, value: true
|
172
537
|
option '--adapter-ip', name: :adapter_ip, value: true
|
173
|
-
option '--adapter-port', name: :adapter_port, value: {type:
|
174
|
-
option '--adapter-mac', name: :adapter_mac, value:
|
538
|
+
option '--adapter-port', name: :adapter_port, value: {type: PortRange.new}
|
539
|
+
option '--adapter-mac', name: :adapter_mac, value: {type: MACAddress.new}
|
175
540
|
option '--adapter-vlan', name: :adapter_vlan, value: true
|
176
|
-
option '--router-mac', name: :router_mac, value:
|
541
|
+
option '--router-mac', name: :router_mac, value: {type: MACAddress.new}
|
177
542
|
option '--ping', name: :ping
|
178
|
-
option '--exclude', name: :exclude, value:
|
543
|
+
option '--exclude', name: :exclude, value: {type: Target.new}, repeats: true
|
179
544
|
option '--excludefile', name: :exclude_file, value: {type: InputFile.new}, repeats: true
|
180
545
|
option '--includefile', name: :include_file, value: {type: InputFile.new}, repeats: true
|
181
546
|
option '--append-output', name: :append_output
|
@@ -205,7 +570,7 @@ module Masscan
|
|
205
570
|
option '--resume-index', name: :resume_index
|
206
571
|
option '--resume-count', name: :resume_count
|
207
572
|
option '--shards', name: :shards, value: {type: Shards.new}
|
208
|
-
option '--rotate', name: :rotate, value:
|
573
|
+
option '--rotate', name: :rotate, value: {type: RotateTime.new}
|
209
574
|
option '--rotate-offset', name: :rotate_offset, value: true
|
210
575
|
option '--rotate-size', name: :rotate_size, value: true
|
211
576
|
option '--rotate-dir', name: :rotate_dir, value: {type: InputDir.new}
|
@@ -235,7 +600,7 @@ module Masscan
|
|
235
600
|
option '-V', name: :version
|
236
601
|
option '-h', name: :help
|
237
602
|
|
238
|
-
argument :ips, repeats: true
|
603
|
+
argument :ips, repeats: true, type: Target.new
|
239
604
|
end
|
240
605
|
|
241
606
|
end
|
data/lib/masscan/output_file.rb
CHANGED
data/lib/masscan/version.rb
CHANGED
data/spec/command_spec.rb
CHANGED
@@ -2,6 +2,164 @@ require 'spec_helper'
|
|
2
2
|
require 'masscan/command'
|
3
3
|
|
4
4
|
describe Masscan::Command do
|
5
|
+
describe described_class::Port do
|
6
|
+
describe "#validate" do
|
7
|
+
context "when given an Integer" do
|
8
|
+
let(:value) { 443 }
|
9
|
+
|
10
|
+
it "must return true" do
|
11
|
+
expect(subject.validate(value)).to be(true)
|
12
|
+
end
|
13
|
+
|
14
|
+
context "but it's less than 1" do
|
15
|
+
let(:value) { 0 }
|
16
|
+
|
17
|
+
it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
|
18
|
+
expect(subject.validate(value)).to eq(
|
19
|
+
[false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "but it's greater than 65535" do
|
25
|
+
let(:value) { 65536 }
|
26
|
+
|
27
|
+
it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
|
28
|
+
expect(subject.validate(value)).to eq(
|
29
|
+
[false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when given a String" do
|
36
|
+
context "and it's a number" do
|
37
|
+
let(:value) { '443' }
|
38
|
+
|
39
|
+
it "must return true" do
|
40
|
+
expect(subject.validate(value)).to be(true)
|
41
|
+
end
|
42
|
+
|
43
|
+
context "but it's less than 1" do
|
44
|
+
let(:value) { '0' }
|
45
|
+
|
46
|
+
it "must return [false, \"must be a valid port number (...)\"]" do
|
47
|
+
expect(subject.validate(value)).to eq(
|
48
|
+
[false, "must be a valid port number (#{value.inspect})"]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "but it's greater than 65535" do
|
54
|
+
let(:value) { '65536' }
|
55
|
+
|
56
|
+
it "must return [false, \"must be a valid port number (...)\"]" do
|
57
|
+
expect(subject.validate(value)).to eq(
|
58
|
+
[false, "must be a valid port number (#{value.inspect})"]
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "but it contains numbers" do
|
65
|
+
let(:value) { "foo" }
|
66
|
+
|
67
|
+
it "must return [false, \"must be a valid port number (...)\"]" do
|
68
|
+
expect(subject.validate(value)).to eq(
|
69
|
+
[false, "must be a valid port number (#{value.inspect})"]
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe described_class::PortRange do
|
78
|
+
describe "#validate" do
|
79
|
+
context "when given an Integer value" do
|
80
|
+
let(:value) { 443 }
|
81
|
+
|
82
|
+
it "must return true" do
|
83
|
+
expect(subject.validate(value)).to be(true)
|
84
|
+
end
|
85
|
+
|
86
|
+
context "but it's less than 1" do
|
87
|
+
let(:value) { 0 }
|
88
|
+
|
89
|
+
it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
|
90
|
+
expect(subject.validate(value)).to eq(
|
91
|
+
[false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "but it's greater than 65535" do
|
97
|
+
let(:value) { 65536 }
|
98
|
+
|
99
|
+
it "must return [false, \"(...) not within the range of acceptable values (1..65535)\"]" do
|
100
|
+
expect(subject.validate(value)).to eq(
|
101
|
+
[false, "(#{value.inspect}) not within the range of acceptable values (1..65535)"]
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when given a String value" do
|
108
|
+
let(:value) { '443' }
|
109
|
+
|
110
|
+
it "must return true" do
|
111
|
+
expect(subject.validate(value)).to be(true)
|
112
|
+
end
|
113
|
+
|
114
|
+
context "but it's less than 1" do
|
115
|
+
let(:value) { '0' }
|
116
|
+
|
117
|
+
it "must return [false, \"must be a valid port range or port number (...)\"]" do
|
118
|
+
expect(subject.validate(value)).to eq(
|
119
|
+
[false, "must be a valid port range or port number (#{value.inspect})"]
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "but it's greater than 65535" do
|
125
|
+
let(:value) { '65536' }
|
126
|
+
|
127
|
+
it "must return [false, \"must be a valid port range or port number (...)\"]" do
|
128
|
+
expect(subject.validate(value)).to eq(
|
129
|
+
[false, "must be a valid port range or port number (#{value.inspect})"]
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "when given a Range of port numbers" do
|
136
|
+
let(:value) { (1..1024) }
|
137
|
+
|
138
|
+
it "must return true" do
|
139
|
+
expect(subject.validate(value)).to be(true)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#format" do
|
145
|
+
context "when given a single port number" do
|
146
|
+
let(:value) { 443 }
|
147
|
+
|
148
|
+
it "must return the formatted port number" do
|
149
|
+
expect(subject.format(value)).to eq(value.to_s)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "when given a Range of port numbers" do
|
154
|
+
let(:value) { 1..1024 }
|
155
|
+
|
156
|
+
it "must return the formatted port number range (ex: 1-102)" do
|
157
|
+
expect(subject.format(value)).to eq("#{value.begin}-#{value.end}")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
5
163
|
describe described_class::PortList do
|
6
164
|
describe "#validate" do
|
7
165
|
context "when given a single port number" do
|
@@ -35,6 +193,118 @@ describe Masscan::Command do
|
|
35
193
|
end
|
36
194
|
end
|
37
195
|
end
|
196
|
+
|
197
|
+
context "when given a String" do
|
198
|
+
context "and it contains a single number" do
|
199
|
+
let(:value) { "443" }
|
200
|
+
|
201
|
+
it "must return true" do
|
202
|
+
expect(subject.validate(value)).to be(true)
|
203
|
+
end
|
204
|
+
|
205
|
+
context "and it's prefixed by 'U:'" do
|
206
|
+
let(:value) { "U:#{super()}" }
|
207
|
+
|
208
|
+
it "must return true" do
|
209
|
+
expect(subject.validate(value)).to be(true)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
context "and it contains a range of ports" do
|
215
|
+
let(:value) { "1-1024" }
|
216
|
+
|
217
|
+
it "must return true" do
|
218
|
+
expect(subject.validate(value)).to be(true)
|
219
|
+
end
|
220
|
+
|
221
|
+
context "and it's prefixed by 'U:'" do
|
222
|
+
let(:value) { "U:#{super()}" }
|
223
|
+
|
224
|
+
it "must return true" do
|
225
|
+
expect(subject.validate(value)).to be(true)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context "and it contains a comma separated list of port numbers" do
|
231
|
+
let(:value) { "80,443" }
|
232
|
+
|
233
|
+
it "must return true" do
|
234
|
+
expect(subject.validate(value)).to be(true)
|
235
|
+
end
|
236
|
+
|
237
|
+
context "and it's prefixed by 'U:'" do
|
238
|
+
let(:value) { "U:#{super()}" }
|
239
|
+
|
240
|
+
it "must return true" do
|
241
|
+
expect(subject.validate(value)).to be(true)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "and it contains a comma separated list of port ranges" do
|
247
|
+
let(:value) { "1-42,80-8080" }
|
248
|
+
|
249
|
+
it "must return true" do
|
250
|
+
expect(subject.validate(value)).to be(true)
|
251
|
+
end
|
252
|
+
|
253
|
+
context "and it's prefixed by 'U:'" do
|
254
|
+
let(:value) { "U:#{super()}" }
|
255
|
+
|
256
|
+
it "must return true" do
|
257
|
+
expect(subject.validate(value)).to be(true)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "and it contains a comma separated list of port numbers and ranges" do
|
263
|
+
let(:value) { "1-42,50,60,70,80-8080,9000" }
|
264
|
+
|
265
|
+
it "must return true" do
|
266
|
+
expect(subject.validate(value)).to be(true)
|
267
|
+
end
|
268
|
+
|
269
|
+
context "and it's prefixed by 'U:'" do
|
270
|
+
let(:value) { "U:#{super()}" }
|
271
|
+
|
272
|
+
it "must return true" do
|
273
|
+
expect(subject.validate(value)).to be(true)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
context "when it contains non-digits" do
|
279
|
+
let(:value) { "1,2,3,4,a,b,c" }
|
280
|
+
|
281
|
+
it "must return false and a validation error message" do
|
282
|
+
expect(subject.validate(value)).to eq(
|
283
|
+
[false, "not a valid port list (#{value.inspect})"]
|
284
|
+
)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
context "when it contains whitespace" do
|
289
|
+
let(:value) { "1,2, 3,4" }
|
290
|
+
|
291
|
+
it "must return false and a validation error message" do
|
292
|
+
expect(subject.validate(value)).to eq(
|
293
|
+
[false, "not a valid port list (#{value.inspect})"]
|
294
|
+
)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context "when it contains new-lines" do
|
299
|
+
let(:value) { "1,2,\n3,4" }
|
300
|
+
|
301
|
+
it "must return false and a validation error message" do
|
302
|
+
expect(subject.validate(value)).to eq(
|
303
|
+
[false, "not a valid port list (#{value.inspect})"]
|
304
|
+
)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
38
308
|
end
|
39
309
|
|
40
310
|
describe "#format" do
|
@@ -69,6 +339,14 @@ describe Masscan::Command do
|
|
69
339
|
end
|
70
340
|
end
|
71
341
|
end
|
342
|
+
|
343
|
+
context "when given a String" do
|
344
|
+
let(:value) { "22,25,80,443" }
|
345
|
+
|
346
|
+
it "must return the String" do
|
347
|
+
expect(subject.format(value)).to eq(value)
|
348
|
+
end
|
349
|
+
end
|
72
350
|
end
|
73
351
|
end
|
74
352
|
|
@@ -89,12 +367,42 @@ describe Masscan::Command do
|
|
89
367
|
expect(subject.validate(value)).to be(true)
|
90
368
|
end
|
91
369
|
|
370
|
+
context "but the Array length is 1" do
|
371
|
+
let(:value) { [1] }
|
372
|
+
|
373
|
+
it "must return a validation error" do
|
374
|
+
expect(subject.validate(value)).to eq(
|
375
|
+
[false, "must contain two elements (#{value.inspect})"]
|
376
|
+
)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
92
380
|
context "but the Array length is > 2" do
|
93
381
|
let(:value) { [1,2,3] }
|
94
382
|
|
95
383
|
it "must return a validation error" do
|
96
384
|
expect(subject.validate(value)).to eq(
|
97
|
-
[false, "
|
385
|
+
[false, "must contain two elements (#{value.inspect})"]
|
386
|
+
)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context "when given a String" do
|
392
|
+
context "and it matches X/Y" do
|
393
|
+
let(:value) { "1/2" }
|
394
|
+
|
395
|
+
it "must return true" do
|
396
|
+
expect(subject.validate(value)).to be(true)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context "but it does not match X/Y" do
|
401
|
+
let(:value) { "1" }
|
402
|
+
|
403
|
+
it "must return a validation error" do
|
404
|
+
expect(subject.validate(value)).to eq(
|
405
|
+
[false, "invalid shards value (#{value.inspect})"]
|
98
406
|
)
|
99
407
|
end
|
100
408
|
end
|
@@ -139,4 +447,256 @@ describe Masscan::Command do
|
|
139
447
|
end
|
140
448
|
end
|
141
449
|
end
|
450
|
+
|
451
|
+
describe described_class::RotateTime do
|
452
|
+
describe "#validate" do
|
453
|
+
context "when given an Integer" do
|
454
|
+
let(:value) { 42 }
|
455
|
+
|
456
|
+
it "must return true" do
|
457
|
+
expect(subject.validate(value)).to be(true)
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
context "when given a String" do
|
462
|
+
context "but the String is a number" do
|
463
|
+
let(:value) { '42' }
|
464
|
+
|
465
|
+
it "must return true" do
|
466
|
+
expect(subject.validate(value)).to be(true)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
context "but the String is 'hourly'" do
|
471
|
+
let(:value) { 'hourly' }
|
472
|
+
|
473
|
+
it "must return true" do
|
474
|
+
expect(subject.validate(value)).to be(true)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context "but the String is '<N>hours'" do
|
479
|
+
let(:value) { '2hours' }
|
480
|
+
|
481
|
+
it "must return true" do
|
482
|
+
expect(subject.validate(value)).to be(true)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
context "but the String is '<N>min'" do
|
487
|
+
let(:value) { '10min' }
|
488
|
+
|
489
|
+
it "must return true" do
|
490
|
+
expect(subject.validate(value)).to be(true)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
context "but the String is not a number" do
|
495
|
+
let(:value) { "abc" }
|
496
|
+
|
497
|
+
it "must return a validation error" do
|
498
|
+
expect(subject.validate(value)).to eq([false, "invalid rotation time (#{value.inspect})"])
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
context "but the String contains a new-line" do
|
503
|
+
let(:value) { "10\nfoo" }
|
504
|
+
|
505
|
+
it "must return a validation error" do
|
506
|
+
expect(subject.validate(value)).to eq([false, "invalid rotation time (#{value.inspect})"])
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
describe described_class::MACAddress do
|
514
|
+
describe "#validate" do
|
515
|
+
context "when given a String" do
|
516
|
+
context "and it's a valid MAC address" do
|
517
|
+
let(:value) { "00:11:22:33:44:55" }
|
518
|
+
|
519
|
+
it "must return true" do
|
520
|
+
expect(subject.validate(value)).to be(true)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
context "but an octent contains less than two hex digits" do
|
525
|
+
let(:value) { "0:11:22:33:44" }
|
526
|
+
|
527
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
528
|
+
expect(subject.validate(value)).to eq(
|
529
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
530
|
+
)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
context "but an octent contains more than two hex digits" do
|
535
|
+
let(:value) { "000:11:22:33:44" }
|
536
|
+
|
537
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
538
|
+
expect(subject.validate(value)).to eq(
|
539
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
540
|
+
)
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
context "but it's contains less than six octets" do
|
545
|
+
let(:value) { "00:11:22:33:44" }
|
546
|
+
|
547
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
548
|
+
expect(subject.validate(value)).to eq(
|
549
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
550
|
+
)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
context "but it's contains more than six octets" do
|
555
|
+
let(:value) { "00:11:22:33:44:55:66" }
|
556
|
+
|
557
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
558
|
+
expect(subject.validate(value)).to eq(
|
559
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
560
|
+
)
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
context "but it contains non-hex characters" do
|
565
|
+
let(:value) { "000:11:22:33:44:xx" }
|
566
|
+
|
567
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
568
|
+
expect(subject.validate(value)).to eq(
|
569
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
570
|
+
)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
context "but it is not separated by ':' characters" do
|
575
|
+
let(:value) { "00.11.22.33.44.55" }
|
576
|
+
|
577
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
578
|
+
expect(subject.validate(value)).to eq(
|
579
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
580
|
+
)
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
context "but it contains spaces" do
|
585
|
+
let(:value) { "00:11:22: 33:44:55" }
|
586
|
+
|
587
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
588
|
+
expect(subject.validate(value)).to eq(
|
589
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
590
|
+
)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
context "but it contains new-line characters" do
|
595
|
+
let(:value) { "00:11:22:\n33:44:55" }
|
596
|
+
|
597
|
+
it "must return [false, \"invalid MAC address (...)\"]" do
|
598
|
+
expect(subject.validate(value)).to eq(
|
599
|
+
[false, "invalid MAC address (#{value.inspect})"]
|
600
|
+
)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
describe described_class::Target do
|
608
|
+
describe "#validate" do
|
609
|
+
context "when given an IPAddr object" do
|
610
|
+
let(:value) { IPAddr.new('127.0.0.1') }
|
611
|
+
|
612
|
+
it "must return true" do
|
613
|
+
expect(subject.validate(value)).to be(true)
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
context "when given a String" do
|
618
|
+
context "and it's an IPv4 address" do
|
619
|
+
let(:value) { '127.0.0.1' }
|
620
|
+
|
621
|
+
it "must return true" do
|
622
|
+
expect(subject.validate(value)).to be(true)
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
context "and it's an IPv4 range" do
|
627
|
+
let(:value) { '127.0.0.1/24' }
|
628
|
+
|
629
|
+
it "must return true" do
|
630
|
+
expect(subject.validate(value)).to be(true)
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
context "and it's an IPv6 address" do
|
635
|
+
context "but it's in compressed notation" do
|
636
|
+
let(:value) { '::1' }
|
637
|
+
|
638
|
+
it "must return true" do
|
639
|
+
expect(subject.validate(value)).to be(true)
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
context "and it's in full notation" do
|
644
|
+
let(:value) { '2606:2800:220:1:248:1893:25c8:1946' }
|
645
|
+
|
646
|
+
it "must return true" do
|
647
|
+
expect(subject.validate(value)).to be(true)
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
context "and it's an IPv6 range" do
|
653
|
+
context "but it's in compressed notation" do
|
654
|
+
let(:value) { '::1/32' }
|
655
|
+
|
656
|
+
it "must return true" do
|
657
|
+
expect(subject.validate(value)).to be(true)
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
context "and it's in full notation" do
|
662
|
+
let(:value) { '2606:2800:220:1:248:1893:25c8:1946/32' }
|
663
|
+
|
664
|
+
it "must return true" do
|
665
|
+
expect(subject.validate(value)).to be(true)
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
context "but it contains non-hex characters" do
|
671
|
+
let(:value) { '2606:2800:220:1:248:1893:25c8:xxxx/32' }
|
672
|
+
|
673
|
+
it "must return [false, \"invalid IP or IP range (...)\"]" do
|
674
|
+
expect(subject.validate(value)).to eq(
|
675
|
+
[false, "invalid IP or IP range (#{value.inspect})"]
|
676
|
+
)
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
context "but it contains spaces" do
|
681
|
+
let(:value) { '2606:2800:220:1: 248:1893:25c8:1946/32' }
|
682
|
+
|
683
|
+
it "must return [false, \"invalid IP or IP range (...)\"]" do
|
684
|
+
expect(subject.validate(value)).to eq(
|
685
|
+
[false, "invalid IP or IP range (#{value.inspect})"]
|
686
|
+
)
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
context "but it contains new-line characters" do
|
691
|
+
let(:value) { "2606:2800:220:1:\n248:1893:25c8:1946/32" }
|
692
|
+
|
693
|
+
it "must return [false, \"invalid IP or IP range (...)\"]" do
|
694
|
+
expect(subject.validate(value)).to eq(
|
695
|
+
[false, "invalid IP or IP range (#{value.inspect})"]
|
696
|
+
)
|
697
|
+
end
|
698
|
+
end
|
699
|
+
end
|
700
|
+
end
|
701
|
+
end
|
142
702
|
end
|
data/spec/output_file_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-masscan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Postmodern
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: command_mapper
|
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
111
|
version: '0'
|
112
112
|
requirements:
|
113
113
|
- masscan >= 1.0.0
|
114
|
-
rubygems_version: 3.
|
114
|
+
rubygems_version: 3.5.9
|
115
115
|
signing_key:
|
116
116
|
specification_version: 4
|
117
117
|
summary: A Ruby interface to masscan.
|