diff --git a/cli/clistat/disk.go b/cli/clistat/disk.go index de79fe8a43d45..6a3b2a0ffff72 100644 --- a/cli/clistat/disk.go +++ b/cli/clistat/disk.go @@ -19,7 +19,7 @@ func (*Statter) Disk(p Prefix, path string) (*Result, error) { return nil, err } var r Result - r.Total = ptr.To(float64(stat.Blocks * uint64(stat.Bsize))) + r.Total = ptr.To(float64(stat.Blocks * uint64(stat.Bsize))) // #nosec G115 -- int64 to uint64 is safe for filesystem stats (always positive) r.Used = float64(stat.Blocks-stat.Bfree) * float64(stat.Bsize) r.Unit = "B" r.Prefix = p diff --git a/cli/cliutil/levenshtein/levenshtein.go b/cli/cliutil/levenshtein/levenshtein.go index f509e5b1000d1..0922ab72816dd 100644 --- a/cli/cliutil/levenshtein/levenshtein.go +++ b/cli/cliutil/levenshtein/levenshtein.go @@ -32,8 +32,9 @@ func Distance(a, b string, maxDist int) (int, error) { if len(b) > 255 { return 0, xerrors.Errorf("levenshtein: b must be less than 255 characters long") } - m := uint8(len(a)) - n := uint8(len(b)) + // We've already checked that len(a) and len(b) are <= 255, so conversion is safe + m := uint8(len(a)) // #nosec G115 -- length is checked to be <= 255 + n := uint8(len(b)) // #nosec G115 -- length is checked to be <= 255 // Special cases for empty strings if m == 0 { @@ -76,7 +77,7 @@ func Distance(a, b string, maxDist int) (int, error) { d[i][j]+subCost, // substitution ) // check maxDist on the diagonal - if maxDist > -1 && i == j && d[i+1][j+1] > uint8(maxDist) { + if maxDist > -1 && i == j && maxDist <= 255 && d[i+1][j+1] > uint8(maxDist) { // #nosec G115 -- we check maxDist <= 255 return int(d[i+1][j+1]), ErrMaxDist } } diff --git a/coderd/database/lock.go b/coderd/database/lock.go index 0bc8b2a75d001..4daea1f9699a5 100644 --- a/coderd/database/lock.go +++ b/coderd/database/lock.go @@ -18,5 +18,7 @@ const ( func GenLockID(name string) int64 { hash := fnv.New64() _, _ = hash.Write([]byte(name)) - return int64(hash.Sum64()) + // For our locking purposes, it's acceptable to have potential overflow + // The important part is consistency of the lock ID for a given name + return int64(hash.Sum64()) // #nosec G115 -- potential overflow is acceptable for lock IDs } diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index a9dbc3e530994..4a50b2f9c488b 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -160,7 +160,8 @@ func (t Template) DeepCopy() Template { func (t Template) AutostartAllowedDays() uint8 { // Just flip the binary 0s to 1s and vice versa. // There is an extra day with the 8th bit that needs to be zeroed. - return ^uint8(t.AutostartBlockDaysOfWeek) & 0b01111111 + // The conversion is safe because AutostartBlockDaysOfWeek is enforced to use only the lower 7 bits + return ^uint8(t.AutostartBlockDaysOfWeek) & 0b01111111 // #nosec G115 -- int16 to uint8 is safe as we only use 7 bits } func (TemplateVersion) RBACObject(template Template) rbac.Object { diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index a68cebd1fac93..5aea493c14347 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -77,7 +77,7 @@ func (r TemplateAutostopRequirement) DaysMap() map[time.Weekday]bool { func daysMap(daysOfWeek uint8) map[time.Weekday]bool { days := make(map[time.Weekday]bool) for i, day := range DaysOfWeek { - days[day] = daysOfWeek&(1< 0) + thresh := uint32(-n) % uint32(n) // #nosec G115 -- int32 to uint32 after negation is an acceptable pattern here for low < thresh { err := binary.Read(rand.Reader, binary.BigEndian, &v) if err != nil { return 0, err } - prod = uint64(v) * uint64(n) - low = uint32(prod) + prod = uint64(v) * uint64(n) // #nosec G115 -- uint32 to uint64 is always safe + low = uint32(prod) // #nosec G115 -- truncation is intentional for the algorithm } } - return int32(prod >> 32), nil + return int32(prod >> 32), nil // #nosec G115 -- proper range is guaranteed by the algorithm } // StringCharset generates a random string using the provided charset and size. @@ -84,12 +84,13 @@ func StringCharset(charSetStr string, size int) (string, error) { buf.Grow(size) for i := 0; i < size; i++ { - r := binary.BigEndian.Uint32(entropy[:4]) + r := binary.BigEndian.Uint32(entropy[:4]) // #nosec G115 -- not a conversion, just reading bytes as uint32 entropy = entropy[4:] + // Charset length is limited by string size, so conversion to int32 is safe ci, err := unbiasedModulo32( r, - int32(len(charSet)), + int32(len(charSet)), // #nosec G115 -- int to int32 is safe for charset length ) if err != nil { return "", err diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index 4585179916477..346790043d90d 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -886,7 +886,7 @@ func (r *Runner) commitQuota(ctx context.Context, resources []*sdkproto.Resource resp, err := r.quotaCommitter.CommitQuota(ctx, &proto.CommitQuotaRequest{ JobId: r.job.JobId, - DailyCost: int32(cost), + DailyCost: int32(cost), // #nosec G115 -- int to int32 is safe for cost values }) if err != nil { r.queueLog(ctx, &proto.Log{ diff --git a/provisionersdk/archive.go b/provisionersdk/archive.go index a069639a1eba6..86a470681c584 100644 --- a/provisionersdk/archive.go +++ b/provisionersdk/archive.go @@ -171,11 +171,12 @@ func Untar(directory string, r io.Reader) error { } } case tar.TypeReg: - err := os.MkdirAll(filepath.Dir(target), os.FileMode(header.Mode)|os.ModeDir|100) + // header.Mode is int64, converting to os.FileMode (uint32) is safe for file permissions + err := os.MkdirAll(filepath.Dir(target), os.FileMode(header.Mode)|os.ModeDir|100) // #nosec G115 -- header.Mode contains file mode bits, safely convertible to uint32 if err != nil { return err } - file, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(header.Mode)) + file, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(header.Mode)) // #nosec G115 -- header.Mode contains file mode bits, safely convertible to uint32 if err != nil { return err } diff --git a/pty/pty_linux.go b/pty/pty_linux.go index c0a5d31f63560..e4e5e33b8371f 100644 --- a/pty/pty_linux.go +++ b/pty/pty_linux.go @@ -1,4 +1,4 @@ -// go:build linux +//go:build linux package pty diff --git a/pty/ssh_other.go b/pty/ssh_other.go index fabe8698709c3..fc3ec75785414 100644 --- a/pty/ssh_other.go +++ b/pty/ssh_other.go @@ -79,7 +79,7 @@ var terminalModeFlagNames = map[uint8]string{ // https://github.com/tailscale/tailscale/blob/main/ssh/tailssh/incubator.go func applyTerminalModesToFd(logger *log.Logger, fd uintptr, req ssh.Pty) error { // Get the current TTY configuration. - tios, err := termios.GTTY(int(fd)) + tios, err := termios.GTTY(int(fd)) // #nosec G115 -- uintptr to int is safe for file descriptors if err != nil { return xerrors.Errorf("GTTY: %w", err) } @@ -90,11 +90,11 @@ func applyTerminalModesToFd(logger *log.Logger, fd uintptr, req ssh.Pty) error { for c, v := range req.Modes { if c == gossh.TTY_OP_ISPEED { - tios.Ispeed = int(v) + tios.Ispeed = int(v) // #nosec G115 -- uint32 to int is safe for TTY speeds continue } if c == gossh.TTY_OP_OSPEED { - tios.Ospeed = int(v) + tios.Ospeed = int(v) // #nosec G115 -- uint32 to int is safe for TTY speeds continue } k, ok := terminalModeFlagNames[c] @@ -105,7 +105,9 @@ func applyTerminalModesToFd(logger *log.Logger, fd uintptr, req ssh.Pty) error { continue } if _, ok := tios.CC[k]; ok { - tios.CC[k] = uint8(v) + if v <= 255 { // Ensure value fits in uint8 + tios.CC[k] = uint8(v) // #nosec G115 -- value is checked to fit in uint8 + } continue } if _, ok := tios.Opts[k]; ok { @@ -117,9 +119,9 @@ func applyTerminalModesToFd(logger *log.Logger, fd uintptr, req ssh.Pty) error { logger.Printf("unsupported terminal mode: k=%s, c=%d, v=%d", k, c, v) } } - + // #nosec G115 -- int to int64 is safe for file descriptors // Save the new TTY configuration. - if _, err := tios.STTY(int(fd)); err != nil { + if _, err := tios.STTY(int(fd)); err != nil { // #nosec G115 -- uintptr to int is safe for file descriptors return xerrors.Errorf("STTY: %w", err) } diff --git a/tailnet/conn.go b/tailnet/conn.go index 8f7f8ef7287a2..f32857ffa60ba 100644 --- a/tailnet/conn.go +++ b/tailnet/conn.go @@ -132,7 +132,8 @@ type TelemetrySink interface { // NodeID creates a Tailscale NodeID from the last 8 bytes of a UUID. It ensures // the returned NodeID is always positive. func NodeID(uid uuid.UUID) tailcfg.NodeID { - id := int64(binary.BigEndian.Uint64(uid[8:])) + // This may overflow, but we handle that by ensuring the result is positive below + id := int64(binary.BigEndian.Uint64(uid[8:])) // #nosec G115 -- potential overflow is handled below // ensure id is positive y := id >> 63 diff --git a/tailnet/convert.go b/tailnet/convert.go index 74b067632f231..38d95a3122132 100644 --- a/tailnet/convert.go +++ b/tailnet/convert.go @@ -31,7 +31,7 @@ func NodeToProto(n *Node) (*proto.Node, error) { } derpForcedWebsocket := make(map[int32]string) for i, s := range n.DERPForcedWebsocket { - derpForcedWebsocket[int32(i)] = s + derpForcedWebsocket[int32(i)] = s // #nosec G115 -- int to int32 is safe for indices } addresses := make([]string, len(n.Addresses)) for i, prefix := range n.Addresses { diff --git a/testutil/port.go b/testutil/port.go index b5720e44a0966..e287dd688993f 100644 --- a/testutil/port.go +++ b/testutil/port.go @@ -41,5 +41,6 @@ func RandomPortNoListen(*testing.T) uint16 { rndMu.Lock() x := rnd.Intn(n) rndMu.Unlock() - return uint16(min + x) + // The calculation is safe as min(49152) + max possible x(11847) = 60999, which fits in uint16 + return uint16(min + x) // #nosec G115 -- range is guaranteed to be within uint16 }