aboutsummaryrefslogtreecommitdiffstats
path: root/routers/private/hook.go
diff options
context:
space:
mode:
Diffstat (limited to 'routers/private/hook.go')
-rw-r--r--routers/private/hook.go69
1 files changed, 68 insertions, 1 deletions
diff --git a/routers/private/hook.go b/routers/private/hook.go
index 38b37fa7b4..846d9b6070 100644
--- a/routers/private/hook.go
+++ b/routers/private/hook.go
@@ -22,9 +22,10 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
pull_service "code.gitea.io/gitea/services/pull"
- "github.com/go-git/go-git/v5/plumbing"
"gitea.com/macaron/macaron"
+ "github.com/go-git/go-git/v5/plumbing"
+ "github.com/gobwas/glob"
)
func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []string) error {
@@ -57,6 +58,52 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
return err
}
+func checkFileProtection(oldCommitID, newCommitID string, patterns []glob.Glob, repo *git.Repository, env []string) error {
+
+ stdoutReader, stdoutWriter, err := os.Pipe()
+ if err != nil {
+ log.Error("Unable to create os.Pipe for %s", repo.Path)
+ return err
+ }
+ defer func() {
+ _ = stdoutReader.Close()
+ _ = stdoutWriter.Close()
+ }()
+
+ err = git.NewCommand("diff", "--name-only", oldCommitID+"..."+newCommitID).
+ RunInDirTimeoutEnvFullPipelineFunc(env, -1, repo.Path,
+ stdoutWriter, nil, nil,
+ func(ctx context.Context, cancel context.CancelFunc) error {
+ _ = stdoutWriter.Close()
+
+ scanner := bufio.NewScanner(stdoutReader)
+ for scanner.Scan() {
+ path := strings.TrimSpace(scanner.Text())
+ if len(path) == 0 {
+ continue
+ }
+ lpath := strings.ToLower(path)
+ for _, pat := range patterns {
+ if pat.Match(lpath) {
+ cancel()
+ return models.ErrFilePathProtected{
+ Path: path,
+ }
+ }
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return err
+ }
+ _ = stdoutReader.Close()
+ return err
+ })
+ if err != nil && !models.IsErrFilePathProtected(err) {
+ log.Error("Unable to check file protection for commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
+ }
+ return err
+}
+
func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository, env []string) error {
scanner := bufio.NewScanner(input)
for scanner.Scan() {
@@ -216,6 +263,26 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
}
}
+ globs := protectBranch.GetProtectedFilePatterns()
+ if len(globs) > 0 {
+ err := checkFileProtection(oldCommitID, newCommitID, globs, gitRepo, env)
+ if err != nil {
+ if !models.IsErrFilePathProtected(err) {
+ log.Error("Unable to check file protection for commits from %s to %s in %-v: %v", oldCommitID, newCommitID, repo, err)
+ ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
+ "err": fmt.Sprintf("Unable to check file protection for commits from %s to %s: %v", oldCommitID, newCommitID, err),
+ })
+ return
+ }
+ protectedFilePath := err.(models.ErrFilePathProtected).Path
+ log.Warn("Forbidden: Branch: %s in %-v is protected from changing file %s", branchName, repo, protectedFilePath)
+ ctx.JSON(http.StatusForbidden, map[string]interface{}{
+ "err": fmt.Sprintf("branch %s is protected from changing file %s", branchName, protectedFilePath),
+ })
+ return
+ }
+ }
+
canPush := false
if opts.IsDeployKey {
canPush = protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)