Module: MarkdownServer::Helpers::PathHelpers
- Defined in:
- lib/markdown_server/helpers/path_helpers.rb
Instance Method Summary collapse
- #encode_path_component(str) ⇒ Object
-
#entry_admitted?(parent_real, parent_relative_str, entry_name) ⇒ Boolean
Name-gate check for an entry in a directory listing.
- #h(text) ⇒ Object
-
#permitted_base_for(real) ⇒ Object
Returns the permitted base (root realpath or a followed-link target realpath) that contains ‘real`, or nil if no permitted base contains it.
- #permitted_bases ⇒ Object
-
#permitted_path?(real) ⇒ Boolean
Returns true if ‘real` is under a permitted base AND every restricted segment of the path under that base is admitted by the configured unhide rules (per Unhide.visible? algorithm).
- #root_dir ⇒ Object
- #safe_path(requested) ⇒ Object
Instance Method Details
#encode_path_component(str) ⇒ Object
12 13 14 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 12 def encode_path_component(str) URI.encode_www_form_component(str).gsub("+", "%20") end |
#entry_admitted?(parent_real, parent_relative_str, entry_name) ⇒ Boolean
Name-gate check for an entry in a directory listing.
Boundary gate (against served root) is NOT applied here so non-followed external non-dot symlinks still appear in listings — matching today’s UX; clicking them produces a 403 via safe_path/permitted_path?.
When the name gate admits a normally-restricted entry (dotfile or EXCLUDED) that is also a symlink, additionally require the symlink’s realpath to be explicitly listed in –follow-link. Otherwise the unhide rule does not surface it. Keeps ‘unhide` (visibility by name) and `–follow-link` (which symlinks may be entered) as orthogonal opt-ins; prevents internal-aliased dotfile symlinks like `.claude → claude` from showing up as duplicates of their targets.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 64 def entry_admitted?(parent_real, parent_relative_str, entry_name) rules = Array(settings.unhide_rules) parent_segs = parent_relative_str.to_s.empty? ? [] : parent_relative_str.split("/") mode = :open parent_segs.each_with_index do |seg, i| ok, mode = MarkdownServer::Unhide.step(mode, parent_segs, i, seg, rules) return false unless ok end ok, _ = MarkdownServer::Unhide.entry_step(mode, parent_segs, entry_name, rules) return false unless ok if MarkdownServer::Unhide.restricted?(entry_name) full = File.join(parent_real, entry_name) if File.symlink?(full) real = File.realpath(full) rescue (return false) return false unless Array(settings.followed_links).include?(real) end end true end |
#h(text) ⇒ Object
8 9 10 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 8 def h(text) CGI.escapeHTML(text.to_s) end |
#permitted_base_for(real) ⇒ Object
Returns the permitted base (root realpath or a followed-link target realpath) that contains ‘real`, or nil if no permitted base contains it.
18 19 20 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 18 def permitted_base_for(real) MarkdownServer::PermittedBases.base_for(real, permitted_bases) end |
#permitted_bases ⇒ Object
22 23 24 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 22 def permitted_bases [File.realpath(root_dir), *Array(settings.followed_links)] end |
#permitted_path?(real) ⇒ Boolean
Returns true if ‘real` is under a permitted base AND every restricted segment of the path under that base is admitted by the configured unhide rules (per Unhide.visible? algorithm).
29 30 31 32 33 34 35 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 29 def permitted_path?(real) base = permitted_base_for(real) return false unless base return true if real == base rel = real.sub("#{base}/", "") MarkdownServer::Unhide.visible?(rel.split("/"), Array(settings.unhide_rules)) end |
#root_dir ⇒ Object
4 5 6 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 4 def root_dir settings.root_dir end |
#safe_path(requested) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/markdown_server/helpers/path_helpers.rb', line 37 def safe_path(requested) base = File.realpath(root_dir) full = File.join(base, requested) begin real = File.realpath(full) rescue Errno::ENOENT halt 404, erb(:layout) { "<h1>Not Found</h1><p>#{h(requested)}</p>" } end halt 403, erb(:layout) { "<h1>Forbidden</h1>" } unless permitted_path?(real) real end |