def system_path(target, start, jail = nil, opts = {})
recover = opts.fetch(:recover, true)
unless jail.nil?
unless is_root? jail
raise SecurityError, "Jail is not an absolute path: #{jail}"
end
jail = posixfy jail
end
if target.to_s.empty?
target_segments = []
else
target_segments, target_root, _ = partition_path(target)
end
if target_segments.empty?
if start.to_s.empty?
return jail.nil? ? @working_dir : jail
elsif is_root? start
if jail.nil?
return expand_path start
end
else
return system_path(start, jail, jail)
end
end
if target_root && target_root != DOT
resolved_target = join_path target_segments, target_root
if jail.nil? || resolved_target.start_with?(jail)
return resolved_target
end
end
if start.to_s.empty?
start = jail.nil? ? @working_dir : jail
elsif is_root? start
start = posixfy start
else
start = system_path(start, jail, jail)
end
if jail == start
jail_segments, jail_root, _ = partition_path(jail)
start_segments = jail_segments.dup
elsif !jail.nil?
if !start.start_with?(jail)
raise SecurityError, "#{opts[:target_name] || 'Start path'} #{start} is outside of jail: #{jail} (disallowed in safe mode)"
end
start_segments, start_root, _ = partition_path(start)
jail_segments, jail_root, _ = partition_path(jail)
else
start_segments, start_root, _ = partition_path(start)
jail_root = start_root
end
resolved_segments = start_segments.dup
warned = false
target_segments.each do |segment|
if segment == DOT_DOT
if !jail.nil?
if resolved_segments.length > jail_segments.length
resolved_segments.pop
elsif !recover
raise SecurityError, "#{opts[:target_name] || 'path'} #{target} refers to location outside jail: #{jail} (disallowed in safe mode)"
elsif !warned
puts "asciidoctor: WARNING: #{opts[:target_name] || 'path'} has illegal reference to ancestor of jail, auto-recovering"
warned = true
end
else
resolved_segments.pop
end
else
resolved_segments.push segment
end
end
join_path resolved_segments, jail_root
end