def system_path target, start, jail = nil, opts = {}
if jail
unless is_root? jail
raise ::SecurityError, %(Jail is not an absolute path: #{jail})
end
jail = posixfy jail
end
if target.nil_or_empty?
target_segments = []
else
target_segments, target_root, _ = partition_path target
end
if target_segments.empty?
if start.nil_or_empty?
return jail ? jail : @working_dir
elsif is_root? start
unless jail
return expand_path start
end
else
return system_path start, jail, jail, opts
end
end
if target_root && target_root != DOT_SLASH
resolved_target = join_path target_segments, target_root
if !jail || (resolved_target.start_with? jail)
return resolved_target
end
end
if start.nil_or_empty?
start = jail ? jail : @working_dir
elsif is_root? start
start = posixfy start
else
start = system_path start, jail, jail, opts
end
if jail == start
jail_segments, jail_root, _ = partition_path jail
start_segments = jail_segments.dup
elsif jail
unless 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
if resolved_segments.length > jail_segments.length
resolved_segments.pop
elsif !(recover ||= (opts.fetch :recover, true))
raise ::SecurityError, %(#{opts[:target_name] || 'path'} #{target} refers to location outside jail: #{jail} (disallowed in safe mode))
elsif !warned
warn %(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