Skip to content

Commit

Permalink
Ensure we read from sanitised paths
Browse files Browse the repository at this point in the history
  • Loading branch information
pablobm committed Aug 11, 2022
1 parent 7b15eb5 commit efe7aa6
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 18 deletions.
1 change: 1 addition & 0 deletions spec/example_app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file is here only for the sake of a spec. It's not really a README.
22 changes: 11 additions & 11 deletions spec/example_app/app/controllers/docs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ def show
def render_page(name, title = nil)
page = DocPage.find(name)

if page
title = title || page.title
@page_title = [title, "Administrate"].compact.join(" - ")
# rubocop:disable Rails/OutputSafety
render layout: "docs", html: page.body.html_safe
# rubocop:enable Rails/OutputSafety
else
render file: Rails.root.join("public", "404.html"),
layout: false,
status: :not_found
end
title = title || page.title
@page_title = [title, "Administrate"].compact.join(" - ")
# rubocop:disable Rails/OutputSafety
render layout: "docs", html: page.body.html_safe
# rubocop:enable Rails/OutputSafety
rescue DocPage::PageNotAllowed, DocPage::PageNotFound
render(
file: Rails.root.join("public", "404.html"),
layout: false,
status: :not_found,
)
end
end
39 changes: 35 additions & 4 deletions spec/example_app/app/models/doc_page.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,43 @@
class DocPage
class PageNotFound < StandardError
def initialize(page)
"Could not find page #{page.inspect}"
end
end

class PageNotAllowed < StandardError
def initialize(page)
"Page #{page.inspect} is not allowed"
end
end

class << self
def find(page)
full_path = Rails.root + "../../#{page}.md"
raise PageNotFound.new(page) unless path_exists?(full_path)

safe_path = filter_unsafe_paths(full_path)
raise PageNotAllowed.new(page) unless safe_path

text = File.read(safe_path)
new(text)
end

private

def path_exists?(full_path)
File.exist?(full_path)
end

def doc_paths
[
Dir.glob(Rails.root + "../../**/*.md"),
Dir.glob(Rails.root + "../../*.md"),
].join
end

if File.exist?(full_path)
text = File.read(full_path)
new(text)
end
def filter_unsafe_paths(full_path)
doc_paths[full_path.to_s]
end
end

Expand Down
12 changes: 9 additions & 3 deletions spec/models/doc_page_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

RSpec.describe DocPage do
describe ".find" do
it "is nil if the page doesn't exist" do
page = DocPage.find("not_a_page")
it "raises an error if the page doesn't exist" do
expect do
DocPage.find("not_a_page")
end.to raise_error(DocPage::PageNotFound)
end

expect(page).to be_nil
it "raises an error on cheeky paths" do
expect do
DocPage.find("docs/../spec/example_app/README")
end.to raise_error(DocPage::PageNotAllowed)
end

it "renders pages without metadata" do
Expand Down

0 comments on commit efe7aa6

Please sign in to comment.