Skip to content

Commit

Permalink
docs: Improve security notice documentation
Browse files Browse the repository at this point in the history
Added links to several CWEs that are excluded from security reports.
Added warnings to Minitar::Input and Minitar::Output.
  • Loading branch information
halostatue committed Dec 31, 2024
1 parent 9f29d5a commit 37e5366
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 57 deletions.
11 changes: 8 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
86 changes: 48 additions & 38 deletions lib/minitar/input.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -31,17 +45,15 @@ 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|
# # ...
# 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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -96,39 +107,38 @@ 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:
#
# <tt>:dir</tt>:: The +entry+ is a directory.
# <tt>:file_start</tt>:: The +entry+ is a file; the extract of the
# file is just beginning.
# <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract
# of the +entry+.
# <tt>:file_start</tt>:: The +entry+ is a file; the extract of the file is just
# beginning.
# <tt>:file_progress</tt>:: Yielded every 4096 bytes during the extract of the
# +entry+.
# <tt>:file_done</tt>:: Yielded when the +entry+ is completed.
#
# The +stats+ hash contains the following keys:
# <tt>:current</tt>:: The current total number of bytes read in the
# +entry+.
# <tt>:currinc</tt>:: The current number of bytes read in this read
# cycle.
# <tt>:entry</tt>:: The entry being extracted; this is a
# Reader::EntryStream, with all methods thereof.
#
# <tt>:current</tt>:: The current total number of bytes read in the +entry+.
# <tt>:currinc</tt>:: The current number of bytes read in this read cycle.
# <tt>:entry</tt>:: 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,
currinc: 0,
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("/")
Expand Down
44 changes: 28 additions & 16 deletions lib/minitar/output.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 37e5366

Please sign in to comment.