From 37e536649799b80c0a1213c9190f928f998bc2a4 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Tue, 31 Dec 2024 13:20:40 -0500 Subject: [PATCH] docs: Improve security notice documentation Added links to several CWEs that are excluded from security reports. Added warnings to Minitar::Input and Minitar::Output. --- SECURITY.md | 11 ++++-- lib/minitar/input.rb | 86 ++++++++++++++++++++++++------------------- lib/minitar/output.rb | 44 ++++++++++++++-------- 3 files changed, 84 insertions(+), 57 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 4594332..8ee7271 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,9 +23,14 @@ age1fc6ngxmn02m62fej5cl30lrvwmxn4k3q2atqu53aatekmnqfwumqj4g93w ## Exclusions -There are several classes of "security" issues which will not be accepted for -Minitar, because any issues arising from these are a matter of the library being -used incorrectly. +There are several classes of potential security issues that will not be accepted +for Minitar There are several classes of "security" issues which will not be +accepted for Minitar, because any issues arising from these are a matter of the +library being used incorrectly. + +- [CWE-073](https://cwe.mitre.org/data/definitions/73.html) +- [CWE-078](https://cwe.mitre.org/data/definitions/78.html) +- [CWE-088](https://cwe.mitre.org/data/definitions/88.html) Minitar does _not_ perform validation or sanitization of path names provided to the convenience classes `Minitar::Output` and `Minitar::Input`, which use diff --git a/lib/minitar/input.rb b/lib/minitar/input.rb index 0fb15ce..6a3f8c2 100644 --- a/lib/minitar/input.rb +++ b/lib/minitar/input.rb @@ -1,16 +1,30 @@ require "minitar/reader" class Minitar - # Wraps a Minitar::Reader with convenience methods and wrapped - # stream management; Input only works with data streams that can be rewound. + # Wraps a Minitar::Reader with convenience methods and wrapped stream management; + # Input only works with data streams that can be rewound. + # + # === Security Notice + # + # Constructing a Minitar::Input will use Kernel.open if the provided input is not + # a readable stream object. Using an untrusted value for input may allow a malicious + # user to execute arbitrary system commands. It is the caller's responsibility to ensure + # that the input value is safe. + # + # * {CWE-073}[https://cwe.mitre.org/data/definitions/73.html] + # * {CWE-078}[https://cwe.mitre.org/data/definitions/78.html] + # * {CWE-088}[https://cwe.mitre.org/data/definitions/88.html] + # + # This notice applies to Minitar::Input.open, Minitar::Input.each_entry, and + # Minitar::Input.new. class Input include Enumerable - # With no associated block, +Input.open+ is a synonym for +Input.new+. If - # the optional code block is given, it will be given the new Input as an - # argument and the Input object will automatically be closed when the block - # terminates (this also closes the wrapped stream object). In this - # instance, +Input.open+ returns the value of the block. + # With no associated block, +Input.open+ is a synonym for +Input.new+. + # + # If a block is given, the new Input will be yielded to the block as an argument and + # the Input object will automatically be closed when the block terminates (this also + # closes the wrapped stream object). The return value will be the value of the block. # # call-seq: # Minitar::Input.open(io) -> input @@ -31,8 +45,7 @@ def self.open(input) end end - # Iterates over each entry in the provided input. This wraps the common - # pattern of: + # Iterates over each entry in the provided input. This wraps the common pattern of: # # Minitar::Input.open(io) do |i| # inp.each do |entry| @@ -40,8 +53,7 @@ def self.open(input) # end # end # - # If a block is not provided, an enumerator will be created with the same - # behaviour. + # If a block is not provided, an enumerator will be created with the same behaviour. # # call-seq: # Minitar::Input.each_entry(io) -> enumerator @@ -56,13 +68,12 @@ def self.each_entry(input) end end - # Creates a new Input object. If +input+ is a stream object that responds - # to #read, then it will simply be wrapped. Otherwise, one will be created - # and opened using Kernel#open. When Input#close is called, the stream - # object wrapped will be closed. + # Creates a new Input object. If +input+ is a stream object that responds to #read, + # then it will simply be wrapped. Otherwise, one will be created and opened using + # Kernel#open. When Input#close is called, the stream object wrapped will be closed. # - # An exception will be raised if the stream that is wrapped does not - # support rewinding. + # An exception will be raised if the stream that is wrapped does not support + # rewinding. # # call-seq: # Minitar::Input.new(io) -> input @@ -81,8 +92,8 @@ def initialize(input) @tar = Reader.new(@io) end - # When provided a block, iterates through each entry in the archive. When - # finished, rewinds to the beginning of the stream. + # When provided a block, iterates through each entry in the archive. When finished, + # rewinds to the beginning of the stream. # # If not provided a block, creates an enumerator with the same semantics. def each_entry @@ -96,25 +107,25 @@ def each_entry end alias_method :each, :each_entry - # Extracts the current +entry+ to +destdir+. If a block is provided, it - # yields an +action+ Symbol, the full name of the file being extracted - # (+name+), and a Hash of statistical information (+stats+). + # Extracts the current +entry+ to +destdir+. If a block is provided, it yields an + # +action+ Symbol, the full name of the file being extracted (+name+), and a Hash of + # statistical information (+stats+). # # The +action+ will be one of: + # # :dir:: The +entry+ is a directory. - # :file_start:: The +entry+ is a file; the extract of the - # file is just beginning. - # :file_progress:: Yielded every 4096 bytes during the extract - # of the +entry+. + # :file_start:: The +entry+ is a file; the extract of the file is just + # beginning. + # :file_progress:: Yielded every 4096 bytes during the extract of the + # +entry+. # :file_done:: Yielded when the +entry+ is completed. # # The +stats+ hash contains the following keys: - # :current:: The current total number of bytes read in the - # +entry+. - # :currinc:: The current number of bytes read in this read - # cycle. - # :entry:: The entry being extracted; this is a - # Reader::EntryStream, with all methods thereof. + # + # :current:: The current total number of bytes read in the +entry+. + # :currinc:: The current number of bytes read in this read cycle. + # :entry:: The entry being extracted; this is a Reader::EntryStream, with + # all methods thereof. def extract_entry(destdir, entry, options = {}, &) # :yields action, name, stats: stats = { current: 0, @@ -122,13 +133,12 @@ def extract_entry(destdir, entry, options = {}, &) # :yields action, name, stats entry: entry } - # extract_entry is not vulnerable to prefix '/' vulnerabilities, but it - # is vulnerable to relative path directories. This code will break this - # vulnerability. For this version, we are breaking relative paths HARD by - # throwing an exception. + # extract_entry is not vulnerable to prefix '/' vulnerabilities, but it is + # vulnerable to relative path directories. This code will break this vulnerability. + # For this version, we are breaking relative paths HARD by throwing an exception. # - # Future versions may permit relative paths as long as the file does not - # leave +destdir+. + # Future versions may permit relative paths as long as the file does not leave + # +destdir+. # # However, squeeze consecutive '/' characters together. full_name = entry.full_name.squeeze("/") diff --git a/lib/minitar/output.rb b/lib/minitar/output.rb index 7f8eff1..fd9c318 100644 --- a/lib/minitar/output.rb +++ b/lib/minitar/output.rb @@ -1,16 +1,29 @@ require "minitar/writer" class Minitar - # Wraps a Minitar::Writer with convenience methods and wrapped - # stream management. If the stream provided to Output does not support random - # access, only Writer#add_file_simple and Writer#mkdir are guaranteed to - # work. + # Wraps a Minitar::Writer with convenience methods and wrapped stream management. If the + # stream provided to Output does not support random access, only Writer#add_file_simple + # and Writer#mkdir are guaranteed to work. + # + # === Security Notice + # + # Constructing a Minitar::Output will use Kernel.open if the provided output is not + # a readable stream object. Using an untrusted value for output may allow a malicious + # user to execute arbitrary system commands. It is the caller's responsibility to ensure + # that the output value is safe. + # + # * {CWE-073}[https://cwe.mitre.org/data/definitions/73.html] + # * {CWE-078}[https://cwe.mitre.org/data/definitions/78.html] + # * {CWE-088}[https://cwe.mitre.org/data/definitions/88.html] + # + # This notice applies to Minitar::Output.open, Minitar::Output.tar, and + # Minitar::Output.new. class Output - # With no associated block, +Output.open+ is a synonym for +Output.new+. If - # the optional code block is given, it will be given the new Output as an - # argument and the Output object will automatically be closed when the - # block terminates (this also closes the wrapped stream object). In this - # instance, +Output.open+ returns the value of the block. + # With no associated block, +Output.open+ is a synonym for +Output.new+. + # + # If a block is given, the new Output will be yielded to the block as an argument and + # the Output object will automatically be closed when the block terminates (this also + # closes the wrapped stream object). The return value will be the value of the block. # # call-seq: # Minitar::Output.open(io) -> output @@ -28,9 +41,9 @@ def self.open(output) end end - # Output.tar is a wrapper for Output.open that yields the owned tar object - # instead of the Output object. If a block is not provided, an enumerator - # will be created with the same behaviour. + # Output.tar is a wrapper for Output.open that yields the owned tar object instead of + # the Output object. If a block is not provided, an enumerator will be created with + # the same behaviour. # # call-seq: # Minitar::Output.tar(io) -> enumerator @@ -43,10 +56,9 @@ def self.tar(output) end end - # Creates a new Output object. If +output+ is a stream object that responds - # to #write, then it will simply be wrapped. Otherwise, one will be created - # and opened using Kernel#open. When Output#close is called, the stream - # object wrapped will be closed. + # Creates a new Output object. If +output+ is a stream object that responds to #write, + # then it will simply be wrapped. Otherwise, one will be created and opened using + # Kernel#open. When Output#close is called, the stream object wrapped will be closed. # # call-seq: # Minitar::Output.new(io) -> output