lib/ronin/recon/output_formats/dot.rb
# frozen_string_literal: true
#
# ronin-recon - A micro-framework and tool for performing reconnaissance.
#
# Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com)
#
# 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_relative 'graph_format'
require_relative '../values/domain'
require_relative '../values/mailserver'
require_relative '../values/nameserver'
require_relative '../values/host'
require_relative '../values/ip'
require_relative '../values/ip_range'
require_relative '../values/open_port'
require_relative '../values/email_address'
require_relative '../values/cert'
require_relative '../values/url'
require_relative '../values/website'
require_relative '../values/wildcard'
require 'ronin/core/output_formats/output_file'
require 'set'
module Ronin
module Recon
module OutputFormats
#
# Represents a GraphViz DOT (`.dot`) output format.
#
class Dot < Core::OutputFormats::OutputFile
include GraphFormat
#
# Initializes the GraphViz DOT (`.dot`) output format.
#
# @param [IO] io
# The output stream to write to.
#
def initialize(io)
super(io)
@io.puts "digraph {"
end
#
# Returns the descriptive type name for the value object.
#
# @param [Value] value
# The value object.
#
# @return [String]
# The type name for the value object.
#
# @raise [NotImplementedError]
# The given value object was not supported.
#
def value_type(value)
case value
when Values::Domain then "Domain"
when Values::Mailserver then "Mailserver"
when Values::Nameserver then "Nameserver"
when Values::Host then "Host"
when Values::IP then "IP address"
when Values::IPRange then "IP range"
when Values::OpenPort then "Open #{value.protocol.upcase} Port"
when Values::EmailAddress then "Email Address"
when Values::Cert then "SSL/TLS Cert"
when Values::URL then "URL"
when Values::Website then "Website"
when Values::Wildcard then "Wildcard"
else
raise(NotImplementedError,"value class #{value.class} not supported")
end
end
#
# Returns the body text for the value object.
#
# @param [Value] value
# The value object.
#
# @return [String]
# The body text for the value object.
#
def value_text(value)
case value
when Values::URL
"#{value.status} #{value}"
when Values::Cert
value.subject.to_h.map { |k,v| "#{k}: #{v}\n" }.join
else
value.to_s
end
end
#
# Writes a value to the GraphViz DOT output stream as a node
# declaration.
#
# @param [Value] value
# The value object to write.
#
def <<(value)
name = value.to_s
label = "#{value_type(value)}\n#{value_text(value)}"
@io.puts "\t#{name.inspect} [label=#{label.inspect}]"
@io.flush
end
#
# Appends a value and it's parent value to the GraphViz DOT output
# stream.
#
# @param [Value] value
# The value to append.
#
# @param [Value] parent
# The parent value of the given value.
#
# @return [self]
#
def []=(value,parent)
@io.puts "\t#{parent.to_s.inspect} -> #{value.to_s.inspect}"
@io.flush
end
#
# Writes the complete JSON Array of values and closes the IO stream.
#
def close
@io.puts "}"
super
end
end
end
end
end