ruby-changes:72072
From: Burdette <ko1@a...>
Date: Tue, 7 Jun 2022 00:37:49 +0900 (JST)
Subject: [ruby-changes:72072] b737998d25 (master): [ruby/fileutils] [DOC] Enhanced RDoc for FileUtils (https://github.com/ruby/fileutils/pull/78)
https://git.ruby-lang.org/ruby.git/commit/?id=b737998d25 From b737998d25f1379117fbf11a578462e6ba12037e Mon Sep 17 00:00:00 2001 From: Burdette Lamar <BurdetteLamar@Y...> Date: Mon, 6 Jun 2022 10:37:18 -0500 Subject: [ruby/fileutils] [DOC] Enhanced RDoc for FileUtils (https://github.com/ruby/fileutils/pull/78) Treats: ::rm ::rm_f ::rm_r ::rm_rf ::remove_entry_secure https://github.com/ruby/fileutils/commit/ce2a438d75 --- lib/fileutils.rb | 204 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 142 insertions(+), 62 deletions(-) diff --git a/lib/fileutils.rb b/lib/fileutils.rb index f072b770fc..cbf7f526c6 100644 --- a/lib/fileutils.rb +++ b/lib/fileutils.rb @@ -101,6 +101,57 @@ end https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L101 # files/directories. This equates to passing the <tt>:noop</tt> and # <tt>:verbose</tt> flags to methods in FileUtils. # +# == Avoiding the TOCTTOU Vulnerability +# +# For certain methods that recursively remove entries, +# there is a potential vulnerability called the +# {Time-of-check to time-of-use}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use], +# or TOCTTOU, vulnerability that can exist when: +# +# - An ancestor directory of the entry at the target path is world writable; +# such directories include <tt>/tmp</tt>. +# - The directory tree at the target path includes: +# +# - A world-writable descendant directory. +# - A symbolic link. +# +# To avoid that vulnerability, you can use this method to remove entries: +# +# - FileUtils.remove_entry_secure: removes recursively +# if the target path points to a directory. +# +# Also available are these methods, +# each of which calls \FileUtils.remove_entry_secure: +# +# - FileUtils.rm_r with keyword argument <tt>secure: true</tt>. +# - FileUtils.rm_rf with keyword argument <tt>secure: true</tt>. +# +# Finally, this method for moving entries calls \FileUtils.remove_entry_secure +# if the source and destination are on different devices +# (which means that the "move" is really a copy and remove): +# +# - FileUtils.mv with keyword argument <tt>secure: true</tt>. +# +# \Method \FileUtils.remove_entry_secure removes securely +# by applying a special pre-process: +# +# - If the target path points to a directory, this method uses +# {chown(2)}[https://man7.org/linux/man-pages/man2/chown.2.html] +# and {chmod(2)}[https://man7.org/linux/man-pages/man2/chmod.2.html] +# in removing directories. +# - The owner of the target directory should be either the current process +# or the super user (root). +# +# WARNING: You must ensure that *ALL* parent directories cannot be +# moved by other untrusted users. For example, parent directories +# should not be owned by untrusted users, and should not be world +# writable except when the sticky bit is set. +# +# For details of this security vulnerability, see Perl cases: +# +# - {CVE-2005-0448}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448]. +# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452]. +# module FileUtils VERSION = "1.6.0" @@ -197,7 +248,7 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L248 # # Creates directories at the paths in the given +list+ # (an array of strings or a single string); - # returns +list+. + # returns +list+ if it is an array, <tt>[list]</tt> otherwise. # # With no keyword arguments, creates a directory at each +path+ in +list+ # by calling: <tt>Dir.mkdir(path, mode)</tt>; @@ -239,7 +290,7 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L290 # Creates directories at the paths in the given +list+ # (an array of strings or a single string), # also creating ancestor directories as needed; - # returns +list+. + # returns +list+ if it is an array, <tt>[list]</tt> otherwise. # # With no keyword arguments, creates a directory at each +path+ in +list+, # along with any needed ancestor directories, @@ -311,7 +362,7 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L362 # # Removes directories at the paths in the given +list+ # (an array of strings or a single string); - # returns +list+. + # returns +list+, if it is an array, <tt>[list]</tt> otherwise. # # With no keyword arguments, removes the directory at each +path+ in +list+, # by calling: <tt>Dir.rmdir(path)</tt>; @@ -865,6 +916,10 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L916 # If +src+ and +dest+ are on different devices, # first copies, then removes +src+. # + # May cause a local vulnerability if not called with keyword argument + # <tt>secure: true</tt>; + # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability]. + # # If +src+ is the path to a single file or directory and +dest+ does not exist, # moves +src+ to +dest+: # @@ -898,13 +953,14 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L953 # | `-- src.txt # `-- src1.txt # - # - <tt>force: true</tt> - attempts to force the move; - # if the move includes removing +src+ + # Keyword arguments: + # + # - <tt>force: true</tt> - if the move includes removing +src+ # (that is, if +src+ and +dest+ are on different devices), # ignores raised exceptions of StandardError and its descendants. # - <tt>noop: true</tt> - does not move files. - # - <tt>secure: true</tt> - removes +src+ securely - # by calling FileUtils.remove_entry_secure. + # - <tt>secure: true</tt> - removes +src+ securely; + # see details at FileUtils.remove_entry_secure. # - <tt>verbose: true</tt> - prints an equivalent command: # # FileUtils.mv('src0', 'dest0', noop: true, verbose: true) @@ -949,13 +1005,29 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L1005 alias move mv module_function :move + # Removes entries at the paths in the given +list+ + # (an array of strings or a single string); + # returns +list+, if it is an array, <tt>[list]</tt> otherwise. + # + # With no keyword arguments, removes files at the paths given in +list+: + # + # FileUtils.touch(['src0.txt', 'src0.dat']) + # FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"] + # + # Keyword arguments: + # + # - <tt>force: true</tt> - ignores raised exceptions of StandardError + # and its descendants. + # - <tt>noop: true</tt> - does not remove files; returns +nil+. + # - <tt>verbose: true</tt> - prints an equivalent command: + # + # FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true) + # + # Output: # - # Remove file(s) specified in +list+. This method cannot remove directories. - # All StandardErrors are ignored when the :force option is set. + # rm src0.dat src0.txt # - # FileUtils.rm %w( junk.txt dust.txt ) - # FileUtils.rm Dir.glob('*.so') - # FileUtils.rm 'NotExistFile', force: true # never raises exception + # FileUtils.remove is an alias for FileUtils.rm. # def rm(list, force: nil, noop: nil, verbose: nil) list = fu_list(list) @@ -971,10 +1043,13 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L1043 alias remove rm module_function :remove + # Equivalent to: # - # Equivalent to + # FileUtils.rm(list, force: true, **kwargs) # - # FileUtils.rm(list, force: true) + # See FileUtils.rm for keyword arguments. + # + # FileUtils.safe_unlink is an alias for FileUtils.rm_f. # def rm_f(list, noop: nil, verbose: nil) rm list, force: true, noop: noop, verbose: verbose @@ -984,24 +1059,50 @@ module FileUtils https://github.com/ruby/ruby/blob/trunk/lib/fileutils.rb#L1059 alias safe_unlink rm_f module_function :safe_unlink + # Removes entries at the paths in the given +list+ + # (an array of strings or a single string); + # returns +list+, if it is an array, <tt>[list]</tt> otherwise. # - # remove files +list+[0] +list+[1]... If +list+[n] is a directory, - # removes its all contents recursively. This method ignores - # StandardError when :force option is set. + # May cause a local vulnerability if not called with keyword argument + # <tt>secure: true</tt>; + # see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability]. # - # FileUtils.rm_r Dir.glob('/tmp/*') - # FileUtils.rm_r 'some_dir', force: true + # For each file path, removes the file at that path: # - # WARNING: This method causes local vulnerability - # if one of parent directories or removing directory tree are world - # writable (including /tmp, whose permission is 1777), and the current - # process has strong privilege such as Unix super user (root), and the - # system has symbolic link. For secure removing, read the documentation - # of remove_entry_secure carefully, and set :secure option to true. - # Default is <tt>secure: false</tt>. + # FileUtils.touch(['src0.txt', 'src0.dat']) + # FileUtils.rm_r(['src0.dat', 'src0.txt']) + # File.exist?('src0.txt') # => false + # File.exist?('src0.dat') # => false # - # NOTE: This method calls remove_entry_secure if :secure option is set. - # See also remove_entry_secure. + # For each directory path, recursively removes files and directories: + # + # system('tree --charset=ascii src1') + # src1 + # |-- dir0 + # | |-- src0.txt + # | `-- src1.txt + # `-- dir1 + # |-- src2.txt + # `-- src3.txt + # FileUtils.rm_r('src1') + # File.exist?('src1') # => false + # + # Keyword arguments: + # + # - <tt>force: true</tt> - ignores raised exceptions of StandardError + # and its descendants. + # - <tt>noop: true</tt> - does not remove entries; returns +nil+. + # - <tt>secure: true</tt> - removes +src+ securely; + # see details at FileUtils.remove_entry_secure. + # - <tt>verbose: true</tt> - prints an equivalent command: + # (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/