Skip to content

Add ronin compress command#184

Draft
moozzi wants to merge 3 commits into
ronin-rb:mainfrom
moozzi:add_ronin_compress
Draft

Add ronin compress command#184
moozzi wants to merge 3 commits into
ronin-rb:mainfrom
moozzi:add_ronin_compress

Conversation

@moozzi

@moozzi moozzi commented Jan 3, 2024

Copy link
Copy Markdown
Member

@moozzi

moozzi commented Jan 3, 2024

Copy link
Copy Markdown
Member Author

@postmodern 0.1 version of this PR.
Let me know if at least my idea is correct.
I'm not sure if I understand CommandKit correctly 😄

@postmodern postmodern left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Submitted a bunch of comments and suggestions. There's multiple ways we could implement this command. This is a good start!

#
# [FILE ...] Optional file(s) to compress
#
class Compress < StringMethodsCommand

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we only want to support processing files, then probably want to inherit from FileProcessorCommand, and define a process_input(io) method to compress the input File or from stdin.

If we do want to support accepting arbitrary Strings (ex: ronin compress --string "hello world"), then we should inherit from StringProcessorCommand.

StringMethodsCommand is more meant to be used by commands which simply map options to certain monkey-patch methods that are added to String (ex: ronin decode --base64 -> String#base64_decode).

Comment thread lib/ronin/cli/commands/compress.rb Outdated
option :gzip, short: '-g',
desc: 'gzip compresses the data' do
@format = :gzip
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we're going to support --gzip, we should also support a --zlib option. ronin-support also happens to have a Ronin::Support::Compression::Zlib module we can use.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
#
# The compression format.
#
# @return Symbol

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I tend to explicitly list the possible values @return [:gzip, :tar, :zip].

Comment thread lib/ronin/cli/commands/compress.rb Outdated
Comment thread lib/ronin/cli/commands/compress.rb Outdated
end

def extension
return "gz" if @format == :gzip

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could probably set an @ext instance variable in the option blocks, along with @format. This way we follow the "Tell, don't ask" principle.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
end

def process_file(file)
compression_class.send(@format, "#{file}.#{extension}") do |c|

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe rename @format to @method or @compression_method if we're going to send() it.

Comment thread lib/ronin/cli/commands/compress.rb Outdated

def process_file(file)
compression_class.send(@format, "#{file}.#{extension}") do |c|
c.write File.read(file)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The files being compressed could be very very large. Probably more efficient to open the input file and read chunks of data from it using input.readpartial(4096) and write those to the output stream.

Comment thread lib/ronin/cli/commands/compress.rb
Comment thread lib/ronin/cli/commands/compress.rb Outdated
end

def process_file(file)
compression_class.send(@format, "#{file}.#{extension}") do |c|

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So here's a question, if you gave this command the --zip option and multiple files, would the user expect each file to be compressed into it's own .zip file, or all files to be compressed into one zip file?

@postmodern

postmodern commented Jan 5, 2024

Copy link
Copy Markdown
Member

@AI-Mozi what do you think about adding separate ronin archive and ronin unarchive commands? This might help with the bifurcating logic that uses Ronin::Support::Compression or Ronin::Support::Archive. ronin archive/ronin unarchive would probably inherit from FileProcessorCommand, while ronin compress/ronin uncompress would inherit from StringProcessorCommand that could accept both --string values and FILEs to allow specifying a raw zlib/gzip compressed string or a .gz file.

@moozzi

moozzi commented Jan 5, 2024

Copy link
Copy Markdown
Member Author

It's definitely a good idea!

Comment thread lib/ronin/cli/commands/compress.rb Outdated
Comment on lines +58 to +64
option :name, short: '-n',
value: {
type: String
},
desc: 'compressed file name' do |name|
@compressed_file_name = name
end

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think we should add an option for specifying compressed file name.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
# The path to the file.
#
def process_file(file)
raise "Files can be compressed using gzip only" if @compression_method != :gzip

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should we raise an error, or just print some message and return?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Call print_error and exit(1), or call print_error and return if you think the error can be printed but the command should continue processing arguments.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
Comment on lines +131 to +132
rescue EOFError
rescue IOError => e

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Rubocop does not like empty rescue :/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ideally, there should be no IO errors when reading the file. I suspect file.eof? should make the rescues unnecessary.

Comment thread lib/ronin/cli/commands/compress.rb Outdated

Ronin::Support::Compression.gzip(compressed_file_name) do |gz|
files.each do |file|
File.open(file, 'rb') do |f|

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Avoid single letter block arguments. Rename file to like path or file_path, and f to file or io.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
Comment on lines +131 to +132
rescue EOFError
rescue IOError => e

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ideally, there should be no IO errors when reading the file. I suspect file.eof? should make the rescues unnecessary.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
if files.empty?
super(*files)
else
raise "Files can be compressed using gzip only" if @compression_method != :gzip

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do a print_error and call exit(1) if invalid combinations of options or arguments are given.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
# The compressed string.
#
def process_string(string)
string.send(@compression_method)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I try to avoid calling the monkey-patched methods when possible. Might be better to do Support::Compression.send(@compression_method,string).

Comment thread lib/ronin/cli/commands/compress.rb Outdated
# @return String
#
def compressed_file_name
@compressed_file_name || "ronin_compressed_#{Time.now.to_i}.gz"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd get rid of the timestamp, or maybe add an option for it. I think a ronin compress command should behave exactly like gzip and just append the .gz extension on the given file name.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
# The path to the file.
#
def process_file(file)
raise "Files can be compressed using gzip only" if @compression_method != :gzip

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Call print_error and exit(1), or call print_error and return if you think the error can be printed but the command should continue processing arguments.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
if files.empty?
super(*files)
else
raise "Files can be compressed using gzip only" if @compression_method != :gzip

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we could support compressing files using SupportCompression.zlib_deflate. Just read the entire file using File.read, compress the String, and write it back out using File.binwrite.

Comment thread lib/ronin/cli/commands/compress.rb Outdated
else
raise "Files can be compressed using gzip only" if @compression_method != :gzip

Ronin::Support::Compression.gzip(compressed_file_name) do |gz|

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also why are there two Ronin::Support::Compression.gzip(compressed_file_name) blocks? One in run and another in process_file?

Comment thread lib/ronin/cli/commands/compress.rb Outdated
# File arguments.
#
def run(*files)
if files.empty?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't this be unless files.empty? or if !files.empty??

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I thought about it like if there is file given as an argument, compress it, otherwise go to StringProcessorCommand#run else statement and process @input_values.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ohh.. now that I read it, it works different than i thought 😅

@postmodern postmodern Jan 8, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm maybe it might be easier to just write all files and strings into a common Support::Compression::GZip output stream? Although, we would have to have different logic for the Compression::Zlib code.

Another idea would be to define separate process_file and process_string methods. The process_file method would read the File and write the gziped/zlib'ed output to a .gz or .zlib file. The process_string method would instead gzip/zlib compress a String and write it to stdout; maybe also support a common --output option?

@moozzi

moozzi commented Apr 18, 2024

Copy link
Copy Markdown
Member Author

@postmodern It's been a while. I did some changes. Let me know what do you thing and we can discuss that later :D

@postmodern

Copy link
Copy Markdown
Member

@AI-Mozi I'm not sure about how the compression and uncompress commands should handle multiple files. Whether they should compress/uncompress each file individually, or concat the output into a single output file or print to stdout, etc. Unless we can come up with a useful and logical solution, maybe we should hold off on these commands.

I do however think there should be a single ronin archive command that has --list, --extract, --create options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants