Skip to content
This repository was archived by the owner on Sep 18, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions modules/git/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,93 @@ func GetAffectedFiles(repo *Repository, oldCommitID, newCommitID string, env []s

return affectedFiles, err
}

func GetChangesFiles(repo *Repository, oldCommitID, newCommitID string, env []string) ([]string, error) {
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
log.Error("Unable to create os.Pipe for %s", repo.Path)
return nil, err
}
defer func() {
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}()

affectedFiles := make([]string, 0, 32)

// Run `git diff --name-status` to get the status of the changed files
err = NewCommand(repo.Ctx, "diff", "--name-status").AddDynamicArguments(oldCommitID, newCommitID).
Run(&RunOpts{
Env: env,
Dir: repo.Path,
Stdout: stdoutWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
// Close the writer end of the pipe to begin processing
_ = stdoutWriter.Close()
defer func() {
// Close the reader on return to terminate the git command if necessary
_ = stdoutReader.Close()
}()
// Now scan the output from the command
scanner := bufio.NewScanner(stdoutReader)
for scanner.Scan() {
path := strings.TrimSpace(scanner.Text())
if len(path) == 0 {
continue
}
affectedFiles = append(affectedFiles, path)
}
return scanner.Err()
},
})
if err != nil {
log.Error("Unable to get changes files for commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err)
}

return affectedFiles, err
}

func GetFileSize(repo *Repository, newCommitID, fileName string, env []string) (int64, error) {
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
log.Error("Unable to create os.Pipe for %s", repo.Path)
return 0, err
}
defer func() {
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}()

var fileSize int64

// Run `git cat-file -s commitId:fileName` to get the file size of the commit
err = NewCommand(repo.Ctx, "cat-file", "-s").AddDynamicArguments(fmt.Sprintf("%s:%s", newCommitID, fileName)).
Run(&RunOpts{
Env: env,
Dir: repo.Path,
Stdout: stdoutWriter,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
// Close the writer end of the pipe to begin processing
_ = stdoutWriter.Close()
defer func() {
// Close the reader on return to terminate the git command if necessary
_ = stdoutReader.Close()
}()
// Now scan the output from the command
scanner := bufio.NewScanner(stdoutReader)
for scanner.Scan() {
size := strings.TrimSpace(scanner.Text())
if len(size) == 0 {
continue
}
fileSize, err = strconv.ParseInt(size, 10, 64)
}
return scanner.Err()
},
})
if err != nil {
log.Error("Unable to get file size of %s for commit %s in %s: %v", fileName, newCommitID, repo.Path, err)
}

return fileSize, err
}
2 changes: 2 additions & 0 deletions modules/setting/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var (
AllowDeleteOfUnadoptedRepositories bool
DisableDownloadSourceArchives bool
AllowForkWithoutMaximumLimit bool
MaxNonLfsFileSize int64

// Repository editor settings
Editor struct {
Expand Down Expand Up @@ -276,6 +277,7 @@ func loadRepositoryFrom(rootCfg ConfigProvider) {
Repository.GoGetCloneURLProtocol = sec.Key("GO_GET_CLONE_URL_PROTOCOL").MustString("https")
Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1)
Repository.DefaultBranch = sec.Key("DEFAULT_BRANCH").MustString(Repository.DefaultBranch)
Repository.MaxNonLfsFileSize = sec.Key("MAX_NON_LFS_FILE_SIZE").MustInt64(-1)
RepoRootPath = sec.Key("ROOT").MustString(path.Join(AppDataPath, "gitea-repositories"))
if !filepath.IsAbs(RepoRootPath) {
RepoRootPath = filepath.Join(AppWorkPath, RepoRootPath)
Expand Down
29 changes: 29 additions & 0 deletions routers/private/hook_pre_receive.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"os"
"strings"

"code.gitea.io/gitea/models"
asymkey_model "code.gitea.io/gitea/models/asymkey"
Expand All @@ -20,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
pull_service "code.gitea.io/gitea/services/pull"
)
Expand Down Expand Up @@ -117,6 +119,8 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
newCommitID := opts.NewCommitIDs[i]
refFullName := opts.RefFullNames[i]

checkFileSize(ourCtx, oldCommitID, newCommitID, generateGitEnv(opts))

switch {
case refFullName.IsBranch():
preReceiveBranch(ourCtx, oldCommitID, newCommitID, refFullName)
Expand Down Expand Up @@ -522,3 +526,28 @@ func (ctx *preReceiveContext) loadPusherAndPermission() bool {
ctx.loadedPusher = true
return true
}

func checkFileSize(ctx *preReceiveContext, oldCommitID, newCommitID string, env []string) {
if setting.Repository.MaxNonLfsFileSize == -1 {
return
}
affectedFiles, err := git.GetChangesFiles(ctx.Repo.GitRepo, oldCommitID, newCommitID, env)
if err != nil {
return
}
for _, change := range affectedFiles {
fileName := change[2:]
if strings.HasPrefix(change, "A\t") || strings.HasPrefix(change, "M\t") {
fileSize, err := git.GetFileSize(ctx.Repo.GitRepo, newCommitID, fileName, env)
if err != nil {
return
}
if fileSize > setting.Repository.MaxNonLfsFileSize {
ctx.JSON(http.StatusRequestEntityTooLarge, private.Response{
UserMsg: fmt.Sprintf("The size of the file %s exceeds %d bytes.", fileName, setting.Repository.MaxNonLfsFileSize),
})
return
}
}
}
}