Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2023-06-05 13:00:51 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2023-06-05 13:41:13 +0300
commitdab5f8597c483943b214ca33181ac8636df3f822 (patch)
tree2bd3fdc99e14dd538366264159d724e9b124e7fc
parent9a0bc6b03f1fd6a954f149f16fbb4bd44c6d8905 (diff)
git: Optionally allow path-scoped revisions in `ValidateRevision()`
The `ValidateRevision()` helper function rejects all revisions that contain a colon. This also restricts this function from being used with path-scoped revisions like `HEAD:README.md`, which require the caller to use a colon. Introduce a new option `AllowPathScopedRevision()` that lets callers accept these kinds of revisions.
-rw-r--r--internal/git/revision.go31
-rw-r--r--internal/git/revision_test.go40
2 files changed, 59 insertions, 12 deletions
diff --git a/internal/git/revision.go b/internal/git/revision.go
index 751317614..d239fd9f0 100644
--- a/internal/git/revision.go
+++ b/internal/git/revision.go
@@ -6,7 +6,8 @@ import (
)
type validateRevisionConfig struct {
- allowEmpty bool
+ allowEmpty bool
+ allowPathScopedRevision bool
}
// ValidateRevisionOption is an option that can be passed to ValidateRevision.
@@ -20,6 +21,15 @@ func AllowEmptyRevision() ValidateRevisionOption {
}
}
+// AllowPathScopedRevision changes ValidateRevision to allow path-scoped revisions like
+// `HEAD:README.md`. Note that path-scoped revisions may contain any character except for NUL bytes.
+// Most importantly, a path-scoped revision may contain newlines.
+func AllowPathScopedRevision() ValidateRevisionOption {
+ return func(cfg *validateRevisionConfig) {
+ cfg.allowPathScopedRevision = true
+ }
+}
+
// ValidateRevision checks if a revision looks valid. The default behaviour can be changed by
// passing ValidateRevisionOptions.
func ValidateRevision(revision []byte, opts ...ValidateRevisionOption) error {
@@ -28,18 +38,25 @@ func ValidateRevision(revision []byte, opts ...ValidateRevisionOption) error {
opt(&cfg)
}
- if !cfg.allowEmpty && len(revision) == 0 {
- return fmt.Errorf("empty revision")
- }
if bytes.HasPrefix(revision, []byte("-")) {
return fmt.Errorf("revision can't start with '-'")
}
- if bytes.ContainsAny(revision, " \t\n\r") {
- return fmt.Errorf("revision can't contain whitespace")
- }
if bytes.Contains(revision, []byte("\x00")) {
return fmt.Errorf("revision can't contain NUL")
}
+
+ if cfg.allowPathScopedRevision {
+ // We don't need to validate the path component, if any, given that it may contain
+ // all bytes except for the NUL byte which we already checked for above.
+ revision, _, _ = bytes.Cut(revision, []byte(":"))
+ }
+
+ if !cfg.allowEmpty && len(revision) == 0 {
+ return fmt.Errorf("empty revision")
+ }
+ if bytes.ContainsAny(revision, " \t\n\r") {
+ return fmt.Errorf("revision can't contain whitespace")
+ }
if bytes.Contains(revision, []byte(":")) {
return fmt.Errorf("revision can't contain ':'")
}
diff --git a/internal/git/revision_test.go b/internal/git/revision_test.go
index f2e210fc8..e65a0aa18 100644
--- a/internal/git/revision_test.go
+++ b/internal/git/revision_test.go
@@ -65,15 +65,45 @@ func TestValidateRevision(t *testing.T) {
expectedErr: fmt.Errorf("revision can't contain NUL"),
},
{
- desc: "colon",
- revision: "foo/bar:baz",
- expectedErr: fmt.Errorf("revision can't contain ':'"),
- },
- {
desc: "backslash",
revision: "foo\\bar\\baz",
expectedErr: fmt.Errorf("revision can't contain '\\'"),
},
+ {
+ desc: "path-scoped revisions refused by default",
+ revision: "refs/heads/main:path",
+ expectedErr: fmt.Errorf("revision can't contain ':'"),
+ },
+ {
+ desc: "path-scoped revision allowed with option",
+ revision: "refs/heads/main:path",
+ opts: []ValidateRevisionOption{
+ AllowPathScopedRevision(),
+ },
+ },
+ {
+ desc: "path-scoped revision does not allow newline in revision",
+ revision: "refs/heads\nmain:path",
+ opts: []ValidateRevisionOption{
+ AllowPathScopedRevision(),
+ },
+ expectedErr: fmt.Errorf("revision can't contain whitespace"),
+ },
+ {
+ desc: "path-scoped revision must not be empty",
+ revision: ":path",
+ opts: []ValidateRevisionOption{
+ AllowPathScopedRevision(),
+ },
+ expectedErr: fmt.Errorf("empty revision"),
+ },
+ {
+ desc: "path-scoped path may contain arbitrary characters",
+ revision: "refs/heads/main:path\n\t\r :\\",
+ opts: []ValidateRevisionOption{
+ AllowPathScopedRevision(),
+ },
+ },
} {
t.Run(tc.desc, func(t *testing.T) {
require.Equal(t, tc.expectedErr, ValidateRevision([]byte(tc.revision), tc.opts...))