Login
gpcr/spec/cgi_auth_spec.cr
Login

[edit]

spec/cgi_auth_spec.cr @ trunk

spec

require 'spec'
scale = 0.75
fontscale = 1.500
box wid 1.380 ht 0.340 with .nw at (0.035, -0.035) fill 0xCBD5E1 color 0xCBD5E1
box wid 1.380 ht 0.340 with .nw at (0.000, 0.000) fill 0x7C83E6 color 0x7C83E6
box wid 0.300 ht 0.065 rad 0.018 at (0.370, -0.022) fill 0xffffff color 0xffffff
line from (0.140, 0.000) to (0.140, -0.340) color white
line from (1.240, 0.000) to (1.240, -0.340) color white
text "require 'spec'" color white at (0.690, -0.170)

crystal source

require "spec"

json block

{
  "kind": "require",
  "name": "spec",
  "doc": null,
  "children": []
}

private def gpcr_bin : String
  File.join(Dir.current, "bin", "gpcr")
end

private def run_cgi(caps : String, path : String, method : String = "GET") : String
  buf = IO::Memory.new
  env = {
    "PATH"                => "/usr/bin:/bin",
    "GATEWAY_INTERFACE"   => "CGI/1.1",
    "REQUEST_METHOD"      => method,
    "PATH_INFO"           => path,
    "QUERY_STRING"        => "",
    "SCRIPT_NAME"         => "/ext/gpcr",
    "FOSSIL_REPOSITORY"   => "/nonexistent/gpcr_auth_test.fossil",
    "FOSSIL_CAPABILITIES" => caps,
  }
  stdin = IO::Memory.new
  Process.run(gpcr_bin, env: env, clear_env: true, input: stdin, output: buf,
    error: Process::Redirect::Close)
  buf.to_s
end

private def is_403?(response : String) : Bool
  response.includes?("Status: 403")
end

describe "GPCR CGI capability matrix" do
  bin = gpcr_bin

  unless File.exists?(bin)
    pending "gpcr binary not present — skipping capability matrix tests"
    next
  end

  describe "no capabilities (FOSSIL_CAPABILITIES='')" do
    it "GET / (explorer) → 403 Forbidden" do
      is_403?(run_cgi("", "/")).should be_true
    end

    it "GET /gallery → 403 Forbidden" do
      is_403?(run_cgi("", "/gallery")).should be_true
    end

    it "GET /src/gpcr/cli.cr (file route) → 403 Forbidden" do
      is_403?(run_cgi("", "/src/gpcr/cli.cr")).should be_true
    end

    it "GET /edit → 403 Forbidden" do
      is_403?(run_cgi("", "/edit")).should be_true
    end

    it "GET /edit.js (fetch-swap asset) → 403 Forbidden" do
      is_403?(run_cgi("", "/edit.js")).should be_true
    end

    it "POST /edit → 403 Forbidden" do
      is_403?(run_cgi("", "/edit", "POST")).should be_true
    end

    it "POST /edit refused → no Location header (zero-state-mutation proof)" do
      run_cgi("", "/edit", "POST").should_not contain("Location:")
    end
  end

  describe "read capabilities (FOSSIL_CAPABILITIES='o')" do
    it "GET / (explorer) → allowed (not 403)" do
      is_403?(run_cgi("o", "/")).should be_false
    end

    it "GET /gallery → allowed (not 403)" do
      is_403?(run_cgi("o", "/gallery")).should be_false
    end

    it "GET /src/gpcr/cli.cr (file route) → allowed (not 403)" do
      is_403?(run_cgi("o", "/src/gpcr/cli.cr")).should be_false
    end

    it "GET /edit → 403 Forbidden (i capability required)" do
      is_403?(run_cgi("o", "/edit")).should be_true
    end

    it "GET /edit.js (fetch-swap asset) → allowed (not 403) — read-only static asset" do
      is_403?(run_cgi("o", "/edit.js")).should be_false
    end

    it "POST /edit → 403 Forbidden (i capability required)" do
      is_403?(run_cgi("o", "/edit", "POST")).should be_true
    end
  end

  describe "edit capabilities (FOSSIL_CAPABILITIES='ohi')" do
    it "GET / (explorer) → allowed (not 403)" do
      is_403?(run_cgi("ohi", "/")).should be_false
    end

    it "GET /gallery → allowed (not 403)" do
      is_403?(run_cgi("ohi", "/gallery")).should be_false
    end

    it "GET /edit → allowed (not 403)" do
      is_403?(run_cgi("ohi", "/edit")).should be_false
    end

    it "POST /edit → allowed (not 403)" do
      is_403?(run_cgi("ohi", "/edit", "POST")).should be_false
    end
  end

  describe "setup capability (FOSSIL_CAPABILITIES='s') — superset, all routes allowed" do
    it "GET / (explorer) → allowed (not 403)" do
      is_403?(run_cgi("s", "/")).should be_false
    end

    it "GET /gallery → allowed (not 403)" do
      is_403?(run_cgi("s", "/gallery")).should be_false
    end

    it "GET /src/gpcr/cli.cr (file route) → allowed (not 403)" do
      is_403?(run_cgi("s", "/src/gpcr/cli.cr")).should be_false
    end

    it "GET /edit → allowed (not 403) (setup implies all caps)" do
      is_403?(run_cgi("s", "/edit")).should be_false
    end

    it "POST /edit → allowed (not 403)" do
      is_403?(run_cgi("s", "/edit", "POST")).should be_false
    end
  end

  describe "admin capability (FOSSIL_CAPABILITIES='a') — superset, all routes allowed" do
    it "GET / (explorer) → allowed (not 403)" do
      is_403?(run_cgi("a", "/")).should be_false
    end

    it "GET /gallery → allowed (not 403)" do
      is_403?(run_cgi("a", "/gallery")).should be_false
    end

    it "GET /src/gpcr/cli.cr (file route) → allowed (not 403)" do
      is_403?(run_cgi("a", "/src/gpcr/cli.cr")).should be_false
    end

    it "GET /edit → allowed (not 403) (admin implies all caps)" do
      is_403?(run_cgi("a", "/edit")).should be_false
    end

    it "POST /edit → allowed (not 403)" do
      is_403?(run_cgi("a", "/edit", "POST")).should be_false
    end
  end

  describe "developer capability (FOSSIL_CAPABILITIES='v') — read+edit allowed" do
    it "GET / (explorer) → allowed (not 403)" do
      is_403?(run_cgi("v", "/")).should be_false
    end

    it "GET /gallery → allowed (not 403)" do
      is_403?(run_cgi("v", "/gallery")).should be_false
    end

    it "GET /src/gpcr/cli.cr (file route) → allowed (not 403)" do
      is_403?(run_cgi("v", "/src/gpcr/cli.cr")).should be_false
    end

    it "GET /edit → allowed (not 403) (v expands to include i)" do
      is_403?(run_cgi("v", "/edit")).should be_false
    end

    it "POST /edit → allowed (not 403)" do
      is_403?(run_cgi("v", "/edit", "POST")).should be_false
    end
  end

  describe "reader capability (FOSSIL_CAPABILITIES='u') — read-only" do
    it "GET / (explorer) → allowed (not 403)" do
      is_403?(run_cgi("u", "/")).should be_false
    end

    it "GET /gallery → allowed (not 403)" do
      is_403?(run_cgi("u", "/gallery")).should be_false
    end

    it "GET /src/gpcr/cli.cr (file route) → allowed (not 403)" do
      is_403?(run_cgi("u", "/src/gpcr/cli.cr")).should be_false
    end

    it "GET /edit → 403 Forbidden (u expands to read caps only, no i)" do
      is_403?(run_cgi("u", "/edit")).should be_true
    end

    it "POST /edit → 403 Forbidden (u expands to read caps only, no i)" do
      is_403?(run_cgi("u", "/edit", "POST")).should be_true
    end
  end

  describe "trailing slash normalization (ticket 8428fb1664)" do
    it "GET /edit/ with no caps → 403 (same as /edit)" do
      is_403?(run_cgi("", "/edit/")).should be_true
    end

    it "GET /edit/ with edit caps → allowed (same as /edit)" do
      is_403?(run_cgi("ohi", "/edit/")).should be_false
    end

    it "POST /edit/ with edit caps → allowed (same as /edit)" do
      is_403?(run_cgi("ohi", "/edit/", "POST")).should be_false
    end

    it "GET /gallery/ with no caps → 403 (same as /gallery)" do
      is_403?(run_cgi("", "/gallery/")).should be_true
    end

    it "GET /gallery/ with read caps → allowed (same as /gallery)" do
      is_403?(run_cgi("o", "/gallery/")).should be_false
    end
  end
end