diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000000000000000000000000000000000..a925cf7993b0b9748283c58c8bd751782a893987 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,36 @@ +--- +name: "CodeQL" + +on: + push: + schedule: + - cron: '44 6 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['javascript', 'ruby'] + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/spec/example_app/README.md b/spec/example_app/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8551b1d99b1e7acc4e69d2e3069081a4d667b211 --- /dev/null +++ b/spec/example_app/README.md @@ -0,0 +1 @@ +This file is here only for the sake of a spec. It's not really a README. diff --git a/spec/example_app/app/controllers/docs_controller.rb b/spec/example_app/app/controllers/docs_controller.rb index 08298a783c77cd4b6474b8343e62f3227e2b9524..78a61d820189e24465397855b4bf0be941b3bb88 100644 --- a/spec/example_app/app/controllers/docs_controller.rb +++ b/spec/example_app/app/controllers/docs_controller.rb @@ -21,16 +21,16 @@ class DocsController < ApplicationController 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 diff --git a/spec/example_app/app/models/doc_page.rb b/spec/example_app/app/models/doc_page.rb index 4e318c65cf18a2da9a4e1d7256245d16aad23e20..deb3255ce16c049b742979249b9a1eff85481b5b 100644 --- a/spec/example_app/app/models/doc_page.rb +++ b/spec/example_app/app/models/doc_page.rb @@ -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 diff --git a/spec/models/doc_page_spec.rb b/spec/models/doc_page_spec.rb index ff3989822958d8c4bd98178108aeda9b88dc8d33..a583988e663c928d1e082f91dc872feb7bce1350 100644 --- a/spec/models/doc_page_spec.rb +++ b/spec/models/doc_page_spec.rb @@ -2,10 +2,16 @@ require "rails_helper" 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