Skip to content

Commit

Permalink
cmd/gorelease: report a diagnostic error for retracted dependencies
Browse files Browse the repository at this point in the history
Fixes golang/go#37781

Change-Id: I109ce5da26c757e7e1bdd6bdcee0ff14be35230b
Reviewed-on: https://go-review.googlesource.com/c/exp/+/310370
Trust: Jean de Klerk <deklerk@google.com>
Run-TryBot: Jean de Klerk <deklerk@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
  • Loading branch information
jeanbza committed Jun 24, 2021
1 parent 725118b commit 7a70d7c
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 0 deletions.
81 changes: 81 additions & 0 deletions cmd/gorelease/gorelease.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
package main

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand All @@ -95,6 +96,7 @@ import (
"path/filepath"
"sort"
"strings"
"unicode"

"golang.org/x/exp/apidiff"
"golang.org/x/mod/modfile"
Expand Down Expand Up @@ -426,6 +428,12 @@ func loadLocalModule(ctx context.Context, modRoot, repoRoot, version string) (m
m.highestTransitiveVersion = highestVersion
}

retracted, err := loadRetractions(ctx, tmpLoadDir)
if err != nil {
return moduleInfo{}, err
}
m.diagnostics = append(m.diagnostics, retracted...)

return m, nil
}

Expand Down Expand Up @@ -1375,3 +1383,76 @@ func copyEnv(ctx context.Context, current []string) []string {
copy(clone, env)
return clone
}

// loadRetractions lists all retracted deps found at the modRoot.
func loadRetractions(ctx context.Context, modRoot string) ([]string, error) {
cmd := exec.CommandContext(ctx, "go", "list", "-json", "-m", "-u", "all")
if env, ok := ctx.Value("env").([]string); ok {
cmd.Env = env
}
cmd.Dir = modRoot
out, err := cmd.Output()
if err != nil {
return nil, cleanCmdError(err)
}

var retracted []string
type message struct {
Path string
Version string
Retracted []string
}

dec := json.NewDecoder(bytes.NewBuffer(out))
for {
var m message
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
return nil, err
}
if len(m.Retracted) == 0 {
continue
}
rationale, ok := shortRetractionRationale(m.Retracted)
if ok {
retracted = append(retracted, fmt.Sprintf("required module %s@%s retracted by module author: %s", m.Path, m.Version, rationale))
} else {
retracted = append(retracted, fmt.Sprintf("required module %s@%s retracted by module author", m.Path, m.Version))
}
}

return retracted, nil
}

// ShortRetractionRationale returns a retraction rationale string that is safe
// to print in a terminal. It returns hard-coded strings if the rationale
// is empty, too long, or contains non-printable characters.
//
// It returns true if the rationale was printable, and false if it was not (too
// long, contains graphics, etc).
func shortRetractionRationale(rationales []string) (string, bool) {
if len(rationales) == 0 {
return "", false
}
rationale := rationales[0]

const maxRationaleBytes = 500
if i := strings.Index(rationale, "\n"); i >= 0 {
rationale = rationale[:i]
}
rationale = strings.TrimSpace(rationale)
if rationale == "" || rationale == "retracted by module author" {
return "", false
}
if len(rationale) > maxRationaleBytes {
return "", false
}
for _, r := range rationale {
if !unicode.IsGraphic(r) && !unicode.IsSpace(r) {
return "", false
}
}
// NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here.
return rationale, true
}
13 changes: 13 additions & 0 deletions cmd/gorelease/testdata/mod/example.com_retract_v0.0.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- go.mod --
module example.com/retract

go 1.12

require example.com/retractdep v1.0.0
-- go.sum --
example.com/retractdep v1.0.0 h1:SOVn6jA2ygQY+v8/5aAwxVUJ9teuLrdH/UmbUtp2C44=
example.com/retractdep v1.0.0/go.mod h1:UjjWSH/ulfbAGgQQwm7pAZ988MFRngUSkJnzcuPsYDI=
-- a.go --
package a

import _ "example.com/retractdep"
8 changes: 8 additions & 0 deletions cmd/gorelease/testdata/mod/example.com_retractdep_v1.0.0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- go.mod --
module example.com/retractdep

go 1.12
-- a.go --
package a

const A = "a"
11 changes: 11 additions & 0 deletions cmd/gorelease/testdata/mod/example.com_retractdep_v1.0.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- go.mod --
module example.com/retractdep

go 1.12

// Remote-triggered crash in package foo. See CVE-2021-01234.
retract v1.0.0
-- a.go --
package a

const A = "a"
12 changes: 12 additions & 0 deletions cmd/gorelease/testdata/mod/example.com_retractdep_v2_v2.0.0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Identical to v1.0.0: just need a new version so that we can test different
# error messages based on the vX.0.1 retraction comments. We can't test them in
# the same major version because go mod will always use the latest version's
# error message.
-- go.mod --
module example.com/retractdep/v2

go 1.12
-- a.go --
package a

const A = "a"
10 changes: 10 additions & 0 deletions cmd/gorelease/testdata/mod/example.com_retractdep_v2_v2.0.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- go.mod --
module example.com/retractdep/v2

go 1.12

retract v2.0.0
-- a.go --
package a

const A = "a"
12 changes: 12 additions & 0 deletions cmd/gorelease/testdata/mod/example.com_retractdep_v3_v3.0.0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Identical to v1.0.0: just need a new version so that we can test different
# error messages based on the vX.0.1 retraction comments. We can't test them in
# the same major version because go mod will always use the latest version's
# error message.
-- go.mod --
module example.com/retractdep/v3

go 1.12
-- a.go --
package a

const A = "a"
11 changes: 11 additions & 0 deletions cmd/gorelease/testdata/mod/example.com_retractdep_v3_v3.0.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- go.mod --
module example.com/retractdep/v3

go 1.12

// This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message. This is a very long message.
retract v3.0.0
-- a.go --
package a

const A = "a"
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- go.mod --
module example.com/retracttransitive

go 1.12

require example.com/retract v0.0.1
-- go.sum --
example.com/retract v0.0.1 h1:Afj8efoHilltHZNLlEARzpc1Vkc5d6ugWKIE/YDmXuQ=
example.com/retract v0.0.1/go.mod h1:DUqXjcGF3aJhkjxsUjQ0DG65b51DDBvFrEbcr9kkyto=
example.com/retractdep v1.0.0 h1:SOVn6jA2ygQY+v8/5aAwxVUJ9teuLrdH/UmbUtp2C44=
example.com/retractdep v1.0.0/go.mod h1:UjjWSH/ulfbAGgQQwm7pAZ988MFRngUSkJnzcuPsYDI=
-- a.go --
package a

import _ "example.com/retract"
6 changes: 6 additions & 0 deletions cmd/gorelease/testdata/retract/retract_verify_direct_dep.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mod=example.com/retract
version=v0.0.1
success=false
-- want --
Inferred base version: v0.0.1
required module example.com/retractdep@v1.0.0 retracted by module author: Remote-triggered crash in package foo. See CVE-2021-01234.
18 changes: 18 additions & 0 deletions cmd/gorelease/testdata/retract/retract_verify_long_msg.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
mod=example.com/retract
success=false
-- want --
Inferred base version: v0.0.1
required module example.com/retractdep/v3@v3.0.0 retracted by module author
-- go.mod --
module example.com/retract

go 1.12

require example.com/retractdep/v3 v3.0.0
-- go.sum --
example.com/retractdep/v3 v3.0.0 h1:LEaqsEpt7J4Er+qSPqL7bENpIkRdZdaOE6KaUaiNB5I=
example.com/retractdep/v3 v3.0.0/go.mod h1:B2rEwAWayv3FJ2jyeiq9O3UBbxSvdDqZUtxmKsLyg6k=
-- a.go --
package a

import _ "example.com/retractdep/v3"
18 changes: 18 additions & 0 deletions cmd/gorelease/testdata/retract/retract_verify_no_msg.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
mod=example.com/retract
success=false
-- want --
Inferred base version: v0.0.1
required module example.com/retractdep/v2@v2.0.0 retracted by module author
-- go.mod --
module example.com/retract

go 1.12

require example.com/retractdep/v2 v2.0.0
-- go.sum --
example.com/retractdep/v2 v2.0.0 h1:ehV4yfX3A3jNlRnBmHPxq1TyVs1EhmCYI5miEva6Gv8=
example.com/retractdep/v2 v2.0.0/go.mod h1:rV+p/Yqwnupg15GPVGFRq+un/MYczBZcF1IZ8ubecag=
-- a.go --
package a

import _ "example.com/retractdep/v2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# When a retracted version is transitively depended upon, it should still
# result in a retraction error.
mod=example.com/retracttransitive
version=v0.0.1
success=false
-- want --
Inferred base version: v0.0.1
required module example.com/retractdep@v1.0.0 retracted by module author: Remote-triggered crash in package foo. See CVE-2021-01234.

0 comments on commit 7a70d7c

Please sign in to comment.