2016-12-29 02:44:32 +03:00
// Copyright 2016 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2016-12-29 02:44:32 +03:00
2019-11-16 03:47:57 +03:00
package gitgraph
2016-12-29 02:44:32 +03:00
import (
2020-07-16 22:24:36 +03:00
"bufio"
"bytes"
"context"
"os"
2016-12-29 02:44:32 +03:00
"strings"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2018-07-23 17:12:06 +03:00
"code.gitea.io/gitea/modules/setting"
2016-12-29 02:44:32 +03:00
)
// GetCommitGraph return a list of commit (GraphItems) from all branches
2021-12-20 07:41:31 +03:00
func GetCommitGraph ( r * git . Repository , page , maxAllowedColors int , hidePRRefs bool , branches , files [ ] string ) ( * Graph , error ) {
2020-11-08 20:21:54 +03:00
format := "DATA:%D|%H|%ad|%h|%s"
2016-12-29 02:44:32 +03:00
2020-07-16 22:24:36 +03:00
if page == 0 {
page = 1
}
2022-10-15 13:49:26 +03:00
graphCmd := git . NewCommand ( r . Ctx , "log" , "--graph" , "--date-order" , "--decorate=full" )
2020-11-08 20:21:54 +03:00
if hidePRRefs {
2022-10-15 13:49:26 +03:00
graphCmd . AddArguments ( "--exclude=" + git . PullPrefix + "*" )
2020-11-08 20:21:54 +03:00
}
if len ( branches ) == 0 {
2022-10-15 13:49:26 +03:00
graphCmd . AddArguments ( "--all" )
2020-11-08 20:21:54 +03:00
}
Refactor git command package to improve security and maintainability (#22678)
This PR follows #21535 (and replace #22592)
## Review without space diff
https://github.com/go-gitea/gitea/pull/22678/files?diff=split&w=1
## Purpose of this PR
1. Make git module command completely safe (risky user inputs won't be
passed as argument option anymore)
2. Avoid low-level mistakes like
https://github.com/go-gitea/gitea/pull/22098#discussion_r1045234918
3. Remove deprecated and dirty `CmdArgCheck` function, hide the `CmdArg`
type
4. Simplify code when using git command
## The main idea of this PR
* Move the `git.CmdArg` to the `internal` package, then no other package
except `git` could use it. Then developers could never do
`AddArguments(git.CmdArg(userInput))` any more.
* Introduce `git.ToTrustedCmdArgs`, it's for user-provided and already
trusted arguments. It's only used in a few cases, for example: use git
arguments from config file, help unit test with some arguments.
* Introduce `AddOptionValues` and `AddOptionFormat`, they make code more
clear and simple:
* Before: `AddArguments("-m").AddDynamicArguments(message)`
* After: `AddOptionValues("-m", message)`
* -
* Before: `AddArguments(git.CmdArg(fmt.Sprintf("--author='%s <%s>'",
sig.Name, sig.Email)))`
* After: `AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)`
## FAQ
### Why these changes were not done in #21535 ?
#21535 is mainly a search&replace, it did its best to not change too
much logic.
Making the framework better needs a lot of changes, so this separate PR
is needed as the second step.
### The naming of `AddOptionXxx`
According to git's manual, the `--xxx` part is called `option`.
### How can it guarantee that `internal.CmdArg` won't be not misused?
Go's specification guarantees that. Trying to access other package's
internal package causes compilation error.
And, `golangci-lint` also denies the git/internal package. Only the
`git/command.go` can use it carefully.
### There is still a `ToTrustedCmdArgs`, will it still allow developers
to make mistakes and pass untrusted arguments?
Generally speaking, no. Because when using `ToTrustedCmdArgs`, the code
will be very complex (see the changes for examples). Then developers and
reviewers can know that something might be unreasonable.
### Why there was a `CmdArgCheck` and why it's removed?
At the moment of #21535, to reduce unnecessary changes, `CmdArgCheck`
was introduced as a hacky patch. Now, almost all code could be written
as `cmd := NewCommand(); cmd.AddXxx(...)`, then there is no need for
`CmdArgCheck` anymore.
### Why many codes for `signArg == ""` is deleted?
Because in the old code, `signArg` could never be empty string, it's
either `-S[key-id]` or `--no-gpg-sign`. So the `signArg == ""` is just
dead code.
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-04 05:30:43 +03:00
graphCmd . AddArguments ( "-C" , "-M" , "--date=iso" ) .
AddOptionFormat ( "-n %d" , setting . UI . GraphMaxCommitNum * page ) .
AddOptionFormat ( "--pretty=format:%s" , format )
2020-11-08 20:21:54 +03:00
if len ( branches ) > 0 {
2022-10-15 13:49:26 +03:00
graphCmd . AddDynamicArguments ( branches ... )
2020-11-08 20:21:54 +03:00
}
if len ( files ) > 0 {
2022-10-23 17:44:45 +03:00
graphCmd . AddDashesAndList ( files ... )
2020-11-08 20:21:54 +03:00
}
2020-08-06 11:04:08 +03:00
graph := NewGraph ( )
2020-07-16 22:24:36 +03:00
stderr := new ( strings . Builder )
stdoutReader , stdoutWriter , err := os . Pipe ( )
2016-12-29 02:44:32 +03:00
if err != nil {
2020-07-16 22:24:36 +03:00
return nil , err
2016-12-29 02:44:32 +03:00
}
2020-07-16 22:24:36 +03:00
commitsToSkip := setting . UI . GraphMaxCommitNum * ( page - 1 )
scanner := bufio . NewScanner ( stdoutReader )
2022-04-01 05:55:30 +03:00
if err := graphCmd . Run ( & git . RunOpts {
Dir : r . Path ,
Stdout : stdoutWriter ,
Stderr : stderr ,
2022-02-11 15:47:22 +03:00
PipelineFunc : func ( ctx context . Context , cancel context . CancelFunc ) error {
_ = stdoutWriter . Close ( )
defer stdoutReader . Close ( )
parser := & Parser { }
parser . firstInUse = - 1
parser . maxAllowedColors = maxAllowedColors
if maxAllowedColors > 0 {
parser . availableColors = make ( [ ] int , maxAllowedColors )
for i := range parser . availableColors {
parser . availableColors [ i ] = i + 1
}
} else {
parser . availableColors = [ ] int { 1 , 2 }
2020-08-06 11:04:08 +03:00
}
2022-02-11 15:47:22 +03:00
for commitsToSkip > 0 && scanner . Scan ( ) {
line := scanner . Bytes ( )
dataIdx := bytes . Index ( line , [ ] byte ( "DATA:" ) )
if dataIdx < 0 {
dataIdx = len ( line )
}
starIdx := bytes . IndexByte ( line , '*' )
if starIdx >= 0 && starIdx < dataIdx {
commitsToSkip --
}
parser . ParseGlyphs ( line [ : dataIdx ] )
2020-07-16 22:24:36 +03:00
}
2020-08-06 11:04:08 +03:00
2022-02-11 15:47:22 +03:00
row := 0
// Skip initial non-commit lines
for scanner . Scan ( ) {
line := scanner . Bytes ( )
if bytes . IndexByte ( line , '*' ) >= 0 {
if err := parser . AddLineToGraph ( graph , row , line ) ; err != nil {
cancel ( )
return err
}
break
}
parser . ParseGlyphs ( line )
}
2020-08-06 11:04:08 +03:00
2022-02-11 15:47:22 +03:00
for scanner . Scan ( ) {
row ++
line := scanner . Bytes ( )
2020-08-06 11:04:08 +03:00
if err := parser . AddLineToGraph ( graph , row , line ) ; err != nil {
2020-07-16 22:24:36 +03:00
cancel ( )
return err
}
}
2022-02-11 15:47:22 +03:00
return scanner . Err ( )
} ,
2020-07-16 22:24:36 +03:00
} ) ; err != nil {
2020-08-06 11:04:08 +03:00
return graph , err
2016-12-29 02:44:32 +03:00
}
2020-08-06 11:04:08 +03:00
return graph , nil
2016-12-29 02:44:32 +03:00
}