Module: Otto::Core::FileSafety
- Included in:
- Otto
- Defined in:
- lib/otto/core/file_safety.rb
Overview
File safety module providing secure file access validation and path traversal protection
Instance Method Summary collapse
Instance Method Details
#add_static_path(path) ⇒ Object
49 50 51 52 53 54 55 56 57 58 |
# File 'lib/otto/core/file_safety.rb', line 49 def add_static_path(path) return unless safe_file?(path) base_path = File.split(path).first # Files in the root directory can refer to themselves base_path = path if base_path == '/' File.join(option[:public], base_path) Otto.logger.debug "new static route: #{base_path} (#{path})" if Otto.debug routes_static[:GET][base_path] = base_path end |
#safe_dir?(path) ⇒ Boolean
34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/otto/core/file_safety.rb', line 34 def safe_dir?(path) return false if path.nil? || path.empty? # Clean and expand the path clean_path = path.delete("\0").strip return false if clean_path.empty? = File.(clean_path) # Check directory exists, is readable, and has proper ownership File.directory?() && File.readable?() && (File.owned?() || File.grpowned?()) end |
#safe_file?(path) ⇒ Boolean
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/otto/core/file_safety.rb', line 9 def safe_file?(path) return false if option[:public].nil? || option[:public].empty? return false if path.nil? || path.empty? # Normalize and resolve the public directory path public_dir = File.(option[:public]) return false unless File.directory?(public_dir) # Clean the requested path - remove null bytes and normalize clean_path = path.delete("\0").strip return false if clean_path.empty? # Join and expand to get the full resolved path requested_path = File.(File.join(public_dir, clean_path)) # Ensure the resolved path is within the public directory (prevents path traversal) return false unless requested_path.start_with?(public_dir + File::SEPARATOR) # Check file exists, is readable, and is not a directory File.exist?(requested_path) && File.readable?(requested_path) && !File.directory?(requested_path) && (File.owned?(requested_path) || File.grpowned?(requested_path)) end |