ruby-masscan 0.1.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 +7 -0
- data/.document +3 -0
- data/.editorconfig +11 -0
- data/.github/workflows/ruby.yml +29 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +6 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +139 -0
- data/Rakefile +23 -0
- data/gemspec.yml +28 -0
- data/lib/masscan/banner.rb +11 -0
- data/lib/masscan/output_file.rb +100 -0
- data/lib/masscan/parsers/binary.rb +591 -0
- data/lib/masscan/parsers/json.rb +106 -0
- data/lib/masscan/parsers/list.rb +84 -0
- data/lib/masscan/parsers/plain_text.rb +151 -0
- data/lib/masscan/parsers.rb +3 -0
- data/lib/masscan/program.rb +100 -0
- data/lib/masscan/status.rb +7 -0
- data/lib/masscan/task.rb +179 -0
- data/lib/masscan/version.rb +4 -0
- data/lib/masscan.rb +2 -0
- data/ruby-masscan.gemspec +61 -0
- data/spec/fixtures/masscan.bin +0 -0
- data/spec/fixtures/masscan.json +17 -0
- data/spec/fixtures/masscan.list +10 -0
- data/spec/fixtures/masscan.ndjson +8 -0
- data/spec/fixtures/masscan.xml +17 -0
- data/spec/output_file_spec.rb +135 -0
- data/spec/parsers/binary_spec.rb +224 -0
- data/spec/parsers/json_spec.rb +157 -0
- data/spec/parsers/list_spec.rb +109 -0
- data/spec/parsers/parser_examples.rb +58 -0
- data/spec/parsers/plain_text_spec.rb +116 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/task_spec.rb +121 -0
- metadata +117 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
shared_examples_for "Parser.open" do
|
4
|
+
context "when not given a block" do
|
5
|
+
it "must return the opened file" do
|
6
|
+
file = subject.open(path)
|
7
|
+
|
8
|
+
expect(file).to be_kind_of(File)
|
9
|
+
expect(file.closed?).to be(false)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when given a block" do
|
14
|
+
it "must yield the opened file" do
|
15
|
+
file_class = nil
|
16
|
+
file_opened = nil
|
17
|
+
|
18
|
+
subject.open(path) do |file|
|
19
|
+
file_class = file.class
|
20
|
+
file_opened = !file.closed?
|
21
|
+
end
|
22
|
+
|
23
|
+
expect(file_class).to be(File)
|
24
|
+
expect(file_opened).to be(true)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
shared_examples_for "Parser.parse" do
|
30
|
+
context "when given a block" do
|
31
|
+
it "must yield Masscan::Status and Masscan::Banner objects" do
|
32
|
+
yielded_record_classes = []
|
33
|
+
|
34
|
+
subject.parse(io) do |record|
|
35
|
+
yielded_record_classes << record.class
|
36
|
+
end
|
37
|
+
|
38
|
+
expect(yielded_record_classes.uniq).to eq([
|
39
|
+
Masscan::Status,
|
40
|
+
Masscan::Banner
|
41
|
+
])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when given no block" do
|
46
|
+
let(:expected_records) do
|
47
|
+
[].tap do |yielded_records|
|
48
|
+
subject.parse(subject.open(path)) do |record|
|
49
|
+
yielded_records << record
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "must return an Enumerator" do
|
55
|
+
expect(subject.parse(io).to_a).to eq(expected_records)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'masscan/parsers/plain_text'
|
3
|
+
|
4
|
+
describe Masscan::Parsers::PlainText do
|
5
|
+
module TestPlainText
|
6
|
+
extend Masscan::Parsers::PlainText
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { TestPlainText }
|
10
|
+
|
11
|
+
describe "#parse_status" do
|
12
|
+
context "when given 'open'" do
|
13
|
+
it "must return :open" do
|
14
|
+
expect(subject.parse_status("open")).to be(:open)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when given 'closed'" do
|
19
|
+
it "must return :closed" do
|
20
|
+
expect(subject.parse_status("closed")).to be(:closed)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when given an unknown String" do
|
25
|
+
it "must return the String" do
|
26
|
+
expect(subject.parse_status("foo")).to eq("foo")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#parse_reason" do
|
32
|
+
context "when given one flag" do
|
33
|
+
it "must map it to a keyword and return an Array" do
|
34
|
+
expect(subject.parse_reason("rst")).to eq([:rst])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when given multiple flags" do
|
39
|
+
it "must split them and map them to keyword" do
|
40
|
+
expect(subject.parse_reason("syn-ack")).to eq([:syn, :ack])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when given an unknown String" do
|
45
|
+
it "must return the String" do
|
46
|
+
expect(subject.parse_reason("foo")).to eq(["foo"])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#parse_ip_protocol" do
|
52
|
+
context "when given 'tcp'" do
|
53
|
+
it "must return :tcp" do
|
54
|
+
expect(subject.parse_ip_protocol("tcp")).to be(:tcp)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when given 'udp'" do
|
59
|
+
it "must return :udp" do
|
60
|
+
expect(subject.parse_ip_protocol("udp")).to be(:udp)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when given 'icmp'" do
|
65
|
+
it "must return :icmp" do
|
66
|
+
expect(subject.parse_ip_protocol("icmp")).to be(:icmp)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when given 'sctp'" do
|
71
|
+
it "must return :sctp" do
|
72
|
+
expect(subject.parse_ip_protocol("sctp")).to be(:sctp)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when given an unknown String" do
|
77
|
+
it "must return the String" do
|
78
|
+
expect(subject.parse_ip_protocol("foo")).to eq("foo")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#parse_app_protocol" do
|
84
|
+
described_class::APP_PROTOCOLS.each do |string,keyword|
|
85
|
+
context "when given '#{string}'" do
|
86
|
+
it "must return #{keyword.inspect}" do
|
87
|
+
expect(subject.parse_app_protocol(string)).to be(keyword)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when given an unknown String" do
|
93
|
+
it "must return the String" do
|
94
|
+
expect(subject.parse_app_protocol("foo")).to eq("foo")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#parse_timestamp" do
|
100
|
+
let(:timestamp) { 1629960470 }
|
101
|
+
let(:time) { Time.at(timestamp) }
|
102
|
+
|
103
|
+
it "must parse a given UNIX timestamp and return a Time object" do
|
104
|
+
expect(subject.parse_timestamp(timestamp)).to eq(time)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#parse_ip" do
|
109
|
+
let(:ip_string) { "1.2.3.4" }
|
110
|
+
let(:ip_addr) { IPAddr.new(ip_string) }
|
111
|
+
|
112
|
+
it "must parse a given IP string and return an IPAddr" do
|
113
|
+
expect(subject.parse_ip(ip_string)).to eq(ip_addr)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/task_spec.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'masscan/task'
|
3
|
+
|
4
|
+
describe Masscan::Task do
|
5
|
+
describe "#ports=" do
|
6
|
+
context "when given an empty Array" do
|
7
|
+
before { subject.ports = [] }
|
8
|
+
|
9
|
+
it "should ignore empty port Arrays" do
|
10
|
+
subject.ports = []
|
11
|
+
|
12
|
+
expect(subject.arguments).to eq([])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when given a String" do
|
17
|
+
let(:ports) { '80,21,25' }
|
18
|
+
|
19
|
+
before { subject.ports = ports }
|
20
|
+
|
21
|
+
it "should emit the String as is" do
|
22
|
+
expect(subject.arguments).to eq(['-p', ports])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when given an Array of Strings" do
|
27
|
+
let(:ports) { %w[80 21 25] }
|
28
|
+
|
29
|
+
before { subject.ports = ports }
|
30
|
+
|
31
|
+
it "should format an Array of String ports" do
|
32
|
+
expect(subject.arguments).to eq(['-p', ports.join(',')])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when given an Array of Integers" do
|
37
|
+
let(:ports) { [80, 21, 25] }
|
38
|
+
|
39
|
+
before { subject.ports = ports }
|
40
|
+
|
41
|
+
it "should format an Array of Integer ports" do
|
42
|
+
expect(subject.arguments).to eq(['-p', ports.join(',')])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "when given an Array containing a Range" do
|
47
|
+
let(:ports) { [80, 21..25] }
|
48
|
+
|
49
|
+
before { subject.ports = ports }
|
50
|
+
|
51
|
+
it "should format the Range" do
|
52
|
+
expect(subject.arguments).to eq([
|
53
|
+
'-p', "#{ports[0]},#{ports[1].begin}-#{ports[1].end}"
|
54
|
+
])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#shards=" do
|
60
|
+
context "when given a Rational value" do
|
61
|
+
let(:rational) { (1/2r) }
|
62
|
+
|
63
|
+
before { subject.shards = rational }
|
64
|
+
|
65
|
+
it "must format it into \#{numerator}/\#{denominator}" do
|
66
|
+
expect(subject.arguments).to eq([
|
67
|
+
"--shards", "#{rational.numerator}/#{rational.denominator}"
|
68
|
+
])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when given an Array value" do
|
73
|
+
let(:array) { [1, 2] }
|
74
|
+
|
75
|
+
before { subject.shards = array }
|
76
|
+
|
77
|
+
it "must format it into \#{array[0]}/\#{array[1]}" do
|
78
|
+
expect(subject.arguments).to eq([
|
79
|
+
"--shards", "#{array[0]}/#{array[1]}"
|
80
|
+
])
|
81
|
+
end
|
82
|
+
|
83
|
+
context "but the Array length is > 2" do
|
84
|
+
let(:array) { [1,2,3] }
|
85
|
+
|
86
|
+
before { subject.shards = array }
|
87
|
+
|
88
|
+
it do
|
89
|
+
expect {
|
90
|
+
subject.arguments
|
91
|
+
}.to raise_error(ArgumentError,"#{described_class}#shards= does not accept more than two values")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "otherwise" do
|
97
|
+
let(:object) { :"1/2" }
|
98
|
+
|
99
|
+
before { subject.shards = object }
|
100
|
+
|
101
|
+
it "must convert the value to a String" do
|
102
|
+
expect(subject.arguments).to eq([
|
103
|
+
"--shards", object.to_s
|
104
|
+
])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#http_field=" do
|
110
|
+
let(:name) { 'X-Foo' }
|
111
|
+
let(:value) { 'bar' }
|
112
|
+
|
113
|
+
before { subject.http_field = [ [name, value] ] }
|
114
|
+
|
115
|
+
it "must join two values together with a ':'" do
|
116
|
+
expect(subject.arguments).to eq([
|
117
|
+
"--http-field", "#{name}:#{value}"
|
118
|
+
])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-masscan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Postmodern
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-08-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rprogram
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
description: A Ruby interface to masscan, an Internet-scale port scanner. Allows automating
|
42
|
+
masscan and parsing masscan Binary, List, JSON, and output file formats.
|
43
|
+
email: postmodern.mod3@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files:
|
47
|
+
- ChangeLog.md
|
48
|
+
- LICENSE.txt
|
49
|
+
- README.md
|
50
|
+
files:
|
51
|
+
- ".document"
|
52
|
+
- ".editorconfig"
|
53
|
+
- ".github/workflows/ruby.yml"
|
54
|
+
- ".gitignore"
|
55
|
+
- ".rspec"
|
56
|
+
- ".yardopts"
|
57
|
+
- ChangeLog.md
|
58
|
+
- Gemfile
|
59
|
+
- LICENSE.txt
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- gemspec.yml
|
63
|
+
- lib/masscan.rb
|
64
|
+
- lib/masscan/banner.rb
|
65
|
+
- lib/masscan/output_file.rb
|
66
|
+
- lib/masscan/parsers.rb
|
67
|
+
- lib/masscan/parsers/binary.rb
|
68
|
+
- lib/masscan/parsers/json.rb
|
69
|
+
- lib/masscan/parsers/list.rb
|
70
|
+
- lib/masscan/parsers/plain_text.rb
|
71
|
+
- lib/masscan/program.rb
|
72
|
+
- lib/masscan/status.rb
|
73
|
+
- lib/masscan/task.rb
|
74
|
+
- lib/masscan/version.rb
|
75
|
+
- ruby-masscan.gemspec
|
76
|
+
- spec/fixtures/masscan.bin
|
77
|
+
- spec/fixtures/masscan.json
|
78
|
+
- spec/fixtures/masscan.list
|
79
|
+
- spec/fixtures/masscan.ndjson
|
80
|
+
- spec/fixtures/masscan.xml
|
81
|
+
- spec/output_file_spec.rb
|
82
|
+
- spec/parsers/binary_spec.rb
|
83
|
+
- spec/parsers/json_spec.rb
|
84
|
+
- spec/parsers/list_spec.rb
|
85
|
+
- spec/parsers/parser_examples.rb
|
86
|
+
- spec/parsers/plain_text_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
- spec/task_spec.rb
|
89
|
+
homepage: https://github.com/postmodern/ruby-masscan#readme
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata:
|
93
|
+
documentation_uri: https://rubydoc.info/gems/ruby-masscan
|
94
|
+
source_code_uri: https://github.com/postmodern/ruby-masscan
|
95
|
+
bug_tracker_uri: https://github.com/postmodern/ruby-masscan/issues
|
96
|
+
changelog_uri: https://github.com/postmodern/ruby-masscan/blob/master/ChangeLog.md
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 2.0.0
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements:
|
112
|
+
- masscan >= 1.0.0
|
113
|
+
rubygems_version: 3.2.22
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: A Ruby interface to masscan.
|
117
|
+
test_files: []
|