Module: SafeImage::PathSafety

Defined in:
lib/safe_image/path_safety.rb

Constant Summary collapse

SAFE_IMAGEMAGICK_PATH =
%r{\A[\w\-\./]+\z}.freeze

Class Method Summary collapse

Class Method Details

.ensure_imagemagick_input_file!(path) ⇒ Object



54
55
56
57
58
59
60
61
# File 'lib/safe_image/path_safety.rb', line 54

def ensure_imagemagick_input_file!(path)
  # Expand to an absolute path first so callers may pass relative paths
  # (matching the rest of the public API), then apply the absolute-path and
  # safe-character checks to the resolved path.
  expanded = Pathname.new(local_path(path)).expand_path.to_s
  ensure_imagemagick_safe!(expanded)
  ensure_regular_file!(Pathname.new(expanded)).to_s
end

.ensure_imagemagick_safe!(path) ⇒ Object

Raises:



44
45
46
47
48
49
50
51
52
# File 'lib/safe_image/path_safety.rb', line 44

def ensure_imagemagick_safe!(path)
  path = local_path(path)
  raise UnsafePathError, "path contains NUL" if path.include?("\0")
  raise UnsafePathError, "path must be absolute" unless path.start_with?("/")
  unless SAFE_IMAGEMAGICK_PATH.match?(path)
    raise UnsafePathError, "path contains characters unsafe for ImageMagick pseudo-filename parsing"
  end
  path
end

.ensure_regular_file!(path) ⇒ Object

Raises:



28
29
30
31
32
# File 'lib/safe_image/path_safety.rb', line 28

def ensure_regular_file!(path)
  path = reject_symlink_components!(path)
  raise UnsafePathError, "not a file: #{path}" unless path.file?
  path
end

.ensure_safe_output_path!(path) ⇒ Object

Raises:



34
35
36
37
38
39
40
41
42
# File 'lib/safe_image/path_safety.rb', line 34

def ensure_safe_output_path!(path)
  path = Pathname.new(local_path(path)).expand_path
  raise UnsafePathError, "path contains NUL" if path.to_s.include?("\0")
  reject_symlink_components!(path.dirname)
  if File.exist?(path.to_s)
    raise UnsafePathError, "output path is a symlink: #{path}" if File.lstat(path.to_s).symlink?
  end
  path
end

.local_path(value) ⇒ Object



11
12
13
14
15
16
17
# File 'lib/safe_image/path_safety.rb', line 11

def local_path(value)
  if value.respond_to?(:path) && value.path
    value.path.to_s
  else
    value.to_s
  end
end


19
20
21
22
23
24
25
26
# File 'lib/safe_image/path_safety.rb', line 19

def reject_symlink_components!(path)
  path = Pathname.new(local_path(path)).expand_path
  path.ascend do |component|
    next unless File.exist?(component.to_s)
    raise UnsafePathError, "symlink paths are not allowed: #{component}" if File.lstat(component.to_s).symlink?
  end
  path
end