Skip to content

Commit

Permalink
Add an optional worker for the ZoomEye API
Browse files Browse the repository at this point in the history
stuff
  • Loading branch information
AI-Mozi committed Aug 20, 2024
1 parent 9e8f021 commit 401f426
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 0 deletions.
113 changes: 113 additions & 0 deletions lib/ronin/recon/builtin/api/zoom_eye.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# frozen_string_literal: true
#
# ronin-recon - A micro-framework and tool for performing reconnaissance.
#
# Copyright (c) 2023-2024 Hal Brodigan ([email protected])
#
# ronin-recon is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-recon is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-recon. If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/recon/worker'

require 'async/http/internet'

module Ronin
module Recon
module API
#
# A recon worker that queries https://api.zoomeye.hk/domain/search
# and returns subdomain and ip addresses for a given domain
#
class ZoomEye < Worker

register 'zoom_eye'

summary "Queries the Domains https://api.zoomeye.hk API"
description <<~DESC
Queries the Domains https://api.zoomeye.hk API and returns subdomains
and ip addresses of the domain.
DESC

accepts Domain
outputs Domain, IP
intensity :passive
concurrency 1

param :api_key, String, required: true,
default: ENV['ZOOM_EYE_API_KEY'],
desc: 'The API key for ZoomEye'

# The HTTP client for `https://api.zoomeye.hk`.
#
# @return [Async::HTTP::Client]
#
# @api private
#
attr_reader :client

#
# Initialize the `api/zoom_eye` worker.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional keyword arguments.
#
# @api private
#
def initialize(**kwargs)
super(**kwargs)

@client = Async::HTTP::Client.new(
Async::HTTP::Endpoint.for('https', 'api.zoomeye.hk')
)
end

#
# Returns associated domain names and ip addresses
#
# @param [Values::Domain] domain
# The domain value to gather subdomains and ip_addresses for.
#
# @yield [value]
# For each subdomain found through the API, a Domain
# and optionaly IP will be yielded.
#
# @yieldparam [Values::Domain, Values::IP] value
# The domain or ip found.
#
def process(domain)
path = "/domain/search?q=#{domain}&type=1"
response = @client.get(path, { 'API-KEY' => params[:api_key] })
body = begin
JSON.parse(response.read, symbolize_names: true)
ensure
response.close
end

list = body.fetch(:list, [])
list.each do |record|
if (subdomain = record[:name])
yield Domain.new(subdomain)
end

ip_addresses = record.fetch(:ip, [])
ip_addresses.each do |ip_addr|
yield IP.new(ip_addr)
end
end
end

end
end
end
end
75 changes: 75 additions & 0 deletions spec/builtin/api/zoom_eye_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
require 'spec_helper'
require 'ronin/recon/builtin/api/zoom_eye'

require 'webmock/rspec'

describe Ronin::Recon::API::ZoomEye do
subject { described_class.new(params: { api_key: 'my-test-api-key'}) }

describe "#initialize" do
it "must initialize #client for 'https://api.zoomeye.hk'" do
expect(subject.client).to be_kind_of(Async::HTTP::Client)
end
end

describe "#process" do
context "for domain with subdomains and ip_addresses" do
let(:domain) { Ronin::Recon::Values::Domain.new("example.com") }
let(:response_json) do
"{\"status\":200,\"total\":183386,\"list\":[{\"name\":\"api.example.com\",\"ip\":[\"1.1.1.1\"]},{\"name\":\"test.example.com\",\"ip\":[\"2.2.2.2\"]}]}"
end
let(:expected) do
[
Ronin::Recon::Values::Domain.new('api.example.com'),
Ronin::Recon::Values::Domain.new('test.example.com'),
Ronin::Recon::Values::IP.new('1.1.1.1'),
Ronin::Recon::Values::IP.new('2.2.2.2')
]
end

before do
stub_request(:get, "https://api.zoomeye.hk/domain/search?q=#{domain}&type=1")
.with(headers: { "API-KEY" => 'my-test-api-key' })
.to_return(status: 200, body: response_json)
end

it "must yield Values::Domain and Values::IP for each subdomain" do
yielded_values = []

Async do
subject.process(domain) do |subdomain|
yielded_values << subdomain
end
end

expect(yielded_values).to_not be_empty
expect(yielded_values).to match_array(expected)
end
end

context "for domain with no subdomains" do
let(:domain) { Ronin::Recon::Values::Domain.new("invalid.com") }
let(:response_json) do
"{\"status\":200,\"total\":183386,\"list\":[]}"
end

before do
stub_request(:get, "https://api.zoomeye.hk/domain/search?q=#{domain}&type=1")
.with(headers: { "API-KEY" => 'my-test-api-key' })
.to_return(status: 200, body: response_json)
end

it "must not yield anything" do
yielded_values = []

Async do
subject.process(domain) do |subdomain|
yielded_values << subdomain
end
end

expect(yielded_values).to be_empty
end
end
end
end

0 comments on commit 401f426

Please sign in to comment.