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