Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/boulder-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion ca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
17 changes: 4 additions & 13 deletions cmd/boulder-ca/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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())
Expand Down
4 changes: 2 additions & 2 deletions cmd/shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down
28 changes: 17 additions & 11 deletions linter/lints/cpcps/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion observer/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions publisher/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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},
Expand Down
2 changes: 1 addition & 1 deletion ratelimits/limiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion sa/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
defer test.ResetIncidentsTestDatabase(t)()

// Inserting and retrieving a row with only the serial populated should work.
_, err = testIncidentsDbMap.ExecContext(ctx,
Expand Down
12 changes: 6 additions & 6 deletions sa/sa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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]{}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions salesforce/pardot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions sfe/forms/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ func (field DropdownField) RenderField() template.HTML {
reqAttr = `required="required"`
}
var b strings.Builder
b.WriteString(fmt.Sprintf(`
fmt.Fprintf(&b, `
<div class="form-field">
<label for="%[1]s">%[2]s</label>
<small class="field-description">%[3]s</small><br>
<select id="%[1]s" name="%[1]s" %[4]s>
<option value="" selected></option>`, field.name, field.displayName, field.description, reqAttr))
<option value="" selected></option>`, field.name, field.displayName, field.description, reqAttr)
for _, o := range field.options {
b.WriteString(fmt.Sprintf(`<option value="%[1]s">%[1]s</option>`, o))
fmt.Fprintf(&b, `<option value="%[1]s">%[1]s</option>`, o)
}
b.WriteString(`</select>
<div class="error-message"></div>
Expand Down
2 changes: 1 addition & 1 deletion test/boulder-tools/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions test/check-req-xrefs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion tools/crldps/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion trees/subtree/subtree.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:]))
}
}
1 change: 0 additions & 1 deletion va/caa.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion va/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 0 additions & 6 deletions va/tlsalpn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)"
Expand Down
2 changes: 1 addition & 1 deletion wfe2/wfe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down