diff --git a/.github/workflows/boulder-ci.yml b/.github/workflows/boulder-ci.yml index d93582d0054..2d031843b0d 100644 --- a/.github/workflows/boulder-ci.yml +++ b/.github/workflows/boulder-ci.yml @@ -36,7 +36,7 @@ jobs: matrix: # Add additional docker image tags here and all tests will be run with the additional image. BOULDER_TOOLS_TAG: - - go1.26.6_2026-08-13 + - go1.26.6_2026-08-21 # Tests command definitions. Use the entire "docker compose" command you want to run. tests: # Run ./test.sh --help for a description of each of the flags. diff --git a/ca/ca.go b/ca/ca.go index ebad4c709cf..b8f6af14d13 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -481,7 +481,7 @@ func (ca *certificateAuthorityImpl) generateSerialNumber() *big.Int { // rand.Read is guaranteed since Go 1.24 not to return error (it crashes the program instead) // https://tip.golang.org/doc/go1.24#cryptorandpkgcryptorand // https://pkg.go.dev/crypto/rand@master#Read - rand.Read(serialBytes[1:]) //nolint:errcheck //rand.Read is infallible + rand.Read(serialBytes[1:]) serialBigInt := big.NewInt(0) serialBigInt = serialBigInt.SetBytes(serialBytes) diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go index dee21261ffa..660ba767502 100644 --- a/cmd/boulder-ca/main.go +++ b/cmd/boulder-ca/main.go @@ -53,19 +53,13 @@ type Config struct { Issuers []issuance.IssuerConfig `validate:"min=1,dive"` } - // What digits we should prepend to serials after randomly generating them. - // Deprecated: Use SerialPrefixHex instead. - SerialPrefix int `validate:"required_without=SerialPrefixHex,omitempty,min=1,max=127"` - // SerialPrefixHex is the hex string to prepend to serials after randomly // generating them. The minimum value is "01" to ensure that at least // one bit in the prefix byte is set. The maximum value is "7f" to // ensure that the first bit in the prefix byte is not set. The validate // library cannot enforce mix/max values on strings, so that is done in // NewCertificateAuthorityImpl. - // - // TODO(#7213): Replace `required_without` with `required` when SerialPrefix is removed. - SerialPrefixHex string `validate:"required_without=SerialPrefix,omitempty,hexadecimal,len=2"` + SerialPrefixHex string `validate:"required,omitempty,hexadecimal,len=2"` // MaxNames is the maximum number of subjectAltNames in a single cert. // The value supplied MUST be greater than 0 and no more than 100. These @@ -134,12 +128,9 @@ func main() { c.CA.DebugAddr = *debugAddr } - serialPrefix := byte(c.CA.SerialPrefix) - if c.CA.SerialPrefixHex != "" { - parsedSerialPrefix, err := strconv.ParseUint(c.CA.SerialPrefixHex, 16, 8) - cmd.FailOnError(err, "Couldn't convert SerialPrefixHex to int") - serialPrefix = byte(parsedSerialPrefix) - } + parsedSerialPrefix, err := strconv.ParseUint(c.CA.SerialPrefixHex, 16, 8) + cmd.FailOnError(err, "Couldn't convert SerialPrefixHex to int") + serialPrefix := byte(parsedSerialPrefix) scope, logger, oTelShutdown := cmd.StatsAndLogging(c.Syslog, c.OpenTelemetry, c.CA.DebugAddr) defer oTelShutdown(context.Background()) diff --git a/cmd/shell_test.go b/cmd/shell_test.go index 16cc8c114a3..defa7836e7a 100644 --- a/cmd/shell_test.go +++ b/cmd/shell_test.go @@ -278,7 +278,7 @@ func TestFailExit(t *testing.T) { return } - cmd := exec.Command(os.Args[0], "-test.run=TestFailExit") + cmd := exec.Command(os.Args[0], "-test.run=TestFailExit") //nolint:gosec // os.Args is untrusted but we're okay with that in test code cmd.Env = append(os.Environ(), "TIME_TO_DIE=1") output, err := cmd.CombinedOutput() test.AssertError(t, err, "running a failing program") @@ -304,7 +304,7 @@ func TestPanicStackTrace(t *testing.T) { return } - cmd := exec.Command(os.Args[0], "-test.run=TestPanicStackTrace") + cmd := exec.Command(os.Args[0], "-test.run=TestPanicStackTrace") //nolint:gosec // os.Args is untrusted but we're okay with that in test code cmd.Env = append(os.Environ(), "AT_THE_DISCO=1") output, err := cmd.CombinedOutput() test.AssertError(t, err, "running a failing program") diff --git a/linter/lints/cpcps/helpers_test.go b/linter/lints/cpcps/helpers_test.go index bac03d60d39..696e1e385cf 100644 --- a/linter/lints/cpcps/helpers_test.go +++ b/linter/lints/cpcps/helpers_test.go @@ -27,6 +27,7 @@ import ( zpkix "github.com/zmap/zcrypto/x509/pkix" "github.com/zmap/zlint/v3/lint" "github.com/zmap/zlint/v3/util" + "golang.org/x/crypto/cryptobyte" ) // testKey generates an ECDSA key on the given curve. @@ -152,18 +153,23 @@ func testSCT(logID [32]byte) []byte { // containing one fake SCT per given log ID. func testSCTListExtension(t *testing.T, logIDs ...[32]byte) pkix.Extension { t.Helper() - var list []byte - for _, logID := range logIDs { - sct := testSCT(logID) - list = append(list, byte(len(sct)>>8), byte(len(sct))) - list = append(list, sct...) - } - full := append([]byte{byte(len(list) >> 8), byte(len(list))}, list...) - value, err := asn1.Marshal(full) - if err != nil { - t.Fatalf("marshalling SCT list: %s", err) + + var sctList cryptobyte.Builder + sctList.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { + for _, logID := range logIDs { + child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { + child.AddBytes(testSCT(logID)) + }) + } + }) + + var extnValue cryptobyte.Builder + extnValue.AddASN1OctetString(sctList.BytesOrPanic()) + + return pkix.Extension{ + Id: asn1.ObjectIdentifier(util.TimestampOID), + Value: extnValue.BytesOrPanic(), } - return pkix.Extension{Id: asn1.ObjectIdentifier(util.TimestampOID), Value: value} } // testLeafTemplate returns a template matching the Subscriber (Server) diff --git a/observer/monitor.go b/observer/monitor.go index 7784fddfa6d..8b6c12f073a 100644 --- a/observer/monitor.go +++ b/observer/monitor.go @@ -30,7 +30,7 @@ func (m monitor) start(ctx context.Context, logger blog.Logger) { for { go func() { - ctx, cancel := context.WithTimeout(context.Background(), m.period/2) + ctx, cancel := context.WithTimeout(ctx, m.period/2) defer cancel() // Attempt to probe the configured target. diff --git a/publisher/publisher.go b/publisher/publisher.go index de88bff92b4..3f4c3270760 100644 --- a/publisher/publisher.go +++ b/publisher/publisher.go @@ -325,14 +325,14 @@ func (pub *Impl) singleLogSubmit( "http_status": "", }).Observe(took) - threshold := uint64(time.Now().Add(time.Minute).UnixMilli()) //nolint: gosec // Current-ish timestamp is guaranteed to fit in a uint64 + threshold := uint64(time.Now().Add(time.Minute).UnixMilli()) if sct.Timestamp > threshold { return nil, fmt.Errorf("SCT Timestamp was too far in the future (%d > %d)", sct.Timestamp, threshold) } // For regular certificates, we could get an old SCT, but that shouldn't // happen for precertificates. - threshold = uint64(time.Now().Add(-10 * time.Minute).UnixMilli()) //nolint: gosec // Current-ish timestamp is guaranteed to fit in a uint64 + threshold = uint64(time.Now().Add(-10 * time.Minute).UnixMilli()) if kind != pubpb.SubmissionType_final && sct.Timestamp < threshold { return nil, fmt.Errorf("SCT Timestamp was too far in the past (%d < %d)", sct.Timestamp, threshold) } @@ -365,7 +365,7 @@ func CreateTestingSignedSCT(req []string, k *ecdsa.PrivateKey, precert bool, tim // Sign the SCT rawKey, _ := x509.MarshalPKIXPublicKey(&k.PublicKey) logID := sha256.Sum256(rawKey) - timestampMillis := uint64(timestamp.UnixMilli()) //nolint: gosec // Current-ish timestamp is guaranteed to fit in a uint64 + timestampMillis := uint64(timestamp.UnixMilli()) serialized, _ := ct.SerializeSCTSignatureInput(ct.SignedCertificateTimestamp{ SCTVersion: ct.V1, LogID: ct.LogID{KeyID: logID}, diff --git a/ratelimits/limiter_test.go b/ratelimits/limiter_test.go index 47427e3c4c5..f8bcfb02a54 100644 --- a/ratelimits/limiter_test.go +++ b/ratelimits/limiter_test.go @@ -49,7 +49,7 @@ func setup(t *testing.T) (context.Context, map[string]*Limiter, *TransactionBuil // runs. randIP := make(net.IP, 4) for i := range 4 { - randIP[i] = byte(rand.IntN(256)) + randIP[i] = byte(rand.IntN(256)) //nolint:gosec // we know the integer is byte-sized } // Construct a limiter for each source. diff --git a/sa/model_test.go b/sa/model_test.go index 2640076dac6..8b7bdcd4d45 100644 --- a/sa/model_test.go +++ b/sa/model_test.go @@ -375,7 +375,7 @@ func TestIncidentSerialModel(t *testing.T) { testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms) test.AssertNotError(t, err, "Couldn't create test dbMap") - defer test.ResetIncidentsTestDatabase(t) + t.Cleanup(test.ResetIncidentsTestDatabase(t)) // Inserting and retrieving a row with only the serial populated should work. _, err = testIncidentsDbMap.ExecContext(ctx, diff --git a/sa/sa_test.go b/sa/sa_test.go index ea644b327d7..a621e8fdbb6 100644 --- a/sa/sa_test.go +++ b/sa/sa_test.go @@ -2918,7 +2918,7 @@ func TestIncidentsForSerial(t *testing.T) { testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms) test.AssertNotError(t, err, "Couldn't create test dbMap") - defer test.ResetIncidentsTestDatabase(t) + t.Cleanup(test.ResetIncidentsTestDatabase(t)) weekAgo := sa.clk.Now().Add(-time.Hour * 24 * 7) @@ -3033,7 +3033,7 @@ func TestSerialsForIncident(t *testing.T) { testIncidentsDbMap, err := DBMapForTest(vars.DBConnIncidentsFullPerms) test.AssertNotError(t, err, "Couldn't create test dbMap") - defer test.ResetIncidentsTestDatabase(t) + t.Cleanup(test.ResetIncidentsTestDatabase(t)) // Request serials from a malformed incident table name. mockServerStream := &fakeServerStream[sapb.IncidentSerial]{} @@ -3900,7 +3900,7 @@ func TestUnpauseAccount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - defer test.ResetBoulderTestDatabase(t) + t.Cleanup(test.ResetBoulderTestDatabase(t)) // Setup table state. for _, state := range tt.state { @@ -4145,7 +4145,7 @@ func TestPauseIdentifiers(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - defer test.ResetBoulderTestDatabase(t) + t.Cleanup(test.ResetBoulderTestDatabase(t)) // Setup table state. for _, state := range tt.state { @@ -4283,7 +4283,7 @@ func TestCheckIdentifiersPaused(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - defer test.ResetBoulderTestDatabase(t) + t.Cleanup(test.ResetBoulderTestDatabase(t)) // Setup table state. for _, state := range tt.state { @@ -4389,7 +4389,7 @@ func TestGetPausedIdentifiers(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - defer test.ResetBoulderTestDatabase(t) + t.Cleanup(test.ResetBoulderTestDatabase(t)) // Setup table state. for _, state := range tt.state { diff --git a/salesforce/pardot_test.go b/salesforce/pardot_test.go index 7407b1d5265..d46ae3cea5c 100644 --- a/salesforce/pardot_test.go +++ b/salesforce/pardot_test.go @@ -10,10 +10,12 @@ import ( "time" "github.com/jmhodges/clock" + "github.com/letsencrypt/boulder/test" ) func defaultTokenHandler(w http.ResponseWriter, r *http.Request) { + //nolint:gosec // G117: The "AccessToken" field is not a real access token err := json.NewEncoder(w).Encode(oauthTokenResp{ AccessToken: "dummy", ExpiresIn: 3600, @@ -113,6 +115,7 @@ func TestSendContactTokenExpiry(t *testing.T) { token = "old_token" tokenRetrieved = true } + //nolint:gosec // G117: The "AccessToken" field is not a real access token err := json.NewEncoder(w).Encode(oauthTokenResp{ AccessToken: token, ExpiresIn: 3600, diff --git a/sfe/forms/fields.go b/sfe/forms/fields.go index 72925d6c457..11a0c56fc9c 100644 --- a/sfe/forms/fields.go +++ b/sfe/forms/fields.go @@ -90,14 +90,14 @@ func (field DropdownField) RenderField() template.HTML { reqAttr = `required="required"` } var b strings.Builder - b.WriteString(fmt.Sprintf(` + fmt.Fprintf(&b, `
%[3]s
diff --git a/test/boulder-tools/Dockerfile b/test/boulder-tools/Dockerfile index 75de8e3521e..f099bb48c8e 100644 --- a/test/boulder-tools/Dockerfile +++ b/test/boulder-tools/Dockerfile @@ -19,7 +19,7 @@ RUN gotip download RUN go install github.com/rubenv/sql-migrate/sql-migrate@v1.1.2 RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.5 RUN go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1 -RUN go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 +RUN go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.1 RUN go install github.com/jsha/minica@v1.1.0 FROM rust:latest AS rustdeps diff --git a/test/check-req-xrefs/main.go b/test/check-req-xrefs/main.go index 69c270c3e1a..ea251efc93d 100644 --- a/test/check-req-xrefs/main.go +++ b/test/check-req-xrefs/main.go @@ -43,7 +43,7 @@ var quoteLine = regexp.MustCompile(`^\s*// (.*)$`) // getDocument returns the lines of the document at the given URL. func getDocument(url string) ([]string, error) { - resp, err := http.Get(url) + resp, err := http.Get(url) //nolint:gosec // G704: this is a local tool, okay to make requests based on inputs if err != nil { return nil, fmt.Errorf("fetching %s: %w", url, err) } @@ -71,7 +71,7 @@ func main() { checked := 0 failed := 0 for _, path := range paths { - contents, err := os.ReadFile(path) + contents, err := os.ReadFile(path) //nolint:gosec // G703: this is a local tool, path traversal is okay. if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/tools/crldps/main.go b/tools/crldps/main.go index cf64d1de08c..3c9aec7312d 100644 --- a/tools/crldps/main.go +++ b/tools/crldps/main.go @@ -42,7 +42,7 @@ func main() { issuer, err := core.LoadCert(*caPath) if err != nil { - log.Fatalf("Failed to load issuer certificate from %q: %s", os.Args[1], err) + log.Fatalf("Failed to load issuer certificate from %q: %s", os.Args[1], err) //nolint:gosec // log injection from command line is okay, this is a tool } if len(issuer.Subject.CommonName) > 63 || !rfc1035label.MatchString(issuer.Subject.CommonName) { diff --git a/trees/subtree/subtree.go b/trees/subtree/subtree.go index 5ccfed6bb77..c4100b196d8 100644 --- a/trees/subtree/subtree.go +++ b/trees/subtree/subtree.go @@ -278,7 +278,7 @@ func MTH(leaves []tlog.Hash) tlog.Hash { return leaves[0] default: // RFC 6962: split at the largest power of two smaller than n. - k := 1 << (bits.Len(uint(len(leaves)-1)) - 1) //nolint:gosec // G115: the default case means len(leaves) >= 2, so len(leaves)-1 is positive. + k := 1 << (bits.Len(uint(len(leaves)-1)) - 1) return tlog.NodeHash(MTH(leaves[:k]), MTH(leaves[k:])) } } diff --git a/va/caa.go b/va/caa.go index da41ad5305c..9abf3680215 100644 --- a/va/caa.go +++ b/va/caa.go @@ -112,7 +112,6 @@ func (va *ValidationAuthorityImpl) DoCAA(ctx context.Context, req *vapb.IsCAAVal opCAA, prob, nil, - //nolint:unparam // core.ValidationRecord is always nil because we don't return those for CAA. func(ctx context.Context) ([]core.ValidationRecord, *corepb.ProblemDetails, error) { result, err := va.experimentalVA.DoCAA(ctx, req) if err != nil { diff --git a/va/http_test.go b/va/http_test.go index 417049348d0..3bae3317581 100644 --- a/va/http_test.go +++ b/va/http_test.go @@ -1323,7 +1323,7 @@ func httpSrv(t *testing.T, token string, ipv6 bool) *httptest.Server { http.Redirect(w, r, fmt.Sprintf("http://other.valid.com:%d/%s", port, path500), http.StatusMovedPermanently) } else if strings.HasSuffix(r.URL.Path, pathLooper) { t.Logf("HTTPSRV: Got a loop req\n") - http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently) + http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently) //nolint:gosec // open redirect, but that's okay, this is a test } else if strings.HasSuffix(r.URL.Path, pathRedirectInvalidPort) { t.Logf("HTTPSRV: Got a port redirect req\n") // Port 8080 is not the VA's httpPort or httpsPort and should be rejected diff --git a/va/tlsalpn_test.go b/va/tlsalpn_test.go index ec6af5cd8f8..24ba9ca4014 100644 --- a/va/tlsalpn_test.go +++ b/va/tlsalpn_test.go @@ -161,9 +161,6 @@ func TestTLSALPNTimeoutAfterConnect(t *testing.T) { t.Fatalf("TLSSNI didn't timeout after %s (took %s to return %#v)", timeout, took, err) } - if err == nil { - t.Fatalf("Connection should've timed out") - } prob := detailedError(err) test.AssertEquals(t, prob.Type, probs.ConnectionProblem) @@ -210,9 +207,6 @@ func TestTLSALPN01DialTimeout(t *testing.T) { if took > 2*timeout { t.Fatalf("TLSSNI didn't timeout after %s", timeout) } - if err == nil { - t.Fatalf("Connection should've timed out") - } prob := detailedError(err) test.AssertEquals(t, prob.Type, probs.ConnectionProblem) expected := "64.112.117.254: Timeout during connect (likely firewall problem)" diff --git a/wfe2/wfe.go b/wfe2/wfe.go index 1d4fadfda4f..3ae903cc15a 100644 --- a/wfe2/wfe.go +++ b/wfe2/wfe.go @@ -383,7 +383,7 @@ func (wfe *WebFrontEndImpl) writeJsonResponse(response http.ResponseWriter, logE response.Header().Set("Content-Type", "application/json") response.WriteHeader(status) - _, err = response.Write(jsonReply) + _, err = response.Write(jsonReply) //nolint:gosec // G705: XSS via taint analysis - not an issue because of Content-Type: application/json if err != nil { // Don't worry about returning this error because the caller will // never handle it.