From 692346f5d864a4eb9965d0201e5c58151570d706 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Thu, 20 Feb 2025 12:32:43 +0800 Subject: [PATCH] ssh/recv: Check hooksPath before receiving packs --- git_hooks_handle.go | 33 +++++++++++++-------------------- ssh_handle_receive_pack.go | 22 ++++++++++++++++++++++ diff --git a/git_hooks_handle.go b/git_hooks_handle.go index 3a8a0312198ed975fac5c7122da48ad677bbb38c..59d29c7b545d6b8fbe5d2a077968993eb2228918 100644 --- a/git_hooks_handle.go +++ b/git_hooks_handle.go @@ -14,9 +14,8 @@ "strconv" "strings" "syscall" + "github.com/go-git/go-git/v5/plumbing" "github.com/jackc/pgx/v5" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing" "go.lindenii.runxiyu.org/lindenii-common/ansiec" ) @@ -131,7 +130,7 @@ } if strings.HasPrefix(ref_name, "refs/heads/contrib/") { if all_zero_num_string(old_oid) { // New branch - fmt.Fprintln(ssh_stderr, ansiec.Blue + "POK" + ansiec.Reset, ref_name) + fmt.Fprintln(ssh_stderr, ansiec.Blue+"POK"+ansiec.Reset, ref_name) var new_mr_id int err = database.QueryRow(ctx, "INSERT INTO merge_requests (repo_id, creator, source_ref, status) VALUES ($1, $2, $3, 'open') RETURNING id", @@ -141,7 +140,7 @@ if err != nil { wf_error(ssh_stderr, "Error creating merge request: %v", err) return 1 } - fmt.Fprintln(ssh_stderr, ansiec.Blue + "Created merge request at", generate_http_remote_url(pack_to_hook.group_name, pack_to_hook.repo_name) + "/contrib/" + strconv.FormatUint(uint64(new_mr_id), 10) + "/" + ansiec.Reset) + fmt.Fprintln(ssh_stderr, ansiec.Blue+"Created merge request at", generate_http_remote_url(pack_to_hook.group_name, pack_to_hook.repo_name)+"/contrib/"+strconv.FormatUint(uint64(new_mr_id), 10)+"/"+ansiec.Reset) } else { // Existing contrib branch var existing_merge_request_user_id int err = database.QueryRow(ctx, @@ -158,25 +157,19 @@ return 1 } if existing_merge_request_user_id == 0 { all_ok = false - fmt.Fprintln(ssh_stderr, ansiec.Red + "NAK" + ansiec.Reset, ref_name, "(branch belongs to unowned MR)") + fmt.Fprintln(ssh_stderr, ansiec.Red+"NAK"+ansiec.Reset, ref_name, "(branch belongs to unowned MR)") continue } if existing_merge_request_user_id != pack_to_hook.user_id { all_ok = false - fmt.Fprintln(ssh_stderr, ansiec.Red + "NAK" + ansiec.Reset, ref_name, "(branch belongs another user's MR)") + fmt.Fprintln(ssh_stderr, ansiec.Red+"NAK"+ansiec.Reset, ref_name, "(branch belongs another user's MR)") continue } - repo, err := git.PlainOpen(pack_to_hook.repo_path) - if err != nil { - wf_error(ssh_stderr, "Daemon failed to open repo: %v", err) - return 1 - } - old_hash := plumbing.NewHash(old_oid) - old_commit, err := repo.CommitObject(old_hash) + old_commit, err := pack_to_hook.repo.CommitObject(old_hash) if err != nil { wf_error(ssh_stderr, "Daemon failed to get old commit: %v", err) return 1 @@ -187,7 +180,7 @@ // detectable as they haven't been merged into the main repo's // objects yet. But it seems to work, and I don't think there's // any reason for this to only work intermitently. new_hash := plumbing.NewHash(new_oid) - new_commit, err := repo.CommitObject(new_hash) + new_commit, err := pack_to_hook.repo.CommitObject(new_hash) if err != nil { wf_error(ssh_stderr, "Daemon failed to get new commit: %v", err) return 1 @@ -202,29 +195,29 @@ if !is_ancestor { // TODO: Create MR snapshot ref instead all_ok = false - fmt.Fprintln(ssh_stderr, ansiec.Red + "NAK" + ansiec.Reset, ref_name, "(force pushes are not supported yet)") + fmt.Fprintln(ssh_stderr, ansiec.Red+"NAK"+ansiec.Reset, ref_name, "(force pushes are not supported yet)") continue } - fmt.Fprintln(ssh_stderr, ansiec.Blue + "POK" + ansiec.Reset, ref_name) + fmt.Fprintln(ssh_stderr, ansiec.Blue+"POK"+ansiec.Reset, ref_name) } } else { // Non-contrib branch all_ok = false - fmt.Fprintln(ssh_stderr, ansiec.Red + "NAK" + ansiec.Reset, ref_name, "(you cannot push to branches outside of contrib/*)") + fmt.Fprintln(ssh_stderr, ansiec.Red+"NAK"+ansiec.Reset, ref_name, "(you cannot push to branches outside of contrib/*)") } } fmt.Fprintln(ssh_stderr) if all_ok { - fmt.Fprintln(ssh_stderr, "Overall " + ansiec.Green + "ACK" + ansiec.Reset + " (all checks passed)") + fmt.Fprintln(ssh_stderr, "Overall "+ansiec.Green+"ACK"+ansiec.Reset+" (all checks passed)") return 0 } else { - fmt.Fprintln(ssh_stderr, "Overall " + ansiec.Red + "NAK" + ansiec.Reset + " (one or more branches failed checks)") + fmt.Fprintln(ssh_stderr, "Overall "+ansiec.Red+"NAK"+ansiec.Reset+" (one or more branches failed checks)") return 1 } } default: - fmt.Fprintln(ssh_stderr, ansiec.Red + "Invalid hook:", args[0] + ansiec.Reset) + fmt.Fprintln(ssh_stderr, ansiec.Red+"Invalid hook:", args[0]+ansiec.Reset) return 1 } }() diff --git a/ssh_handle_receive_pack.go b/ssh_handle_receive_pack.go index af576bc6e7fe5a145c1bc5098832ffce0465671b..971d11c2d3dd0078d002a551ff12ca8e2db30993 100644 --- a/ssh_handle_receive_pack.go +++ b/ssh_handle_receive_pack.go @@ -7,11 +7,13 @@ "os" "os/exec" glider_ssh "github.com/gliderlabs/ssh" + "github.com/go-git/go-git/v5" "go.lindenii.runxiyu.org/lindenii-common/cmap" ) type pack_to_hook_t struct { session glider_ssh.Session + repo *git.Repository pubkey string direct_access bool repo_path string @@ -28,6 +30,25 @@ func ssh_handle_receive_pack(session glider_ssh.Session, pubkey string, repo_identifier string) (err error) { group_name, repo_name, repo_id, repo_path, direct_access, contrib_requirements, user_type, user_id, err := get_repo_path_perms_from_ssh_path_pubkey(session.Context(), repo_identifier, pubkey) if err != nil { return err + } + repo, err := git.PlainOpen(repo_path) + if err != nil { + return err + } + + repo_config, err := repo.Config() + if err != nil { + return err + } + + repo_config_core := repo_config.Raw.Section("core") + if repo_config_core == nil { + return errors.New("Repository has no core section in config") + } + + hooksPath := repo_config_core.OptionAll("hooksPath") + if len(hooksPath) != 1 || hooksPath[0] != config.Hooks.Execs { + return errors.New("Repository has hooksPath set to an unexpected value") } if !direct_access { @@ -73,6 +94,7 @@ user_id: user_id, repo_id: repo_id, group_name: group_name, repo_name: repo_name, + repo: repo, }) defer pack_to_hook_by_cookie.Delete(cookie) // The Delete won't execute until proc.Wait returns unless something -- 2.48.1