Module: RailVerdict::PathSafety
- Defined in:
- lib/rail_verdict/path_safety.rb
Overview
Symlink-aware path containment used by CLI and MCP surfaces.
Class Method Summary collapse
- .assert_contained!(root, path, label) ⇒ Object
-
.contained?(root, path) ⇒ Boolean
True when
path(absolute or root-relative) resolves, through its deepest EXISTING ancestor, to insideroot.
Class Method Details
.assert_contained!(root, path, label) ⇒ Object
33 34 35 36 37 |
# File 'lib/rail_verdict/path_safety.rb', line 33 def assert_contained!(root, path, label) raise UsageError, "#{label} escapes working directory: #{path}" unless contained?(root, path) File.(path.to_s, File.realpath(root)) end |
.contained?(root, path) ⇒ Boolean
True when path (absolute or root-relative) resolves, through its
deepest EXISTING ancestor, to inside root. Trailing nonexistent
segments are checked lexically against the resolved ancestor so a
symlink cannot escape after creation either.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/rail_verdict/path_safety.rb', line 12 def contained?(root, path) root_real = File.realpath(root) target = File.(path.to_s, root_real) existing = target until File.exist?(existing) || File.symlink?(existing) parent = File.dirname(existing) return false if parent == existing existing = parent end real_existing = File.realpath(existing) return false unless real_existing == root_real || real_existing.start_with?(root_real + File::SEPARATOR) # Remaining (nonexistent) tail must not traverse upward. tail = target[real_existing.length..].to_s !tail.split(File::SEPARATOR).include?("..") rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP false end |