ruby-amass 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 +27 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +10 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +20 -0
- data/README.md +94 -0
- data/Rakefile +23 -0
- data/gemspec.yml +28 -0
- data/lib/amass/address.rb +64 -0
- data/lib/amass/command.rb +341 -0
- data/lib/amass/hostname.rb +73 -0
- data/lib/amass/output_file.rb +124 -0
- data/lib/amass/parsers/json.rb +80 -0
- data/lib/amass/parsers/txt.rb +37 -0
- data/lib/amass/version.rb +4 -0
- data/lib/amass.rb +2 -0
- data/ruby-amass.gemspec +61 -0
- data/spec/address_spec.rb +35 -0
- data/spec/amass_spec.rb +8 -0
- data/spec/fixtures/enum/amass.json +14 -0
- data/spec/fixtures/enum/amass.txt +24 -0
- data/spec/hostname_spec.rb +93 -0
- data/spec/output_file_spec.rb +111 -0
- data/spec/parsers/json_spec.rb +60 -0
- data/spec/parsers/txt_spec.rb +28 -0
- data/spec/spec_helper.rb +3 -0
- metadata +110 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'amass/address'
|
2
|
+
|
3
|
+
module Amass
|
4
|
+
#
|
5
|
+
# A hostname.
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
#
|
9
|
+
class Hostname
|
10
|
+
|
11
|
+
# The hostname.
|
12
|
+
#
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
# The domain the hostname belongs to.
|
17
|
+
#
|
18
|
+
# @return [String, nil]
|
19
|
+
attr_reader :domain
|
20
|
+
|
21
|
+
# The addresses associated with the hostname.
|
22
|
+
#
|
23
|
+
# @return [Array<Address>]
|
24
|
+
attr_reader :addresses
|
25
|
+
|
26
|
+
# The tag from `amass`.
|
27
|
+
#
|
28
|
+
# @return [String, nil]
|
29
|
+
attr_reader :tag
|
30
|
+
|
31
|
+
# The source(s) that discovered the hostname.
|
32
|
+
#
|
33
|
+
# @return [Array<String>]
|
34
|
+
attr_reader :sources
|
35
|
+
|
36
|
+
#
|
37
|
+
# Initializes the hostname.
|
38
|
+
#
|
39
|
+
# @param [String] name
|
40
|
+
# The hostname.
|
41
|
+
#
|
42
|
+
# @param [String, nil] domain
|
43
|
+
# The domain the hostname belongs to.
|
44
|
+
#
|
45
|
+
# @param [Array<Address>] addresses
|
46
|
+
# The addresses associated with the hostname.
|
47
|
+
#
|
48
|
+
# @param [String, nil] tag
|
49
|
+
# The `amass` tag.
|
50
|
+
#
|
51
|
+
# @param [Array<String>] sources
|
52
|
+
# The source(s) that discovered the hostname.
|
53
|
+
#
|
54
|
+
def initialize(name: , domain: nil, addresses: [], tag: nil, sources: [])
|
55
|
+
@name = name
|
56
|
+
@domain = domain
|
57
|
+
@addresses = addresses
|
58
|
+
@tag = tag
|
59
|
+
@sources = sources
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Converts the hostname to a String.
|
64
|
+
#
|
65
|
+
# @return [String]
|
66
|
+
# The hostname.
|
67
|
+
#
|
68
|
+
def to_s
|
69
|
+
@name
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'amass/parsers/json'
|
2
|
+
require 'amass/parsers/txt'
|
3
|
+
|
4
|
+
module Amass
|
5
|
+
#
|
6
|
+
# Represents either a `.json` or `.txt` output file.
|
7
|
+
#
|
8
|
+
# ## Example
|
9
|
+
#
|
10
|
+
# require 'amass/output_file'
|
11
|
+
#
|
12
|
+
# output_file = Amass::OutputFile.new('/path/to/amass.json')
|
13
|
+
# output_file.each do |hostname|
|
14
|
+
# p hostname
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
#
|
19
|
+
class OutputFile
|
20
|
+
|
21
|
+
# Mapping of formats to parsers.
|
22
|
+
#
|
23
|
+
# @api semipublic
|
24
|
+
PARSERS = {
|
25
|
+
:json => Parsers::JSON,
|
26
|
+
:txt => Parsers::TXT
|
27
|
+
}
|
28
|
+
|
29
|
+
# The path to the output file.
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
attr_reader :path
|
33
|
+
|
34
|
+
# The format of the output file.
|
35
|
+
#
|
36
|
+
# @return [:json, :txt]
|
37
|
+
attr_reader :format
|
38
|
+
|
39
|
+
# The parser for the output file format.
|
40
|
+
#
|
41
|
+
# @return [Parsers::JSON, Parsers::TXT]
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
attr_reader :parser
|
45
|
+
|
46
|
+
#
|
47
|
+
# Initializes the output file.
|
48
|
+
#
|
49
|
+
# @param [String] path
|
50
|
+
# The path to the output file.
|
51
|
+
#
|
52
|
+
# @param [:json, :txt] format
|
53
|
+
# The optional format of the output file. If not given, it will be
|
54
|
+
# inferred by the path's file extension.
|
55
|
+
#
|
56
|
+
def initialize(path, format: self.class.infer_format(path))
|
57
|
+
@path = File.expand_path(path)
|
58
|
+
@format = format
|
59
|
+
|
60
|
+
@parser = PARSERS.fetch(format) do
|
61
|
+
raise(ArgumentError,"unrecognized file type: #{@path.inspect}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Mapping of file extensions to formats
|
66
|
+
#
|
67
|
+
# @api semipublic
|
68
|
+
FILE_FORMATS = {
|
69
|
+
'.json' => :json,
|
70
|
+
'.txt' => :txt
|
71
|
+
}
|
72
|
+
|
73
|
+
#
|
74
|
+
# Infers the format from the output file's extension name.
|
75
|
+
#
|
76
|
+
# @param [String] path
|
77
|
+
# The path to the output file.
|
78
|
+
#
|
79
|
+
# @return [:json, :txt]
|
80
|
+
# The output format inferred from the file's extension name.
|
81
|
+
#
|
82
|
+
# @raise [ArgumentError]
|
83
|
+
# The output format could not be inferred from the file's name.
|
84
|
+
#
|
85
|
+
# @api semipublic
|
86
|
+
#
|
87
|
+
def self.infer_format(path)
|
88
|
+
FILE_FORMATS.fetch(File.extname(path)) do
|
89
|
+
raise(ArgumentError,"could not infer format of #{path}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Parses the contents of the output file.
|
95
|
+
#
|
96
|
+
# @yield [hostname]
|
97
|
+
# The given block will be passed each parsed hostname.
|
98
|
+
#
|
99
|
+
# @yieldparam [Hostname] hostname
|
100
|
+
# A parsed hostname from the output file.
|
101
|
+
#
|
102
|
+
# @return [Enumerator]
|
103
|
+
# If no block is given, an Enumerator object will be returned.
|
104
|
+
#
|
105
|
+
def each(&block)
|
106
|
+
return enum_for(__method__) unless block
|
107
|
+
|
108
|
+
File.open(@path) do |file|
|
109
|
+
@parser.parse(file,&block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Converts the output file to a String.
|
115
|
+
#
|
116
|
+
# @return [String]
|
117
|
+
# The path to the output file.
|
118
|
+
#
|
119
|
+
def to_s
|
120
|
+
@path
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'amass/hostname'
|
2
|
+
require 'amass/address'
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Amass
|
7
|
+
module Parsers
|
8
|
+
#
|
9
|
+
# Parses single-line JSON hashes.
|
10
|
+
#
|
11
|
+
# @api semipublic
|
12
|
+
#
|
13
|
+
module JSON
|
14
|
+
#
|
15
|
+
# Parses a single-line of JSON.
|
16
|
+
#
|
17
|
+
# @param [IO] io
|
18
|
+
# The IO stream to parse.
|
19
|
+
#
|
20
|
+
# @yield [hostname]
|
21
|
+
# The given block will be passed each parsed hostname.
|
22
|
+
#
|
23
|
+
# @yieldparam [Hostname] hostname
|
24
|
+
# The parsed hostname.
|
25
|
+
#
|
26
|
+
# @return [Enumerator]
|
27
|
+
# If no block is given, an Enumerator will be returned.
|
28
|
+
#
|
29
|
+
def self.parse(io)
|
30
|
+
return enum_for(__method__,io) unless block_given?
|
31
|
+
|
32
|
+
io.each_line do |line|
|
33
|
+
line.chomp!
|
34
|
+
json = ::JSON.parse(line)
|
35
|
+
|
36
|
+
yield map_hostname(json)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
#
|
43
|
+
# Maps a JSON Hash to a {Hostname}.
|
44
|
+
#
|
45
|
+
# @param [Hash{Symbol => Object}] json
|
46
|
+
# The parsed JSON Hash.
|
47
|
+
#
|
48
|
+
# @return [Hostname]
|
49
|
+
# The resulting hostname.
|
50
|
+
#
|
51
|
+
def self.map_hostname(json)
|
52
|
+
Hostname.new(
|
53
|
+
name: json['name'],
|
54
|
+
domain: json['domain'],
|
55
|
+
addresses: json['addresses'].map(&method(:map_address)),
|
56
|
+
tag: json['tag'],
|
57
|
+
sources: json['sources']
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Maps a JSON Hash to an {Address}.
|
63
|
+
#
|
64
|
+
# @param [Hash{Symbol => Object}] json
|
65
|
+
# The parsed JSON Hash.
|
66
|
+
#
|
67
|
+
# @return [Address]
|
68
|
+
# The resulting address.
|
69
|
+
#
|
70
|
+
def self.map_address(json)
|
71
|
+
Address.new(
|
72
|
+
ip: json['ip'],
|
73
|
+
cidr: json['cidr'],
|
74
|
+
asn: json['asn'],
|
75
|
+
desc: json['desc']
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'amass/hostname'
|
2
|
+
|
3
|
+
module Amass
|
4
|
+
module Parsers
|
5
|
+
#
|
6
|
+
# Parses single-line hostnames.
|
7
|
+
#
|
8
|
+
# @api semipublic
|
9
|
+
#
|
10
|
+
module TXT
|
11
|
+
#
|
12
|
+
# Parses a single line of plain-text.
|
13
|
+
#
|
14
|
+
# @param [IO] io
|
15
|
+
# The IO stream to parse.
|
16
|
+
#
|
17
|
+
# @yield [hostname]
|
18
|
+
# The given block will be passed each parsed hostname.
|
19
|
+
#
|
20
|
+
# @yieldparam [Hostname] hostname
|
21
|
+
# The parsed hostname.
|
22
|
+
#
|
23
|
+
# @return [Enumerator]
|
24
|
+
# If no block is given, an Enumerator will be returned.
|
25
|
+
#
|
26
|
+
def self.parse(io)
|
27
|
+
return enum_for(__method__,io) unless block_given?
|
28
|
+
|
29
|
+
io.each_line do |line|
|
30
|
+
line.chomp!
|
31
|
+
|
32
|
+
yield Hostname.new(name: line)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/amass.rb
ADDED
data/ruby-amass.gemspec
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
7
|
+
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
12
|
+
|
13
|
+
require 'amass/version'
|
14
|
+
Amass::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
24
|
+
|
25
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
26
|
+
|
27
|
+
gem.files = `git ls-files`.split($/)
|
28
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
29
|
+
|
30
|
+
gem.executables = gemspec.fetch('executables') do
|
31
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
32
|
+
end
|
33
|
+
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
|
34
|
+
|
35
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
36
|
+
gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
|
37
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
38
|
+
|
39
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
40
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
41
|
+
})
|
42
|
+
|
43
|
+
gem.requirements = Array(gemspec['requirements'])
|
44
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
45
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
46
|
+
gem.post_install_message = gemspec['post_install_message']
|
47
|
+
|
48
|
+
split = lambda { |string| string.split(/,\s*/) }
|
49
|
+
|
50
|
+
if gemspec['dependencies']
|
51
|
+
gemspec['dependencies'].each do |name,versions|
|
52
|
+
gem.add_dependency(name,split[versions])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if gemspec['development_dependencies']
|
57
|
+
gemspec['development_dependencies'].each do |name,versions|
|
58
|
+
gem.add_development_dependency(name,split[versions])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'amass/address'
|
3
|
+
|
4
|
+
describe Amass::Address do
|
5
|
+
let(:ip) { "93.184.216.34" }
|
6
|
+
let(:cidr) { "93.184.216.0/24" }
|
7
|
+
let(:asn) { 15133 }
|
8
|
+
let(:desc) { "EDGECAST - MCI Communications Services, Inc. d/b/a Verizon Business" }
|
9
|
+
|
10
|
+
subject { described_class.new(ip: ip, cidr: cidr, asn: asn, desc: desc) }
|
11
|
+
|
12
|
+
describe "#initialize" do
|
13
|
+
it "must set #ip" do
|
14
|
+
expect(subject.ip).to eq(ip)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "must set #cidr" do
|
18
|
+
expect(subject.cidr).to eq(cidr)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "must set #asn" do
|
22
|
+
expect(subject.asn).to eq(asn)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "must set #desc" do
|
26
|
+
expect(subject.desc).to eq(desc)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#to_s" do
|
31
|
+
it "must return the #ip" do
|
32
|
+
expect(subject.to_s).to eq(ip)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/amass_spec.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
{"name":"www.example.com","domain":"example.com","addresses":[{"ip":"93.184.216.34","cidr":"93.184.216.0/24","asn":15133,"desc":"EDGECAST - MCI Communications Services, Inc. d/b/a Verizon Business"},{"ip":"2606:2800:220:1:248:1893:25c8:1946","cidr":"2606:2800:220::/48","asn":15133,"desc":"EDGECAST - MCI Communications Services, Inc. d/b/a Verizon Business"}],"tag":"cert","sources":["CertSpotter"]}
|
2
|
+
{"name":"example.com","domain":"example.com","addresses":[{"ip":"2606:2800:220:1:248:1893:25c8:1946","cidr":"2606:2800:220::/48","asn":15133,"desc":"EDGECAST - MCI Communications Services, Inc. d/b/a Verizon Business"},{"ip":"93.184.216.34","cidr":"93.184.216.0/24","asn":15133,"desc":"EDGECAST - MCI Communications Services, Inc. d/b/a Verizon Business"}],"tag":"cert","sources":["CertSpotter"]}
|
3
|
+
{"name":"waterregion643.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"api","sources":["Sublist3rAPI"]}
|
4
|
+
{"name":"ringneo5.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"api","sources":["Sublist3rAPI"]}
|
5
|
+
{"name":"ratara72.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"api","sources":["Sublist3rAPI"]}
|
6
|
+
{"name":"serafim.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"api","sources":["Sublist3rAPI"]}
|
7
|
+
{"name":"a8boyyy.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"api","sources":["Sublist3rAPI"]}
|
8
|
+
{"name":"bikamzhaz.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"api","sources":["Sublist3rAPI"]}
|
9
|
+
{"name":"wwwakamai.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"alt","sources":["Alterations"]}
|
10
|
+
{"name":"wwwakamaiint.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"alt","sources":["Alterations"]}
|
11
|
+
{"name":"billing-serafim.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"alt","sources":["Alterations"]}
|
12
|
+
{"name":"bikamzhazstaff.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"alt","sources":["Alterations"]}
|
13
|
+
{"name":"bikamzhaz-6.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"alt","sources":["Alterations"]}
|
14
|
+
{"name":"wwwakamaiintorigin.example.com","domain":"example.com","addresses":[{"ip":"54.144.128.85","cidr":"54.144.0.0/14","asn":14618,"desc":"AMAZON-AES - Amazon.com, Inc."}],"tag":"alt","sources":["Alterations"]}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
example.com
|
2
|
+
wbldr2.example.com
|
3
|
+
qwewowgo40.example.com
|
4
|
+
intermarksavills.example.com
|
5
|
+
www.example.com
|
6
|
+
wild1990.example.com
|
7
|
+
artsupergol1.example.com
|
8
|
+
wild1990server.example.com
|
9
|
+
gaev.ilya.s.example.com
|
10
|
+
wbldr22.example.com
|
11
|
+
www-las.example.com
|
12
|
+
work1.rabot.example.com
|
13
|
+
wild1990serverphp.example.com
|
14
|
+
69.48.131.151.static.example.com
|
15
|
+
email-wbldr22.example.com
|
16
|
+
wbldr22ops.example.com
|
17
|
+
wbldr22ids.example.com
|
18
|
+
wbldr22opsbrasil.example.com
|
19
|
+
email-wbldr22stage.example.com
|
20
|
+
email-wbldr22stageaws.example.com
|
21
|
+
wbldr22opsredirector.example.com
|
22
|
+
machinework1.rabot.example.com
|
23
|
+
wild1990serverphp-testing.example.com
|
24
|
+
169.48.131.151.static.example.com
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'amass/hostname'
|
3
|
+
|
4
|
+
describe Amass::Hostname do
|
5
|
+
let(:name) { "www.example.com" }
|
6
|
+
let(:domain) { "example.com" }
|
7
|
+
|
8
|
+
let(:ip1) { "93.184.216.34" }
|
9
|
+
let(:cidr1) { "93.184.216.0/24" }
|
10
|
+
let(:asn1) { 15133 }
|
11
|
+
let(:desc1) { "EDGECAST - MCI Communications Services, Inc. d/b/a Verizon Business" }
|
12
|
+
|
13
|
+
let(:ip2) { "2606:2800:220:1:248:1893:25c8:1946" }
|
14
|
+
let(:cidr2) { "2606:2800:220::/48" }
|
15
|
+
let(:asn2) { 15133 }
|
16
|
+
let(:desc2) { "EDGECAST - MCI Communications Services, Inc. d/b/a Verizon Business" }
|
17
|
+
|
18
|
+
let(:tag) { "cert" }
|
19
|
+
let(:source1) { "CertSpotter" }
|
20
|
+
|
21
|
+
let(:addresses) do
|
22
|
+
[
|
23
|
+
Amass::Address.new(ip: ip1, cidr: cidr1, asn: asn1, desc: desc1),
|
24
|
+
Amass::Address.new(ip: ip2, cidr: cidr2, asn: asn2, desc: desc2)
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:sources) { [source1] }
|
29
|
+
|
30
|
+
subject do
|
31
|
+
described_class.new(
|
32
|
+
name: name,
|
33
|
+
domain: domain,
|
34
|
+
addresses: addresses,
|
35
|
+
tag: tag,
|
36
|
+
sources: sources
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#intialize" do
|
41
|
+
context "when only the name: keyword is given" do
|
42
|
+
subject { described_class.new(name: name) }
|
43
|
+
|
44
|
+
it "must set #name" do
|
45
|
+
expect(subject.name).to eq(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "must initialize the Hostname's #domain to nil" do
|
49
|
+
expect(subject.domain).to be(nil)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "must initialize the Hostname's #addresses to []" do
|
53
|
+
expect(subject.addresses).to eq([])
|
54
|
+
end
|
55
|
+
|
56
|
+
it "must initialize the Hostname's #tag to nil" do
|
57
|
+
expect(subject.tag).to be(nil)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "must initialize the Hostname's #sources to []" do
|
61
|
+
expect(subject.sources).to eq([])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when all keyword arguments are given" do
|
66
|
+
it "must set #name" do
|
67
|
+
expect(subject.name).to eq(name)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "must set #domain" do
|
71
|
+
expect(subject.domain).to be(domain)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "must set #addresses" do
|
75
|
+
expect(subject.addresses).to eq(addresses)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "must set #tag" do
|
79
|
+
expect(subject.tag).to eq(tag)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "must set #sources" do
|
83
|
+
expect(subject.sources).to eq(sources)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#to_s" do
|
89
|
+
it "must return #name" do
|
90
|
+
expect(subject.to_s).to eq(name)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'amass/output_file'
|
3
|
+
|
4
|
+
describe Amass::OutputFile do
|
5
|
+
let(:fixtures_dir) { File.expand_path(File.join(__dir__,'fixtures')) }
|
6
|
+
describe ".infer_format" do
|
7
|
+
subject { described_class.infer_format(path) }
|
8
|
+
|
9
|
+
context "when the path ends in .json" do
|
10
|
+
let(:path) { '/path/to/file.json' }
|
11
|
+
|
12
|
+
it { expect(subject).to eq(:json) }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when the path ends in .txt" do
|
16
|
+
let(:path) { '/path/to/file.txt' }
|
17
|
+
|
18
|
+
it { expect(subject).to eq(:txt) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "PARSERS" do
|
23
|
+
subject { described_class::PARSERS }
|
24
|
+
|
25
|
+
describe ":txt" do
|
26
|
+
it { expect(subject[:txt]).to eq(Amass::Parsers::TXT) }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe ":json" do
|
30
|
+
it { expect(subject[:json]).to eq(Amass::Parsers::JSON) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "FILE_FORMATS" do
|
35
|
+
subject { described_class::FILE_FORMATS }
|
36
|
+
|
37
|
+
describe ".txt" do
|
38
|
+
it { expect(subject['.txt']).to be(:txt) }
|
39
|
+
end
|
40
|
+
|
41
|
+
describe ".json" do
|
42
|
+
it { expect(subject['.json']).to be(:json) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#initialize" do
|
47
|
+
let(:path) { "/path/to/file.json" }
|
48
|
+
|
49
|
+
subject { described_class.new(path) }
|
50
|
+
|
51
|
+
it "must set #path" do
|
52
|
+
expect(subject.path).to eq(path)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "must infer the format from the file's extname" do
|
56
|
+
expect(subject.format).to eq(:json)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "must set #parser based on #format" do
|
60
|
+
expect(subject.parser).to eq(described_class::PARSERS[subject.format])
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when given an format: keyword" do
|
64
|
+
let(:format) { :txt }
|
65
|
+
|
66
|
+
subject { described_class.new(path, format: format) }
|
67
|
+
|
68
|
+
it "must set #format" do
|
69
|
+
expect(subject.format).to be(format)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "must set #parser based on the given format:" do
|
73
|
+
expect(subject.parser).to eq(described_class::PARSERS[format])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
let(:path) { File.join(fixtures_dir,'enum','amass.json') }
|
79
|
+
|
80
|
+
subject { described_class.new(path) }
|
81
|
+
|
82
|
+
describe "#each" do
|
83
|
+
context "when a block is given" do
|
84
|
+
it "must yield each parsed Hostname object" do
|
85
|
+
yielded_hostnames = []
|
86
|
+
|
87
|
+
subject.each do |hostname|
|
88
|
+
yielded_hostnames << hostname
|
89
|
+
end
|
90
|
+
|
91
|
+
expect(yielded_hostnames).to_not be_empty
|
92
|
+
expect(yielded_hostnames).to all(be_kind_of(Amass::Hostname))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when no block is given" do
|
97
|
+
it "must return an Enumerator of the parsed Hostname objects" do
|
98
|
+
hostnames = subject.each.to_a
|
99
|
+
|
100
|
+
expect(hostnames).to_not be_empty
|
101
|
+
expect(hostnames).to all(be_kind_of(Amass::Hostname))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#to_s" do
|
107
|
+
it "must return #path" do
|
108
|
+
expect(subject.to_s).to eq(path)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|