cmd/go: validating version format in mod edit
Version strings set by -retract and -exclude are not canonicalized
by go mod commands. This change adds validation to go mod edit to
prevent invalid version strings from being added to the go.mod file.
For golang/go#43280
Change-Id: I3708b7a09111a56effac1fe1165122772e3f2d75
Reviewed-on: https://go-review.googlesource.com/c/mod/+/279394
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Trust: Michael Matloob <matloob@golang.org>
diff --git a/modfile/rule.go b/modfile/rule.go
index 83398dd..c6a189d 100644
--- a/modfile/rule.go
+++ b/modfile/rule.go
@@ -832,7 +832,16 @@
return nil
}
+// AddExclude adds a exclude statement to the mod file. Errors if the provided
+// version is not a canonical version string
func (f *File) AddExclude(path, vers string) error {
+ if !isCanonicalVersion(vers) {
+ return &module.InvalidVersionError{
+ Version: vers,
+ Err: errors.New("must be of the form v1.2.3"),
+ }
+ }
+
var hint *Line
for _, x := range f.Exclude {
if x.Mod.Path == path && x.Mod.Version == vers {
@@ -904,7 +913,22 @@
return nil
}
+// AddRetract adds a retract statement to the mod file. Errors if the provided
+// version interval does not consist of canonical version strings
func (f *File) AddRetract(vi VersionInterval, rationale string) error {
+ if !isCanonicalVersion(vi.High) {
+ return &module.InvalidVersionError{
+ Version: vi.High,
+ Err: errors.New("must be of the form v1.2.3"),
+ }
+ }
+ if !isCanonicalVersion(vi.Low) {
+ return &module.InvalidVersionError{
+ Version: vi.Low,
+ Err: errors.New("must be of the form v1.2.3"),
+ }
+ }
+
r := &Retract{
VersionInterval: vi,
}
@@ -1061,3 +1085,9 @@
}
return semver.Compare(vii.High, vij.High) > 0
}
+
+// isCanonicalVersion tests if the provided version string represents a valid
+// canonical version.
+func isCanonicalVersion(vers string) bool {
+ return vers != "" && semver.Canonical(vers) == vers
+}
diff --git a/modfile/rule_test.go b/modfile/rule_test.go
index fbf144d..03123ed 100644
--- a/modfile/rule_test.go
+++ b/modfile/rule_test.go
@@ -568,6 +568,48 @@
},
}
+var addRetractValidateVersionTests = []struct {
+ dsc, low, high string
+}{
+ {
+ "blank_version",
+ "",
+ "",
+ },
+ {
+ "missing_prefix",
+ "1.0.0",
+ "1.0.0",
+ },
+ {
+ "non_canonical",
+ "v1.2",
+ "v1.2",
+ },
+ {
+ "invalid_range",
+ "v1.2.3",
+ "v1.3",
+ },
+}
+
+var addExcludeValidateVersionTests = []struct {
+ dsc, ver string
+}{
+ {
+ "blank_version",
+ "",
+ },
+ {
+ "missing_prefix",
+ "1.0.0",
+ },
+ {
+ "non_canonical",
+ "v1.2",
+ },
+}
+
func TestAddRequire(t *testing.T) {
for _, tt := range addRequireTests {
t.Run(tt.desc, func(t *testing.T) {
@@ -699,3 +741,31 @@
return f
}
+
+func TestAddRetractValidateVersion(t *testing.T) {
+ for _, tt := range addRetractValidateVersionTests {
+ t.Run(tt.dsc, func(t *testing.T) {
+ f, err := Parse("in", []byte("module m"), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = f.AddRetract(VersionInterval{Low: tt.low, High: tt.high}, ""); err == nil {
+ t.Fatal("expected AddRetract to complain about version format")
+ }
+ })
+ }
+}
+
+func TestAddExcludeValidateVersion(t *testing.T) {
+ for _, tt := range addExcludeValidateVersionTests {
+ t.Run(tt.dsc, func(t *testing.T) {
+ f, err := Parse("in", []byte("module m"), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = f.AddExclude("aa", tt.ver); err == nil {
+ t.Fatal("expected AddExclude to complain about version format")
+ }
+ })
+ }
+}