Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ASN tests and pry. Update .gitignore. #192

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
*.log
*.swp
*~
.gem
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ group :development do
gem 'stackprof', require: false, platform: :mri
gem 'rubocop', require: false, platform: :mri
gem 'rubocop-ronin', require: false, platform: :mri
gem 'pry', require: false
end
337 changes: 337 additions & 0 deletions spec/cli/commands/asn_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,344 @@
require 'spec_helper'
require 'ronin/cli/commands/asn'
require_relative 'man_page_example'
require 'ronin/support/network/asn/list'
require 'ronin/support/cli/printing'
require 'tempfile'

describe Ronin::CLI::Commands::Asn do
include_examples "man_page"

subject { described_class.new }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually the default value of subject.


let(:ip2asn_truncated) do
<<~TSV
223.255.233.0\t223.255.233.255\t140694\tAU\tARAFURA-AS-AP Northern Technology Solutions
223.255.234.0\t223.255.234.255\t0\tNone\tNot routed
223.255.235.0\t223.255.235.255\t140694\tAU\tARAFURA-AS-AP Northern Technology Solutions
223.255.236.0\t223.255.239.255\t56000\tCN\tHERBALIFE-CNDC HERBALIFESHANGHAIMANAGEMENT CO.,LTD.
223.255.240.0\t223.255.243.255\t55649\tHK\tMETRONET-HK Flat C, 16F, Skyline Tower
223.255.244.0\t223.255.247.255\t45117\tIN\tINPL-IN-AP Ishans Network
223.255.248.0\t223.255.251.255\t63199\tUS\tCDSC-AS1
223.255.252.0\t223.255.253.255\t58519\tCN\tCHINATELECOM-CTCLOUD Cloud Computing Corporation
223.255.254.0\t223.255.254.255\t55415\tSG\tMBS-SG 4 Shenton Way
::\t::1\t0\tNone\tNot routed
64:ff9b::1:0:0\t100::ffff:ffff:ffff:ffff\t0\tNone\tNot routed
100:0:0:1::\t2001:0:ffff:ffff:ffff:ffff:ffff:ffff\t0\tNone\tNot routed
2001:1::\t2001:4:111:ffff:ffff:ffff:ffff:ffff\t0\tNone\tNot routed
2001:4:112::\t2001:4:112:ffff:ffff:ffff:ffff:ffff\t112\tUS\t-Reserved AS-
2001:4:113::\t2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff\t0\tNone\tNot routed
2001:200::\t2001:200:5ff:ffff:ffff:ffff:ffff:ffff\t2500\tJP\tWIDE-BB WIDE Project
2001:200:600::\t2001:200:6ff:ffff:ffff:ffff:ffff:ffff\t7667\tJP\tKDDLAB KDDI R&D Laboratories, INC.
2001:200:700::\t2001:200:8ff:ffff:ffff:ffff:ffff:ffff\t2500\tJP\tWIDE-BB WIDE Project
2001:200:900::\t2001:200:9ff:ffff:ffff:ffff:ffff:ffff\t7660\tJP\tAPAN-JP Asia Pacific Advanced Network - Japan
2001:200:a00::\t2001:200:dff:ffff:ffff:ffff:ffff:ffff\t2500\tJP\tWIDE-BB WIDE Project
TSV
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a good idea to move this out into a file in spec/cli/commands/fixtures/.


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also maybe add a top-level before { stub_const("Ronin::Support::Network::ASN::List::PATH",asn_list_file) } at the top instead of setting options[:file] in all of the other specs.

let(:ip2asn_file) do
Tempfile.new.tap do |file|
file.write(ip2asn_truncated)
file.rewind
end
end

after do
ip2asn_file.close
ip2asn_file.unlink
end

describe '#run?' do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra ? got in there.

context "when the file does not exist" do
before do
subject.options[:file] = 'nonexistent_file'
allow(subject).to receive(:download)
allow(subject).to receive(:print_asn_records)
end

it 'calls #download' do
expect(subject).to receive(:download)
subject.run
end
end

context "when options[:update] is true" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention "--update option" in the spec description.

before do
subject.options[:update] = true
allow(subject).to receive(:update)
allow(subject).to receive(:print_asn_records)
end

it 'calls #update' do
expect(subject).to receive(:update)
subject.run
end
end

context "when the file is stale" do
before do
allow(Ronin::Support::Network::ASN::List).to receive(:stale?).and_return(true)
allow(subject).to receive(:update)
allow(subject).to receive(:print_asn_records)
end

it 'calls #update' do
expect(subject).to receive(:update)
subject.run
end
end

context "when given default options and an IP address" do
before do
subject.options[:ip] = '223.255.233.1'
allow(subject).to receive(:query_ip).and_return(true)
allow(subject).to receive(:print_asn_record)
end

it "calls #print_asn_record" do
expect(subject).to receive(:print_asn_record)
subject.run
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should actually test the output of the command here. It might be OK to let it call query_ip, which will perform a DNS query. Any tests that hit the network should also be tagged with :network.

end
end

# FIXME: (cmhobbs) the use of #exit has caused this to be difficult to test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do:

        expect {
          ...
        }.to raise_error(SystemExit) do |error|
          expect(error.status).to eq(1)
        end

pending "when given default options and an invalid IP address"
# context "when given default options and an invalid IP address" do
# before do
# subject.options[:ip] = 'invalid'
# allow(subject).to receive(:query_ip).and_return(false)
# allow(subject).to receive(:exit)
# end

# it "calls print_error" do
# error_message = "could not find a record for the IP: invalid"
# expect(Ronin::Support::CLI::Printing).to receive(:print_error)
# #expect { subject.run }.to output(error_message)
# subject.run
# end
# end

context "when given default options and no IP address" do
before do
allow(subject).to receive(:update)
end

it "calls print_asn_records" do
expect(subject).to receive(:print_asn_records)
subject.run
end
end
end

describe '#stale?' do
let(:file) { 'file' }

before do
subject.options[:file] = file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stub_const().

end

it 'calls Support::Network::ASN::List.stale? with the file' do
expect(Ronin::Support::Network::ASN::List).to receive(:stale?).with(file)
subject.stale?
end
end

describe '#download' do
context "with default parameters" do
let(:url) { 'https://iptoasn.com/data/ip2asn-combined.tsv.gz' }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to use Ronin::Support::Network::ASN::List::URL here.

let(:file) { "#{Dir.home}/.cache/ronin/ronin-support/ip2asn-combined.tsv.gz" }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably better to use Ronin::Support::Network::ASN::List::PATH here.


it 'calls Support::Network::ASN::List.download with default parameters' do
expect(Ronin::Support::Network::ASN::List).to receive(:download).with(
url: url,
path: file
)
subject.download
end
end

context "with optional parameters" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should mention "--url and --file options" in the spec description.

let(:url) { 'http://example.com' }
let(:file) { 'file' }

before do
subject.options[:url] = url
subject.options[:file] = file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stub_const().

end

it 'calls Support::Network::ASN::List.download with optional parameters' do
expect(Ronin::Support::Network::ASN::List).to receive(:download).with(
url: url,
path: file
)
subject.download
end
end
end

describe "#update" do
context "with default parameters" do
let(:url) { 'https://iptoasn.com/data/ip2asn-combined.tsv.gz' }
let(:file) { "#{Dir.home}/.cache/ronin/ronin-support/ip2asn-combined.tsv.gz" }

it 'calls Support::Network::ASN::List.update with default parameters' do
expect(Ronin::Support::Network::ASN::List).to receive(:update).with(
url: url,
path: file
)
subject.update
end
end

context "with optional parameters" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should mention "--url and --file options" in the spec description.

let(:url) { 'http://example.com' }
let(:file) { 'file' }

before do
subject.options[:url] = url
subject.options[:file] = file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stub_const().

end

it 'calls Support::Network::ASN::List.update with optional parameters' do
expect(Ronin::Support::Network::ASN::List).to receive(:update).with(
url: url,
path: file
)
subject.update
end
end
end

describe "#list_file" do
let(:file) { 'file' }

before do
subject.options[:file] = file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stub_const().

end

it 'calls Support::Network::ASN::List.parse' do
expect(Ronin::Support::Network::ASN::List).to receive(:parse).with(file)
subject.list_file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should actually test that .list_file returns an Enumerator and yields Ronin::Support::Network::ASN::Record objects if a block is given. Can probably copy/paste some specs from ronin-support/spec/network/asn/list_spec.rb.

end
end

describe "#query_ip" do
let(:ip) { '127.0.0.1' }

it 'calls Support::Network::ASN::List.query_ip' do
expect(Ronin::Support::Network::ASN).to receive(:query).with(ip)
subject.query_ip(ip)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably test the return value of query_ip. We can allow the spec to query DNS if it's tagged with :network.

end
end

describe "#search_asn_records" do
before do
subject.options[:file] = ip2asn_file.path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use stub_const("Ronin::Support::Network::ASN::List::PATH",...) instead to force it to use the test file.

end

context "given the ipv4 option" do
it "returns the ipv4 records" do
subject.options[:ipv4] = true
records = subject.search_asn_records
expect(records).to be_a(Ronin::Support::Network::ASN::RecordSet)
expect(records.to_a.length).to eq(9)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should test that all of the records are actually .ipv4?.

end
end

context "given the ipv6 option" do
it "returns the ipv6 records" do
subject.options[:ipv6] = true
records = subject.search_asn_records
expect(records).to be_a(Ronin::Support::Network::ASN::RecordSet)
expect(records.to_a.length).to eq(11)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also test that all of the records are actually .ipv6?.

end
end

context "given a country code" do
it "returns the records for the country code" do
subject.options[:country_code] = 'JP'
records = subject.search_asn_records
expect(records).to be_a(Ronin::Support::Network::ASN::RecordSet)
expect(records.to_a.length).to eq(5)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should test that all of the records have .country_code of "JP".

end

context "given an ASN number" do
it "returns the records for the ASN number" do
subject.options[:number] = 140694
records = subject.search_asn_records
expect(records).to be_a(Ronin::Support::Network::ASN::RecordSet)
expect(records.to_a.length).to eq(2)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should test that all of the records have .number of 140694.

end

context "given an ASN name" do
it "returns the records for the ASN name" do
subject.options[:name] = 'WIDE-BB WIDE Project'
records = subject.search_asn_records
expect(records).to be_a(Ronin::Support::Network::ASN::RecordSet)
expect(records.to_a.length).to eq(3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should test that all of the records have the same .name.

end
end
end

describe "#print_asn_records" do
before do
subject.options[:file] = ip2asn_file.path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stub_const().

end

it "calls #print_asn_record for each record" do
records = subject.search_asn_records

expect(subject).to receive(:print_asn_record).exactly(20).times
subject.print_asn_records(records)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should test the actual output of the method.

end
end

describe "#print_asn_record" do
context "with the enum_ips option" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should mention --enum-ips.

before do
subject.options[:enum_ips] = true
subject.options[:file] = ip2asn_file.path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use stub_const().

end

it "prints each IP address in the range" do
record = subject.search_asn_records.first

# NOTE: we're not testing output here due to the large number of IPs
expect(record.range).to receive(:each)
subject.print_asn_record(record)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should test the output of the method. Maybe use a fixtures file that has very small IP ranges? Or programmatically generate the expected output string?

end
end

context "with the verbose option set" do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should mention --verbose.

before do
subject.options[:verbose] = true
subject.options[:file] = ip2asn_file.path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use stub_const().

end

it "prints the record in verbose format" do
output(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing the expect() bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that got chopped out on accident when i was fixing my branch... good catch.

"[ AS140694 ]\n\n" \
"ASN: 140694\n" \
"IP range: 223.255.233.0 - 223.255.233.255\n" \
"Country: AU\n" \
"Name: ARAFURA-AS-AP Northern Technology Solutions\n\n"
).to_stdout
end
end

context "with default options" do
before do
subject.options[:file] = ip2asn_file.path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use stub_const().

end

it "prints the record in default format" do
record = subject.search_asn_records.first

expect { subject.print_asn_record(record) }.to output(
"223.255.233.0 - 223.255.233.255 AS140694 (AU) ARAFURA-AS-AP Northern Technology Solutions\n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use #{$/} instead of \n, in case we ever want to run the specs on Windows; it's unlikely that we'll do this anytime soon, but generally is a good practice to use $/.

).to_stdout
end
end
end
end
Loading