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:
parent
52cfd2743c
commit
bbd910ed1b
15 changed files with 202 additions and 23 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue