main
rb 61 lines 2.34 KB
Raw
1 # frozen_string_literal: true
2
3 require "rails_helper"
4 require "tmpdir"
5 require "fileutils"
6
7 # Mirror repos are read-only over git: a push (receive-pack) must be rejected
8 # with a message the user actually sees, while clone/fetch and normal repos are
9 # unaffected.
10 RSpec.describe "Git HTTP mirror read-only enforcement", type: :request do
11 let(:user) { User.create!(smbcloud_id: 55, email: "m@example.com", username: "mirroruser") }
12
13 around do |example|
14 Dir.mktmpdir("git-http-mirror") do |tmp|
15 @bare = File.join(tmp, "mirrored.git")
16 system("git", "init", "--bare", "-q", @bare, exception: true)
17 example.run
18 end
19 end
20
21 let!(:mirror) do
22 user.repositories.create!(
23 name: "mirrored", disk_path: @bare, default_branch: "main",
24 mirror: true, upstream_url: "https://github.com/owner/mirrored.git", mirror_status: "ok"
25 )
26 end
27
28 it "rejects a push to a mirror with a clear git error message" do
29 get "/mirroruser/mirrored.git/info/refs", params: { service: "git-receive-pack" }
30
31 expect(response).to have_http_status(:ok)
32 expect(response.body).to include("ERR")
33 expect(response.body).to include("read-only mirror")
34 expect(response.body).to include("Detach")
35 end
36
37 it "also blocks the receive-pack RPC endpoint directly" do
38 post "/mirroruser/mirrored.git/git-receive-pack"
39 # The advertisement carries the ERR packet rather than proxying to git.
40 expect(response.body).to include("read-only mirror")
41 end
42
43 it "still allows fetch/clone (upload-pack) from a public mirror" do
44 get "/mirroruser/mirrored.git/info/refs", params: { service: "git-upload-pack" }
45 expect(response).to have_http_status(:ok)
46 expect(response.body).not_to include("read-only mirror")
47 end
48
49 it "does not block pushes on a normal (non-mirror) repo" do
50 normal_dir = File.join(Dir.tmpdir, "normal-#{SecureRandom.hex(4)}.git")
51 system("git", "init", "--bare", "-q", normal_dir, exception: true)
52 user.repositories.create!(name: "normal", disk_path: normal_dir, default_branch: "main")
53
54 get "/mirroruser/normal.git/info/refs", params: { service: "git-receive-pack" }
55 # No mirror rejection; instead it falls through to auth (401 challenge).
56 expect(response.body).not_to include("read-only mirror")
57 expect(response).to have_http_status(:unauthorized)
58 ensure
59 FileUtils.rm_rf(normal_dir) if normal_dir
60 end
61 end