Go-based Git command DSL

Note: this issue should remain confidential until after the critical security release https://gitlab.com/gitlab-org/gitlab-ce/issues/65334

There have been a few exploits discovered that target Gitaly's git subprocess spawning (see #1682, #1799, #1801 (closed) #1802). While we have patched these specific exploits, more may emerge in the future.

At the root of this problem is a lack of validation from the various inputs that may end up in a git commands (note: normally these inputs come from RPC request parameters). Enforcing a strict policy for how these commands are formed may guard against some of these attacks.

One approach to enforcing better argument validation is to iteratively implement a Domain Specific Language (DSL) in Go for forming Git commands. This would allow us to specify a syntax for the various command components that would leverage the compiler for enforcement.

Git Command Status Quo

A partial listing of existing commands can be found by searching for git.BareCommand or git.Command usage. See other git command constructs in internal/git/command.go.

  • log example:
    cmd, err := git.Command(ctx, repo, "log", "--format=%H", "--max-count=1", revision, "--", path)
  • fsck example:
    args := []string{"--git-dir", repoPath, "fsck"}
    cmd, err := git.BareCommand(ctx, nil, &stdout, &stderr, env, args...)
  • archive example:
    archiveCommand, err := git.Command(ctx, in.GetRepository(), "archive",
     "--format="+format, "--prefix="+in.GetPrefix()+"/", in.GetCommitId(), "--", path)
  • grep example:
     cmd, err := git.Command(ctx, repo, "grep",
     	"--ignore-case",
     	"-I", // Don't match binary, there is no long-name for this one
     	"--line-number",
     	"--null",
     	"--before-context", surroundContext,
     	"--after-context", surroundContext,
     	"--perl-regexp",
     	"-e", // next arg is pattern, keep this last
     	req.GetQuery(),
     	string(req.GetRef()),
     )
  • catfile example:
    batchCmdArgs := []string{"--git-dir", repoPath, "cat-file", "--batch"}
    batchCmd, err := git.BareCommand(ctx, stdinReader, nil, nil, env, batchCmdArgs...)
Edited by Paul Okstad (ex-GitLab)