Allow to set protected file patterns that can not be changed under no conditions (#10806)

Co-Authored-By: zeripath <art27@cantab.net>
This commit is contained in:
Lauris BH 2020-03-27 00:26:34 +02:00 committed by GitHub
parent 52cfd2743c
commit bbd910ed1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 202 additions and 23 deletions

View file

@ -339,6 +339,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec
BlockOnRejectedReviews: form.BlockOnRejectedReviews,
DismissStaleApprovals: form.DismissStaleApprovals,
RequireSignedCommits: form.RequireSignedCommits,
ProtectedFilePatterns: form.ProtectedFilePatterns,
}
err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
@ -470,6 +471,10 @@ func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtection
protectBranch.RequireSignedCommits = *form.RequireSignedCommits
}
if form.ProtectedFilePatterns != nil {
protectBranch.ProtectedFilePatterns = *form.ProtectedFilePatterns
}
var whitelistUsers []int64
if form.PushWhitelistUsernames != nil {
whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)

View file

@ -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)

View file

@ -247,6 +247,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
protectBranch.BlockOnRejectedReviews = f.BlockOnRejectedReviews
protectBranch.DismissStaleApprovals = f.DismissStaleApprovals
protectBranch.RequireSignedCommits = f.RequireSignedCommits
protectBranch.ProtectedFilePatterns = f.ProtectedFilePatterns
err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, models.WhitelistOptions{
UserIDs: whitelistUsers,