Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3bece14
Migrate golangci-lint to V2
prestonvasquez Nov 21, 2025
c2a228c
(gosec) Create binaryutil package
prestonvasquez Nov 21, 2025
4a0bdf2
(gosec) Create mathutil package
prestonvasquez Nov 21, 2025
1fb1a90
(gosec) Apply G115 fixes to bson package
prestonvasquez Nov 21, 2025
7b9184c
(gosec) Apply G115 fixes to mongo package
prestonvasquez Nov 21, 2025
919a35a
(gosec) Apply G115 fixes to x/bsonx/bsoncore package
prestonvasquez Nov 21, 2025
e0c22c9
(gosec) Apply G115 fixes to x/mongo/driver package
prestonvasquez Nov 21, 2025
ebbed81
(gosec) Apply G115 fixes to x/mongo/driver/auth package
prestonvasquez Nov 21, 2025
725a6b2
(gosec) Apply G115 fixes to x/mongo/driver/mongocrypt package
prestonvasquez Nov 21, 2025
7a2da4a
(gosec) Apply G115 fixes to x/mongo/driver/operation package
prestonvasquez Nov 21, 2025
787d625
(gosec) Apply G115 fixes to x/mongo/driver/session package
prestonvasquez Nov 21, 2025
ec91a18
(gosec) Apply G115 fixes to x/mongo/driver/topology package
prestonvasquez Nov 21, 2025
21379ba
(gosec) Apply G115 fixes to x/mongo/driver/wiremessage package
prestonvasquez Nov 21, 2025
db90e0d
(gosec) Apply G115 fixes to internal/decimal128 package
prestonvasquez Nov 21, 2025
9a59258
(gosec) Apply G115 fixes to internal/driverutil package
prestonvasquez Nov 21, 2025
a5725e7
(gosec) Apply G115 fixes to internal/logger package
prestonvasquez Nov 21, 2025
8e74ce9
(gosec) Apply G115 fixes to internal/integration/unified package
prestonvasquez Nov 21, 2025
90c0463
(gosec) Apply G115 fixes to remaining internal packages
prestonvasquez Nov 21, 2025
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
219 changes: 123 additions & 96 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
run:
timeout: 5m

version: "2"
linters:
disable-all: true
# TODO(GODRIVER-2156): Enable all commented-out linters.
default: none
enable:
- errcheck
# - errorlint
- exportloopref
- gocritic
- goimports
- gosimple
- gosec
- govet
- ineffassign
Expand All @@ -21,95 +14,129 @@ linters:
- prealloc
- revive
- staticcheck
- typecheck
- unused
- unconvert
- unparam
- unused
settings:
errcheck:
exclude-functions:
- .errcheck-excludes
govet:
disable:
- cgocall
- composites
paralleltest:
# Ignore missing calls to `t.Parallel()` and only report incorrect uses of
# `t.Parallel()`.
ignore-missing: true
staticcheck:
checks:
- all
# Disable deprecation warnings for now.
- -SA1012
# Disable "do not pass a nil Context" to allow testing nil contexts in
# tests.
- -SA1019
exclusions:
generated: lax
rules:
# Ignore some linters for example code that is intentionally simplified.
- linters:
- errcheck
- revive
path: examples/
# Disable "unused" linter for code files that depend on the
# "mongocrypt.MongoCrypt" type because the linter build doesn't work
# correctly with CGO enabled. As a result, all calls to a
# "mongocrypt.MongoCrypt" API appear to always panic (see
# mongocrypt_not_enabled.go), leading to confusing messages about unused
# code.
- linters:
- unused
path: x/mongo/driver/crypt.go|mongo/(crypt_retrievers|mongocryptd).go
# Ignore "TLS MinVersion too low", "TLS InsecureSkipVerify set true", and
# "Use of weak random number generator (math/rand instead of crypto/rand)"
# in tests. Disable gosec entirely for test files to reduce noise.
- linters:
- gosec
path: _test\.go
# Ignore missing comments for exported variable/function/type for code in
# the "internal" and "benchmark" directories.
- path: (internal\/|benchmark\/)
text: exported (.+) should have comment( \(or a comment on this block\))? or be unexported
# Ignore missing package comments for directories that aren't frequently
# used by external users.
- path: (internal\/|benchmark\/|x\/|cmd\/|mongo\/integration\/)
text: should have a package comment

linters-settings:
errcheck:
exclude-functions: .errcheck-excludes
govet:
disable:
- cgocall
- composites
paralleltest:
# Ignore missing calls to `t.Parallel()` and only report incorrect uses of `t.Parallel()`.
ignore-missing: true
staticcheck:
checks: [
"all",
"-SA1019", # Disable deprecation warnings for now.
"-SA1012", # Disable "do not pass a nil Context" to allow testing nil contexts in tests.
]

issues:
exclude-dirs-use-default: false
exclude-dirs:
- (^|/)testdata($|/)
- (^|/)etc($|/)
# Disable all linters for copied third-party code.
- internal/rand
- internal/aws
- internal/assert
exclude-use-default: false
exclude:
# Add all default excluded issues except issues related to exported types/functions not having
# comments; we want those warnings. The defaults are copied from the "--exclude-use-default"
# documentation on https://golangci-lint.run/usage/configuration/#command-line-options
## Defaults ##
# EXC0001 errcheck: Almost all programs ignore errors on these functions and in most cases it's ok
- Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked
# EXC0003 golint: False positive when tests are defined in package 'test'
- func name will be used as test\.Test.* by other packages, and that stutters; consider calling this
# EXC0004 govet: Common false positives
- (possible misuse of unsafe.Pointer|should have signature)
# EXC0005 staticcheck: Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore
- ineffective break statement. Did you mean to break out of the outer loop
# EXC0006 gosec: Too many false-positives on 'unsafe' usage
- Use of unsafe calls should be audited
# EXC0007 gosec: Too many false-positives for parametrized shell calls
- Subprocess launch(ed with variable|ing should be audited)
# EXC0008 gosec: Duplicated errcheck checks
- (G104|G307)
# EXC0009 gosec: Too many issues in popular repos
- (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)
# EXC0010 gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)'
- Potential file inclusion via variable
## End Defaults ##
# Add all default excluded issues except issues related to exported
# types/functions not having comments; we want those warnings. The
# defaults are copied from the "--exclude-use-default" documentation on
# https://golangci-lint.run/usage/configuration/#command-line-options
#
## Defaults ##
#
# EXC0001 errcheck: Almost all programs ignore errors on these functions
# and in most cases it's ok
- path: (.+)\.go$
text: Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*print(f|ln)?|os\.(Un)?Setenv). is not checked
# EXC0003 golint: False positive when tests are defined in package 'test'
- path: (.+)\.go$
text: func name will be used as test\.Test.* by other packages, and that stutters; consider calling this
# EXC0004 govet: Common false positives
- path: (.+)\.go$
text: (possible misuse of unsafe.Pointer|should have signature)
# EXC0005 staticcheck: Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore
- path: (.+)\.go$
text: ineffective break statement. Did you mean to break out of the outer loop
# EXC0006 gosec: Too many false-positives on 'unsafe' usage
- path: (.+)\.go$
text: Use of unsafe calls should be audited
# EXC0007 gosec: Too many false-positives for parametrized shell calls
- path: (.+)\.go$
text: Subprocess launch(ed with variable|ing should be audited)
# EXC0008 gosec: Duplicated errcheck checks
- path: (.+)\.go$
text: (G104|G307)
# EXC0009 gosec: Too many issues in popular repos
- path: (.+)\.go$
text: (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)
# EXC0010 gosec: False positive is triggered by
# 'src, err := ioutil.ReadFile(filename)'
- path: (.+)\.go$
text: Potential file inclusion via variable
## End Defaults ##

# Ignore capitalization warning for this weird field name.
- "var-naming: struct field CqCssWxW should be CqCSSWxW"
# Ignore warnings for common "wiremessage.Read..." usage because the safest way to use that API
# is by assigning possibly unused returned byte buffers.
- "SA4006: this value of `wm` is never used"
- "SA4006: this value of `rem` is never used"
- "ineffectual assignment to wm"
- "ineffectual assignment to rem"
# Ignore capitalization warning for this weird field name.
- path: (.+)\.go$
text: "var-naming: struct field CqCssWxW should be CqCSSWxW"

exclude-rules:
# Ignore some linters for example code that is intentionally simplified.
- path: examples/
linters:
- revive
- errcheck
# Disable "unused" linter for code files that depend on the "mongocrypt.MongoCrypt" type because
# the linter build doesn't work correctly with CGO enabled. As a result, all calls to a
# "mongocrypt.MongoCrypt" API appear to always panic (see mongocrypt_not_enabled.go), leading
# to confusing messages about unused code.
- path: x/mongo/driver/crypt.go|mongo/(crypt_retrievers|mongocryptd).go
linters:
- unused
# Ignore "TLS MinVersion too low", "TLS InsecureSkipVerify set true", and "Use of weak random
# number generator (math/rand instead of crypto/rand)" in tests.
- path: _test\.go
text: G401|G402|G404
linters:
- gosec
# Ignore missing comments for exported variable/function/type for code in the "internal" and
# "benchmark" directories.
- path: (internal\/|benchmark\/)
text: exported (.+) should have comment( \(or a comment on this block\))? or be unexported
# Ignore missing package comments for directories that aren't frequently used by external users.
- path: (internal\/|benchmark\/|x\/|cmd\/|mongo\/integration\/)
text: should have a package comment
# Ignore warnings for common "wiremessage.Read..." usage because the
# safest way to use that API is by assigning possibly unused returned byte
# buffers.
- path: (.+)\.go$
text: "SA4006: this value of `wm` is never used"
- path: (.+)\.go$
text: "SA4006: this value of `rem` is never used"
- path: (.+)\.go$
text: ineffectual assignment to wm
- path: (.+)\.go$
text: ineffectual assignment to rem
paths:
- (^|/)testdata($|/)
- (^|/)etc($|/)
# Disable all linters for copied third-party code.
- internal/rand
- internal/aws
- internal/assert
formatters:
enable:
- goimports
exclusions:
generated: lax
paths:
- (^|/)testdata($|/)
- (^|/)etc($|/)
- internal/rand
- internal/aws
- internal/assert
9 changes: 8 additions & 1 deletion bson/buffered_byte_src.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ package bson
import (
"bytes"
"io"

"go.mongodb.org/mongo-driver/v2/internal/mathutil"
)

// bufferedByteSrc implements the low-level byteSrc interface by reading
Expand Down Expand Up @@ -115,7 +117,12 @@ func (b *bufferedByteSrc) regexLength() (int32, error) {

// Total length = first C-string length (pattern) + second C-string length
// (options) + 2 null terminators
return int32(i + j + 2), nil
length, err := mathutil.SafeConvertNumeric[int32](i + j + 2)
if err != nil {
return 0, err
}

return length, nil
}

func (*bufferedByteSrc) streamable() bool {
Expand Down
3 changes: 3 additions & 0 deletions bson/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@ func (d Decimal128) BigInt() (*big.Int, int, error) {
if high>>61&3 == 3 {
// Bits: 1*sign 2*ignored 14*exponent 111*significand.
// Implicit 0b100 prefix in significand.
// nolint:gosec // G115: bitmasked to 14 bits, safe conversion
exp = int(high >> 47 & (1<<14 - 1))
// Spec says all of these values are out of range.
high, low = 0, 0
} else {
// Bits: 1*sign 14*exponent 113*significand
// nolint:gosec // G115: bitmasked to 14 bits, safe conversion
exp = int(high >> 49 & (1<<14 - 1))
high &= (1<<49 - 1)
}
Expand Down Expand Up @@ -314,6 +316,7 @@ func ParseDecimal128FromBigInt(bi *big.Int, exp int) (Decimal128, bool) {
l = l<<8 | uint64(b[i])
}

// nolint:gosec // G115: exp is within MinDecimal128Exp to MaxDecimal128Exp range
h |= uint64(exp-MinDecimal128Exp) & uint64(1<<14-1) << 49
if bi.Sign() == -1 {
h |= 1 << 63
Expand Down
23 changes: 19 additions & 4 deletions bson/default_value_encoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"reflect"
"sync"

"go.mongodb.org/mongo-driver/v2/internal/mathutil"
"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore"
)

Expand Down Expand Up @@ -120,17 +121,30 @@ func fitsIn32Bits(i int64) bool {
func intEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {
switch val.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32:
return vw.WriteInt32(int32(val.Int()))
i64 := val.Int()
i32, err := mathutil.SafeConvertNumeric[int32](i64)
if err != nil {
return ValueEncoderError{Name: "IntEncodeValue", Kinds: []reflect.Kind{val.Kind()}, Received: val}
}
return vw.WriteInt32(i32)
case reflect.Int:
i64 := val.Int()
if fitsIn32Bits(i64) {
return vw.WriteInt32(int32(i64))
i32, err := mathutil.SafeConvertNumeric[int32](i64)
if err != nil {
return ValueEncoderError{Name: "IntEncodeValue", Kinds: []reflect.Kind{reflect.Int}, Received: val}
}
return vw.WriteInt32(i32)
}
return vw.WriteInt64(i64)
case reflect.Int64:
i64 := val.Int()
if ec.minSize && fitsIn32Bits(i64) {
return vw.WriteInt32(int32(i64))
i32, err := mathutil.SafeConvertNumeric[int32](i64)
if err != nil {
return ValueEncoderError{Name: "IntEncodeValue", Kinds: []reflect.Kind{reflect.Int64}, Received: val}
}
return vw.WriteInt32(i32)
}
return vw.WriteInt64(i64)
}
Expand Down Expand Up @@ -369,7 +383,8 @@ func binaryEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error
func vectorEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {
t := val.Type()
if !val.IsValid() || t != tVector {
return ValueEncoderError{Name: "VectorEncodeValue",
return ValueEncoderError{
Name: "VectorEncodeValue",
Types: []reflect.Type{tVector},
Received: val,
}
Expand Down
2 changes: 1 addition & 1 deletion bson/mgoregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func getterEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) erro
return vw.WriteNull()
}
vv := reflect.ValueOf(x)
encoder, err := ec.Registry.LookupEncoder(vv.Type())
encoder, err := ec.LookupEncoder(vv.Type())
if err != nil {
return err
}
Expand Down
20 changes: 15 additions & 5 deletions bson/objectid.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"io"
"sync/atomic"
"time"

"go.mongodb.org/mongo-driver/v2/internal/mathutil"
)

// ErrInvalidHex indicates that a hex string cannot be converted to an ObjectID.
Expand All @@ -31,11 +33,15 @@ type ObjectID [12]byte
// NilObjectID is the zero value for ObjectID.
var NilObjectID ObjectID

var objectIDCounter = readRandomUint32()
var processUnique = processUniqueBytes()
var (
objectIDCounter = readRandomUint32()
processUnique = processUniqueBytes()
)

var _ encoding.TextMarshaler = ObjectID{}
var _ encoding.TextUnmarshaler = &ObjectID{}
var (
_ encoding.TextMarshaler = ObjectID{}
_ encoding.TextUnmarshaler = &ObjectID{}
)

// NewObjectID generates a new ObjectID.
func NewObjectID() ObjectID {
Expand All @@ -46,7 +52,11 @@ func NewObjectID() ObjectID {
func NewObjectIDFromTimestamp(timestamp time.Time) ObjectID {
var b [12]byte

binary.BigEndian.PutUint32(b[0:4], uint32(timestamp.Unix()))
secs, err := mathutil.SafeConvertNumeric[uint32](timestamp.Unix())
if err != nil {
secs = 0
}
binary.BigEndian.PutUint32(b[0:4], secs)
copy(b[4:9], processUnique[:])
putUint24(b[9:12], atomic.AddUint32(&objectIDCounter, 1))

Expand Down
2 changes: 2 additions & 0 deletions bson/uint_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var _ typeDecoder = &uintCodec{}
func (uic *uintCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {
switch val.Kind() {
case reflect.Uint8, reflect.Uint16:
// nolint:gosec // G115: uint8 and uint16 fit within int32
return vw.WriteInt32(int32(val.Uint()))
case reflect.Uint, reflect.Uint32, reflect.Uint64:
u64 := val.Uint()
Expand All @@ -35,6 +36,7 @@ func (uic *uintCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.
useMinSize := ec.minSize || (uic.encodeToMinSize && val.Kind() != reflect.Uint64)

if u64 <= math.MaxInt32 && useMinSize {
// nolint:gosec // G115: checked against MaxInt32
return vw.WriteInt32(int32(u64))
}
if u64 > math.MaxInt64 {
Expand Down
Loading
Loading