Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Improve security notice documentation #79

Merged
merged 1 commit into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading