diff --git a/lib/ronin/recon/output_formats.rb b/lib/ronin/recon/output_formats.rb
index 823910e..c8ef06f 100644
--- a/lib/ronin/recon/output_formats.rb
+++ b/lib/ronin/recon/output_formats.rb
@@ -23,6 +23,8 @@
require_relative 'output_formats/svg'
require_relative 'output_formats/png'
require_relative 'output_formats/pdf'
+require_relative 'output_formats/archive'
+require_relative 'output_formats/git_archive'
require 'ronin/core/output_formats'
@@ -35,15 +37,17 @@ module Recon
module OutputFormats
include Core::OutputFormats
- register :txt, '.txt', TXT
- register :csv, '.csv', CSV
- register :json, '.json', JSON
- register :ndjson, '.ndjson', NDJSON
- register :dir, '', Dir
- register :dot, '.dot', Dot
- register :svg, '.svg', SVG
- register :png, '.png', PNG
- register :pdf, '.pdf', PDF
+ register :txt, '.txt', TXT
+ register :csv, '.csv', CSV
+ register :json, '.json', JSON
+ register :ndjson, '.ndjson', NDJSON
+ register :dir, '', Dir
+ register :dot, '.dot', Dot
+ register :svg, '.svg', SVG
+ register :png, '.png', PNG
+ register :pdf, '.pdf', PDF
+ register :web_archive, '', Archive
+ register :web_git_archive, '', GitArchive
end
end
end
diff --git a/lib/ronin/recon/output_formats/archive.rb b/lib/ronin/recon/output_formats/archive.rb
new file mode 100644
index 0000000..f4cbe88
--- /dev/null
+++ b/lib/ronin/recon/output_formats/archive.rb
@@ -0,0 +1,56 @@
+# 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 .
+#
+
+require 'ronin/web/spider/archive'
+
+module Ronin
+ module Recon
+ module OutputFormats
+ #
+ # Represents a web archive directory.
+ #
+ class Archive
+
+ #
+ # Initializes new archive.
+ #
+ # @param [String] root
+ # The path to the root directory.
+ #
+ def initialize(root)
+ @archive = Ronin::Web::Spider::Archive.new(root)
+ end
+
+ #
+ # Writes a new URL to it's specific file.
+ #
+ # @param [Value] value
+ # The value to write.
+ #
+ def <<(value)
+ if Values::URL === value
+ @archive.write(value.uri, value.body)
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/ronin/recon/output_formats/git_archive.rb b/lib/ronin/recon/output_formats/git_archive.rb
new file mode 100644
index 0000000..02d7857
--- /dev/null
+++ b/lib/ronin/recon/output_formats/git_archive.rb
@@ -0,0 +1,57 @@
+# 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 .
+#
+
+require 'ronin/web/spider/git_archive'
+
+module Ronin
+ module Recon
+ module OutputFormats
+ #
+ # Represents a web archive directory that is backed by Git.
+ #
+ class GitArchive
+
+ #
+ # Initializes new Git repository.
+ #
+ # @param [String] root
+ # The path to the root directory.
+ #
+ def initialize(root)
+ @git_archive = Ronin::Web::Spider::GitArchive.new(root)
+ @git_archive.init unless @git_archive.git?
+ end
+
+ #
+ # Writes a new URL to it's specific file in Git archive.
+ #
+ # @param [Value] value
+ # The value to write.
+ #
+ def <<(value)
+ if Values::URL === value
+ @git_archive.write(value.uri, value.body)
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/spec/output_formats/archive_spec.rb b/spec/output_formats/archive_spec.rb
new file mode 100644
index 0000000..8694786
--- /dev/null
+++ b/spec/output_formats/archive_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+require 'ronin/recon/output_formats/archive'
+require 'ronin/recon/values/url'
+require 'ronin/recon/values/domain'
+require 'tmpdir'
+
+describe Ronin::Recon::OutputFormats::Archive do
+ subject { described_class.new(path) }
+
+ let(:path) { Dir.mktmpdir('ronin-recon-output-archive') }
+
+ describe "#<<" do
+ context "for Values::URL" do
+ let(:value) { Ronin::Recon::Values::URL.new('https://www.example.com/foo.html') }
+ let(:expected_path) { File.join(path,value.path) }
+
+ it "must create a new file with webpage" do
+ subject << value
+
+ expect(File.exist?(expected_path)).to be(true)
+ end
+ end
+
+ context "for other values" do
+ let(:value) { Ronin::Recon::Values::Domain.new('example.com') }
+
+ it "must not create any files" do
+ subject << value
+
+ expect(Dir.glob("#{path}/*")).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/output_formats/git_archive_spec.rb b/spec/output_formats/git_archive_spec.rb
new file mode 100644
index 0000000..1ec3967
--- /dev/null
+++ b/spec/output_formats/git_archive_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+require 'ronin/recon/output_formats/git_archive'
+require 'ronin/recon/values/url'
+require 'ronin/recon/values/domain'
+require 'tmpdir'
+
+describe Ronin::Recon::OutputFormats::GitArchive do
+ subject { described_class.new(path) }
+
+ let(:path) { Dir.mktmpdir('ronin-recon-output-git-archive') }
+
+ describe "#<<" do
+ context "for Values::URL" do
+ let(:value) { Ronin::Recon::Values::URL.new('https://www.example.com/foo.html') }
+ let(:expected_path) { File.join(path,value.path) }
+
+ it "must create a new file with webpage" do
+ subject << value
+
+ expect(File.exist?(expected_path)).to be(true)
+ end
+ end
+
+ context "for other values" do
+ let(:value) { Ronin::Recon::Values::Domain.new('example.com') }
+
+ it "must not create any files" do
+ subject << value
+
+ expect(Dir.glob("#{path}/*")).to be_empty
+ end
+ end
+ end
+end