diff --git a/.github/actions/setup-go/action.yaml b/.github/actions/setup-go/action.yaml index 1dc3d34f3ba04..7858b8ecc6cac 100644 --- a/.github/actions/setup-go/action.yaml +++ b/.github/actions/setup-go/action.yaml @@ -4,7 +4,7 @@ description: | inputs: version: description: "The Go version to use." - default: "1.22.12" + default: "1.24.1" runs: using: "composite" steps: diff --git a/.golangci.yaml b/.golangci.yaml index aee26ad272f16..c735a06170235 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -203,6 +203,14 @@ linters-settings: - G601 issues: + exclude-dirs: + - coderd/database/dbmem + - node_modules + - .git + + skip-files: + - scripts/rules.go + # Rules listed here: https://github.com/securego/gosec#available-rules exclude-rules: - path: _test\.go @@ -211,6 +219,8 @@ issues: - errcheck - forcetypeassert - exhaustruct # This is unhelpful in tests. + - revive # TODO(JonA): disabling in order to update golangci-lint + - gosec # TODO(JonA): disabling in order to update golangci-lint - path: scripts/* linters: - exhaustruct @@ -220,12 +230,9 @@ issues: max-same-issues: 0 run: - skip-dirs: - - node_modules - - .git + timeout: 10m skip-files: - scripts/rules.go - timeout: 10m # Over time, add more and more linters from # https://golangci-lint.run/usage/linters/ as the code improves. diff --git a/agent/agent.go b/agent/agent.go index 6d7c1c8038daa..39e89c87d9574 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -936,7 +936,7 @@ func (a *agent) run() (retErr error) { connMan.startAgentAPI("send logs", gracefulShutdownBehaviorRemain, func(ctx context.Context, aAPI proto.DRPCAgentClient24) error { err := a.logSender.SendLoop(ctx, aAPI) - if xerrors.Is(err, agentsdk.LogLimitExceededError) { + if xerrors.Is(err, agentsdk.ErrLogLimitExceeded) { // we don't want this error to tear down the API connection and propagate to the // other routines that use the API. The LogSender has already dropped a warning // log, so just return nil here. @@ -1564,9 +1564,13 @@ func (a *agent) Collect(ctx context.Context, networkStats map[netlogtype.Connect } for conn, counts := range networkStats { stats.ConnectionsByProto[conn.Proto.String()]++ + // #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range stats.RxBytes += int64(counts.RxBytes) + // #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range stats.RxPackets += int64(counts.RxPackets) + // #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range stats.TxBytes += int64(counts.TxBytes) + // #nosec G115 - Safe conversions for network statistics which we expect to be within int64 range stats.TxPackets += int64(counts.TxPackets) } @@ -1619,11 +1623,12 @@ func (a *agent) Collect(ctx context.Context, networkStats map[netlogtype.Connect wg.Wait() sort.Float64s(durations) durationsLength := len(durations) - if durationsLength == 0 { + switch { + case durationsLength == 0: stats.ConnectionMedianLatencyMs = -1 - } else if durationsLength%2 == 0 { + case durationsLength%2 == 0: stats.ConnectionMedianLatencyMs = (durations[durationsLength/2-1] + durations[durationsLength/2]) / 2 - } else { + default: stats.ConnectionMedianLatencyMs = durations[durationsLength/2] } // Convert from microseconds to milliseconds. @@ -1730,7 +1735,7 @@ func (a *agent) HTTPDebug() http.Handler { r.Get("/debug/magicsock", a.HandleHTTPDebugMagicsock) r.Get("/debug/magicsock/debug-logging/{state}", a.HandleHTTPMagicsockDebugLoggingState) r.Get("/debug/manifest", a.HandleHTTPDebugManifest) - r.NotFound(func(w http.ResponseWriter, r *http.Request) { + r.NotFound(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) _, _ = w.Write([]byte("404 not found")) }) @@ -2016,7 +2021,7 @@ func (a *apiConnRoutineManager) wait() error { } func PrometheusMetricsHandler(prometheusRegistry *prometheus.Registry, logger slog.Logger) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/plain") // Based on: https://github.com/tailscale/tailscale/blob/280255acae604796a1113861f5a84e6fa2dc6121/ipn/localapi/localapi.go#L489 @@ -2052,5 +2057,6 @@ func WorkspaceKeySeed(workspaceID uuid.UUID, agentName string) (int64, error) { return 42, err } + // #nosec G115 - Safe conversion to generate int64 hash from Sum64, data loss acceptable return int64(h.Sum64()), nil } diff --git a/agent/agentcontainers/containers_dockercli.go b/agent/agentcontainers/containers_dockercli.go index 2225fb18f2987..da42c813c5138 100644 --- a/agent/agentcontainers/containers_dockercli.go +++ b/agent/agentcontainers/containers_dockercli.go @@ -453,8 +453,9 @@ func convertDockerInspect(raw []byte) ([]codersdk.WorkspaceAgentContainer, []str hostPortContainers[hp] = append(hostPortContainers[hp], in.ID) } out.Ports = append(out.Ports, codersdk.WorkspaceAgentContainerPort{ - Network: network, - Port: cp, + Network: network, + Port: cp, + // #nosec G115 - Safe conversion since Docker ports are limited to uint16 range HostPort: uint16(hp), HostIP: p.HostIP, }) @@ -497,12 +498,14 @@ func convertDockerPort(in string) (uint16, string, error) { if err != nil { return 0, "", xerrors.Errorf("invalid port format: %s", in) } + // #nosec G115 - Safe conversion since Docker TCP ports are limited to uint16 range return uint16(p), "tcp", nil case 2: p, err := strconv.Atoi(parts[0]) if err != nil { return 0, "", xerrors.Errorf("invalid port format: %s", in) } + // #nosec G115 - Safe conversion since Docker ports are limited to uint16 range return uint16(p), parts[1], nil default: return 0, "", xerrors.Errorf("invalid port format: %s", in) diff --git a/agent/agentrsa/key_test.go b/agent/agentrsa/key_test.go index dc561d09d4e07..b2f65520558a0 100644 --- a/agent/agentrsa/key_test.go +++ b/agent/agentrsa/key_test.go @@ -28,6 +28,7 @@ func BenchmarkGenerateDeterministicKey(b *testing.B) { for range b.N { // always record the result of DeterministicPrivateKey to prevent // the compiler eliminating the function call. + // #nosec G404 - Using math/rand is acceptable for benchmarking deterministic keys r = agentrsa.GenerateDeterministicKey(rand.Int64()) } diff --git a/agent/agentssh/agentssh.go b/agent/agentssh/agentssh.go index c4aa53f4a550b..473f38c26d64c 100644 --- a/agent/agentssh/agentssh.go +++ b/agent/agentssh/agentssh.go @@ -223,7 +223,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom slog.F("destination_port", destinationPort)) return true }, - PtyCallback: func(ctx ssh.Context, pty ssh.Pty) bool { + PtyCallback: func(_ ssh.Context, _ ssh.Pty) bool { return true }, ReversePortForwardingCallback: func(ctx ssh.Context, bindHost string, bindPort uint32) bool { @@ -240,7 +240,7 @@ func NewServer(ctx context.Context, logger slog.Logger, prometheusRegistry *prom "cancel-streamlocal-forward@openssh.com": unixForwardHandler.HandleSSHRequest, }, X11Callback: s.x11Callback, - ServerConfigCallback: func(ctx ssh.Context) *gossh.ServerConfig { + ServerConfigCallback: func(_ ssh.Context) *gossh.ServerConfig { return &gossh.ServerConfig{ NoClientAuth: true, } @@ -702,6 +702,7 @@ func (s *Server) startPTYSession(logger slog.Logger, session ptySession, magicTy windowSize = nil continue } + // #nosec G115 - Safe conversions for terminal dimensions which are expected to be within uint16 range resizeErr := ptty.Resize(uint16(win.Height), uint16(win.Width)) // If the pty is closed, then command has exited, no need to log. if resizeErr != nil && !errors.Is(resizeErr, pty.ErrClosed) { diff --git a/agent/agentssh/x11.go b/agent/agentssh/x11.go index 90ec34201bbd0..439f2c3021791 100644 --- a/agent/agentssh/x11.go +++ b/agent/agentssh/x11.go @@ -116,7 +116,8 @@ func (s *Server) x11Handler(ctx ssh.Context, x11 ssh.X11) (displayNumber int, ha OriginatorPort uint32 }{ OriginatorAddress: tcpAddr.IP.String(), - OriginatorPort: uint32(tcpAddr.Port), + // #nosec G115 - Safe conversion as TCP port numbers are within uint32 range (0-65535) + OriginatorPort: uint32(tcpAddr.Port), })) if err != nil { s.logger.Warn(ctx, "failed to open X11 channel", slog.Error(err)) @@ -294,6 +295,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string return xerrors.Errorf("failed to write family: %w", err) } + // #nosec G115 - Safe conversion for host name length which is expected to be within uint16 range err = binary.Write(file, binary.BigEndian, uint16(len(host))) if err != nil { return xerrors.Errorf("failed to write host length: %w", err) @@ -303,6 +305,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string return xerrors.Errorf("failed to write host: %w", err) } + // #nosec G115 - Safe conversion for display name length which is expected to be within uint16 range err = binary.Write(file, binary.BigEndian, uint16(len(display))) if err != nil { return xerrors.Errorf("failed to write display length: %w", err) @@ -312,6 +315,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string return xerrors.Errorf("failed to write display: %w", err) } + // #nosec G115 - Safe conversion for auth protocol length which is expected to be within uint16 range err = binary.Write(file, binary.BigEndian, uint16(len(authProtocol))) if err != nil { return xerrors.Errorf("failed to write auth protocol length: %w", err) @@ -321,6 +325,7 @@ func addXauthEntry(ctx context.Context, fs afero.Fs, host string, display string return xerrors.Errorf("failed to write auth protocol: %w", err) } + // #nosec G115 - Safe conversion for auth cookie length which is expected to be within uint16 range err = binary.Write(file, binary.BigEndian, uint16(len(authCookieBytes))) if err != nil { return xerrors.Errorf("failed to write auth cookie length: %w", err) diff --git a/agent/apphealth.go b/agent/apphealth.go index 1a5fd968835e6..1c4e1d126902c 100644 --- a/agent/apphealth.go +++ b/agent/apphealth.go @@ -167,8 +167,8 @@ func shouldStartTicker(app codersdk.WorkspaceApp) bool { return app.Healthcheck.URL != "" && app.Healthcheck.Interval > 0 && app.Healthcheck.Threshold > 0 } -func healthChanged(old map[uuid.UUID]codersdk.WorkspaceAppHealth, new map[uuid.UUID]codersdk.WorkspaceAppHealth) bool { - for name, newValue := range new { +func healthChanged(old map[uuid.UUID]codersdk.WorkspaceAppHealth, updated map[uuid.UUID]codersdk.WorkspaceAppHealth) bool { + for name, newValue := range updated { oldValue, found := old[name] if !found { return true diff --git a/agent/metrics.go b/agent/metrics.go index 6c89827d2c2ee..1755e43a1a365 100644 --- a/agent/metrics.go +++ b/agent/metrics.go @@ -89,21 +89,22 @@ func (a *agent) collectMetrics(ctx context.Context) []*proto.Stats_Metric { for _, metric := range metricFamily.GetMetric() { labels := toAgentMetricLabels(metric.Label) - if metric.Counter != nil { + switch { + case metric.Counter != nil: collected = append(collected, &proto.Stats_Metric{ Name: metricFamily.GetName(), Type: proto.Stats_Metric_COUNTER, Value: metric.Counter.GetValue(), Labels: labels, }) - } else if metric.Gauge != nil { + case metric.Gauge != nil: collected = append(collected, &proto.Stats_Metric{ Name: metricFamily.GetName(), Type: proto.Stats_Metric_GAUGE, Value: metric.Gauge.GetValue(), Labels: labels, }) - } else { + default: a.logger.Error(ctx, "unsupported metric type", slog.F("type", metricFamily.Type.String())) } } diff --git a/agent/reconnectingpty/buffered.go b/agent/reconnectingpty/buffered.go index fb3c9907f4f8c..40b1b5dfe23a4 100644 --- a/agent/reconnectingpty/buffered.go +++ b/agent/reconnectingpty/buffered.go @@ -60,6 +60,7 @@ func newBuffered(ctx context.Context, logger slog.Logger, execer agentexec.Exece // Add TERM then start the command with a pty. pty.Cmd duplicates Path as the // first argument so remove it. cmdWithEnv := execer.PTYCommandContext(ctx, cmd.Path, cmd.Args[1:]...) + //nolint:gocritic cmdWithEnv.Env = append(rpty.command.Env, "TERM=xterm-256color") cmdWithEnv.Dir = rpty.command.Dir ptty, process, err := pty.Start(cmdWithEnv) @@ -236,7 +237,7 @@ func (rpty *bufferedReconnectingPTY) Wait() { _, _ = rpty.state.waitForState(StateClosing) } -func (rpty *bufferedReconnectingPTY) Close(error error) { +func (rpty *bufferedReconnectingPTY) Close(err error) { // The closing state change will be handled by the lifecycle. - rpty.state.setState(StateClosing, error) + rpty.state.setState(StateClosing, err) } diff --git a/agent/reconnectingpty/screen.go b/agent/reconnectingpty/screen.go index 98d21c5959d7b..533c11a06bf4a 100644 --- a/agent/reconnectingpty/screen.go +++ b/agent/reconnectingpty/screen.go @@ -225,6 +225,7 @@ func (rpty *screenReconnectingPTY) doAttach(ctx context.Context, conn net.Conn, rpty.command.Path, // pty.Cmd duplicates Path as the first argument so remove it. }, rpty.command.Args[1:]...)...) + //nolint:gocritic cmd.Env = append(rpty.command.Env, "TERM=xterm-256color") cmd.Dir = rpty.command.Dir ptty, process, err := pty.Start(cmd, pty.WithPTYOption( @@ -340,6 +341,7 @@ func (rpty *screenReconnectingPTY) sendCommand(ctx context.Context, command stri // -X runs a command in the matching session. "-X", command, ) + //nolint:gocritic cmd.Env = append(rpty.command.Env, "TERM=xterm-256color") cmd.Dir = rpty.command.Dir cmd.Stdout = &stdout diff --git a/apiversion/apiversion.go b/apiversion/apiversion.go index 349b5c9fecc15..9435320a11f01 100644 --- a/apiversion/apiversion.go +++ b/apiversion/apiversion.go @@ -10,10 +10,10 @@ import ( // New returns an *APIVersion with the given major.minor and // additional supported major versions. -func New(maj, min int) *APIVersion { +func New(maj, minor int) *APIVersion { v := &APIVersion{ supportedMajor: maj, - supportedMinor: min, + supportedMinor: minor, additionalMajors: make([]int, 0), } return v diff --git a/cli/agent.go b/cli/agent.go index 0a9031aed57c1..bf189a4fc57c2 100644 --- a/cli/agent.go +++ b/cli/agent.go @@ -127,6 +127,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { logger.Info(ctx, "spawning reaper process") // Do not start a reaper on the child process. It's important // to do this else we fork bomb ourselves. + //nolint:gocritic args := append(os.Args, "--no-reap") err := reaper.ForkReap( reaper.WithExecArgs(args...), @@ -327,10 +328,11 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { } agnt := agent.New(agent.Options{ - Client: client, - Logger: logger, - LogDir: logDir, - ScriptDataDir: scriptDataDir, + Client: client, + Logger: logger, + LogDir: logDir, + ScriptDataDir: scriptDataDir, + // #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535) TailnetListenPort: uint16(tailnetListenPort), ExchangeToken: func(ctx context.Context) (string, error) { if exchangeToken == nil { diff --git a/cli/clistat/disk.go b/cli/clistat/disk.go index de79fe8a43d45..ea1f343c9ff35 100644 --- a/cli/clistat/disk.go +++ b/cli/clistat/disk.go @@ -19,6 +19,7 @@ func (*Statter) Disk(p Prefix, path string) (*Result, error) { return nil, err } var r Result + // #nosec G115 - Safe conversion because stat.Bsize is always positive and within uint64 range r.Total = ptr.To(float64(stat.Blocks * uint64(stat.Bsize))) r.Used = float64(stat.Blocks-stat.Bfree) * float64(stat.Bsize) r.Unit = "B" diff --git a/cli/clitest/golden.go b/cli/clitest/golden.go index e70e527b66a45..e79006ebb58e3 100644 --- a/cli/clitest/golden.go +++ b/cli/clitest/golden.go @@ -58,6 +58,7 @@ func TestCommandHelp(t *testing.T, getRoot func(t *testing.T) *serpent.Command, ExtractCommandPathsLoop: for _, cp := range extractVisibleCommandPaths(nil, root.Children) { name := fmt.Sprintf("coder %s --help", strings.Join(cp, " ")) + //nolint:gocritic cmd := append(cp, "--help") for _, tt := range cases { if tt.Name == name { diff --git a/cli/cliui/cliui.go b/cli/cliui/cliui.go index 5373fbae25333..50b39ba94cf8a 100644 --- a/cli/cliui/cliui.go +++ b/cli/cliui/cliui.go @@ -12,7 +12,7 @@ import ( "github.com/coder/pretty" ) -var Canceled = xerrors.New("canceled") +var ErrCanceled = xerrors.New("canceled") // DefaultStyles compose visual elements of the UI. var DefaultStyles Styles diff --git a/cli/cliui/parameter.go b/cli/cliui/parameter.go index 8080ef1a96906..2e639f8dfa425 100644 --- a/cli/cliui/parameter.go +++ b/cli/cliui/parameter.go @@ -33,7 +33,8 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te var err error var value string - if templateVersionParameter.Type == "list(string)" { + switch { + case templateVersionParameter.Type == "list(string)": // Move the cursor up a single line for nicer display! _, _ = fmt.Fprint(inv.Stdout, "\033[1A") @@ -60,7 +61,7 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te ) value = string(v) } - } else if len(templateVersionParameter.Options) > 0 { + case len(templateVersionParameter.Options) > 0: // Move the cursor up a single line for nicer display! _, _ = fmt.Fprint(inv.Stdout, "\033[1A") var richParameterOption *codersdk.TemplateVersionParameterOption @@ -74,7 +75,7 @@ func RichParameter(inv *serpent.Invocation, templateVersionParameter codersdk.Te pretty.Fprintf(inv.Stdout, DefaultStyles.Prompt, "%s\n", richParameterOption.Name) value = richParameterOption.Value } - } else { + default: text := "Enter a value" if !templateVersionParameter.Required { text += fmt.Sprintf(" (default: %q)", defaultValue) diff --git a/cli/cliui/prompt.go b/cli/cliui/prompt.go index 3d1ee4204fb63..b432f75afeaaf 100644 --- a/cli/cliui/prompt.go +++ b/cli/cliui/prompt.go @@ -124,7 +124,7 @@ func Prompt(inv *serpent.Invocation, opts PromptOptions) (string, error) { return "", err case line := <-lineCh: if opts.IsConfirm && line != "yes" && line != "y" { - return line, xerrors.Errorf("got %q: %w", line, Canceled) + return line, xerrors.Errorf("got %q: %w", line, ErrCanceled) } if opts.Validate != nil { err := opts.Validate(line) @@ -139,7 +139,7 @@ func Prompt(inv *serpent.Invocation, opts PromptOptions) (string, error) { case <-interrupt: // Print a newline so that any further output starts properly on a new line. _, _ = fmt.Fprintln(inv.Stdout) - return "", Canceled + return "", ErrCanceled } } diff --git a/cli/cliui/provisionerjob.go b/cli/cliui/provisionerjob.go index f9ecbf3d8ab17..36efa04a8a91a 100644 --- a/cli/cliui/provisionerjob.go +++ b/cli/cliui/provisionerjob.go @@ -204,7 +204,7 @@ func ProvisionerJob(ctx context.Context, wr io.Writer, opts ProvisionerJobOption switch job.Status { case codersdk.ProvisionerJobCanceled: jobMutex.Unlock() - return Canceled + return ErrCanceled case codersdk.ProvisionerJobSucceeded: jobMutex.Unlock() return nil diff --git a/cli/cliui/provisionerjob_test.go b/cli/cliui/provisionerjob_test.go index f75a8bc53f12a..aa31c9b4a40cb 100644 --- a/cli/cliui/provisionerjob_test.go +++ b/cli/cliui/provisionerjob_test.go @@ -250,7 +250,7 @@ func newProvisionerJob(t *testing.T) provisionerJobTest { defer close(done) err := inv.WithContext(context.Background()).Run() if err != nil { - assert.ErrorIs(t, err, cliui.Canceled) + assert.ErrorIs(t, err, cliui.ErrCanceled) } }() t.Cleanup(func() { diff --git a/cli/cliui/select.go b/cli/cliui/select.go index 4697dda09d660..40f63d92e279d 100644 --- a/cli/cliui/select.go +++ b/cli/cliui/select.go @@ -147,7 +147,7 @@ func Select(inv *serpent.Invocation, opts SelectOptions) (string, error) { } if model.canceled { - return "", Canceled + return "", ErrCanceled } return model.selected, nil @@ -360,7 +360,7 @@ func MultiSelect(inv *serpent.Invocation, opts MultiSelectOptions) ([]string, er } if model.canceled { - return nil, Canceled + return nil, ErrCanceled } return model.selectedOptions(), nil diff --git a/cli/cliutil/levenshtein/levenshtein.go b/cli/cliutil/levenshtein/levenshtein.go index f509e5b1000d1..7b6965fecd705 100644 --- a/cli/cliutil/levenshtein/levenshtein.go +++ b/cli/cliutil/levenshtein/levenshtein.go @@ -32,7 +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") } + // #nosec G115 - Safe conversion since we've checked that len(a) < 255 m := uint8(len(a)) + // #nosec G115 - Safe conversion since we've checked that len(b) < 255 n := uint8(len(b)) // Special cases for empty strings @@ -70,12 +72,13 @@ func Distance(a, b string, maxDist int) (int, error) { subCost = 1 } // Don't forget: matrix is +1 size - d[i+1][j+1] = min( + d[i+1][j+1] = minOf( d[i][j+1]+1, // deletion d[i+1][j]+1, // insertion d[i][j]+subCost, // substitution ) // check maxDist on the diagonal + // #nosec G115 - Safe conversion as maxDist is expected to be small for edit distances if maxDist > -1 && i == j && d[i+1][j+1] > uint8(maxDist) { return int(d[i+1][j+1]), ErrMaxDist } @@ -85,9 +88,9 @@ func Distance(a, b string, maxDist int) (int, error) { return int(d[m][n]), nil } -func min[T constraints.Ordered](ts ...T) T { +func minOf[T constraints.Ordered](ts ...T) T { if len(ts) == 0 { - panic("min: no arguments") + panic("minOf: no arguments") } m := ts[0] for _, t := range ts[1:] { diff --git a/cli/configssh.go b/cli/configssh.go index b3c29f711bdb6..952120c30b477 100644 --- a/cli/configssh.go +++ b/cli/configssh.go @@ -268,7 +268,7 @@ func (r *RootCmd) configSSH() *serpent.Command { IsConfirm: true, }) if err != nil { - if line == "" && xerrors.Is(err, cliui.Canceled) { + if line == "" && xerrors.Is(err, cliui.ErrCanceled) { return nil } // Selecting "no" will use the last config. diff --git a/cli/create.go b/cli/create.go index bb2e8dde0255a..fbf26349b3b95 100644 --- a/cli/create.go +++ b/cli/create.go @@ -104,7 +104,8 @@ func (r *RootCmd) create() *serpent.Command { var template codersdk.Template var templateVersionID uuid.UUID - if templateName == "" { + switch { + case templateName == "": _, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Wrap, "Select a template below to preview the provisioned infrastructure:")) templates, err := client.Templates(inv.Context(), codersdk.TemplateFilter{}) @@ -161,13 +162,13 @@ func (r *RootCmd) create() *serpent.Command { template = templateByName[option] templateVersionID = template.ActiveVersionID - } else if sourceWorkspace.LatestBuild.TemplateVersionID != uuid.Nil { + case sourceWorkspace.LatestBuild.TemplateVersionID != uuid.Nil: template, err = client.Template(inv.Context(), sourceWorkspace.TemplateID) if err != nil { return xerrors.Errorf("get template by name: %w", err) } templateVersionID = sourceWorkspace.LatestBuild.TemplateVersionID - } else { + default: templates, err := client.Templates(inv.Context(), codersdk.TemplateFilter{ ExactName: templateName, }) diff --git a/cli/exp_errors.go b/cli/exp_errors.go index fbcaf8091c95b..7e35badadc91b 100644 --- a/cli/exp_errors.go +++ b/cli/exp_errors.go @@ -16,7 +16,7 @@ func (RootCmd) errorExample() *serpent.Command { errorCmd := func(use string, err error) *serpent.Command { return &serpent.Command{ Use: use, - Handler: func(inv *serpent.Invocation) error { + Handler: func(_ *serpent.Invocation) error { return err }, } @@ -70,7 +70,7 @@ func (RootCmd) errorExample() *serpent.Command { // A multi-error { Use: "multi-error", - Handler: func(inv *serpent.Invocation) error { + Handler: func(_ *serpent.Invocation) error { return xerrors.Errorf("wrapped: %w", errors.Join( xerrors.Errorf("first error: %w", errorWithStackTrace()), xerrors.Errorf("second error: %w", errorWithStackTrace()), @@ -81,7 +81,7 @@ func (RootCmd) errorExample() *serpent.Command { { Use: "multi-multi-error", Short: "This is a multi error inside a multi error", - Handler: func(inv *serpent.Invocation) error { + Handler: func(_ *serpent.Invocation) error { return errors.Join( xerrors.Errorf("parent error: %w", errorWithStackTrace()), errors.Join( @@ -100,7 +100,7 @@ func (RootCmd) errorExample() *serpent.Command { Required: true, Flag: "magic-word", Default: "", - Value: serpent.Validate(&magicWord, func(value *serpent.String) error { + Value: serpent.Validate(&magicWord, func(_ *serpent.String) error { return xerrors.Errorf("magic word is incorrect") }), }, diff --git a/cli/externalauth.go b/cli/externalauth.go index 61d2139eb349d..1a60e3c8e6903 100644 --- a/cli/externalauth.go +++ b/cli/externalauth.go @@ -91,7 +91,7 @@ fi if err != nil { return err } - return cliui.Canceled + return cliui.ErrCanceled } if extra != "" { if extAuth.TokenExtra == nil { diff --git a/cli/externalauth_test.go b/cli/externalauth_test.go index 4e04ce6b89e09..c14b144a2e1b6 100644 --- a/cli/externalauth_test.go +++ b/cli/externalauth_test.go @@ -29,7 +29,7 @@ func TestExternalAuth(t *testing.T) { inv.Stdout = pty.Output() waiter := clitest.StartWithWaiter(t, inv) pty.ExpectMatch("https://github.com") - waiter.RequireIs(cliui.Canceled) + waiter.RequireIs(cliui.ErrCanceled) }) t.Run("SuccessWithToken", func(t *testing.T) { t.Parallel() diff --git a/cli/gitaskpass.go b/cli/gitaskpass.go index 88d2d652dc758..7e03cb2160bb5 100644 --- a/cli/gitaskpass.go +++ b/cli/gitaskpass.go @@ -53,7 +53,7 @@ func (r *RootCmd) gitAskpass() *serpent.Command { cliui.Warn(inv.Stderr, "Coder was unable to handle this git request. The default git behavior will be used instead.", lines..., ) - return cliui.Canceled + return cliui.ErrCanceled } return xerrors.Errorf("get git token: %w", err) } diff --git a/cli/gitaskpass_test.go b/cli/gitaskpass_test.go index 92fe3943c1eb8..8e51411de9587 100644 --- a/cli/gitaskpass_test.go +++ b/cli/gitaskpass_test.go @@ -59,7 +59,7 @@ func TestGitAskpass(t *testing.T) { pty := ptytest.New(t) inv.Stderr = pty.Output() err := inv.Run() - require.ErrorIs(t, err, cliui.Canceled) + require.ErrorIs(t, err, cliui.ErrCanceled) pty.ExpectMatch("Nope!") }) diff --git a/cli/gitssh.go b/cli/gitssh.go index 4a83ace678a3b..22303ce2311fc 100644 --- a/cli/gitssh.go +++ b/cli/gitssh.go @@ -138,7 +138,7 @@ var fallbackIdentityFiles = strings.Join([]string{ // // The extra arguments work without issue and lets us run the command // as-is without stripping out the excess (git-upload-pack 'coder/coder'). -func parseIdentityFilesForHost(ctx context.Context, args, env []string) (identityFiles []string, error error) { +func parseIdentityFilesForHost(ctx context.Context, args, env []string) (identityFiles []string, err error) { home, err := os.UserHomeDir() if err != nil { return nil, xerrors.Errorf("get user home dir failed: %w", err) diff --git a/cli/help.go b/cli/help.go index b4b0a1e20caf5..26ed694dd10c6 100644 --- a/cli/help.go +++ b/cli/help.go @@ -42,6 +42,7 @@ func ttyWidth() int { // wrapTTY wraps a string to the width of the terminal, or 80 no terminal // is detected. func wrapTTY(s string) string { + // #nosec G115 - Safe conversion as TTY width is expected to be within uint range return wordwrap.WrapString(s, uint(ttyWidth())) } @@ -57,12 +58,8 @@ var usageTemplate = func() *template.Template { return template.Must( template.New("usage").Funcs( template.FuncMap{ - "version": func() string { - return buildinfo.Version() - }, - "wrapTTY": func(s string) string { - return wrapTTY(s) - }, + "version": buildinfo.Version, + "wrapTTY": wrapTTY, "trimNewline": func(s string) string { return strings.TrimSuffix(s, "\n") }, @@ -189,7 +186,7 @@ var usageTemplate = func() *template.Template { }, "formatGroupDescription": func(s string) string { s = strings.ReplaceAll(s, "\n", "") - s = s + "\n" + s += "\n" s = wrapTTY(s) return s }, diff --git a/cli/login.go b/cli/login.go index e7a1d0eb8eb13..fcba1ee50eb74 100644 --- a/cli/login.go +++ b/cli/login.go @@ -48,7 +48,7 @@ func promptFirstUsername(inv *serpent.Invocation) (string, error) { Text: "What " + pretty.Sprint(cliui.DefaultStyles.Field, "username") + " would you like?", Default: currentUser.Username, }) - if errors.Is(err, cliui.Canceled) { + if errors.Is(err, cliui.ErrCanceled) { return "", nil } if err != nil { @@ -64,7 +64,7 @@ func promptFirstName(inv *serpent.Invocation) (string, error) { Default: "", }) if err != nil { - if errors.Is(err, cliui.Canceled) { + if errors.Is(err, cliui.ErrCanceled) { return "", nil } return "", err @@ -76,11 +76,9 @@ func promptFirstName(inv *serpent.Invocation) (string, error) { func promptFirstPassword(inv *serpent.Invocation) (string, error) { retry: password, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "Enter a " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", - Secret: true, - Validate: func(s string) error { - return userpassword.Validate(s) - }, + Text: "Enter a " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", + Secret: true, + Validate: userpassword.Validate, }) if err != nil { return "", xerrors.Errorf("specify password prompt: %w", err) @@ -508,7 +506,7 @@ func promptTrialInfo(inv *serpent.Invocation, fieldName string) (string, error) }, }) if err != nil { - if errors.Is(err, cliui.Canceled) { + if errors.Is(err, cliui.ErrCanceled) { return "", nil } return "", err diff --git a/cli/open.go b/cli/open.go index ef62e1542d0bf..d0946854ddb25 100644 --- a/cli/open.go +++ b/cli/open.go @@ -89,7 +89,7 @@ func (r *RootCmd) openVSCode() *serpent.Command { }) if err != nil { if xerrors.Is(err, context.Canceled) { - return cliui.Canceled + return cliui.ErrCanceled } return xerrors.Errorf("agent: %w", err) } @@ -99,7 +99,7 @@ func (r *RootCmd) openVSCode() *serpent.Command { // However, if no directory is set, the expanded directory will // not be set either. if workspaceAgent.Directory != "" { - workspace, workspaceAgent, err = waitForAgentCond(ctx, client, workspace, workspaceAgent, func(a codersdk.WorkspaceAgent) bool { + workspace, workspaceAgent, err = waitForAgentCond(ctx, client, workspace, workspaceAgent, func(_ codersdk.WorkspaceAgent) bool { return workspaceAgent.LifecycleState != codersdk.WorkspaceAgentLifecycleCreated }) if err != nil { diff --git a/cli/remoteforward.go b/cli/remoteforward.go index bffc50694c061..cfa3d41fb38ba 100644 --- a/cli/remoteforward.go +++ b/cli/remoteforward.go @@ -40,7 +40,7 @@ func validateRemoteForward(flag string) bool { return isRemoteForwardTCP(flag) || isRemoteForwardUnixSocket(flag) } -func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) { +func parseRemoteForwardTCP(matches []string) (local net.Addr, remote net.Addr, err error) { remotePort, err := strconv.Atoi(matches[1]) if err != nil { return nil, nil, xerrors.Errorf("remote port is invalid: %w", err) @@ -69,7 +69,7 @@ func parseRemoteForwardTCP(matches []string) (net.Addr, net.Addr, error) { // parseRemoteForwardUnixSocket parses a remote forward flag. Note that // we don't verify that the local socket path exists because the user // may create it later. This behavior matches OpenSSH. -func parseRemoteForwardUnixSocket(matches []string) (net.Addr, net.Addr, error) { +func parseRemoteForwardUnixSocket(matches []string) (local net.Addr, remote net.Addr, err error) { remoteSocket := matches[1] localSocket := matches[2] @@ -85,7 +85,7 @@ func parseRemoteForwardUnixSocket(matches []string) (net.Addr, net.Addr, error) return localAddr, remoteAddr, nil } -func parseRemoteForward(flag string) (net.Addr, net.Addr, error) { +func parseRemoteForward(flag string) (local net.Addr, remote net.Addr, err error) { tcpMatches := remoteForwardRegexTCP.FindStringSubmatch(flag) if len(tcpMatches) > 0 { diff --git a/cli/resetpassword.go b/cli/resetpassword.go index f77ed81d14db4..f356b07b5e1ec 100644 --- a/cli/resetpassword.go +++ b/cli/resetpassword.go @@ -62,11 +62,9 @@ func (*RootCmd) resetPassword() *serpent.Command { } password, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "Enter new " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", - Secret: true, - Validate: func(s string) error { - return userpassword.Validate(s) - }, + Text: "Enter new " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", + Secret: true, + Validate: userpassword.Validate, }) if err != nil { return xerrors.Errorf("password prompt: %w", err) diff --git a/cli/root.go b/cli/root.go index 816d7b769eb0d..25351576af94c 100644 --- a/cli/root.go +++ b/cli/root.go @@ -171,15 +171,15 @@ func (r *RootCmd) RunWithSubcommands(subcommands []*serpent.Command) { code = exitErr.code err = exitErr.err } - if errors.Is(err, cliui.Canceled) { - //nolint:revive + if errors.Is(err, cliui.ErrCanceled) { + //nolint:revive,gocritic os.Exit(code) } f := PrettyErrorFormatter{w: os.Stderr, verbose: r.verbose} if err != nil { f.Format(err) } - //nolint:revive + //nolint:revive,gocritic os.Exit(code) } } @@ -891,7 +891,7 @@ func DumpHandler(ctx context.Context, name string) { done: if sigStr == "SIGQUIT" { - //nolint:revive + //nolint:revive,gocritic os.Exit(1) } } @@ -1045,7 +1045,7 @@ func formatMultiError(from string, multi []error, opts *formatOpts) string { prefix := fmt.Sprintf("%d. ", i+1) if len(prefix) < len(indent) { // Indent the prefix to match the indent - prefix = prefix + strings.Repeat(" ", len(indent)-len(prefix)) + prefix += strings.Repeat(" ", len(indent)-len(prefix)) } errStr = prefix + errStr // Now looks like diff --git a/cli/server.go b/cli/server.go index 0b64cd8aa6899..4e099b4657782 100644 --- a/cli/server.go +++ b/cli/server.go @@ -1768,9 +1768,9 @@ func parseTLSCipherSuites(ciphers []string) ([]tls.CipherSuite, error) { // hasSupportedVersion is a helper function that returns true if the list // of supported versions contains a version between min and max. // If the versions list is outside the min/max, then it returns false. -func hasSupportedVersion(min, max uint16, versions []uint16) bool { +func hasSupportedVersion(minVal, maxVal uint16, versions []uint16) bool { for _, v := range versions { - if v >= min && v <= max { + if v >= minVal && v <= maxVal { // If one version is in between min/max, return true. return true } diff --git a/cli/server_test.go b/cli/server_test.go index d9019391114f3..b9bc91c15a1e5 100644 --- a/cli/server_test.go +++ b/cli/server_test.go @@ -1701,6 +1701,7 @@ func TestServer(t *testing.T) { // Next, we instruct the same server to display the YAML config // and then save it. inv = inv.WithContext(testutil.Context(t, testutil.WaitMedium)) + //nolint:gocritic inv.Args = append(args, "--write-config") fi, err := os.OpenFile(testutil.TempFile(t, "", "coder-config-test-*"), os.O_WRONLY|os.O_CREATE, 0o600) require.NoError(t, err) diff --git a/cli/ssh.go b/cli/ssh.go index da84a7886b048..6baaa2eff01a4 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -264,7 +264,7 @@ func (r *RootCmd) ssh() *serpent.Command { }) if err != nil { if xerrors.Is(err, context.Canceled) { - return cliui.Canceled + return cliui.ErrCanceled } return err } diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 6126cbff9dc42..d6f8f72dc5f23 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -341,7 +341,7 @@ func TestSSH(t *testing.T) { cmdDone := tGo(t, func() { err := inv.WithContext(ctx).Run() - assert.ErrorIs(t, err, cliui.Canceled) + assert.ErrorIs(t, err, cliui.ErrCanceled) }) pty.ExpectMatch(wantURL) cancel() diff --git a/cli/templateedit.go b/cli/templateedit.go index 44d77ff4489b6..b115350ab4437 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -147,12 +147,13 @@ func (r *RootCmd) templateEdit() *serpent.Command { autostopRequirementWeeks = template.AutostopRequirement.Weeks } - if len(autostartRequirementDaysOfWeek) == 1 && autostartRequirementDaysOfWeek[0] == "all" { + switch { + case len(autostartRequirementDaysOfWeek) == 1 && autostartRequirementDaysOfWeek[0] == "all": // Set it to every day of the week autostartRequirementDaysOfWeek = []string{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"} - } else if !userSetOption(inv, "autostart-requirement-weekdays") { + case !userSetOption(inv, "autostart-requirement-weekdays"): autostartRequirementDaysOfWeek = template.AutostartRequirement.DaysOfWeek - } else if len(autostartRequirementDaysOfWeek) == 0 { + case len(autostartRequirementDaysOfWeek) == 0: autostartRequirementDaysOfWeek = []string{} } diff --git a/cli/templatepush_test.go b/cli/templatepush_test.go index ae8f60bd9c551..89fd024b0c33a 100644 --- a/cli/templatepush_test.go +++ b/cli/templatepush_test.go @@ -723,6 +723,7 @@ func TestTemplatePush(t *testing.T) { template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID) // Test the cli command. + //nolint:gocritic modifiedTemplateVariables := append(initialTemplateVariables, &proto.TemplateVariable{ Name: "second_variable", @@ -792,6 +793,7 @@ func TestTemplatePush(t *testing.T) { template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID) // Test the cli command. + //nolint:gocritic modifiedTemplateVariables := append(initialTemplateVariables, &proto.TemplateVariable{ Name: "second_variable", @@ -839,6 +841,7 @@ func TestTemplatePush(t *testing.T) { template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID) // Test the cli command. + //nolint:gocritic modifiedTemplateVariables := append(initialTemplateVariables, &proto.TemplateVariable{ Name: "second_variable", @@ -905,6 +908,7 @@ func TestTemplatePush(t *testing.T) { template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID) // Test the cli command. + //nolint:gocritic modifiedTemplateVariables := append(initialTemplateVariables, &proto.TemplateVariable{ Name: "second_variable", diff --git a/cli/util.go b/cli/util.go index 2d408f7731c48..9f86f3cbc9551 100644 --- a/cli/util.go +++ b/cli/util.go @@ -167,7 +167,7 @@ func parseCLISchedule(parts ...string) (*cron.Schedule, error) { func parseDuration(raw string) (time.Duration, error) { // If the user input a raw number, assume minutes if isDigit(raw) { - raw = raw + "m" + raw += "m" } d, err := time.ParseDuration(raw) if err != nil { diff --git a/cli/vscodessh.go b/cli/vscodessh.go index 630c405241d17..872f7d837c0cd 100644 --- a/cli/vscodessh.go +++ b/cli/vscodessh.go @@ -142,7 +142,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command { }) if err != nil { if xerrors.Is(err, context.Canceled) { - return cliui.Canceled + return cliui.ErrCanceled } } diff --git a/cmd/cliui/main.go b/cmd/cliui/main.go index da7f75f5cfd18..6a363a3404618 100644 --- a/cmd/cliui/main.go +++ b/cmd/cliui/main.go @@ -89,7 +89,7 @@ func main() { return nil }, }) - if errors.Is(err, cliui.Canceled) { + if errors.Is(err, cliui.ErrCanceled) { return nil } if err != nil { @@ -100,7 +100,7 @@ func main() { Default: cliui.ConfirmYes, IsConfirm: true, }) - if errors.Is(err, cliui.Canceled) { + if errors.Is(err, cliui.ErrCanceled) { return nil } if err != nil { @@ -371,7 +371,7 @@ func main() { gitlabAuthed.Store(true) }() return cliui.ExternalAuth(inv.Context(), inv.Stdout, cliui.ExternalAuthOptions{ - Fetch: func(ctx context.Context) ([]codersdk.TemplateVersionExternalAuth, error) { + Fetch: func(_ context.Context) ([]codersdk.TemplateVersionExternalAuth, error) { count.Add(1) return []codersdk.TemplateVersionExternalAuth{{ ID: "github", diff --git a/cmd/coder/main.go b/cmd/coder/main.go index 27918798b3a12..0fcbf38721947 100644 --- a/cmd/coder/main.go +++ b/cmd/coder/main.go @@ -1,26 +1,16 @@ package main import ( - "fmt" - "os" _ "time/tzdata" - tea "github.com/charmbracelet/bubbletea" - - "github.com/coder/coder/v2/agent/agentexec" _ "github.com/coder/coder/v2/buildinfo/resources" "github.com/coder/coder/v2/cli" ) func main() { - if len(os.Args) > 1 && os.Args[1] == "agent-exec" { - err := agentexec.CLI() - _, _ = fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } // This preserves backwards compatibility with an init function that is causing grief for // web terminals using agent-exec + screen. See https://github.com/coder/coder/pull/15817 - tea.InitTerminal() + var rootCmd cli.RootCmd rootCmd.RunWithSubcommands(rootCmd.AGPL()) } diff --git a/coderd/agentapi/logs.go b/coderd/agentapi/logs.go index 1d63f32b7b0dd..ce772088c09ab 100644 --- a/coderd/agentapi/logs.go +++ b/coderd/agentapi/logs.go @@ -101,11 +101,12 @@ func (a *LogsAPI) BatchCreateLogs(ctx context.Context, req *agentproto.BatchCrea } logs, err := a.Database.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ - AgentID: workspaceAgent.ID, - CreatedAt: a.now(), - Output: output, - Level: level, - LogSourceID: logSourceID, + AgentID: workspaceAgent.ID, + CreatedAt: a.now(), + Output: output, + Level: level, + LogSourceID: logSourceID, + // #nosec G115 - Safe conversion as output length is expected to be within int32 range OutputLength: int32(outputLength), }) if err != nil { diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index fe6aacf84d5dd..e2a2ff2be7bf2 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -11561,7 +11561,7 @@ const docTemplate = `{ } }, "address": { - "description": "DEPRECATED: Use HTTPAddress or TLS.Address instead.", + "description": "Deprecated: Use HTTPAddress or TLS.Address instead.", "allOf": [ { "$ref": "#/definitions/serpent.HostPort" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 7a399a0e044b4..00f0ed816cf31 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -10325,7 +10325,7 @@ } }, "address": { - "description": "DEPRECATED: Use HTTPAddress or TLS.Address instead.", + "description": "Deprecated: Use HTTPAddress or TLS.Address instead.", "allOf": [ { "$ref": "#/definitions/serpent.HostPort" diff --git a/coderd/apikey.go b/coderd/apikey.go index 858a090ebd479..becb9737ed62e 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -257,12 +257,12 @@ func (api *API) tokens(rw http.ResponseWriter, r *http.Request) { return } - var userIds []uuid.UUID + var userIDs []uuid.UUID for _, key := range keys { - userIds = append(userIds, key.UserID) + userIDs = append(userIDs, key.UserID) } - users, _ := api.Database.GetUsersByIDs(ctx, userIds) + users, _ := api.Database.GetUsersByIDs(ctx, userIDs) usersByID := map[uuid.UUID]database.User{} for _, user := range users { usersByID[user.ID] = user diff --git a/coderd/apikey/apikey_test.go b/coderd/apikey/apikey_test.go index 41f64fe0d866f..ef4d260ddf0a6 100644 --- a/coderd/apikey/apikey_test.go +++ b/coderd/apikey/apikey_test.go @@ -134,20 +134,22 @@ func TestGenerate(t *testing.T) { assert.WithinDuration(t, dbtime.Now(), key.CreatedAt, time.Second*5) assert.WithinDuration(t, dbtime.Now(), key.UpdatedAt, time.Second*5) - if tc.params.LifetimeSeconds > 0 { + switch { + case tc.params.LifetimeSeconds > 0: assert.Equal(t, tc.params.LifetimeSeconds, key.LifetimeSeconds) - } else if !tc.params.ExpiresAt.IsZero() { + case !tc.params.ExpiresAt.IsZero(): // Should not be a delta greater than 5 seconds. assert.InDelta(t, time.Until(tc.params.ExpiresAt).Seconds(), key.LifetimeSeconds, 5) - } else { + default: assert.Equal(t, int64(tc.params.DefaultLifetime.Seconds()), key.LifetimeSeconds) } - if !tc.params.ExpiresAt.IsZero() { + switch { + case !tc.params.ExpiresAt.IsZero(): assert.Equal(t, tc.params.ExpiresAt.UTC(), key.ExpiresAt) - } else if tc.params.LifetimeSeconds > 0 { + case tc.params.LifetimeSeconds > 0: assert.WithinDuration(t, dbtime.Now().Add(time.Duration(tc.params.LifetimeSeconds)*time.Second), key.ExpiresAt, time.Second*5) - } else { + default: assert.WithinDuration(t, dbtime.Now().Add(tc.params.DefaultLifetime), key.ExpiresAt, time.Second*5) } diff --git a/coderd/audit.go b/coderd/audit.go index 4e99cbf1e0b58..ee647fba2f39b 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -54,7 +54,9 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) { }) return } + // #nosec G115 - Safe conversion as pagination offset is expected to be within int32 range filter.OffsetOpt = int32(page.Offset) + // #nosec G115 - Safe conversion as pagination limit is expected to be within int32 range filter.LimitOpt = int32(page.Limit) if filter.Username == "me" { diff --git a/coderd/audit/audit.go b/coderd/audit/audit.go index 2a264605c6428..2b3a34d3a8f51 100644 --- a/coderd/audit/audit.go +++ b/coderd/audit/audit.go @@ -13,7 +13,7 @@ import ( type Auditor interface { Export(ctx context.Context, alog database.AuditLog) error - diff(old, new any) Map + diff(old, newVal any) Map } type AdditionalFields struct { diff --git a/coderd/audit/diff.go b/coderd/audit/diff.go index 0a4c35814df0c..39d13ff789efc 100644 --- a/coderd/audit/diff.go +++ b/coderd/audit/diff.go @@ -60,10 +60,10 @@ func Diff[T Auditable](a Auditor, left, right T) Map { return a.diff(left, right // the Auditor feature interface. Only types in the same package as the // interface can implement unexported methods. type Differ struct { - DiffFn func(old, new any) Map + DiffFn func(old, newVal any) Map } //nolint:unused -func (d Differ) diff(old, new any) Map { - return d.DiffFn(old, new) +func (d Differ) diff(old, newVal any) Map { + return d.DiffFn(old, newVal) } diff --git a/coderd/audit/request.go b/coderd/audit/request.go index d837d30518805..fd755e39c5216 100644 --- a/coderd/audit/request.go +++ b/coderd/audit/request.go @@ -407,11 +407,12 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request var userID uuid.UUID key, ok := httpmw.APIKeyOptional(p.Request) - if ok { + switch { + case ok: userID = key.UserID - } else if req.UserID != uuid.Nil { + case req.UserID != uuid.Nil: userID = req.UserID - } else { + default: // if we do not have a user associated with the audit action // we do not want to audit // (this pertains to logins; we don't want to capture non-user login attempts) @@ -425,16 +426,17 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request ip := ParseIP(p.Request.RemoteAddr) auditLog := database.AuditLog{ - ID: uuid.New(), - Time: dbtime.Now(), - UserID: userID, - Ip: ip, - UserAgent: sql.NullString{String: p.Request.UserAgent(), Valid: true}, - ResourceType: either(req.Old, req.New, ResourceType[T], req.params.Action), - ResourceID: either(req.Old, req.New, ResourceID[T], req.params.Action), - ResourceTarget: either(req.Old, req.New, ResourceTarget[T], req.params.Action), - Action: action, - Diff: diffRaw, + ID: uuid.New(), + Time: dbtime.Now(), + UserID: userID, + Ip: ip, + UserAgent: sql.NullString{String: p.Request.UserAgent(), Valid: true}, + ResourceType: either(req.Old, req.New, ResourceType[T], req.params.Action), + ResourceID: either(req.Old, req.New, ResourceID[T], req.params.Action), + ResourceTarget: either(req.Old, req.New, ResourceTarget[T], req.params.Action), + Action: action, + Diff: diffRaw, + // #nosec G115 - Safe conversion as HTTP status code is expected to be within int32 range (typically 100-599) StatusCode: int32(sw.Status), RequestID: httpmw.RequestID(p.Request), AdditionalFields: additionalFieldsRaw, @@ -475,17 +477,18 @@ func BackgroundAudit[T Auditable](ctx context.Context, p *BackgroundAuditParams[ } auditLog := database.AuditLog{ - ID: uuid.New(), - Time: p.Time, - UserID: p.UserID, - OrganizationID: requireOrgID[T](ctx, p.OrganizationID, p.Log), - Ip: ip, - UserAgent: sql.NullString{Valid: p.UserAgent != "", String: p.UserAgent}, - ResourceType: either(p.Old, p.New, ResourceType[T], p.Action), - ResourceID: either(p.Old, p.New, ResourceID[T], p.Action), - ResourceTarget: either(p.Old, p.New, ResourceTarget[T], p.Action), - Action: p.Action, - Diff: diffRaw, + ID: uuid.New(), + Time: p.Time, + UserID: p.UserID, + OrganizationID: requireOrgID[T](ctx, p.OrganizationID, p.Log), + Ip: ip, + UserAgent: sql.NullString{Valid: p.UserAgent != "", String: p.UserAgent}, + ResourceType: either(p.Old, p.New, ResourceType[T], p.Action), + ResourceID: either(p.Old, p.New, ResourceID[T], p.Action), + ResourceTarget: either(p.Old, p.New, ResourceTarget[T], p.Action), + Action: p.Action, + Diff: diffRaw, + // #nosec G115 - Safe conversion as HTTP status code is expected to be within int32 range (typically 100-599) StatusCode: int32(p.Status), RequestID: p.RequestID, AdditionalFields: p.AdditionalFields, @@ -554,17 +557,19 @@ func BaggageFromContext(ctx context.Context) WorkspaceBuildBaggage { return d } -func either[T Auditable, R any](old, new T, fn func(T) R, auditAction database.AuditAction) R { - if ResourceID(new) != uuid.Nil { - return fn(new) - } else if ResourceID(old) != uuid.Nil { +func either[T Auditable, R any](old, newVal T, fn func(T) R, auditAction database.AuditAction) R { + switch { + case ResourceID(newVal) != uuid.Nil: + return fn(newVal) + case ResourceID(old) != uuid.Nil: return fn(old) - } else if auditAction == database.AuditActionLogin || auditAction == database.AuditActionLogout { + case auditAction == database.AuditActionLogin || auditAction == database.AuditActionLogout: // If the request action is a login or logout, we always want to audit it even if // there is no diff. See the comment in audit.InitRequest for more detail. return fn(old) + default: + panic("both old and new are nil") } - panic("both old and new are nil") } func ParseIP(ipStr string) pqtype.Inet { diff --git a/coderd/autobuild/lifecycle_executor_internal_test.go b/coderd/autobuild/lifecycle_executor_internal_test.go index 2b75a9782d7b6..bfe3bb53592b3 100644 --- a/coderd/autobuild/lifecycle_executor_internal_test.go +++ b/coderd/autobuild/lifecycle_executor_internal_test.go @@ -52,6 +52,7 @@ func Test_isEligibleForAutostart(t *testing.T) { for i, weekday := range schedule.DaysOfWeek { // Find the local weekday if okTick.In(localLocation).Weekday() == weekday { + // #nosec G115 - Safe conversion as i is the index of a 7-day week and will be in the range 0-6 okWeekdayBit = 1 << uint(i) } } diff --git a/coderd/coderd.go b/coderd/coderd.go index 190a043a92ac9..3fbbd756eae72 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -829,7 +829,7 @@ func New(options *Options) *API { // we do not override subdomain app routes. r.Get("/latency-check", tracing.StatusWriterMiddleware(prometheusMW(LatencyCheck())).ServeHTTP) - r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("OK")) }) + r.Get("/healthz", func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("OK")) }) // Attach workspace apps routes. r.Group(func(r chi.Router) { @@ -844,7 +844,7 @@ func New(options *Options) *API { r.Route("/derp", func(r chi.Router) { r.Get("/", derpHandler.ServeHTTP) // This is used when UDP is blocked, and latency must be checked via HTTP(s). - r.Get("/latency-check", func(w http.ResponseWriter, r *http.Request) { + r.Get("/latency-check", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }) }) @@ -901,7 +901,7 @@ func New(options *Options) *API { r.Route("/api/v2", func(r chi.Router) { api.APIHandler = r - r.NotFound(func(rw http.ResponseWriter, r *http.Request) { httpapi.RouteNotFound(rw) }) + r.NotFound(func(rw http.ResponseWriter, _ *http.Request) { httpapi.RouteNotFound(rw) }) r.Use( // Specific routes can specify different limits, but every rate // limit must be configurable by the admin. @@ -1421,7 +1421,7 @@ func New(options *Options) *API { // global variable here. r.Get("/swagger/*", globalHTTPSwaggerHandler) } else { - swaggerDisabled := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + swaggerDisabled := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { httpapi.Write(context.Background(), rw, http.StatusNotFound, codersdk.Response{ Message: "Swagger documentation is disabled.", }) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index f2297d07ec2c2..6b435157a2e95 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -1194,7 +1194,7 @@ func MustWorkspace(t testing.TB, client *codersdk.Client, workspaceID uuid.UUID) // RequestExternalAuthCallback makes a request with the proper OAuth2 state cookie // to the external auth callback endpoint. func RequestExternalAuthCallback(t testing.TB, providerID string, client *codersdk.Client, opts ...func(*http.Request)) *http.Response { - client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + client.HTTPClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse } state := "somestate" diff --git a/coderd/coderdtest/oidctest/idp.go b/coderd/coderdtest/oidctest/idp.go index e0fd1bb9b0be2..67186a4fd7ddf 100644 --- a/coderd/coderdtest/oidctest/idp.go +++ b/coderd/coderdtest/oidctest/idp.go @@ -339,8 +339,8 @@ func NewFakeIDP(t testing.TB, opts ...FakeIDPOpt) *FakeIDP { refreshIDTokenClaims: syncmap.New[string, jwt.MapClaims](), deviceCode: syncmap.New[string, deviceFlow](), hookOnRefresh: func(_ string) error { return nil }, - hookUserInfo: func(email string) (jwt.MapClaims, error) { return jwt.MapClaims{}, nil }, - hookValidRedirectURL: func(redirectURL string) error { return nil }, + hookUserInfo: func(_ string) (jwt.MapClaims, error) { return jwt.MapClaims{}, nil }, + hookValidRedirectURL: func(_ string) error { return nil }, defaultExpire: time.Minute * 5, } @@ -553,7 +553,7 @@ func (f *FakeIDP) ExternalLogin(t testing.TB, client *codersdk.Client, opts ...f f.SetRedirect(t, coderOauthURL.String()) cli := f.HTTPClient(client.HTTPClient) - cli.CheckRedirect = func(req *http.Request, via []*http.Request) error { + cli.CheckRedirect = func(req *http.Request, _ []*http.Request) error { // Store the idTokenClaims to the specific state request. This ties // the claims 1:1 with a given authentication flow. state := req.URL.Query().Get("state") @@ -1210,7 +1210,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler { }.Encode()) })) - mux.NotFound(func(rw http.ResponseWriter, r *http.Request) { + mux.NotFound(func(_ http.ResponseWriter, r *http.Request) { f.logger.Error(r.Context(), "http call not found", slogRequestFields(r)...) t.Errorf("unexpected request to IDP at path %q. Not supported", r.URL.Path) }) diff --git a/coderd/coderdtest/swaggerparser.go b/coderd/coderdtest/swaggerparser.go index 45907819fd60d..d7d46711a9df6 100644 --- a/coderd/coderdtest/swaggerparser.go +++ b/coderd/coderdtest/swaggerparser.go @@ -151,7 +151,7 @@ func VerifySwaggerDefinitions(t *testing.T, router chi.Router, swaggerComments [ assertUniqueRoutes(t, swaggerComments) assertSingleAnnotations(t, swaggerComments) - err := chi.Walk(router, func(method, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + err := chi.Walk(router, func(method, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error { method = strings.ToLower(method) if route != "/" && strings.HasSuffix(route, "/") { route = route[:len(route)-1] diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 94c0c7ef62c56..beb914c8c0cd1 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -33,8 +33,8 @@ var _ database.Store = (*querier)(nil) const wrapname = "dbauthz.querier" -// NoActorError is returned if no actor is present in the context. -var NoActorError = xerrors.Errorf("no authorization actor in context") +// ErrNoActor is returned if no actor is present in the context. +var ErrNoActor = xerrors.Errorf("no authorization actor in context") // NotAuthorizedError is a sentinel error that unwraps to sql.ErrNoRows. // This allows the internal error to be read by the caller if needed. Otherwise @@ -69,7 +69,7 @@ func IsNotAuthorizedError(err error) bool { if err == nil { return false } - if xerrors.Is(err, NoActorError) { + if xerrors.Is(err, ErrNoActor) { return true } @@ -140,7 +140,7 @@ func (q *querier) Wrappers() []string { func (q *querier) authorizeContext(ctx context.Context, action policy.Action, object rbac.Objecter) error { act, ok := ActorFromContext(ctx) if !ok { - return NoActorError + return ErrNoActor } err := q.auth.Authorize(ctx, act, action, object.RBACObject()) @@ -466,7 +466,7 @@ func insertWithAction[ // Fetch the rbac subject act, ok := ActorFromContext(ctx) if !ok { - return empty, NoActorError + return empty, ErrNoActor } // Authorize the action @@ -544,7 +544,7 @@ func fetchWithAction[ // Fetch the rbac subject act, ok := ActorFromContext(ctx) if !ok { - return empty, NoActorError + return empty, ErrNoActor } // Fetch the database object @@ -620,7 +620,7 @@ func fetchAndQuery[ // Fetch the rbac subject act, ok := ActorFromContext(ctx) if !ok { - return empty, NoActorError + return empty, ErrNoActor } // Fetch the database object @@ -654,7 +654,7 @@ func fetchWithPostFilter[ // Fetch the rbac subject act, ok := ActorFromContext(ctx) if !ok { - return empty, NoActorError + return empty, ErrNoActor } // Fetch the database object @@ -673,7 +673,7 @@ func fetchWithPostFilter[ func prepareSQLFilter(ctx context.Context, authorizer rbac.Authorizer, action policy.Action, resourceType string) (rbac.PreparedAuthorized, error) { act, ok := ActorFromContext(ctx) if !ok { - return nil, NoActorError + return nil, ErrNoActor } return authorizer.Prepare(ctx, act, action, resourceType) @@ -752,7 +752,7 @@ func (*querier) convertToDeploymentRoles(names []string) []rbac.RoleIdentifier { func (q *querier) canAssignRoles(ctx context.Context, orgID uuid.UUID, added, removed []rbac.RoleIdentifier) error { actor, ok := ActorFromContext(ctx) if !ok { - return NoActorError + return ErrNoActor } roleAssign := rbac.ResourceAssignRole @@ -961,7 +961,7 @@ func (q *querier) customRoleEscalationCheck(ctx context.Context, actor rbac.Subj func (q *querier) customRoleCheck(ctx context.Context, role database.CustomRole) error { act, ok := ActorFromContext(ctx) if !ok { - return NoActorError + return ErrNoActor } // Org permissions require an org role @@ -1663,8 +1663,8 @@ func (q *querier) GetDeploymentWorkspaceStats(ctx context.Context) (database.Get return q.db.GetDeploymentWorkspaceStats(ctx) } -func (q *querier) GetEligibleProvisionerDaemonsByProvisionerJobIDs(ctx context.Context, provisionerJobIds []uuid.UUID) ([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, error) { - return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetEligibleProvisionerDaemonsByProvisionerJobIDs)(ctx, provisionerJobIds) +func (q *querier) GetEligibleProvisionerDaemonsByProvisionerJobIDs(ctx context.Context, provisionerJobIDs []uuid.UUID) ([]database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, error) { + return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.GetEligibleProvisionerDaemonsByProvisionerJobIDs)(ctx, provisionerJobIDs) } func (q *querier) GetExternalAuthLink(ctx context.Context, arg database.GetExternalAuthLinkParams) (database.ExternalAuthLink, error) { @@ -3017,11 +3017,11 @@ func (q *querier) GetWorkspaceResourcesCreatedAfter(ctx context.Context, created return q.db.GetWorkspaceResourcesCreatedAfter(ctx, createdAt) } -func (q *querier) GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx context.Context, templateIds []uuid.UUID) ([]database.GetWorkspaceUniqueOwnerCountByTemplateIDsRow, error) { +func (q *querier) GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx context.Context, templateIDs []uuid.UUID) ([]database.GetWorkspaceUniqueOwnerCountByTemplateIDsRow, error) { if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil { return nil, err } - return q.db.GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx, templateIds) + return q.db.GetWorkspaceUniqueOwnerCountByTemplateIDs(ctx, templateIDs) } func (q *querier) GetWorkspaces(ctx context.Context, arg database.GetWorkspacesParams) ([]database.GetWorkspacesRow, error) { @@ -3212,6 +3212,7 @@ func (q *querier) InsertOrganizationMember(ctx context.Context, arg database.Ins } // All roles are added roles. Org member is always implied. + //nolint:gocritic addedRoles := append(orgRoles, rbac.ScopedRoleOrgMember(arg.OrganizationID)) err = q.canAssignRoles(ctx, arg.OrganizationID, addedRoles, []rbac.RoleIdentifier{}) if err != nil { @@ -3364,7 +3365,7 @@ func (q *querier) InsertUserGroupsByName(ctx context.Context, arg database.Inser // This will add the user to all named groups. This counts as updating a group. // NOTE: instead of checking if the user has permission to update each group, we instead // check if the user has permission to update *a* group in the org. - fetch := func(ctx context.Context, arg database.InsertUserGroupsByNameParams) (rbac.Objecter, error) { + fetch := func(_ context.Context, arg database.InsertUserGroupsByNameParams) (rbac.Objecter, error) { return rbac.ResourceGroup.InOrg(arg.OrganizationID), nil } return update(q.log, q.auth, fetch, q.db.InsertUserGroupsByName)(ctx, arg) @@ -3796,6 +3797,7 @@ func (q *querier) UpdateMemberRoles(ctx context.Context, arg database.UpdateMemb } // The org member role is always implied. + //nolint:gocritic impliedTypes := append(scopedGranted, rbac.ScopedRoleOrgMember(arg.OrgID)) added, removed := rbac.ChangeRoleSet(originalRoles, impliedTypes) @@ -3896,7 +3898,7 @@ func (q *querier) UpdateProvisionerJobWithCancelByID(ctx context.Context, arg da // Only owners can cancel workspace builds actor, ok := ActorFromContext(ctx) if !ok { - return NoActorError + return ErrNoActor } if !slice.Contains(actor.Roles.Names(), rbac.RoleOwner()) { return xerrors.Errorf("only owners can cancel workspace builds") diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index 1a822254a9e7a..776667ba053cc 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -252,7 +252,7 @@ func (s *MethodTestSuite) NoActorErrorTest(callMethod func(ctx context.Context) s.Run("AsRemoveActor", func() { // Call without any actor _, err := callMethod(context.Background()) - s.ErrorIs(err, dbauthz.NoActorError, "method should return NoActorError error when no actor is provided") + s.ErrorIs(err, dbauthz.ErrNoActor, "method should return NoActorError error when no actor is provided") }) } diff --git a/coderd/database/dbfake/builder.go b/coderd/database/dbfake/builder.go index 6803374e72445..67600c1856894 100644 --- a/coderd/database/dbfake/builder.go +++ b/coderd/database/dbfake/builder.go @@ -40,6 +40,7 @@ type OrganizationResponse struct { func (b OrganizationBuilder) EveryoneAllowance(allowance int) OrganizationBuilder { //nolint: revive // returns modified struct + // #nosec G115 - Safe conversion as allowance is expected to be within int32 range b.allUsersAllowance = int32(allowance) return b } diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 56e272c7ba048..c29efaef2d63e 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -5977,6 +5977,7 @@ func (q *FakeQuerier) GetTemplateVersionsByTemplateID(_ context.Context, arg dat if arg.LimitOpt > 0 { if int(arg.LimitOpt) > len(version) { + // #nosec G115 - Safe conversion as version slice length is expected to be within int32 range arg.LimitOpt = int32(len(version)) } version = version[:arg.LimitOpt] @@ -6601,6 +6602,7 @@ func (q *FakeQuerier) GetUsers(_ context.Context, params database.GetUsersParams if params.LimitOpt > 0 { if int(params.LimitOpt) > len(users) { + // #nosec G115 - Safe conversion as users slice length is expected to be within int32 range params.LimitOpt = int32(len(users)) } users = users[:params.LimitOpt] @@ -7528,6 +7530,7 @@ func (q *FakeQuerier) GetWorkspaceBuildsByWorkspaceID(_ context.Context, if params.LimitOpt > 0 { if int(params.LimitOpt) > len(history) { + // #nosec G115 - Safe conversion as history slice length is expected to be within int32 range params.LimitOpt = int32(len(history)) } history = history[:params.LimitOpt] @@ -9188,6 +9191,7 @@ func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.I LogSourceID: arg.LogSourceID, Output: output, }) + // #nosec G115 - Safe conversion as log output length is expected to be within int32 range outputLength += int32(len(output)) } for index, agent := range q.workspaceAgents { @@ -12323,17 +12327,23 @@ TemplateUsageStatsInsertLoop: // SELECT tus := database.TemplateUsageStat{ - StartTime: stat.TimeBucket, - EndTime: stat.TimeBucket.Add(30 * time.Minute), - TemplateID: stat.TemplateID, - UserID: stat.UserID, - UsageMins: int16(stat.UsageMins), - MedianLatencyMs: sql.NullFloat64{Float64: latency.MedianLatencyMS, Valid: latencyOk}, - SshMins: int16(stat.SSHMins), - SftpMins: int16(stat.SFTPMins), + StartTime: stat.TimeBucket, + EndTime: stat.TimeBucket.Add(30 * time.Minute), + TemplateID: stat.TemplateID, + UserID: stat.UserID, + // #nosec G115 - Safe conversion for usage minutes which are expected to be within int16 range + UsageMins: int16(stat.UsageMins), + MedianLatencyMs: sql.NullFloat64{Float64: latency.MedianLatencyMS, Valid: latencyOk}, + // #nosec G115 - Safe conversion for SSH minutes which are expected to be within int16 range + SshMins: int16(stat.SSHMins), + // #nosec G115 - Safe conversion for SFTP minutes which are expected to be within int16 range + SftpMins: int16(stat.SFTPMins), + // #nosec G115 - Safe conversion for ReconnectingPTY minutes which are expected to be within int16 range ReconnectingPtyMins: int16(stat.ReconnectingPTYMins), - VscodeMins: int16(stat.VSCodeMins), - JetbrainsMins: int16(stat.JetBrainsMins), + // #nosec G115 - Safe conversion for VSCode minutes which are expected to be within int16 range + VscodeMins: int16(stat.VSCodeMins), + // #nosec G115 - Safe conversion for JetBrains minutes which are expected to be within int16 range + JetbrainsMins: int16(stat.JetBrainsMins), } if len(stat.AppUsageMinutes) > 0 { tus.AppUsageMins = make(map[string]int64, len(stat.AppUsageMinutes)) diff --git a/coderd/database/lock.go b/coderd/database/lock.go index 0bc8b2a75d001..025f7e71fca1a 100644 --- a/coderd/database/lock.go +++ b/coderd/database/lock.go @@ -18,5 +18,6 @@ const ( func GenLockID(name string) int64 { hash := fnv.New64() _, _ = hash.Write([]byte(name)) + // #nosec G115 - Safe conversion as FNV hash should be treated as random value and both uint64/int64 have the same range of unique values return int64(hash.Sum64()) } diff --git a/coderd/database/migrations/migrate_test.go b/coderd/database/migrations/migrate_test.go index 62e301a422e55..65dc9e6267310 100644 --- a/coderd/database/migrations/migrate_test.go +++ b/coderd/database/migrations/migrate_test.go @@ -199,7 +199,7 @@ func (s *tableStats) Add(table string, n int) { s.mu.Lock() defer s.mu.Unlock() - s.s[table] = s.s[table] + n + s.s[table] += n } func (s *tableStats) Empty() []string { diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index a9dbc3e530994..5d0a5ccdea418 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -160,6 +160,7 @@ 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. + // #nosec G115 - Safe conversion for AutostartBlockDaysOfWeek which is 7 bits return ^uint8(t.AutostartBlockDaysOfWeek) & 0b01111111 } diff --git a/coderd/database/pglocks.go b/coderd/database/pglocks.go index 85e1644b3825c..09f17fcad4ad7 100644 --- a/coderd/database/pglocks.go +++ b/coderd/database/pglocks.go @@ -112,7 +112,7 @@ func (l PGLocks) String() string { // Difference returns the difference between two sets of locks. // This is helpful to determine what changed between the two sets. -func (l PGLocks) Difference(to PGLocks) (new PGLocks, removed PGLocks) { +func (l PGLocks) Difference(to PGLocks) (newVal PGLocks, removed PGLocks) { return slice.SymmetricDifferenceFunc(l, to, func(a, b PGLock) bool { return a.Equal(b) }) diff --git a/coderd/database/querier_test.go b/coderd/database/querier_test.go index 837068f1fa03e..96b9396e707cd 100644 --- a/coderd/database/querier_test.go +++ b/coderd/database/querier_test.go @@ -2008,10 +2008,11 @@ func createTemplateVersion(t testing.TB, db database.Store, tpl database.Templat dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ WorkspaceID: wrk.ID, TemplateVersionID: version.ID, - BuildNumber: int32(i) + 2, - Transition: trans, - InitiatorID: tpl.CreatedBy, - JobID: latestJob.ID, + // #nosec G115 - Safe conversion as build number is expected to be within int32 range + BuildNumber: int32(i) + 2, + Transition: trans, + InitiatorID: tpl.CreatedBy, + JobID: latestJob.ID, }) } @@ -3071,21 +3072,22 @@ func TestGetUserStatusCounts(t *testing.T) { row.Date.In(location).String(), i, ) - if row.Date.Before(createdAt) { + switch { + case row.Date.Before(createdAt): require.Equal(t, int64(0), row.Count) - } else if row.Date.Before(firstTransitionTime) { + case row.Date.Before(firstTransitionTime): if row.Status == tc.initialStatus { require.Equal(t, int64(1), row.Count) } else if row.Status == tc.targetStatus { require.Equal(t, int64(0), row.Count) } - } else if !row.Date.After(today) { + case !row.Date.After(today): if row.Status == tc.initialStatus { require.Equal(t, int64(0), row.Count) } else if row.Status == tc.targetStatus { require.Equal(t, int64(1), row.Count) } - } else { + default: t.Errorf("date %q beyond expected range end %q", row.Date, today) } } @@ -3226,18 +3228,19 @@ func TestGetUserStatusCounts(t *testing.T) { expectedCounts[d][tc.user2Transition.to] = 0 // Counted Values - if d.Before(createdAt) { + switch { + case d.Before(createdAt): continue - } else if d.Before(firstTransitionTime) { + case d.Before(firstTransitionTime): expectedCounts[d][tc.user1Transition.from]++ expectedCounts[d][tc.user2Transition.from]++ - } else if d.Before(secondTransitionTime) { + case d.Before(secondTransitionTime): expectedCounts[d][tc.user1Transition.to]++ expectedCounts[d][tc.user2Transition.from]++ - } else if d.Before(today) { + case d.Before(today): expectedCounts[d][tc.user1Transition.to]++ expectedCounts[d][tc.user2Transition.to]++ - } else { + default: t.Fatalf("date %q beyond expected range end %q", d, today) } } @@ -3330,11 +3333,12 @@ func TestGetUserStatusCounts(t *testing.T) { i, ) require.Equal(t, database.UserStatusActive, row.Status) - if row.Date.Before(createdAt) { + switch { + case row.Date.Before(createdAt): require.Equal(t, int64(0), row.Count) - } else if i == len(userStatusChanges)-1 { + case i == len(userStatusChanges)-1: require.Equal(t, int64(0), row.Count) - } else { + default: require.Equal(t, int64(1), row.Count) } } diff --git a/coderd/externalauth/externalauth.go b/coderd/externalauth/externalauth.go index 95ee751ca674e..600aacf62f7dd 100644 --- a/coderd/externalauth/externalauth.go +++ b/coderd/externalauth/externalauth.go @@ -664,7 +664,7 @@ func copyDefaultSettings(config *codersdk.ExternalAuthConfig, defaults codersdk. if config.Regex == "" { config.Regex = defaults.Regex } - if config.Scopes == nil || len(config.Scopes) == 0 { + if len(config.Scopes) == 0 { config.Scopes = defaults.Scopes } if config.DeviceCodeURL == "" { @@ -676,7 +676,7 @@ func copyDefaultSettings(config *codersdk.ExternalAuthConfig, defaults codersdk. if config.DisplayIcon == "" { config.DisplayIcon = defaults.DisplayIcon } - if config.ExtraTokenKeys == nil || len(config.ExtraTokenKeys) == 0 { + if len(config.ExtraTokenKeys) == 0 { config.ExtraTokenKeys = defaults.ExtraTokenKeys } diff --git a/coderd/healthcheck/derphealth/derp.go b/coderd/healthcheck/derphealth/derp.go index fa24ebe7574c6..e6d34cdff3aa1 100644 --- a/coderd/healthcheck/derphealth/derp.go +++ b/coderd/healthcheck/derphealth/derp.go @@ -197,14 +197,15 @@ func (r *RegionReport) Run(ctx context.Context) { return } - if len(r.Region.Nodes) == 1 { + switch { + case len(r.Region.Nodes) == 1: r.Healthy = r.NodeReports[0].Severity != health.SeverityError r.Severity = r.NodeReports[0].Severity - } else if unhealthyNodes == 1 { + case unhealthyNodes == 1: // r.Healthy = true (by default) r.Severity = health.SeverityWarning r.Warnings = append(r.Warnings, health.Messagef(health.CodeDERPOneNodeUnhealthy, oneNodeUnhealthy)) - } else if unhealthyNodes > 1 { + case unhealthyNodes > 1: r.Healthy = false // Review node reports and select the highest severity. diff --git a/coderd/healthcheck/workspaceproxy_test.go b/coderd/healthcheck/workspaceproxy_test.go index a5fab6c63b40d..d5bd5c12210b8 100644 --- a/coderd/healthcheck/workspaceproxy_test.go +++ b/coderd/healthcheck/workspaceproxy_test.go @@ -195,10 +195,8 @@ func TestWorkspaceProxies(t *testing.T) { assert.Equal(t, tt.expectedSeverity, rpt.Severity) if tt.expectedError != "" && assert.NotNil(t, rpt.Error) { assert.Contains(t, *rpt.Error, tt.expectedError) - } else { - if !assert.Nil(t, rpt.Error) { - t.Logf("error: %v", *rpt.Error) - } + } else if !assert.Nil(t, rpt.Error) { + t.Logf("error: %v", *rpt.Error) } if tt.expectedWarningCode != "" && assert.NotEmpty(t, rpt.Warnings) { var found bool diff --git a/coderd/httpapi/queryparams.go b/coderd/httpapi/queryparams.go index 1d814b863a85f..0e4a20920e526 100644 --- a/coderd/httpapi/queryparams.go +++ b/coderd/httpapi/queryparams.go @@ -226,11 +226,9 @@ func (p *QueryParamParser) Time(vals url.Values, def time.Time, queryParam, layo // Time uses the default time format of RFC3339Nano and always returns a UTC time. func (p *QueryParamParser) Time3339Nano(vals url.Values, def time.Time, queryParam string) time.Time { layout := time.RFC3339Nano - return p.timeWithMutate(vals, def, queryParam, layout, func(term string) string { - // All search queries are forced to lowercase. But the RFC format requires - // upper case letters. So just uppercase the term. - return strings.ToUpper(term) - }) + // All search queries are forced to lowercase. But the RFC format requires + // upper case letters. So just uppercase the term. + return p.timeWithMutate(vals, def, queryParam, layout, strings.ToUpper) } func (p *QueryParamParser) timeWithMutate(vals url.Values, def time.Time, queryParam, layout string, mutate func(term string) string) time.Time { diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index 38ba74031ba46..1574affa30b65 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -203,7 +203,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon // Write wraps writing a response to redirect if the handler // specified it should. This redirect is used for user-facing pages // like workspace applications. - write := func(code int, response codersdk.Response) (*database.APIKey, *rbac.Subject, bool) { + write := func(code int, response codersdk.Response) (apiKey *database.APIKey, subject *rbac.Subject, ok bool) { if cfg.RedirectToLogin { RedirectToLogin(rw, r, nil, response.Message) return nil, nil, false diff --git a/coderd/httpmw/cors.go b/coderd/httpmw/cors.go index dd69c714379a4..2350a7dd3b8a6 100644 --- a/coderd/httpmw/cors.go +++ b/coderd/httpmw/cors.go @@ -46,7 +46,7 @@ func Cors(allowAll bool, origins ...string) func(next http.Handler) http.Handler func WorkspaceAppCors(regex *regexp.Regexp, app appurl.ApplicationURL) func(next http.Handler) http.Handler { return cors.Handler(cors.Options{ - AllowOriginFunc: func(r *http.Request, rawOrigin string) bool { + AllowOriginFunc: func(_ *http.Request, rawOrigin string) bool { origin, err := url.Parse(rawOrigin) if rawOrigin == "" || origin.Host == "" || err != nil { return false diff --git a/coderd/httpmw/recover_test.go b/coderd/httpmw/recover_test.go index 5b9758c978c34..b76c5b105baf5 100644 --- a/coderd/httpmw/recover_test.go +++ b/coderd/httpmw/recover_test.go @@ -15,7 +15,7 @@ import ( func TestRecover(t *testing.T) { t.Parallel() - handler := func(isPanic, hijack bool) http.Handler { + handler := func(isPanic, _ bool) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if isPanic { panic("Oh no!") diff --git a/coderd/insights.go b/coderd/insights.go index 9f2bbf5d8b463..b8ae6e6481bdf 100644 --- a/coderd/insights.go +++ b/coderd/insights.go @@ -325,7 +325,8 @@ func (api *API) insightsUserStatusCounts(rw http.ResponseWriter, r *http.Request rows, err := api.Database.GetUserStatusCounts(ctx, database.GetUserStatusCountsParams{ StartTime: sixtyDaysAgo, EndTime: nextHourInLoc, - Interval: int32(interval), + // #nosec G115 - Interval value is small and fits in int32 (typically days or hours) + Interval: int32(interval), }) if err != nil { if httpapi.IsUnauthorizedError(err) { diff --git a/coderd/members.go b/coderd/members.go index 1852e6448408f..bac4244c0ff9f 100644 --- a/coderd/members.go +++ b/coderd/members.go @@ -201,8 +201,10 @@ func (api *API) paginatedMembers(rw http.ResponseWriter, r *http.Request) { paginatedMemberRows, err := api.Database.PaginatedOrganizationMembers(ctx, database.PaginatedOrganizationMembersParams{ OrganizationID: organization.ID, - LimitOpt: int32(paginationParams.Limit), - OffsetOpt: int32(paginationParams.Offset), + // #nosec G115 - Pagination limits are small and fit in int32 + LimitOpt: int32(paginationParams.Limit), + // #nosec G115 - Pagination offsets are small and fit in int32 + OffsetOpt: int32(paginationParams.Offset), }) if httpapi.Is404Error(err) { httpapi.ResourceNotFound(rw) diff --git a/coderd/notifications/dispatch/smtp.go b/coderd/notifications/dispatch/smtp.go index 14ce6b63b4e33..69c3848ddd8b0 100644 --- a/coderd/notifications/dispatch/smtp.go +++ b/coderd/notifications/dispatch/smtp.go @@ -34,10 +34,10 @@ import ( ) var ( - ValidationNoFromAddressErr = xerrors.New("'from' address not defined") - ValidationNoToAddressErr = xerrors.New("'to' address(es) not defined") - ValidationNoSmarthostErr = xerrors.New("'smarthost' address not defined") - ValidationNoHelloErr = xerrors.New("'hello' not defined") + ErrValidationNoFromAddress = xerrors.New("'from' address not defined") + ErrValidationNoToAddress = xerrors.New("'to' address(es) not defined") + ErrValidationNoSmarthost = xerrors.New("'smarthost' address not defined") + ErrValidationNoHello = xerrors.New("'hello' not defined") //go:embed smtp/html.gotmpl htmlTemplate string @@ -493,7 +493,7 @@ func (*SMTPHandler) validateFromAddr(from string) (string, error) { return "", xerrors.Errorf("parse 'from' address: %w", err) } if len(addrs) != 1 { - return "", ValidationNoFromAddressErr + return "", ErrValidationNoFromAddress } return from, nil } @@ -505,7 +505,7 @@ func (s *SMTPHandler) validateToAddrs(to string) ([]string, error) { } if len(addrs) == 0 { s.log.Warn(context.Background(), "no valid 'to' address(es) defined; some may be invalid", slog.F("defined", to)) - return nil, ValidationNoToAddressErr + return nil, ErrValidationNoToAddress } var out []string @@ -522,7 +522,7 @@ func (s *SMTPHandler) validateToAddrs(to string) ([]string, error) { func (s *SMTPHandler) smarthost() (string, string, error) { smarthost := strings.TrimSpace(string(s.cfg.Smarthost)) if smarthost == "" { - return "", "", ValidationNoSmarthostErr + return "", "", ErrValidationNoSmarthost } host, port, err := net.SplitHostPort(string(s.cfg.Smarthost)) @@ -538,7 +538,7 @@ func (s *SMTPHandler) smarthost() (string, string, error) { func (s *SMTPHandler) hello() (string, error) { val := s.cfg.Hello.String() if val == "" { - return "", ValidationNoHelloErr + return "", ErrValidationNoHello } return val, nil } diff --git a/coderd/notifications/manager.go b/coderd/notifications/manager.go index eb3a3ea01938f..ee85bd2d7a3c4 100644 --- a/coderd/notifications/manager.go +++ b/coderd/notifications/manager.go @@ -337,6 +337,7 @@ func (m *Manager) syncUpdates(ctx context.Context) { uctx, cancel := context.WithTimeout(ctx, time.Second*30) defer cancel() + // #nosec G115 - Safe conversion for max send attempts which is expected to be within int32 range failureParams.MaxAttempts = int32(m.cfg.MaxSendAttempts) failureParams.RetryInterval = int32(m.cfg.RetryInterval.Value().Seconds()) n, err := m.store.BulkMarkNotificationMessagesFailed(uctx, failureParams) diff --git a/coderd/notifications/manager_test.go b/coderd/notifications/manager_test.go index 0e6890ae0cef4..590cc4f73cb03 100644 --- a/coderd/notifications/manager_test.go +++ b/coderd/notifications/manager_test.go @@ -192,6 +192,7 @@ type syncInterceptor struct { func (b *syncInterceptor) BulkMarkNotificationMessagesSent(ctx context.Context, arg database.BulkMarkNotificationMessagesSentParams) (int64, error) { updated, err := b.Store.BulkMarkNotificationMessagesSent(ctx, arg) + // #nosec G115 - Safe conversion as the count of updated notification messages is expected to be within int32 range b.sent.Add(int32(updated)) if err != nil { b.err.Store(err) @@ -201,6 +202,7 @@ func (b *syncInterceptor) BulkMarkNotificationMessagesSent(ctx context.Context, func (b *syncInterceptor) BulkMarkNotificationMessagesFailed(ctx context.Context, arg database.BulkMarkNotificationMessagesFailedParams) (int64, error) { updated, err := b.Store.BulkMarkNotificationMessagesFailed(ctx, arg) + // #nosec G115 - Safe conversion as the count of updated notification messages is expected to be within int32 range b.failed.Add(int32(updated)) if err != nil { b.err.Store(err) diff --git a/coderd/notifications/metrics_test.go b/coderd/notifications/metrics_test.go index 052d52873b153..6e7be0d49efbe 100644 --- a/coderd/notifications/metrics_test.go +++ b/coderd/notifications/metrics_test.go @@ -169,7 +169,7 @@ func TestMetrics(t *testing.T) { // See TestPendingUpdatesMetric for a more precise test. return true }, - "coderd_notifications_synced_updates_total": func(metric *dto.Metric, series string) bool { + "coderd_notifications_synced_updates_total": func(metric *dto.Metric, _ string) bool { if debug { t.Logf("coderd_notifications_synced_updates_total = %v: %v", maxAttempts+1, metric.Counter.GetValue()) } diff --git a/coderd/notifications/notifier.go b/coderd/notifications/notifier.go index ba5d22a870a3c..b2713533cecb3 100644 --- a/coderd/notifications/notifier.go +++ b/coderd/notifications/notifier.go @@ -209,7 +209,9 @@ func (n *notifier) process(ctx context.Context, success chan<- dispatchResult, f // messages until they are dispatched - or until the lease expires (in exceptional cases). func (n *notifier) fetch(ctx context.Context) ([]database.AcquireNotificationMessagesRow, error) { msgs, err := n.store.AcquireNotificationMessages(ctx, database.AcquireNotificationMessagesParams{ - Count: int32(n.cfg.LeaseCount), + // #nosec G115 - Safe conversion for lease count which is expected to be within int32 range + Count: int32(n.cfg.LeaseCount), + // #nosec G115 - Safe conversion for max send attempts which is expected to be within int32 range MaxAttemptCount: int32(n.cfg.MaxSendAttempts), NotifierID: n.id, LeaseSeconds: int32(n.cfg.LeasePeriod.Value().Seconds()), @@ -336,6 +338,7 @@ func (n *notifier) newFailedDispatch(msg database.AcquireNotificationMessagesRow var result string // If retryable and not the last attempt, it's a temporary failure. + // #nosec G115 - Safe conversion as MaxSendAttempts is expected to be small enough to fit in int32 if retryable && msg.AttemptCount < int32(n.cfg.MaxSendAttempts)-1 { result = ResultTempFail } else { diff --git a/coderd/prometheusmetrics/aggregator_test.go b/coderd/prometheusmetrics/aggregator_test.go index 59a4b629bf5a5..0930f186bd328 100644 --- a/coderd/prometheusmetrics/aggregator_test.go +++ b/coderd/prometheusmetrics/aggregator_test.go @@ -196,11 +196,12 @@ func verifyCollectedMetrics(t *testing.T, expected []*agentproto.Stats_Metric, a err := actual[i].Write(&d) require.NoError(t, err) - if e.Type == agentproto.Stats_Metric_COUNTER { + switch e.Type { + case agentproto.Stats_Metric_COUNTER: require.Equal(t, e.Value, d.Counter.GetValue()) - } else if e.Type == agentproto.Stats_Metric_GAUGE { + case agentproto.Stats_Metric_GAUGE: require.Equal(t, e.Value, d.Gauge.GetValue()) - } else { + default: require.Failf(t, "unsupported type: %s", string(e.Type)) } diff --git a/coderd/prometheusmetrics/insights/metricscollector.go b/coderd/prometheusmetrics/insights/metricscollector.go index f7ecb06e962f0..41d3a0220f391 100644 --- a/coderd/prometheusmetrics/insights/metricscollector.go +++ b/coderd/prometheusmetrics/insights/metricscollector.go @@ -287,7 +287,7 @@ func convertParameterInsights(rows []database.GetTemplateParameterInsightsRow) [ if _, ok := m[key]; !ok { m[key] = 0 } - m[key] = m[key] + r.Count + m[key] += r.Count } } diff --git a/coderd/prometheusmetrics/prometheusmetrics_test.go b/coderd/prometheusmetrics/prometheusmetrics_test.go index 38ceadb45162e..9911a026ea67a 100644 --- a/coderd/prometheusmetrics/prometheusmetrics_test.go +++ b/coderd/prometheusmetrics/prometheusmetrics_test.go @@ -216,11 +216,9 @@ func TestWorkspaceLatestBuildTotals(t *testing.T) { Total int Status map[codersdk.ProvisionerJobStatus]int }{{ - Name: "None", - Database: func() database.Store { - return dbmem.New() - }, - Total: 0, + Name: "None", + Database: dbmem.New, + Total: 0, }, { Name: "Multiple", Database: func() database.Store { @@ -289,10 +287,8 @@ func TestWorkspaceLatestBuildStatuses(t *testing.T) { ExpectedWorkspaces int ExpectedStatuses map[codersdk.ProvisionerJobStatus]int }{{ - Name: "None", - Database: func() database.Store { - return dbmem.New() - }, + Name: "None", + Database: dbmem.New, ExpectedWorkspaces: 0, }, { Name: "Multiple", diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index dfddd8db24982..0a8c6696b9c5c 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -121,7 +121,7 @@ type server struct { // We use the null byte (0x00) in generating a canonical map key for tags, so // it cannot be used in the tag keys or values. -var ErrorTagsContainNullByte = xerrors.New("tags cannot contain the null byte (0x00)") +var ErrTagsContainNullByte = xerrors.New("tags cannot contain the null byte (0x00)") type Tags map[string]string @@ -136,7 +136,7 @@ func (t Tags) ToJSON() (json.RawMessage, error) { func (t Tags) Valid() error { for k, v := range t { if slices.Contains([]byte(k), 0x00) || slices.Contains([]byte(v), 0x00) { - return ErrorTagsContainNullByte + return ErrTagsContainNullByte } } return nil @@ -1996,7 +1996,8 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()), InstanceMetadata: pqtype.NullRawMessage{}, ResourceMetadata: pqtype.NullRawMessage{}, - DisplayOrder: int32(prAgent.Order), + // #nosec G115 - Order represents a display order value that's always small and fits in int32 + DisplayOrder: int32(prAgent.Order), }) if err != nil { return xerrors.Errorf("insert agent: %w", err) @@ -2011,7 +2012,8 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. Key: md.Key, Timeout: md.Timeout, Interval: md.Interval, - DisplayOrder: int32(md.Order), + // #nosec G115 - Order represents a display order value that's always small and fits in int32 + DisplayOrder: int32(md.Order), } err := db.InsertWorkspaceAgentMetadata(ctx, p) if err != nil { @@ -2194,9 +2196,10 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. HealthcheckInterval: app.Healthcheck.Interval, HealthcheckThreshold: app.Healthcheck.Threshold, Health: health, - DisplayOrder: int32(app.Order), - Hidden: app.Hidden, - OpenIn: openIn, + // #nosec G115 - Order represents a display order value that's always small and fits in int32 + DisplayOrder: int32(app.Order), + Hidden: app.Hidden, + OpenIn: openIn, }) if err != nil { return xerrors.Errorf("insert app: %w", err) diff --git a/coderd/rbac/regosql/compile.go b/coderd/rbac/regosql/compile.go index 7c843d619aa26..a2a3e1efecb09 100644 --- a/coderd/rbac/regosql/compile.go +++ b/coderd/rbac/regosql/compile.go @@ -78,6 +78,7 @@ func convertQuery(cfg ConvertConfig, q ast.Body) (sqltypes.BooleanNode, error) { func convertExpression(cfg ConvertConfig, e *ast.Expr) (sqltypes.BooleanNode, error) { if e.IsCall() { + //nolint:forcetypeassert n, err := convertCall(cfg, e.Terms.([]*ast.Term)) if err != nil { return nil, xerrors.Errorf("call: %w", err) diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index a68cebd1fac93..0e3d3306ab892 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -77,6 +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 { + // #nosec G115 - Safe conversion, i ranges from 0-6 for days of the week days[day] = daysOfWeek&(1< 0b11111111 { return xerrors.New("invalid autostop requirement days, too large") } @@ -106,6 +108,7 @@ func VerifyTemplateAutostartRequirement(days uint8) error { if days&0b10000000 != 0 { return xerrors.New("invalid autostart requirement days, last bit is set") } + //nolint:staticcheck if days > 0b11111111 { return xerrors.New("invalid autostart requirement days, too large") } diff --git a/coderd/searchquery/search.go b/coderd/searchquery/search.go index b31eca2206e18..938f725330cd0 100644 --- a/coderd/searchquery/search.go +++ b/coderd/searchquery/search.go @@ -97,8 +97,10 @@ func Workspaces(ctx context.Context, db database.Store, query string, page coder filter := database.GetWorkspacesParams{ AgentInactiveDisconnectTimeoutSeconds: int64(agentInactiveDisconnectTimeout.Seconds()), + // #nosec G115 - Safe conversion for pagination offset which is expected to be within int32 range Offset: int32(page.Offset), - Limit: int32(page.Limit), + // #nosec G115 - Safe conversion for pagination limit which is expected to be within int32 range + Limit: int32(page.Limit), } if query == "" { diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index 21e1c39fc096f..4572f4f3dfe8d 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -729,7 +729,8 @@ func ConvertWorkspaceBuild(build database.WorkspaceBuild) WorkspaceBuild { WorkspaceID: build.WorkspaceID, JobID: build.JobID, TemplateVersionID: build.TemplateVersionID, - BuildNumber: uint32(build.BuildNumber), + // #nosec G115 - Safe conversion as build numbers are expected to be positive and within uint32 range + BuildNumber: uint32(build.BuildNumber), } } @@ -1035,11 +1036,12 @@ func ConvertTemplate(dbTemplate database.Template) Template { FailureTTLMillis: time.Duration(dbTemplate.FailureTTL).Milliseconds(), TimeTilDormantMillis: time.Duration(dbTemplate.TimeTilDormant).Milliseconds(), TimeTilDormantAutoDeleteMillis: time.Duration(dbTemplate.TimeTilDormantAutoDelete).Milliseconds(), - AutostopRequirementDaysOfWeek: codersdk.BitmapToWeekdays(uint8(dbTemplate.AutostopRequirementDaysOfWeek)), - AutostopRequirementWeeks: dbTemplate.AutostopRequirementWeeks, - AutostartAllowedDays: codersdk.BitmapToWeekdays(dbTemplate.AutostartAllowedDays()), - RequireActiveVersion: dbTemplate.RequireActiveVersion, - Deprecated: dbTemplate.Deprecated != "", + // #nosec G115 - Safe conversion as AutostopRequirementDaysOfWeek is a bitmap of 7 days, easily within uint8 range + AutostopRequirementDaysOfWeek: codersdk.BitmapToWeekdays(uint8(dbTemplate.AutostopRequirementDaysOfWeek)), + AutostopRequirementWeeks: dbTemplate.AutostopRequirementWeeks, + AutostartAllowedDays: codersdk.BitmapToWeekdays(dbTemplate.AutostartAllowedDays()), + RequireActiveVersion: dbTemplate.RequireActiveVersion, + Deprecated: dbTemplate.Deprecated != "", } } diff --git a/coderd/templates.go b/coderd/templates.go index f5ff871650823..13e8c8309e3a4 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -1045,7 +1045,7 @@ func (api *API) convertTemplate( TimeTilDormantMillis: time.Duration(template.TimeTilDormant).Milliseconds(), TimeTilDormantAutoDeleteMillis: time.Duration(template.TimeTilDormantAutoDelete).Milliseconds(), AutostopRequirement: codersdk.TemplateAutostopRequirement{ - DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.AutostopRequirementDaysOfWeek)), + DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.AutostopRequirementDaysOfWeek)), // #nosec G115 - Safe conversion as AutostopRequirementDaysOfWeek is a 7-bit bitmap Weeks: autostopRequirementWeeks, }, AutostartRequirement: codersdk.TemplateAutostartRequirement{ diff --git a/coderd/templateversions.go b/coderd/templateversions.go index d47a3f96cefc1..a12082e11d717 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -843,9 +843,11 @@ func (api *API) templateVersionsByTemplate(rw http.ResponseWriter, r *http.Reque versions, err := store.GetTemplateVersionsByTemplateID(ctx, database.GetTemplateVersionsByTemplateIDParams{ TemplateID: template.ID, AfterID: paginationParams.AfterID, - LimitOpt: int32(paginationParams.Limit), - OffsetOpt: int32(paginationParams.Offset), - Archived: archiveFilter, + // #nosec G115 - Pagination limits are small and fit in int32 + LimitOpt: int32(paginationParams.Limit), + // #nosec G115 - Pagination offsets are small and fit in int32 + OffsetOpt: int32(paginationParams.Offset), + Archived: archiveFilter, }) if errors.Is(err, sql.ErrNoRows) { httpapi.Write(ctx, rw, http.StatusOK, apiVersions) @@ -1280,10 +1282,8 @@ func (api *API) setArchiveTemplateVersion(archive bool) func(rw http.ResponseWri if archiveError != nil { err = archiveError - } else { - if len(archived) == 0 { - err = xerrors.New("Unable to archive specified version, the version is likely in use by a workspace or currently set to the active version") - } + } else if len(archived) == 0 { + err = xerrors.New("Unable to archive specified version, the version is likely in use by a workspace or currently set to the active version") } } else { err = api.Database.UnarchiveTemplateVersion(ctx, database.UnarchiveTemplateVersionParams{ diff --git a/coderd/tracing/exporter.go b/coderd/tracing/exporter.go index 29ebafd6e3b30..461066346d4c2 100644 --- a/coderd/tracing/exporter.go +++ b/coderd/tracing/exporter.go @@ -98,7 +98,7 @@ func TracerProvider(ctx context.Context, service string, opts TracerOpts) (*sdkt tracerProvider := sdktrace.NewTracerProvider(tracerOpts...) otel.SetTracerProvider(tracerProvider) // Ignore otel errors! - otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {})) + otel.SetErrorHandler(otel.ErrorHandlerFunc(func(_ error) {})) otel.SetTextMapPropagator( propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, diff --git a/coderd/tracing/slog.go b/coderd/tracing/slog.go index ad60f6895e55a..6b2841162a3ce 100644 --- a/coderd/tracing/slog.go +++ b/coderd/tracing/slog.go @@ -78,6 +78,7 @@ func slogFieldsToAttributes(m slog.Map) []attribute.KeyValue { case []int64: value = attribute.Int64SliceValue(v) case uint: + // #nosec G115 - Safe conversion from uint to int64 as we're only using this for non-critical logging/tracing value = attribute.Int64Value(int64(v)) // no uint slice method case uint8: @@ -90,6 +91,8 @@ func slogFieldsToAttributes(m slog.Map) []attribute.KeyValue { value = attribute.Int64Value(int64(v)) // no uint32 slice method case uint64: + // #nosec G115 - Safe conversion from uint64 to int64 as we're only using this for non-critical logging/tracing + // This is intentionally lossy for very large values, but acceptable for tracing purposes value = attribute.Int64Value(int64(v)) // no uint64 slice method case string: diff --git a/coderd/tracing/slog_test.go b/coderd/tracing/slog_test.go index 5dae380e07c42..90b7a5ca4a075 100644 --- a/coderd/tracing/slog_test.go +++ b/coderd/tracing/slog_test.go @@ -176,6 +176,7 @@ func mapToBasicMap(m map[string]interface{}) map[string]interface{} { case int32: val = int64(v) case uint: + // #nosec G115 - Safe conversion for test data val = int64(v) case uint8: val = int64(v) @@ -184,6 +185,7 @@ func mapToBasicMap(m map[string]interface{}) map[string]interface{} { case uint32: val = int64(v) case uint64: + // #nosec G115 - Safe conversion for test data with small test values val = int64(v) case time.Duration: val = v.String() diff --git a/coderd/updatecheck/updatecheck.go b/coderd/updatecheck/updatecheck.go index de14071a903b6..67f47262016cf 100644 --- a/coderd/updatecheck/updatecheck.go +++ b/coderd/updatecheck/updatecheck.go @@ -73,7 +73,7 @@ func New(db database.Store, log slog.Logger, opts Options) *Checker { opts.UpdateTimeout = 30 * time.Second } if opts.Notify == nil { - opts.Notify = func(r Result) {} + opts.Notify = func(_ Result) {} } ctx, cancel := context.WithCancel(context.Background()) diff --git a/coderd/userauth.go b/coderd/userauth.go index 63f54f6d157ff..5840187fe8f97 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -1508,7 +1508,8 @@ func (api *API) accessTokenClaims(ctx context.Context, rw http.ResponseWriter, s func (api *API) userInfoClaims(ctx context.Context, rw http.ResponseWriter, state httpmw.OAuth2State, logger slog.Logger) (userInfoClaims map[string]interface{}, ok bool) { userInfoClaims = make(map[string]interface{}) userInfo, err := api.OIDCConfig.Provider.UserInfo(ctx, oauth2.StaticTokenSource(state.Token)) - if err == nil { + switch { + case err == nil: err = userInfo.Claims(&userInfoClaims) if err != nil { logger.Error(ctx, "oauth2: unable to unmarshal user info claims", slog.Error(err)) @@ -1523,14 +1524,14 @@ func (api *API) userInfoClaims(ctx context.Context, rw http.ResponseWriter, stat slog.F("claim_fields", claimFields(userInfoClaims)), slog.F("blank", blankFields(userInfoClaims)), ) - } else if !strings.Contains(err.Error(), "user info endpoint is not supported by this provider") { + case !strings.Contains(err.Error(), "user info endpoint is not supported by this provider"): logger.Error(ctx, "oauth2: unable to obtain user information claims", slog.Error(err)) httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Failed to obtain user information claims.", Detail: "The attempt to fetch claims via the UserInfo endpoint failed: " + err.Error(), }) return nil, false - } else { + default: // The OIDC provider does not support the UserInfo endpoint. // This is not an error, but we should log it as it may mean // that some claims are missing. diff --git a/coderd/userauth_test.go b/coderd/userauth_test.go index ee6ee957ba861..10a0bd8e431f0 100644 --- a/coderd/userauth_test.go +++ b/coderd/userauth_test.go @@ -1452,7 +1452,7 @@ func TestUserOIDC(t *testing.T) { oidctest.WithStaticUserInfo(tc.UserInfoClaims), } - if tc.AccessTokenClaims != nil && len(tc.AccessTokenClaims) > 0 { + if len(tc.AccessTokenClaims) > 0 { opts = append(opts, oidctest.WithAccessTokenJWTHook(func(email string, exp time.Time) jwt.MapClaims { return tc.AccessTokenClaims })) diff --git a/coderd/users.go b/coderd/users.go index 34969f363737c..6334896c18a97 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -306,8 +306,10 @@ func (api *API) GetUsers(rw http.ResponseWriter, r *http.Request) ([]database.Us CreatedAfter: params.CreatedAfter, CreatedBefore: params.CreatedBefore, GithubComUserID: params.GithubComUserID, - OffsetOpt: int32(paginationParams.Offset), - LimitOpt: int32(paginationParams.Limit), + // #nosec G115 - Pagination offsets are small and fit in int32 + OffsetOpt: int32(paginationParams.Offset), + // #nosec G115 - Pagination limits are small and fit in int32 + LimitOpt: int32(paginationParams.Limit), }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ diff --git a/coderd/util/syncmap/map.go b/coderd/util/syncmap/map.go index d245973efa844..178aa3e4f6fd0 100644 --- a/coderd/util/syncmap/map.go +++ b/coderd/util/syncmap/map.go @@ -51,8 +51,8 @@ func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { return act.(V), loaded } -func (m *Map[K, V]) CompareAndSwap(key K, old V, new V) bool { - return m.m.CompareAndSwap(key, old, new) +func (m *Map[K, V]) CompareAndSwap(key K, old V, newVal V) bool { + return m.m.CompareAndSwap(key, old, newVal) } func (m *Map[K, V]) CompareAndDelete(key K, old V) (deleted bool) { diff --git a/coderd/util/tz/tz_linux.go b/coderd/util/tz/tz_linux.go index f35febfbd39ed..5dcfce1de812d 100644 --- a/coderd/util/tz/tz_linux.go +++ b/coderd/util/tz/tz_linux.go @@ -35,7 +35,7 @@ func TimezoneIANA() (*time.Location, error) { if err != nil { return nil, xerrors.Errorf("read location of %s: %w", etcLocaltime, err) } - stripped := strings.Replace(lp, zoneInfoPath, "", -1) + stripped := strings.ReplaceAll(lp, zoneInfoPath, "") stripped = strings.TrimPrefix(stripped, string(filepath.Separator)) loc, err = time.LoadLocation(stripped) if err != nil { diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index a06cf96ea8616..0c87c39a1a52d 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -215,11 +215,12 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) } logs, err := api.Database.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ - AgentID: workspaceAgent.ID, - CreatedAt: dbtime.Now(), - Output: output, - Level: level, - LogSourceID: req.LogSourceID, + AgentID: workspaceAgent.ID, + CreatedAt: dbtime.Now(), + Output: output, + Level: level, + LogSourceID: req.LogSourceID, + // #nosec G115 - Log output length is limited and fits in int32 OutputLength: int32(outputLength), }) if err != nil { @@ -979,10 +980,11 @@ func (api *API) handleResumeToken(ctx context.Context, rw http.ResponseWriter, r peerID, err = api.Options.CoordinatorResumeTokenProvider.VerifyResumeToken(ctx, resumeToken) // If the token is missing the key ID, it's probably an old token in which // case we just want to generate a new peer ID. - if xerrors.Is(err, jwtutils.ErrMissingKeyID) { + switch { + case xerrors.Is(err, jwtutils.ErrMissingKeyID): peerID = uuid.New() err = nil - } else if err != nil { + case err != nil: httpapi.Write(ctx, rw, http.StatusUnauthorized, codersdk.Response{ Message: workspacesdk.CoordinateAPIInvalidResumeToken, Detail: err.Error(), @@ -991,7 +993,7 @@ func (api *API) handleResumeToken(ctx context.Context, rw http.ResponseWriter, r }, }) return peerID, err - } else { + default: api.Logger.Debug(ctx, "accepted coordinate resume token for peer", slog.F("peer_id", peerID.String())) } diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index 899708ce1fb06..aded00d3e38ea 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -843,6 +843,7 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) { o.PortCacheDuration = time.Millisecond }) resources := coderdtest.AwaitWorkspaceAgents(t, client, r.Workspace.ID) + // #nosec G115 - Safe conversion as TCP port numbers are within uint16 range (0-65535) return client, uint16(coderdPort), resources[0].Agents[0].ID } @@ -877,6 +878,7 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) { _ = l.Close() }) + // #nosec G115 - Safe conversion as TCP port numbers are within uint16 range (0-65535) port = uint16(tcpAddr.Port) return true }, testutil.WaitShort, testutil.IntervalFast) diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go index 91d8d7b3fbd6a..4e48e60d2d47f 100644 --- a/coderd/workspaceapps/apptest/apptest.go +++ b/coderd/workspaceapps/apptest/apptest.go @@ -1667,6 +1667,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { require.True(t, ok) appDetails := setupProxyTest(t, &DeploymentOptions{ + // #nosec G115 - Safe conversion as TCP port numbers are within uint16 range (0-65535) port: uint16(tcpAddr.Port), }) diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go index 06544446fe6e2..9d1df9e7fe09d 100644 --- a/coderd/workspaceapps/apptest/setup.go +++ b/coderd/workspaceapps/apptest/setup.go @@ -127,7 +127,7 @@ func (d *Details) AppClient(t *testing.T) *codersdk.Client { client := codersdk.New(d.PathAppBaseURL) client.SetSessionToken(d.SDKClient.SessionToken()) forceURLTransport(t, client) - client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + client.HTTPClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse } @@ -182,7 +182,7 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De // Configure the HTTP client to not follow redirects and to route all // requests regardless of hostname to the coderd test server. - deployment.SDKClient.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + deployment.SDKClient.HTTPClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse } forceURLTransport(t, deployment.SDKClient) diff --git a/coderd/workspaceapps/appurl/appurl.go b/coderd/workspaceapps/appurl/appurl.go index 31ec677354b79..1b1be9197b958 100644 --- a/coderd/workspaceapps/appurl/appurl.go +++ b/coderd/workspaceapps/appurl/appurl.go @@ -267,7 +267,7 @@ func CompileHostnamePattern(pattern string) (*regexp.Regexp, error) { regexPattern = strings.Replace(regexPattern, "*", "([^.]+)", 1) // Allow trailing period. - regexPattern = regexPattern + "\\.?" + regexPattern += "\\.?" // Allow optional port number. regexPattern += "(:\\d+)?" diff --git a/coderd/workspaceapps/db.go b/coderd/workspaceapps/db.go index 1a23723084748..90c6f107daa5e 100644 --- a/coderd/workspaceapps/db.go +++ b/coderd/workspaceapps/db.go @@ -120,7 +120,7 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r * // (later on) fails and the user is not authenticated, they will be // redirected to the login page or app auth endpoint using code below. Optional: true, - SessionTokenFunc: func(r *http.Request) string { + SessionTokenFunc: func(_ *http.Request) string { return issueReq.SessionToken }, }) @@ -132,13 +132,14 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r * // Lookup workspace app details from DB. dbReq, err := appReq.getDatabase(dangerousSystemCtx, p.Database) - if xerrors.Is(err, sql.ErrNoRows) { + switch { + case xerrors.Is(err, sql.ErrNoRows): WriteWorkspaceApp404(p.Logger, p.DashboardURL, rw, r, &appReq, nil, err.Error()) return nil, "", false - } else if xerrors.Is(err, errWorkspaceStopped) { + case xerrors.Is(err, errWorkspaceStopped): WriteWorkspaceOffline(p.Logger, p.DashboardURL, rw, r, &appReq) return nil, "", false - } else if err != nil { + case err != nil: WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "get app details from database") return nil, "", false } @@ -464,6 +465,7 @@ func (p *DBTokenProvider) auditInitRequest(ctx context.Context, w http.ResponseW Ip: ip, UserAgent: userAgent, SlugOrPort: appInfo.SlugOrPort, + // #nosec G115 - Safe conversion as HTTP status code is expected to be within int32 range (typically 100-599) StatusCode: int32(statusCode), StartedAt: aReq.time, UpdatedAt: aReq.time, diff --git a/coderd/workspaceapps/proxy.go b/coderd/workspaceapps/proxy.go index 836279b76191b..de97f6197a28c 100644 --- a/coderd/workspaceapps/proxy.go +++ b/coderd/workspaceapps/proxy.go @@ -45,7 +45,7 @@ const ( // login page. // It is important that this URL can never match a valid app hostname. // - // DEPRECATED: we no longer use this, but we still redirect from it to the + // Deprecated: we no longer use this, but we still redirect from it to the // main login page. appLogoutHostname = "coder-logout" ) @@ -693,6 +693,7 @@ func (s *Server) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) { } defer release() log.Debug(ctx, "dialed workspace agent") + // #nosec G115 - Safe conversion for terminal height/width which are expected to be within uint16 range (0-65535) ptNetConn, err := agentConn.ReconnectingPTY(ctx, reconnect, uint16(height), uint16(width), r.URL.Query().Get("command"), func(arp *workspacesdk.AgentReconnectingPTYInit) { arp.Container = container arp.ContainerUser = containerUser diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 735d6025dd16f..2eb6e1714eedd 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -161,9 +161,11 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { req := database.GetWorkspaceBuildsByWorkspaceIDParams{ WorkspaceID: workspace.ID, AfterID: paginationParams.AfterID, - OffsetOpt: int32(paginationParams.Offset), - LimitOpt: int32(paginationParams.Limit), - Since: dbtime.Time(since), + // #nosec G115 - Pagination offsets are small and fit in int32 + OffsetOpt: int32(paginationParams.Offset), + // #nosec G115 - Pagination limits are small and fit in int32 + LimitOpt: int32(paginationParams.Limit), + Since: dbtime.Time(since), } workspaceBuilds, err = store.GetWorkspaceBuildsByWorkspaceID(ctx, req) if xerrors.Is(err, sql.ErrNoRows) { diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 8ee23dcd5100d..76e85b0716181 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -129,7 +129,7 @@ func TestWorkspace(t *testing.T) { want = want[:32-5] + "-test" } // Sometimes truncated names result in `--test` which is not an allowed name. - want = strings.Replace(want, "--", "-", -1) + want = strings.ReplaceAll(want, "--", "-") err := client.UpdateWorkspace(ctx, ws1.ID, codersdk.UpdateWorkspaceRequest{ Name: want, }) diff --git a/coderd/workspacestats/reporter.go b/coderd/workspacestats/reporter.go index 07d2e9cb3e191..58d177f1c2071 100644 --- a/coderd/workspacestats/reporter.go +++ b/coderd/workspacestats/reporter.go @@ -68,6 +68,7 @@ func (r *Reporter) ReportAppStats(ctx context.Context, stats []workspaceapps.Sta batch.SessionID = append(batch.SessionID, stat.SessionID) batch.SessionStartedAt = append(batch.SessionStartedAt, stat.SessionStartedAt) batch.SessionEndedAt = append(batch.SessionEndedAt, stat.SessionEndedAt) + // #nosec G115 - Safe conversion as request count is expected to be within int32 range batch.Requests = append(batch.Requests, int32(stat.Requests)) if len(batch.UserID) >= r.opts.AppStatBatchSize { @@ -154,16 +155,17 @@ func (r *Reporter) ReportAgentStats(ctx context.Context, now time.Time, workspac templateSchedule, err := (*(r.opts.TemplateScheduleStore.Load())).Get(ctx, r.opts.Database, workspace.TemplateID) // If the template schedule fails to load, just default to bumping // without the next transition and log it. - if err == nil { + switch { + case err == nil: next, allowed := schedule.NextAutostart(now, workspace.AutostartSchedule.String, templateSchedule) if allowed { nextAutostart = next } - } else if database.IsQueryCanceledError(err) { + case database.IsQueryCanceledError(err): r.opts.Logger.Debug(ctx, "query canceled while loading template schedule", slog.F("workspace_id", workspace.ID), slog.F("template_id", workspace.TemplateID)) - } else { + default: r.opts.Logger.Error(ctx, "failed to load template schedule bumping activity, defaulting to bumping by 60min", slog.F("workspace_id", workspace.ID), slog.F("template_id", workspace.TemplateID), diff --git a/coderd/workspaceupdates.go b/coderd/workspaceupdates.go index 630a4be49ec6b..f8d22af0ad159 100644 --- a/coderd/workspaceupdates.go +++ b/coderd/workspaceupdates.go @@ -70,10 +70,9 @@ func (s *sub) handleEvent(ctx context.Context, event wspubsub.WorkspaceEvent, er default: if err == nil { return - } else { - // Always attempt an update if the pubsub lost connection - s.logger.Warn(ctx, "failed to handle workspace event", slog.Error(err)) } + // Always attempt an update if the pubsub lost connection + s.logger.Warn(ctx, "failed to handle workspace event", slog.Error(err)) } // Use context containing actor @@ -199,7 +198,7 @@ func (u *updatesProvider) Subscribe(ctx context.Context, userID uuid.UUID) (tail return sub, nil } -func produceUpdate(old, new workspacesByID) (out *proto.WorkspaceUpdate, updated bool) { +func produceUpdate(oldWS, newWS workspacesByID) (out *proto.WorkspaceUpdate, updated bool) { out = &proto.WorkspaceUpdate{ UpsertedWorkspaces: []*proto.Workspace{}, UpsertedAgents: []*proto.Agent{}, @@ -207,8 +206,8 @@ func produceUpdate(old, new workspacesByID) (out *proto.WorkspaceUpdate, updated DeletedAgents: []*proto.Agent{}, } - for wsID, newWorkspace := range new { - oldWorkspace, exists := old[wsID] + for wsID, newWorkspace := range newWS { + oldWorkspace, exists := oldWS[wsID] // Upsert both workspace and agents if the workspace is new if !exists { out.UpsertedWorkspaces = append(out.UpsertedWorkspaces, &proto.Workspace{ @@ -256,8 +255,8 @@ func produceUpdate(old, new workspacesByID) (out *proto.WorkspaceUpdate, updated } // Delete workspace and agents if the workspace is deleted - for wsID, oldWorkspace := range old { - if _, exists := new[wsID]; !exists { + for wsID, oldWorkspace := range oldWS { + if _, exists := newWS[wsID]; !exists { out.DeletedWorkspaces = append(out.DeletedWorkspaces, &proto.Workspace{ Id: tailnet.UUIDToByteSlice(wsID), Name: oldWorkspace.WorkspaceName, diff --git a/coderd/workspaceupdates_test.go b/coderd/workspaceupdates_test.go index f5977b5c4e985..a41c71c1ee28d 100644 --- a/coderd/workspaceupdates_test.go +++ b/coderd/workspaceupdates_test.go @@ -364,6 +364,7 @@ func (*mockAuthorizer) Authorize(context.Context, rbac.Subject, policy.Action, r // Prepare implements rbac.Authorizer. func (*mockAuthorizer) Prepare(context.Context, rbac.Subject, policy.Action, string) (rbac.PreparedAuthorized, error) { + //nolint:nilnil return nil, nil } diff --git a/codersdk/agentsdk/convert.go b/codersdk/agentsdk/convert.go index abaa8820c7e7e..ed93cb79ef2dd 100644 --- a/codersdk/agentsdk/convert.go +++ b/codersdk/agentsdk/convert.go @@ -62,11 +62,12 @@ func ProtoFromManifest(manifest Manifest) (*proto.Manifest, error) { return nil, xerrors.Errorf("convert workspace apps: %w", err) } return &proto.Manifest{ - AgentId: manifest.AgentID[:], - AgentName: manifest.AgentName, - OwnerUsername: manifest.OwnerName, - WorkspaceId: manifest.WorkspaceID[:], - WorkspaceName: manifest.WorkspaceName, + AgentId: manifest.AgentID[:], + AgentName: manifest.AgentName, + OwnerUsername: manifest.OwnerName, + WorkspaceId: manifest.WorkspaceID[:], + WorkspaceName: manifest.WorkspaceName, + // #nosec G115 - Safe conversion for GitAuthConfigs which is expected to be small and positive GitAuthConfigs: uint32(manifest.GitAuthConfigs), EnvironmentVariables: manifest.EnvironmentVariables, Directory: manifest.Directory, diff --git a/codersdk/agentsdk/logs.go b/codersdk/agentsdk/logs.go index 2a90f14a315b9..38201177738a8 100644 --- a/codersdk/agentsdk/logs.go +++ b/codersdk/agentsdk/logs.go @@ -355,7 +355,7 @@ func (l *LogSender) Flush(src uuid.UUID) { // the map. } -var LogLimitExceededError = xerrors.New("Log limit exceeded") +var ErrLogLimitExceeded = xerrors.New("Log limit exceeded") // SendLoop sends any pending logs until it hits an error or the context is canceled. It does not // retry as it is expected that a higher layer retries establishing connection to the agent API and @@ -365,7 +365,7 @@ func (l *LogSender) SendLoop(ctx context.Context, dest LogDest) error { defer l.L.Unlock() if l.exceededLogLimit { l.logger.Debug(ctx, "aborting SendLoop because log limit is already exceeded") - return LogLimitExceededError + return ErrLogLimitExceeded } ctxDone := false @@ -438,7 +438,7 @@ func (l *LogSender) SendLoop(ctx context.Context, dest LogDest) error { // no point in keeping anything we have queued around, server will not accept them l.queues = make(map[uuid.UUID]*logQueue) l.Broadcast() // might unblock WaitUntilEmpty - return LogLimitExceededError + return ErrLogLimitExceeded } // Since elsewhere we only append to the logs, here we can remove them diff --git a/codersdk/agentsdk/logs_internal_test.go b/codersdk/agentsdk/logs_internal_test.go index 6333ffa19fbf5..2c8bc4748e2e0 100644 --- a/codersdk/agentsdk/logs_internal_test.go +++ b/codersdk/agentsdk/logs_internal_test.go @@ -157,7 +157,7 @@ func TestLogSender_LogLimitExceeded(t *testing.T) { &proto.BatchCreateLogsResponse{LogLimitExceeded: true}) err := testutil.RequireRecvCtx(ctx, t, loopErr) - require.ErrorIs(t, err, LogLimitExceededError) + require.ErrorIs(t, err, ErrLogLimitExceeded) // Should also unblock WaitUntilEmpty err = testutil.RequireRecvCtx(ctx, t, empty) @@ -180,7 +180,7 @@ func TestLogSender_LogLimitExceeded(t *testing.T) { loopErr <- err }() err = testutil.RequireRecvCtx(ctx, t, loopErr) - require.ErrorIs(t, err, LogLimitExceededError) + require.ErrorIs(t, err, ErrLogLimitExceeded) } func TestLogSender_SkipHugeLog(t *testing.T) { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 428ebac4944f5..1adcbd9219862 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -397,7 +397,7 @@ type DeploymentValues struct { Config serpent.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"` WriteConfig serpent.Bool `json:"write_config,omitempty" typescript:",notnull"` - // DEPRECATED: Use HTTPAddress or TLS.Address instead. + // Deprecated: Use HTTPAddress or TLS.Address instead. Address serpent.HostPort `json:"address,omitempty" typescript:",notnull"` } diff --git a/codersdk/richparameters.go b/codersdk/richparameters.go index 6fd082d5faf6c..24609bea0e68c 100644 --- a/codersdk/richparameters.go +++ b/codersdk/richparameters.go @@ -102,17 +102,17 @@ func validateBuildParameter(richParameter TemplateVersionParameter, buildParamet return nil } - var min, max int + var minVal, maxVal int if richParameter.ValidationMin != nil { - min = int(*richParameter.ValidationMin) + minVal = int(*richParameter.ValidationMin) } if richParameter.ValidationMax != nil { - max = int(*richParameter.ValidationMax) + maxVal = int(*richParameter.ValidationMax) } validation := &provider.Validation{ - Min: min, - Max: max, + Min: minVal, + Max: maxVal, MinDisabled: richParameter.ValidationMin == nil, MaxDisabled: richParameter.ValidationMax == nil, Regex: richParameter.ValidationRegex, diff --git a/codersdk/templatevariables.go b/codersdk/templatevariables.go index 8ad79b7639ce9..3e02f6910642f 100644 --- a/codersdk/templatevariables.go +++ b/codersdk/templatevariables.go @@ -121,15 +121,16 @@ func parseVariableValuesFromHCL(content []byte) ([]VariableValue, error) { } ctyType := ctyValue.Type() - if ctyType.Equals(cty.String) { + switch { + case ctyType.Equals(cty.String): stringData[attribute.Name] = ctyValue.AsString() - } else if ctyType.Equals(cty.Number) { + case ctyType.Equals(cty.Number): stringData[attribute.Name] = ctyValue.AsBigFloat().String() - } else if ctyType.IsTupleType() { + case ctyType.IsTupleType(): // In case of tuples, Coder only supports the list(string) type. var items []string var err error - _ = ctyValue.ForEachElement(func(key, val cty.Value) (stop bool) { + _ = ctyValue.ForEachElement(func(_, val cty.Value) (stop bool) { if !val.Type().Equals(cty.String) { err = xerrors.Errorf("unsupported tuple item type: %s ", val.GoString()) return true @@ -146,7 +147,7 @@ func parseVariableValuesFromHCL(content []byte) ([]VariableValue, error) { return nil, err } stringData[attribute.Name] = string(m) - } else { + default: return nil, xerrors.Errorf("unsupported value type (name: %s): %s", attribute.Name, ctyType.GoString()) } } diff --git a/codersdk/workspacesdk/agentconn.go b/codersdk/workspacesdk/agentconn.go index 8c4a3c169b564..fa569080f7dd2 100644 --- a/codersdk/workspacesdk/agentconn.go +++ b/codersdk/workspacesdk/agentconn.go @@ -154,6 +154,7 @@ func (c *AgentConn) ReconnectingPTY(ctx context.Context, id uuid.UUID, height, w return nil, err } data = append(make([]byte, 2), data...) + // #nosec G115 - Safe conversion as the data length is expected to be within uint16 range for PTY initialization binary.LittleEndian.PutUint16(data, uint16(len(data)-2)) _, err = conn.Write(data) diff --git a/codersdk/workspacesdk/workspacesdk.go b/codersdk/workspacesdk/workspacesdk.go index e28579216d526..ca4a3d48d7ef2 100644 --- a/codersdk/workspacesdk/workspacesdk.go +++ b/codersdk/workspacesdk/workspacesdk.go @@ -123,6 +123,7 @@ func init() { // Add a thousand more ports to the ignore list during tests so it's easier // to find an available port. for i := 63000; i < 64000; i++ { + // #nosec G115 - Safe conversion as port numbers are within uint16 range (0-65535) AgentIgnoredListeningPorts[uint16(i)] = struct{}{} } } diff --git a/cryptorand/numbers.go b/cryptorand/numbers.go index aa5046ae8e17f..d6a4889b80562 100644 --- a/cryptorand/numbers.go +++ b/cryptorand/numbers.go @@ -47,10 +47,10 @@ func Int63() (int64, error) { return rng.Int63(), cs.err } -// Intn returns a non-negative integer in [0,max) as an int. -func Intn(max int) (int, error) { +// Intn returns a non-negative integer in [0,maxVal) as an int. +func Intn(maxVal int) (int, error) { rng, cs := secureRand() - return rng.Intn(max), cs.err + return rng.Intn(maxVal), cs.err } // Float64 returns a random number in [0.0,1.0) as a float64. diff --git a/cryptorand/strings.go b/cryptorand/strings.go index 69e9d529d5993..158a6a0c807a4 100644 --- a/cryptorand/strings.go +++ b/cryptorand/strings.go @@ -44,19 +44,28 @@ const ( // //nolint:varnamelen func unbiasedModulo32(v uint32, n int32) (int32, error) { + // #nosec G115 - These conversions are safe within the context of this algorithm + // The conversions here are part of an unbiased modulo algorithm for random number generation + // where the values are properly handled within their respective ranges. prod := uint64(v) * uint64(n) + // #nosec G115 - Safe conversion as part of the unbiased modulo algorithm low := uint32(prod) + // #nosec G115 - Safe conversion as part of the unbiased modulo algorithm if low < uint32(n) { + // #nosec G115 - Safe conversion as part of the unbiased modulo algorithm thresh := uint32(-n) % uint32(n) for low < thresh { err := binary.Read(rand.Reader, binary.BigEndian, &v) if err != nil { return 0, err } + // #nosec G115 - Safe conversion as part of the unbiased modulo algorithm prod = uint64(v) * uint64(n) + // #nosec G115 - Safe conversion as part of the unbiased modulo algorithm low = uint32(prod) } } + // #nosec G115 - Safe conversion as part of the unbiased modulo algorithm return int32(prod >> 32), nil } @@ -89,7 +98,7 @@ func StringCharset(charSetStr string, size int) (string, error) { ci, err := unbiasedModulo32( r, - int32(len(charSet)), + int32(len(charSet)), // #nosec G115 - Safe conversion as len(charSet) will be reasonably small for character sets ) if err != nil { return "", err diff --git a/cryptorand/strings_test.go b/cryptorand/strings_test.go index 60be57ce0f400..8557667457a6c 100644 --- a/cryptorand/strings_test.go +++ b/cryptorand/strings_test.go @@ -160,7 +160,7 @@ func BenchmarkStringUnsafe20(b *testing.B) { for i := 0; i < size; i++ { n := binary.BigEndian.Uint32(ibuf[i*4 : (i+1)*4]) - _, _ = buf.WriteRune(charSet[n%uint32(len(charSet))]) + _, _ = buf.WriteRune(charSet[n%uint32(len(charSet))]) // #nosec G115 - Safe conversion as len(charSet) will be reasonably small for character sets } return buf.String(), nil diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index a7e5e1421e06e..02818183cf2a4 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -2644,7 +2644,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o |--------------------------------------|------------------------------------------------------------------------------------------------------|----------|--------------|--------------------------------------------------------------------| | `access_url` | [serpent.URL](#serpenturl) | false | | | | `additional_csp_policy` | array of string | false | | | -| `address` | [serpent.HostPort](#serpenthostport) | false | | Address Use HTTPAddress or TLS.Address instead. | +| `address` | [serpent.HostPort](#serpenthostport) | false | | Deprecated: Use HTTPAddress or TLS.Address instead. | | `agent_fallback_troubleshooting_url` | [serpent.URL](#serpenturl) | false | | | | `agent_stat_refresh_interval` | integer | false | | | | `allow_workspace_renames` | boolean | false | | | diff --git a/dogfood/coder/Dockerfile b/dogfood/coder/Dockerfile index 9fbc673bcb52b..5e6a77cdcdd27 100644 --- a/dogfood/coder/Dockerfile +++ b/dogfood/coder/Dockerfile @@ -271,7 +271,7 @@ RUN systemctl enable \ ARG CLOUD_SQL_PROXY_VERSION=2.2.0 \ DIVE_VERSION=0.10.0 \ DOCKER_GCR_VERSION=2.1.8 \ - GOLANGCI_LINT_VERSION=1.55.2 \ + GOLANGCI_LINT_VERSION=1.64.8 \ GRYPE_VERSION=0.61.1 \ HELM_VERSION=3.12.0 \ KUBE_LINTER_VERSION=0.6.3 \ diff --git a/enterprise/audit/audit.go b/enterprise/audit/audit.go index 999923893043a..152d32d7d128c 100644 --- a/enterprise/audit/audit.go +++ b/enterprise/audit/audit.go @@ -35,8 +35,8 @@ func NewAuditor(db database.Store, filter Filter, backends ...Backend) audit.Aud db: db, filter: filter, backends: backends, - Differ: audit.Differ{DiffFn: func(old, new any) audit.Map { - return diffValues(old, new, AuditableResources) + Differ: audit.Differ{DiffFn: func(old, newVal any) audit.Map { + return diffValues(old, newVal, AuditableResources) }}, } } diff --git a/enterprise/audit/filter.go b/enterprise/audit/filter.go index 113bfc101b799..b3ab780062be0 100644 --- a/enterprise/audit/filter.go +++ b/enterprise/audit/filter.go @@ -29,7 +29,7 @@ type Filter interface { // DefaultFilter is the default filter used when exporting audit logs. It allows // storage and exporting for all audit logs. -var DefaultFilter Filter = FilterFunc(func(ctx context.Context, alog database.AuditLog) (FilterDecision, error) { +var DefaultFilter Filter = FilterFunc(func(_ context.Context, _ database.AuditLog) (FilterDecision, error) { // Store and export all audit logs for now. return FilterDecisionStore | FilterDecisionExport, nil }) diff --git a/enterprise/cli/proxyserver.go b/enterprise/cli/proxyserver.go index a4a989ae0460f..ec77936accd12 100644 --- a/enterprise/cli/proxyserver.go +++ b/enterprise/cli/proxyserver.go @@ -308,7 +308,7 @@ func (r *RootCmd) proxyServer() *serpent.Command { // TODO: So this obviously is not going to work well. errCh := make(chan error, 1) - go rpprof.Do(ctx, rpprof.Labels("service", "workspace-proxy"), func(ctx context.Context) { + go rpprof.Do(ctx, rpprof.Labels("service", "workspace-proxy"), func(_ context.Context) { errCh <- httpServers.Serve(httpServer) }) diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 2a91fbbfd6f93..cb2a342fb1c8a 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -529,8 +529,9 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { // We always want to run the replica manager even if we don't have DERP // enabled, since it's used to detect other coder servers for licensing. api.replicaManager, err = replicasync.New(ctx, options.Logger, options.Database, options.Pubsub, &replicasync.Options{ - ID: api.AGPL.ID, - RelayAddress: options.DERPServerRelayAddress, + ID: api.AGPL.ID, + RelayAddress: options.DERPServerRelayAddress, + // #nosec G115 - DERP region IDs are small and fit in int32 RegionID: int32(options.DERPServerRegionID), TLSConfig: meshTLSConfig, UpdateInterval: options.ReplicaSyncUpdateInterval, diff --git a/enterprise/coderd/groups.go b/enterprise/coderd/groups.go index 6b94adb2c5b78..71c35236ecaad 100644 --- a/enterprise/coderd/groups.go +++ b/enterprise/coderd/groups.go @@ -61,6 +61,7 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request) DisplayName: req.DisplayName, OrganizationID: org.ID, AvatarURL: req.AvatarURL, + // #nosec G115 - Quota allowance is small and fits in int32 QuotaAllowance: int32(req.QuotaAllowance), }) if database.IsUniqueViolation(err) { @@ -218,6 +219,7 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) { updateGroupParams.Name = req.Name } if req.QuotaAllowance != nil { + // #nosec G115 - Quota allowance is small and fits in int32 updateGroupParams.QuotaAllowance = int32(*req.QuotaAllowance) } if req.DisplayName != nil { diff --git a/enterprise/coderd/jfrog.go b/enterprise/coderd/jfrog.go index f176f48960c0e..1b7cc27247936 100644 --- a/enterprise/coderd/jfrog.go +++ b/enterprise/coderd/jfrog.go @@ -32,10 +32,13 @@ func (api *API) postJFrogXrayScan(rw http.ResponseWriter, r *http.Request) { err := api.Database.UpsertJFrogXrayScanByWorkspaceAndAgentID(ctx, database.UpsertJFrogXrayScanByWorkspaceAndAgentIDParams{ WorkspaceID: req.WorkspaceID, AgentID: req.AgentID, - Critical: int32(req.Critical), - High: int32(req.High), - Medium: int32(req.Medium), - ResultsUrl: req.ResultsURL, + // #nosec G115 - Vulnerability counts are small and fit in int32 + Critical: int32(req.Critical), + // #nosec G115 - Vulnerability counts are small and fit in int32 + High: int32(req.High), + // #nosec G115 - Vulnerability counts are small and fit in int32 + Medium: int32(req.Medium), + ResultsUrl: req.ResultsURL, }) if httpapi.Is404Error(err) { httpapi.ResourceNotFound(rw) diff --git a/enterprise/coderd/license/license.go b/enterprise/coderd/license/license.go index 6f0e827eb3320..e7ee0bd292eb7 100644 --- a/enterprise/coderd/license/license.go +++ b/enterprise/coderd/license/license.go @@ -389,7 +389,7 @@ func ParseClaimsIgnoreNbf(rawJWT string, keys map[string]ed25519.PublicKey) (*Cl var vErr *jwt.ValidationError if xerrors.As(err, &vErr) { // zero out the NotValidYet error to check if there were other problems - vErr.Errors = vErr.Errors & (^jwt.ValidationErrorNotValidYet) + vErr.Errors &= (^jwt.ValidationErrorNotValidYet) if vErr.Errors != 0 { // There are other errors besides not being valid yet. We _could_ go // through all the jwt.ValidationError bits and try to work out the diff --git a/enterprise/coderd/notifications.go b/enterprise/coderd/notifications.go index 3f3ea2b911026..45b9b93c8bc09 100644 --- a/enterprise/coderd/notifications.go +++ b/enterprise/coderd/notifications.go @@ -75,7 +75,7 @@ func (api *API) updateNotificationTemplateMethod(rw http.ResponseWriter, r *http err := api.Database.InTx(func(tx database.Store) error { var err error - template, err = api.Database.UpdateNotificationTemplateMethodByID(r.Context(), database.UpdateNotificationTemplateMethodByIDParams{ + template, err = tx.UpdateNotificationTemplateMethodByID(r.Context(), database.UpdateNotificationTemplateMethodByIDParams{ ID: template.ID, Method: nm, }) diff --git a/enterprise/coderd/portsharing/portsharing.go b/enterprise/coderd/portsharing/portsharing.go index 6d7c138726e11..b45fa8b3c387f 100644 --- a/enterprise/coderd/portsharing/portsharing.go +++ b/enterprise/coderd/portsharing/portsharing.go @@ -14,15 +14,15 @@ func NewEnterprisePortSharer() *EnterprisePortSharer { } func (EnterprisePortSharer) AuthorizedLevel(template database.Template, level codersdk.WorkspaceAgentPortShareLevel) error { - max := codersdk.WorkspaceAgentPortShareLevel(template.MaxPortSharingLevel) + maxLevel := codersdk.WorkspaceAgentPortShareLevel(template.MaxPortSharingLevel) switch level { case codersdk.WorkspaceAgentPortShareLevelPublic: - if max != codersdk.WorkspaceAgentPortShareLevelPublic { - return xerrors.Errorf("port sharing level not allowed. Max level is '%s'", max) + if maxLevel != codersdk.WorkspaceAgentPortShareLevelPublic { + return xerrors.Errorf("port sharing level not allowed. Max level is '%s'", maxLevel) } case codersdk.WorkspaceAgentPortShareLevelAuthenticated: - if max == codersdk.WorkspaceAgentPortShareLevelOwner { - return xerrors.Errorf("port sharing level not allowed. Max level is '%s'", max) + if maxLevel == codersdk.WorkspaceAgentPortShareLevelOwner { + return xerrors.Errorf("port sharing level not allowed. Max level is '%s'", maxLevel) } default: return xerrors.New("port sharing level is invalid.") diff --git a/enterprise/coderd/schedule/template.go b/enterprise/coderd/schedule/template.go index b1065aee7d2b6..855dea4989c73 100644 --- a/enterprise/coderd/schedule/template.go +++ b/enterprise/coderd/schedule/template.go @@ -78,6 +78,7 @@ func (*EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.Sto if tpl.AutostopRequirementWeeks == 0 { tpl.AutostopRequirementWeeks = 1 } + // #nosec G115 - Safe conversion as we've verified tpl.AutostopRequirementDaysOfWeek is <= 255 err = agpl.VerifyTemplateAutostopRequirement(uint8(tpl.AutostopRequirementDaysOfWeek), tpl.AutostopRequirementWeeks) if err != nil { return agpl.TemplateScheduleOptions{}, err @@ -89,6 +90,7 @@ func (*EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.Sto DefaultTTL: time.Duration(tpl.DefaultTTL), ActivityBump: time.Duration(tpl.ActivityBump), AutostopRequirement: agpl.TemplateAutostopRequirement{ + // #nosec G115 - Safe conversion as we've verified tpl.AutostopRequirementDaysOfWeek is <= 255 DaysOfWeek: uint8(tpl.AutostopRequirementDaysOfWeek), Weeks: tpl.AutostopRequirementWeeks, }, diff --git a/enterprise/coderd/scim.go b/enterprise/coderd/scim.go index 3efbc89363ad6..d6bb6b368beea 100644 --- a/enterprise/coderd/scim.go +++ b/enterprise/coderd/scim.go @@ -508,13 +508,13 @@ func (api *API) scimPutUser(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, sUser) } -func immutabilityViolation[T comparable](old, new T) bool { +func immutabilityViolation[T comparable](old, newVal T) bool { var empty T - if new == empty { + if newVal == empty { // No change return false } - return old != new + return old != newVal } //nolint:revive // active is not a control flag diff --git a/enterprise/coderd/workspaceproxy.go b/enterprise/coderd/workspaceproxy.go index 4008de69e4faa..f495f1091a336 100644 --- a/enterprise/coderd/workspaceproxy.go +++ b/enterprise/coderd/workspaceproxy.go @@ -605,6 +605,7 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request) } startingRegionID, _ := getProxyDERPStartingRegionID(api.Options.BaseDERPMap) + // #nosec G115 - Safe conversion as DERP region IDs are small integers expected to be within int32 range regionID := int32(startingRegionID) + proxy.RegionID err := api.Database.InTx(func(db database.Store) error { @@ -625,7 +626,8 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request) // it if it exists. If it doesn't exist, create it. now := time.Now() replica, err := db.GetReplicaByID(ctx, req.ReplicaID) - if err == nil { + switch { + case err == nil: // Replica exists, update it. if replica.StoppedAt.Valid && !replica.StartedAt.IsZero() { // If the replica deregistered, it shouldn't be able to @@ -650,7 +652,7 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request) if err != nil { return xerrors.Errorf("update replica: %w", err) } - } else if xerrors.Is(err, sql.ErrNoRows) { + case xerrors.Is(err, sql.ErrNoRows): // Replica doesn't exist, create it. replica, err = db.InsertReplica(ctx, database.InsertReplicaParams{ ID: req.ReplicaID, @@ -667,7 +669,7 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request) if err != nil { return xerrors.Errorf("insert replica: %w", err) } - } else { + default: return xerrors.Errorf("get replica: %w", err) } diff --git a/enterprise/coderd/workspacequota.go b/enterprise/coderd/workspacequota.go index 7ea42ea24f491..29ab00e0cda30 100644 --- a/enterprise/coderd/workspacequota.go +++ b/enterprise/coderd/workspacequota.go @@ -113,9 +113,11 @@ func (c *committer) CommitQuota( } return &proto.CommitQuotaResponse{ - Ok: permit, + Ok: permit, + // #nosec G115 - Safe conversion as quota credits consumed value is expected to be within int32 range CreditsConsumed: int32(consumed), - Budget: int32(budget), + // #nosec G115 - Safe conversion as quota budget value is expected to be within int32 range + Budget: int32(budget), }, nil } diff --git a/enterprise/dbcrypt/cipher_internal_test.go b/enterprise/dbcrypt/cipher_internal_test.go index c70796ba27e97..f3884df23f0bc 100644 --- a/enterprise/dbcrypt/cipher_internal_test.go +++ b/enterprise/dbcrypt/cipher_internal_test.go @@ -59,7 +59,7 @@ func TestCipherAES256(t *testing.T) { munged := make([]byte, len(encrypted1)) copy(munged, encrypted1) - munged[0] = munged[0] ^ 0xff + munged[0] ^= 0xff _, err = cipher.Decrypt(munged) var decryptErr *DecryptFailedError require.ErrorAs(t, err, &decryptErr, "munging the first byte of the encrypted data should cause decryption to fail") diff --git a/enterprise/replicasync/replicasync.go b/enterprise/replicasync/replicasync.go index a6922837b33d4..0a60ccfd0a1fc 100644 --- a/enterprise/replicasync/replicasync.go +++ b/enterprise/replicasync/replicasync.go @@ -65,14 +65,15 @@ func New(ctx context.Context, logger slog.Logger, db database.Store, ps pubsub.P } // nolint:gocritic // Inserting a replica is a system function. replica, err := db.InsertReplica(dbauthz.AsSystemRestricted(ctx), database.InsertReplicaParams{ - ID: options.ID, - CreatedAt: dbtime.Now(), - StartedAt: dbtime.Now(), - UpdatedAt: dbtime.Now(), - Hostname: hostname, - RegionID: options.RegionID, - RelayAddress: options.RelayAddress, - Version: buildinfo.Version(), + ID: options.ID, + CreatedAt: dbtime.Now(), + StartedAt: dbtime.Now(), + UpdatedAt: dbtime.Now(), + Hostname: hostname, + RegionID: options.RegionID, + RelayAddress: options.RelayAddress, + Version: buildinfo.Version(), + // #nosec G115 - Safe conversion for microseconds latency which is expected to be within int32 range DatabaseLatency: int32(databaseLatency.Microseconds()), Primary: true, }) @@ -202,7 +203,7 @@ func (m *Manager) subscribe(ctx context.Context) error { updating = false updateMutex.Unlock() } - cancelFunc, err := m.pubsub.Subscribe(PubsubEvent, func(ctx context.Context, message []byte) { + cancelFunc, err := m.pubsub.Subscribe(PubsubEvent, func(_ context.Context, message []byte) { updateMutex.Lock() defer updateMutex.Unlock() id, err := uuid.Parse(string(message)) @@ -313,15 +314,16 @@ func (m *Manager) syncReplicas(ctx context.Context) error { defer m.mutex.Unlock() // nolint:gocritic // Updating a replica is a system function. replica, err := m.db.UpdateReplica(dbauthz.AsSystemRestricted(ctx), database.UpdateReplicaParams{ - ID: m.self.ID, - UpdatedAt: dbtime.Now(), - StartedAt: m.self.StartedAt, - StoppedAt: m.self.StoppedAt, - RelayAddress: m.self.RelayAddress, - RegionID: m.self.RegionID, - Hostname: m.self.Hostname, - Version: m.self.Version, - Error: replicaError, + ID: m.self.ID, + UpdatedAt: dbtime.Now(), + StartedAt: m.self.StartedAt, + StoppedAt: m.self.StoppedAt, + RelayAddress: m.self.RelayAddress, + RegionID: m.self.RegionID, + Hostname: m.self.Hostname, + Version: m.self.Version, + Error: replicaError, + // #nosec G115 - Safe conversion for microseconds latency which is expected to be within int32 range DatabaseLatency: int32(databaseLatency.Microseconds()), Primary: m.self.Primary, }) @@ -332,14 +334,15 @@ func (m *Manager) syncReplicas(ctx context.Context) error { // self replica has been cleaned up, we must reinsert // nolint:gocritic // Updating a replica is a system function. replica, err = m.db.InsertReplica(dbauthz.AsSystemRestricted(ctx), database.InsertReplicaParams{ - ID: m.self.ID, - CreatedAt: dbtime.Now(), - UpdatedAt: dbtime.Now(), - StartedAt: m.self.StartedAt, - RelayAddress: m.self.RelayAddress, - RegionID: m.self.RegionID, - Hostname: m.self.Hostname, - Version: m.self.Version, + ID: m.self.ID, + CreatedAt: dbtime.Now(), + UpdatedAt: dbtime.Now(), + StartedAt: m.self.StartedAt, + RelayAddress: m.self.RelayAddress, + RegionID: m.self.RegionID, + Hostname: m.self.Hostname, + Version: m.self.Version, + // #nosec G115 - Safe conversion for microseconds latency which is expected to be within int32 range DatabaseLatency: int32(databaseLatency.Microseconds()), Primary: m.self.Primary, }) diff --git a/enterprise/wsproxy/wsproxy.go b/enterprise/wsproxy/wsproxy.go index af4d5064f4531..9108283513e4f 100644 --- a/enterprise/wsproxy/wsproxy.go +++ b/enterprise/wsproxy/wsproxy.go @@ -398,13 +398,13 @@ func New(ctx context.Context, opts *Options) (*Server, error) { r.Route("/derp", func(r chi.Router) { r.Get("/", derpHandler.ServeHTTP) // This is used when UDP is blocked, and latency must be checked via HTTP(s). - r.Get("/latency-check", func(w http.ResponseWriter, r *http.Request) { + r.Get("/latency-check", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }) }) } else { r.Route("/derp", func(r chi.Router) { - r.HandleFunc("/*", func(rw http.ResponseWriter, r *http.Request) { + r.HandleFunc("/*", func(rw http.ResponseWriter, _ *http.Request) { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "DERP is disabled on this proxy.", }) @@ -413,7 +413,7 @@ func New(ctx context.Context, opts *Options) (*Server, error) { } r.Get("/api/v2/buildinfo", s.buildInfo) - r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("OK")) }) + r.Get("/healthz", func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("OK")) }) // TODO: @emyrk should this be authenticated or debounced? r.Get("/healthz-report", s.healthReport) r.NotFound(func(rw http.ResponseWriter, r *http.Request) { diff --git a/enterprise/wsproxy/wsproxysdk/wsproxysdk.go b/enterprise/wsproxy/wsproxysdk/wsproxysdk.go index fe605558eeb80..b0051551a0f3d 100644 --- a/enterprise/wsproxy/wsproxysdk/wsproxysdk.go +++ b/enterprise/wsproxy/wsproxysdk/wsproxysdk.go @@ -38,7 +38,7 @@ func New(serverURL *url.URL) *Client { sdkClient.SessionTokenHeader = httpmw.WorkspaceProxyAuthTokenHeader sdkClientIgnoreRedirects := codersdk.New(serverURL) - sdkClientIgnoreRedirects.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { + sdkClientIgnoreRedirects.HTTPClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse } sdkClientIgnoreRedirects.SessionTokenHeader = httpmw.WorkspaceProxyAuthTokenHeader diff --git a/go.mod b/go.mod index e555afe0ebf1d..34b472db86fd2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/coder/coder/v2 -go 1.22.12 +go 1.24.1 // Required until a v3 of chroma is created to lazily initialize all XML files. // None of our dependencies seem to use the registries anyways, so this @@ -89,7 +89,7 @@ require ( github.com/chromedp/chromedp v0.11.0 github.com/cli/safeexec v1.0.1 github.com/coder/flog v1.1.0 - github.com/coder/guts v1.0.1 + github.com/coder/guts v1.1.0 github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 github.com/coder/quartz v0.1.2 github.com/coder/retry v1.5.1 diff --git a/go.sum b/go.sum index 694bd19f9ee4c..aa921b67521f9 100644 --- a/go.sum +++ b/go.sum @@ -222,8 +222,8 @@ github.com/coder/go-httpstat v0.0.0-20230801153223-321c88088322 h1:m0lPZjlQ7vdVp github.com/coder/go-httpstat v0.0.0-20230801153223-321c88088322/go.mod h1:rOLFDDVKVFiDqZFXoteXc97YXx7kFi9kYqR+2ETPkLQ= github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136 h1:0RgB61LcNs24WOxc3PBvygSNTQurm0PYPujJjLLOzs0= github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136/go.mod h1:VkD1P761nykiq75dz+4iFqIQIZka189tx1BQLOp0Skc= -github.com/coder/guts v1.0.1 h1:tU9pW+1jftCSX1eBxnNHiouQBSBJIej3I+kqfjIyeJU= -github.com/coder/guts v1.0.1/go.mod h1:z8LHbF6vwDOXQOReDvay7Rpwp/jHwCZiZwjd6wfLcJg= +github.com/coder/guts v1.1.0 h1:EACEds9o4nwFjynDWsw1mvls0Xg91e74vBrqwz8BcGY= +github.com/coder/guts v1.1.0/go.mod h1:31NO4z6MVTOD4WaCLqE/hUAHGgNok9sRbuMc/LZFopI= github.com/coder/pq v1.10.5-0.20240813183442-0c420cb5a048 h1:3jzYUlGH7ZELIH4XggXhnTnP05FCYiAFeQpoN+gNR5I= github.com/coder/pq v1.10.5-0.20240813183442-0c420cb5a048/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= diff --git a/helm/provisioner/tests/chart_test.go b/helm/provisioner/tests/chart_test.go index 728e63d4b6d2f..8830ab87c9b88 100644 --- a/helm/provisioner/tests/chart_test.go +++ b/helm/provisioner/tests/chart_test.go @@ -160,7 +160,7 @@ func TestRenderChart(t *testing.T) { require.NoError(t, err, "failed to read golden file %q", goldenFilePath) // Remove carriage returns to make tests pass on Windows. - goldenBytes = bytes.Replace(goldenBytes, []byte("\r"), []byte(""), -1) + goldenBytes = bytes.ReplaceAll(goldenBytes, []byte("\r"), []byte("")) expected := string(goldenBytes) require.NoError(t, err, "failed to load golden file %q") diff --git a/provisioner/terraform/cleanup.go b/provisioner/terraform/cleanup.go index 9480185ad24df..c6a51d907b5e7 100644 --- a/provisioner/terraform/cleanup.go +++ b/provisioner/terraform/cleanup.go @@ -130,7 +130,7 @@ func CleanStaleTerraformPlugins(ctx context.Context, cachePath string, fs afero. // the last created/modified file. func latestModTime(fs afero.Fs, pluginPath string) (time.Time, error) { var latest time.Time - err := afero.Walk(fs, pluginPath, func(path string, info os.FileInfo, err error) error { + err := afero.Walk(fs, pluginPath, func(_ string, info os.FileInfo, err error) error { if err != nil { return err } diff --git a/provisioner/terraform/install.go b/provisioner/terraform/install.go index f3f2f232aeac1..67d4da34050f2 100644 --- a/provisioner/terraform/install.go +++ b/provisioner/terraform/install.go @@ -27,7 +27,7 @@ var ( minTerraformVersion = version.Must(version.NewVersion("1.1.0")) maxTerraformVersion = version.Must(version.NewVersion("1.11.9")) // use .9 to automatically allow patch releases - terraformMinorVersionMismatch = xerrors.New("Terraform binary minor version mismatch.") + errTerraformMinorVersionMismatch = xerrors.New("Terraform binary minor version mismatch.") ) // Install implements a thread-safe, idempotent Terraform Install diff --git a/provisioner/terraform/provision_test.go b/provisioner/terraform/provision_test.go index cd09ea2adf018..00b459ca1df1a 100644 --- a/provisioner/terraform/provision_test.go +++ b/provisioner/terraform/provision_test.go @@ -11,7 +11,6 @@ import ( "net/http" "os" "path/filepath" - "runtime" "sort" "strings" "testing" @@ -119,10 +118,6 @@ func sendApply(sess proto.DRPCProvisioner_SessionClient, transition proto.Worksp // one process tries to do this simultaneously, it can cause "text file busy" // nolint: paralleltest func TestProvision_Cancel(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("This test uses interrupts and is not supported on Windows") - } - cwd, err := os.Getwd() require.NoError(t, err) fakeBin := filepath.Join(cwd, "testdata", "fake_cancel.sh") @@ -215,10 +210,6 @@ func TestProvision_Cancel(t *testing.T) { // one process tries to do this, it can cause "text file busy" // nolint: paralleltest func TestProvision_CancelTimeout(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("This test uses interrupts and is not supported on Windows") - } - cwd, err := os.Getwd() require.NoError(t, err) fakeBin := filepath.Join(cwd, "testdata", "fake_cancel_hang.sh") @@ -278,10 +269,6 @@ func TestProvision_CancelTimeout(t *testing.T) { // terraform-provider-coder // nolint: paralleltest func TestProvision_TextFileBusy(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("This test uses unix sockets and is not supported on Windows") - } - cwd, err := os.Getwd() require.NoError(t, err) fakeBin := filepath.Join(cwd, "testdata", "fake_text_file_busy.sh") diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index fd0429af131ad..cfc9d0c94c6aa 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -42,7 +42,7 @@ type agentAttributes struct { ID string `mapstructure:"id"` Token string `mapstructure:"token"` Env map[string]string `mapstructure:"env"` - // Deprecated, but remains here for backwards compatibility. + // Deprecated: but remains here for backwards compatibility. StartupScript string `mapstructure:"startup_script"` StartupScriptBehavior string `mapstructure:"startup_script_behavior"` StartupScriptTimeoutSeconds int32 `mapstructure:"startup_script_timeout"` @@ -756,8 +756,9 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s DefaultValue: param.Default, Icon: param.Icon, Required: !param.Optional, - Order: int32(param.Order), - Ephemeral: param.Ephemeral, + // #nosec G115 - Safe conversion as parameter order value is expected to be within int32 range + Order: int32(param.Order), + Ephemeral: param.Ephemeral, } if len(param.Validation) == 1 { protoParam.ValidationRegex = param.Validation[0].Regex @@ -940,6 +941,7 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s } func PtrInt32(number int) *int32 { + // #nosec G115 - Safe conversion as the number is expected to be within int32 range n := int32(number) return &n } diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index 553f131e3fcbd..3638c157d310c 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -1210,12 +1210,9 @@ func TestParameterValidation(t *testing.T) { tfPlanGraph, err := os.ReadFile(filepath.Join(dir, "rich-parameters.tfplan.dot")) require.NoError(t, err) - // Change all names to be identical. - var names []string for _, resource := range tfPlan.PriorState.Values.RootModule.Resources { if resource.Type == "coder_parameter" { resource.AttributeValues["name"] = "identical" - names = append(names, resource.Name) } } @@ -1226,11 +1223,9 @@ func TestParameterValidation(t *testing.T) { // Make two sets of identical names. count := 0 - names = nil for _, resource := range tfPlan.PriorState.Values.RootModule.Resources { if resource.Type == "coder_parameter" { resource.AttributeValues["name"] = fmt.Sprintf("identical-%d", count%2) - names = append(names, resource.Name) count++ } } @@ -1242,11 +1237,9 @@ func TestParameterValidation(t *testing.T) { // Once more with three sets. count = 0 - names = nil for _, resource := range tfPlan.PriorState.Values.RootModule.Resources { if resource.Type == "coder_parameter" { resource.AttributeValues["name"] = fmt.Sprintf("identical-%d", count%3) - names = append(names, resource.Name) count++ } } diff --git a/provisioner/terraform/serve.go b/provisioner/terraform/serve.go index 764b57da84ed3..a84e8caf6b5ab 100644 --- a/provisioner/terraform/serve.go +++ b/provisioner/terraform/serve.go @@ -76,7 +76,7 @@ func systemBinary(ctx context.Context) (*systemBinaryDetails, error) { } if installedVersion.LessThan(minTerraformVersion) { - return details, terraformMinorVersionMismatch + return details, errTerraformMinorVersionMismatch } return details, nil @@ -94,7 +94,7 @@ func Serve(ctx context.Context, options *ServeOptions) error { return xerrors.Errorf("system binary context canceled: %w", err) } - if errors.Is(err, terraformMinorVersionMismatch) { + if errors.Is(err, errTerraformMinorVersionMismatch) { options.Logger.Warn(ctx, "installed terraform version too old, will download known good version to cache, or use a previously cached version", slog.F("installed_version", binaryDetails.version.String()), slog.F("min_version", minTerraformVersion.String())) diff --git a/provisioner/terraform/serve_internal_test.go b/provisioner/terraform/serve_internal_test.go index 0e4a673cd2c6f..c87ee30724ed7 100644 --- a/provisioner/terraform/serve_internal_test.go +++ b/provisioner/terraform/serve_internal_test.go @@ -29,7 +29,7 @@ func Test_absoluteBinaryPath(t *testing.T) { { name: "TestOldVersion", terraformVersion: "1.0.9", - expectedErr: terraformMinorVersionMismatch, + expectedErr: errTerraformMinorVersionMismatch, }, { name: "TestNewVersion", diff --git a/provisioner/terraform/testdata/resources/version.txt b/provisioner/terraform/testdata/resources/version.txt new file mode 100644 index 0000000000000..ca7176690dd6f --- /dev/null +++ b/provisioner/terraform/testdata/resources/version.txt @@ -0,0 +1 @@ +1.11.2 diff --git a/provisioner/terraform/tfparse/tfparse.go b/provisioner/terraform/tfparse/tfparse.go index 281ce55f99146..74905afb6493a 100644 --- a/provisioner/terraform/tfparse/tfparse.go +++ b/provisioner/terraform/tfparse/tfparse.go @@ -279,7 +279,7 @@ func WriteArchive(bs []byte, mimetype string, path string) error { return xerrors.Errorf("read zip file: %w", err) } else if tarBytes, err := archive.CreateTarFromZip(zr, maxFileSizeBytes); err != nil { return xerrors.Errorf("convert zip to tar: %w", err) - } else { + } else { //nolint:revive rdr = bytes.NewReader(tarBytes) } default: @@ -558,9 +558,8 @@ func CtyValueString(val cty.Value) (string, error) { case cty.Bool: if val.True() { return "true", nil - } else { - return "false", nil } + return "false", nil case cty.Number: return val.AsBigFloat().String(), nil case cty.String: diff --git a/provisionerd/runner/runner.go b/provisionerd/runner/runner.go index 4585179916477..70d424c47a0c6 100644 --- a/provisionerd/runner/runner.go +++ b/provisionerd/runner/runner.go @@ -885,7 +885,8 @@ func (r *Runner) commitQuota(ctx context.Context, resources []*sdkproto.Resource const stage = "Commit quota" resp, err := r.quotaCommitter.CommitQuota(ctx, &proto.CommitQuotaRequest{ - JobId: r.job.JobId, + JobId: r.job.JobId, + // #nosec G115 - Safe conversion as cost is expected to be within int32 range for provisioning costs DailyCost: int32(cost), }) if err != nil { diff --git a/provisionersdk/archive.go b/provisionersdk/archive.go index a069639a1eba6..bbae813db0ca0 100644 --- a/provisionersdk/archive.go +++ b/provisionersdk/archive.go @@ -171,10 +171,12 @@ func Untar(directory string, r io.Reader) error { } } case tar.TypeReg: + // #nosec G115 - Safe conversion as tar header mode fits within uint32 err := os.MkdirAll(filepath.Dir(target), os.FileMode(header.Mode)|os.ModeDir|100) if err != nil { return err } + // #nosec G115 - Safe conversion as tar header mode fits within uint32 file, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(header.Mode)) 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/ptytest/ptytest.go b/pty/ptytest/ptytest.go index 42d9f34a7bae0..3991bdeb04142 100644 --- a/pty/ptytest/ptytest.go +++ b/pty/ptytest/ptytest.go @@ -164,9 +164,7 @@ func (e *outExpecter) expectMatchContextFunc(str string, fn func(ctx context.Con // TODO(mafredri): Rename this to ExpectMatch when refactoring. func (e *outExpecter) ExpectMatchContext(ctx context.Context, str string) string { - return e.expectMatcherFunc(ctx, str, func(src, pattern string) bool { - return strings.Contains(src, pattern) - }) + return e.expectMatcherFunc(ctx, str, strings.Contains) } func (e *outExpecter) ExpectRegexMatchContext(ctx context.Context, str string) string { diff --git a/pty/ssh_other.go b/pty/ssh_other.go index fabe8698709c3..2ee90a1ca73b0 100644 --- a/pty/ssh_other.go +++ b/pty/ssh_other.go @@ -105,6 +105,7 @@ func applyTerminalModesToFd(logger *log.Logger, fd uintptr, req ssh.Pty) error { continue } if _, ok := tios.CC[k]; ok { + // #nosec G115 - Safe conversion for terminal control characters which are all in the uint8 range tios.CC[k] = uint8(v) continue } diff --git a/scaletest/agentconn/run.go b/scaletest/agentconn/run.go index a5aaddee4e1d1..dba21cc24e3a0 100644 --- a/scaletest/agentconn/run.go +++ b/scaletest/agentconn/run.go @@ -368,7 +368,7 @@ func agentHTTPClient(conn *workspacesdk.AgentConn) *http.Client { return &http.Client{ Transport: &http.Transport{ DisableKeepAlives: true, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + DialContext: func(ctx context.Context, _ string, addr string) (net.Conn, error) { _, port, err := net.SplitHostPort(addr) if err != nil { return nil, xerrors.Errorf("split host port %q: %w", addr, err) diff --git a/scaletest/dashboard/chromedp.go b/scaletest/dashboard/chromedp.go index d4d944a845071..f20a2f4fc8e26 100644 --- a/scaletest/dashboard/chromedp.go +++ b/scaletest/dashboard/chromedp.go @@ -119,7 +119,7 @@ func clickRandomElement(ctx context.Context, log slog.Logger, randIntn func(int) return "", nil, xerrors.Errorf("no matches found") } match := pick(matches, randIntn) - act := func(actx context.Context) error { + act := func(_ context.Context) error { log.Debug(ctx, "clicking", slog.F("label", match.Label), slog.F("xpath", match.ClickOn)) if err := runWithDeadline(ctx, deadline, chromedp.Click(match.ClickOn, chromedp.NodeReady)); err != nil { log.Error(ctx, "click failed", slog.F("label", match.Label), slog.F("xpath", match.ClickOn), slog.Error(err)) diff --git a/scaletest/harness/strategies.go b/scaletest/harness/strategies.go index 4d321e9ad3116..24bb04e871880 100644 --- a/scaletest/harness/strategies.go +++ b/scaletest/harness/strategies.go @@ -153,6 +153,7 @@ func (cryptoRandSource) Int63() int64 { } // mask off sign bit to ensure positive number + // #nosec G115 - Safe conversion because we're masking the highest bit to ensure a positive int64 return int64(binary.LittleEndian.Uint64(b[:]) & (1<<63 - 1)) } diff --git a/scaletest/workspacetraffic/conn.go b/scaletest/workspacetraffic/conn.go index dcd741fb088e3..7640203e6c224 100644 --- a/scaletest/workspacetraffic/conn.go +++ b/scaletest/workspacetraffic/conn.go @@ -218,6 +218,7 @@ func connectSSH(ctx context.Context, client *codersdk.Client, agentID uuid.UUID, // The exit status is 255 when the command is // interrupted by a signal. This is expected. if exitErr.ExitStatus() != 255 { + // #nosec G115 - Safe conversion as SSH exit status is expected to be within int32 range (usually 0-255) merr = errors.Join(merr, xerrors.Errorf("ssh session exited with unexpected status: %d", int32(exitErr.ExitStatus()))) } } else { diff --git a/scripts/apitypings/main.go b/scripts/apitypings/main.go index 16fdf13f1a7b1..c36636510451f 100644 --- a/scripts/apitypings/main.go +++ b/scripts/apitypings/main.go @@ -116,7 +116,7 @@ func TypeMappings(gen *guts.GoParser) error { // 'serpent.Struct' overrides the json.Marshal to use the underlying type, // so the typescript type should be the underlying type. func FixSerpentStruct(gen *guts.Typescript) { - gen.ForEach(func(key string, originalNode bindings.Node) { + gen.ForEach(func(_ string, originalNode bindings.Node) { isInterface, ok := originalNode.(*bindings.Interface) if ok && isInterface.Name.Ref() == "SerpentStruct" { // replace it with diff --git a/scripts/clidocgen/gen.go b/scripts/clidocgen/gen.go index 6f82168781d01..af86cc16448b1 100644 --- a/scripts/clidocgen/gen.go +++ b/scripts/clidocgen/gen.go @@ -54,10 +54,8 @@ func init() { "wrapCode": func(s string) string { return fmt.Sprintf("%s", s) }, - "commandURI": func(cmd *serpent.Command) string { - return fmtDocFilename(cmd) - }, - "fullName": fullName, + "commandURI": fmtDocFilename, + "fullName": fullName, "tableHeader": func() string { return `| | | | --- | --- |` diff --git a/scripts/dbgen/main.go b/scripts/dbgen/main.go index 5070b0a42aa15..8758048ccb68e 100644 --- a/scripts/dbgen/main.go +++ b/scripts/dbgen/main.go @@ -53,7 +53,7 @@ func run() error { } databasePath := filepath.Join(localPath, "..", "..", "..", "coderd", "database") - err = orderAndStubDatabaseFunctions(filepath.Join(databasePath, "dbmem", "dbmem.go"), "q", "FakeQuerier", func(params stubParams) string { + err = orderAndStubDatabaseFunctions(filepath.Join(databasePath, "dbmem", "dbmem.go"), "q", "FakeQuerier", func(_ stubParams) string { return `panic("not implemented")` }) if err != nil { @@ -72,7 +72,7 @@ return %s return xerrors.Errorf("stub dbmetrics: %w", err) } - err = orderAndStubDatabaseFunctions(filepath.Join(databasePath, "dbauthz", "dbauthz.go"), "q", "querier", func(params stubParams) string { + err = orderAndStubDatabaseFunctions(filepath.Join(databasePath, "dbauthz", "dbauthz.go"), "q", "querier", func(_ stubParams) string { return `panic("not implemented")` }) if err != nil { diff --git a/scripts/echoserver/main.go b/scripts/echoserver/main.go index cb30a0b3839df..cc1768f83e402 100644 --- a/scripts/echoserver/main.go +++ b/scripts/echoserver/main.go @@ -20,19 +20,19 @@ func main() { defer l.Close() tcpAddr, valid := l.Addr().(*net.TCPAddr) if !valid { - log.Fatal("address is not valid") + log.Panic("address is not valid") } remotePort := tcpAddr.Port _, err = fmt.Println(remotePort) if err != nil { - log.Fatalf("print error: err=%s", err) + log.Panicf("print error: err=%s", err) } for { conn, err := l.Accept() if err != nil { - log.Fatalf("accept error, err=%s", err) + log.Panicf("accept error, err=%s", err) return } @@ -43,7 +43,7 @@ func main() { if errors.Is(err, io.EOF) { return } else if err != nil { - log.Fatalf("copy error, err=%s", err) + log.Panicf("copy error, err=%s", err) } }() } diff --git a/scripts/migrate-test/main.go b/scripts/migrate-test/main.go index 145ccb3e1a361..889bc89f9dfcf 100644 --- a/scripts/migrate-test/main.go +++ b/scripts/migrate-test/main.go @@ -82,25 +82,25 @@ func main() { _, _ = fmt.Fprintf(os.Stderr, "Init database at version %q\n", migrateFromVersion) if err := migrations.UpWithFS(conn, migrateFromFS); err != nil { friendlyError(os.Stderr, err, migrateFromVersion, migrateToVersion) - os.Exit(1) + panic("") } _, _ = fmt.Fprintf(os.Stderr, "Migrate to version %q\n", migrateToVersion) if err := migrations.UpWithFS(conn, migrateToFS); err != nil { friendlyError(os.Stderr, err, migrateFromVersion, migrateToVersion) - os.Exit(1) + panic("") } _, _ = fmt.Fprintf(os.Stderr, "Dump schema at version %q\n", migrateToVersion) dumpBytesAfter, err := dbtestutil.PGDumpSchemaOnly(postgresURL) if err != nil { friendlyError(os.Stderr, err, migrateFromVersion, migrateToVersion) - os.Exit(1) + panic("") } if diff := cmp.Diff(string(dumpBytesAfter), string(stripGenPreamble(expectedSchemaAfter))); diff != "" { friendlyError(os.Stderr, xerrors.Errorf("Schema differs from expected after migration: %s", diff), migrateFromVersion, migrateToVersion) - os.Exit(1) + panic("") } _, _ = fmt.Fprintf(os.Stderr, "OK\n") } diff --git a/scripts/release/main.go b/scripts/release/main.go index 6be81a57773ed..599fec4f1a38c 100644 --- a/scripts/release/main.go +++ b/scripts/release/main.go @@ -126,7 +126,7 @@ func main() { err = cmd.Invoke().WithOS().Run() if err != nil { - if errors.Is(err, cliui.Canceled) { + if errors.Is(err, cliui.ErrCanceled) { os.Exit(1) } r.logger.Error(context.Background(), "release command failed", "err", err) diff --git a/scripts/testidp/main.go b/scripts/testidp/main.go index 52b10ab94e975..a6188ace2ce9b 100644 --- a/scripts/testidp/main.go +++ b/scripts/testidp/main.go @@ -38,7 +38,7 @@ func main() { flag.Parse() // This is just a way to run tests outside go test - testing.Main(func(pat, str string) (bool, error) { + testing.Main(func(_, _ string) (bool, error) { return true, nil }, []testing.InternalTest{ { diff --git a/support/support.go b/support/support.go index 5ae48ddb37cba..30e9be934ead7 100644 --- a/support/support.go +++ b/support/support.go @@ -241,11 +241,9 @@ func WorkspaceInfo(ctx context.Context, client *codersdk.Client, log slog.Logger return xerrors.Errorf("fetch provisioner job logs: %w", err) } defer closer.Close() - var logs []codersdk.ProvisionerJobLog for log := range buildLogCh { - logs = append(w.BuildLogs, log) + w.BuildLogs = append(w.BuildLogs, log) } - w.BuildLogs = logs return nil }) diff --git a/tailnet/conn.go b/tailnet/conn.go index 8f7f8ef7287a2..59ddefc636d13 100644 --- a/tailnet/conn.go +++ b/tailnet/conn.go @@ -132,6 +132,7 @@ 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 { + // #nosec G115 - This is safe because the next lines ensure the ID is always positive id := int64(binary.BigEndian.Uint64(uid[8:])) // ensure id is positive diff --git a/tailnet/controllers_test.go b/tailnet/controllers_test.go index ee3c07ff745ac..16f254e3240a7 100644 --- a/tailnet/controllers_test.go +++ b/tailnet/controllers_test.go @@ -35,7 +35,7 @@ import ( "github.com/coder/quartz" ) -var unimplementedError = drpcerr.WithCode(xerrors.New("Unimplemented"), drpcerr.Unimplemented) +var errUnimplemented = drpcerr.WithCode(xerrors.New("Unimplemented"), drpcerr.Unimplemented) func TestInMemoryCoordination(t *testing.T) { t.Parallel() @@ -708,7 +708,7 @@ func TestBasicTelemetryController_Unimplemented(t *testing.T) { call = testutil.RequireRecvCtx(ctx, t, ft.calls) // for real this time - telemetryError = unimplementedError + telemetryError = errUnimplemented testutil.RequireSendCtx(ctx, t, call.errCh, telemetryError) testutil.RequireRecvCtx(ctx, t, sendDone) @@ -948,7 +948,7 @@ func TestBasicResumeTokenController_Unimplemented(t *testing.T) { cw := uut.New(fr) call := testutil.RequireRecvCtx(ctx, t, fr.calls) - testutil.RequireSendCtx(ctx, t, call.errCh, unimplementedError) + testutil.RequireSendCtx(ctx, t, call.errCh, errUnimplemented) err := testutil.RequireRecvCtx(ctx, t, cw.Wait()) require.NoError(t, err) _, ok = uut.Token() @@ -974,13 +974,13 @@ func (f *fakeResumeTokenClient) RefreshResumeToken(_ context.Context, _ *proto.R } select { case <-f.ctx.Done(): - return nil, timeoutOnFakeErr + return nil, errTimeoutOnFake case f.calls <- call: // OK } select { case <-f.ctx.Done(): - return nil, timeoutOnFakeErr + return nil, errTimeoutOnFake case err := <-call.errCh: return nil, err case resp := <-call.resp: @@ -1245,10 +1245,10 @@ func (p *pipeDialer) Dial(_ context.Context, _ tailnet.ResumeTokenController) (t }, nil } -// timeoutOnFakeErr is the error we send when fakes fail to send calls or receive responses before +// errTimeoutOnFake is the error we send when fakes fail to send calls or receive responses before // their context times out. We don't want to send the context error since that often doesn't trigger // test failures or logging. -var timeoutOnFakeErr = xerrors.New("test timeout") +var errTimeoutOnFake = xerrors.New("test timeout") type fakeCoordinatorClient struct { ctx context.Context @@ -1263,13 +1263,13 @@ func (f fakeCoordinatorClient) Close() error { errs := make(chan error) select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case f.close <- errs: // OK } select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case err := <-errs: return err } @@ -1284,13 +1284,13 @@ func (f fakeCoordinatorClient) Send(request *proto.CoordinateRequest) error { } select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case f.reqs <- call: // OK } select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case err := <-errs: return err } @@ -1306,13 +1306,13 @@ func (f fakeCoordinatorClient) Recv() (*proto.CoordinateResponse, error) { } select { case <-f.ctx.Done(): - return nil, timeoutOnFakeErr + return nil, errTimeoutOnFake case f.resps <- call: // OK } select { case <-f.ctx.Done(): - return nil, timeoutOnFakeErr + return nil, errTimeoutOnFake case err := <-errs: return nil, err case resp := <-resps: @@ -1352,13 +1352,13 @@ func (f *fakeWorkspaceUpdateClient) Close() error { errs := make(chan error) select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case f.close <- errs: // OK } select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case err := <-errs: return err } @@ -1374,13 +1374,13 @@ func (f *fakeWorkspaceUpdateClient) Recv() (*proto.WorkspaceUpdate, error) { } select { case <-f.ctx.Done(): - return nil, timeoutOnFakeErr + return nil, errTimeoutOnFake case f.recv <- call: // OK } select { case <-f.ctx.Done(): - return nil, timeoutOnFakeErr + return nil, errTimeoutOnFake case err := <-errs: return nil, err case resp := <-resps: @@ -1440,13 +1440,13 @@ func (f *fakeDNSSetter) SetDNSHosts(hosts map[dnsname.FQDN][]netip.Addr) error { } select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case f.calls <- call: // OK } select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case err := <-errs: return err } @@ -1470,7 +1470,7 @@ func (f *fakeUpdateHandler) Update(wu tailnet.WorkspaceUpdate) error { f.t.Helper() select { case <-f.ctx.Done(): - return timeoutOnFakeErr + return errTimeoutOnFake case f.ch <- wu: // OK } @@ -1946,7 +1946,7 @@ func (f fakeWorkspaceUpdatesController) New(client tailnet.WorkspaceUpdatesClien select { case <-f.ctx.Done(): cw := newFakeCloserWaiter() - cw.errCh <- timeoutOnFakeErr + cw.errCh <- errTimeoutOnFake return cw case f.calls <- call: // OK @@ -1954,7 +1954,7 @@ func (f fakeWorkspaceUpdatesController) New(client tailnet.WorkspaceUpdatesClien select { case <-f.ctx.Done(): cw := newFakeCloserWaiter() - cw.errCh <- timeoutOnFakeErr + cw.errCh <- errTimeoutOnFake return cw case resp := <-resps: return resp diff --git a/tailnet/convert.go b/tailnet/convert.go index 74b067632f231..c2d8c58e5cb80 100644 --- a/tailnet/convert.go +++ b/tailnet/convert.go @@ -31,6 +31,7 @@ func NodeToProto(n *Node) (*proto.Node, error) { } derpForcedWebsocket := make(map[int32]string) for i, s := range n.DERPForcedWebsocket { + // #nosec G115 - Safe conversion for DERP region IDs which are small positive integers derpForcedWebsocket[int32(i)] = s } addresses := make([]string, len(n.Addresses)) @@ -50,10 +51,11 @@ func NodeToProto(n *Node) (*proto.Node, error) { allowedIPs[i] = string(s) } return &proto.Node{ - Id: int64(n.ID), - AsOf: timestamppb.New(n.AsOf), - Key: k, - Disco: string(disco), + Id: int64(n.ID), + AsOf: timestamppb.New(n.AsOf), + Key: k, + Disco: string(disco), + // #nosec G115 - Safe conversion as DERP region IDs are small integers expected to be within int32 range PreferredDerp: int32(n.PreferredDERP), DerpLatency: n.DERPLatency, DerpForcedWebsocket: derpForcedWebsocket, @@ -190,14 +192,16 @@ func DERPNodeToProto(node *tailcfg.DERPNode) *proto.DERPMap_Region_Node { } return &proto.DERPMap_Region_Node{ - Name: node.Name, - RegionId: int64(node.RegionID), - HostName: node.HostName, - CertName: node.CertName, - Ipv4: node.IPv4, - Ipv6: node.IPv6, - StunPort: int32(node.STUNPort), - StunOnly: node.STUNOnly, + Name: node.Name, + RegionId: int64(node.RegionID), + HostName: node.HostName, + CertName: node.CertName, + Ipv4: node.IPv4, + Ipv6: node.IPv6, + // #nosec G115 - Safe conversion as STUN port is within int32 range (0-65535) + StunPort: int32(node.STUNPort), + StunOnly: node.STUNOnly, + // #nosec G115 - Safe conversion as DERP port is within int32 range (0-65535) DerpPort: int32(node.DERPPort), InsecureForTests: node.InsecureForTests, ForceHttp: node.ForceHTTP, diff --git a/tailnet/coordinator.go b/tailnet/coordinator.go index 3f2f3a1a698fa..f0f2c311f6e23 100644 --- a/tailnet/coordinator.go +++ b/tailnet/coordinator.go @@ -323,7 +323,7 @@ func (c *core) handleReadyForHandshakeLocked(src *peer, rfhs []*proto.Coordinate return nil } -func (c *core) nodeUpdateLocked(p *peer, node *proto.Node) error { +func (c *core) nodeUpdateLocked(p *peer, node *proto.Node) (err error) { c.logger.Debug(context.Background(), "processing node update", slog.F("peer_id", p.id), slog.F("node", node.String())) diff --git a/tailnet/peer.go b/tailnet/peer.go index 7d69764abe103..0b265a1300074 100644 --- a/tailnet/peer.go +++ b/tailnet/peer.go @@ -33,7 +33,7 @@ type peer struct { func (p *peer) updateMappingLocked(id uuid.UUID, n *proto.Node, k proto.CoordinateResponse_PeerUpdate_Kind, reason string) error { logger := p.logger.With(slog.F("from_id", id), slog.F("kind", k), slog.F("reason", reason)) update, err := p.storeMappingLocked(id, n, k, reason) - if xerrors.Is(err, noResp) { + if xerrors.Is(err, errNoResp) { logger.Debug(context.Background(), "skipping update") return nil } @@ -61,7 +61,7 @@ func (p *peer) batchUpdateMappingLocked(others []*peer, k proto.CoordinateRespon continue } update, err := p.storeMappingLocked(other.id, other.node, k, reason) - if xerrors.Is(err, noResp) { + if xerrors.Is(err, errNoResp) { continue } if err != nil { @@ -82,7 +82,7 @@ func (p *peer) batchUpdateMappingLocked(others []*peer, k proto.CoordinateRespon } } -var noResp = xerrors.New("no response needed") +var errNoResp = xerrors.New("no response needed") func (p *peer) storeMappingLocked( id uuid.UUID, n *proto.Node, k proto.CoordinateResponse_PeerUpdate_Kind, reason string, @@ -95,7 +95,7 @@ func (p *peer) storeMappingLocked( switch { case !ok && (k == proto.CoordinateResponse_PeerUpdate_LOST || k == proto.CoordinateResponse_PeerUpdate_DISCONNECTED): // we don't need to send a lost/disconnect update if we've never sent an update about this peer - return nil, noResp + return nil, errNoResp case !ok && k == proto.CoordinateResponse_PeerUpdate_NODE: p.sent[id] = n case ok && k == proto.CoordinateResponse_PeerUpdate_LOST: @@ -109,7 +109,7 @@ func (p *peer) storeMappingLocked( return nil, xerrors.Errorf("failed to compare nodes: %s", sn.String()) } if eq { - return nil, noResp + return nil, errNoResp } p.sent[id] = n } diff --git a/tailnet/service.go b/tailnet/service.go index cfbbb77a9833f..abb91acef8772 100644 --- a/tailnet/service.go +++ b/tailnet/service.go @@ -322,7 +322,7 @@ func NewNetworkTelemetryBatcher(clk quartz.Clock, frequency time.Duration, maxSi done: make(chan struct{}), } if b.batchFn == nil { - b.batchFn = func(batch []*proto.TelemetryEvent) {} + b.batchFn = func(_ []*proto.TelemetryEvent) {} } b.start() return b diff --git a/tailnet/telemetry.go b/tailnet/telemetry.go index 1b8d2d603e445..482894d11fd3d 100644 --- a/tailnet/telemetry.go +++ b/tailnet/telemetry.go @@ -106,13 +106,14 @@ func (b *TelemetryStore) changedConntype(addr string) bool { b.mu.Lock() defer b.mu.Unlock() - if b.p2p && addr != "" { + switch { + case b.p2p && addr != "": return false - } else if !b.p2p && addr != "" { + case !b.p2p && addr != "": b.p2p = true b.p2pSetupTime = time.Since(b.lastDerpTime) return true - } else if b.p2p && addr == "" { + case b.p2p && addr == "": b.p2p = false b.lastDerpTime = time.Now() b.p2pSetupTime = 0 @@ -131,6 +132,7 @@ func (b *TelemetryStore) updateRemoteNodeIDLocked(nm *netmap.NetworkMap) { for _, p := range nm.Peers { for _, a := range p.Addresses { if a.Addr() == ip && a.IsSingleIP() { + // #nosec G115 - Safe conversion as p.ID is expected to be within uint64 range for node IDs b.nodeIDRemote = uint64(p.ID) } } @@ -188,6 +190,7 @@ func (b *TelemetryStore) updateByNodeLocked(n *tailcfg.Node) bool { if n == nil { return false } + // #nosec G115 - Safe conversion as n.ID is expected to be within uint64 range for node IDs b.nodeIDSelf = uint64(n.ID) derpIP, err := netip.ParseAddrPort(n.DERP) if err != nil { diff --git a/tailnet/telemetry_internal_test.go b/tailnet/telemetry_internal_test.go index 8e4234f66c1f4..c738ddb3314a8 100644 --- a/tailnet/telemetry_internal_test.go +++ b/tailnet/telemetry_internal_test.go @@ -70,7 +70,9 @@ func TestTelemetryStore(t *testing.T) { e := telemetry.newEvent() // DERPMapToProto already tested require.Equal(t, DERPMapToProto(nm.DERPMap), e.DerpMap) + // #nosec G115 - Safe conversion in test code as node IDs are within uint64 range require.Equal(t, uint64(nm.Peers[1].ID), e.NodeIdRemote) + // #nosec G115 - Safe conversion in test code as node IDs are within uint64 range require.Equal(t, uint64(nm.SelfNode.ID), e.NodeIdSelf) require.Equal(t, application, e.Application) require.Equal(t, nm.SelfNode.DERP, fmt.Sprintf("127.3.3.40:%d", e.HomeDerp)) diff --git a/tailnet/test/peer.go b/tailnet/test/peer.go index d8b7f540e7fff..e3064389d7dc9 100644 --- a/tailnet/test/peer.go +++ b/tailnet/test/peer.go @@ -234,7 +234,7 @@ func (p *Peer) AssertEventuallyResponsesClosed() { p.t.Helper() for { err := p.readOneResp() - if xerrors.Is(err, responsesClosed) { + if xerrors.Is(err, errResponsesClosed) { return } if !assert.NoError(p.t, err) { @@ -278,7 +278,7 @@ func (p *Peer) AssertEventuallyReadyForHandshake(other uuid.UUID) { } err := p.readOneResp() - if xerrors.Is(err, responsesClosed) { + if xerrors.Is(err, errResponsesClosed) { return } } @@ -288,7 +288,7 @@ func (p *Peer) AssertEventuallyGetsError(match string) { p.t.Helper() for { err := p.readOneResp() - if xerrors.Is(err, responsesClosed) { + if xerrors.Is(err, errResponsesClosed) { p.t.Error("closed before target error") return } @@ -312,7 +312,7 @@ func (p *Peer) AssertNeverUpdateKind(peer uuid.UUID, kind proto.CoordinateRespon } } -var responsesClosed = xerrors.New("responses closed") +var errResponsesClosed = xerrors.New("responses closed") func (p *Peer) readOneResp() error { select { @@ -320,7 +320,7 @@ func (p *Peer) readOneResp() error { return p.ctx.Err() case resp, ok := <-p.resps: if !ok { - return responsesClosed + return errResponsesClosed } err := p.handleResp(resp) if err != nil { diff --git a/testutil/port.go b/testutil/port.go index b5720e44a0966..0bb4b05354a39 100644 --- a/testutil/port.go +++ b/testutil/port.go @@ -34,12 +34,13 @@ func RandomPort(t *testing.T) int { func RandomPortNoListen(*testing.T) uint16 { const ( // Overlap of windows, linux in https://en.wikipedia.org/wiki/Ephemeral_port - min = 49152 - max = 60999 + minPort = 49152 + maxPort = 60999 ) - n := max - min + n := maxPort - minPort rndMu.Lock() x := rnd.Intn(n) rndMu.Unlock() - return uint16(min + x) + // #nosec G115 - Safe conversion since minPort and x are explicitly within the uint16 range + return uint16(minPort + x) } diff --git a/vpn/router.go b/vpn/router.go index 6dfc49b4f2e44..a3fab4bf9bdd2 100644 --- a/vpn/router.go +++ b/vpn/router.go @@ -40,35 +40,39 @@ func convertRouterConfig(cfg router.Config) *NetworkSettingsRequest { v6LocalAddrs := make([]string, 0) v6PrefixLengths := make([]uint32, 0) for _, addrs := range cfg.LocalAddrs { - if addrs.Addr().Is4() { + switch { + case addrs.Addr().Is4(): v4LocalAddrs = append(v4LocalAddrs, addrs.Addr().String()) v4SubnetMasks = append(v4SubnetMasks, prefixToSubnetMask(addrs)) - } else if addrs.Addr().Is6() { + case addrs.Addr().Is6(): v6LocalAddrs = append(v6LocalAddrs, addrs.Addr().String()) + // #nosec G115 - Safe conversion as IPv6 prefix lengths are always within uint32 range (0-128) v6PrefixLengths = append(v6PrefixLengths, uint32(addrs.Bits())) - } else { + default: continue } } v4Routes := make([]*NetworkSettingsRequest_IPv4Settings_IPv4Route, 0) v6Routes := make([]*NetworkSettingsRequest_IPv6Settings_IPv6Route, 0) for _, route := range cfg.Routes { - if route.Addr().Is4() { + switch { + case route.Addr().Is4(): v4Routes = append(v4Routes, convertToIPV4Route(route)) - } else if route.Addr().Is6() { + case route.Addr().Is6(): v6Routes = append(v6Routes, convertToIPV6Route(route)) - } else { + default: continue } } v4ExcludedRoutes := make([]*NetworkSettingsRequest_IPv4Settings_IPv4Route, 0) v6ExcludedRoutes := make([]*NetworkSettingsRequest_IPv6Settings_IPv6Route, 0) for _, route := range cfg.LocalRoutes { - if route.Addr().Is4() { + switch { + case route.Addr().Is4(): v4ExcludedRoutes = append(v4ExcludedRoutes, convertToIPV4Route(route)) - } else if route.Addr().Is6() { + case route.Addr().Is6(): v6ExcludedRoutes = append(v6ExcludedRoutes, convertToIPV6Route(route)) - } else { + default: continue } } @@ -95,6 +99,7 @@ func convertRouterConfig(cfg router.Config) *NetworkSettingsRequest { } return &NetworkSettingsRequest{ + // #nosec G115 - Safe conversion as MTU values are expected to be small positive integers Mtu: uint32(cfg.NewMTU), Ipv4Settings: v4Settings, Ipv6Settings: v6Settings, @@ -113,7 +118,8 @@ func convertToIPV4Route(route netip.Prefix) *NetworkSettingsRequest_IPv4Settings func convertToIPV6Route(route netip.Prefix) *NetworkSettingsRequest_IPv6Settings_IPv6Route { return &NetworkSettingsRequest_IPv6Settings_IPv6Route{ - Destination: route.Addr().String(), + Destination: route.Addr().String(), + // #nosec G115 - Safe conversion as prefix lengths are always within uint32 range (0-128) PrefixLength: uint32(route.Bits()), Router: "", // N/A } diff --git a/vpn/serdes.go b/vpn/serdes.go index a058ee71e637e..f45af951b8ec2 100644 --- a/vpn/serdes.go +++ b/vpn/serdes.go @@ -81,6 +81,7 @@ func (s *serdes[S, _, _]) sendLoop() { s.logger.Critical(s.ctx, "failed to marshal message", slog.Error(err)) return } + // #nosec G115 - Safe conversion as protobuf message length is expected to be within uint32 range if err := binary.Write(s.conn, binary.BigEndian, uint32(len(mb))); err != nil { s.logger.Debug(s.ctx, "failed to write length", slog.Error(err)) return diff --git a/vpn/speaker_internal_test.go b/vpn/speaker_internal_test.go index 5985043307107..1e276d8a6afb0 100644 --- a/vpn/speaker_internal_test.go +++ b/vpn/speaker_internal_test.go @@ -74,6 +74,7 @@ func TestSpeaker_RawPeer(t *testing.T) { msgBuf := make([]byte, msgLen) n, err = mp.Read(msgBuf) require.NoError(t, err) + // #nosec G115 - Safe conversion of read bytes count to uint32 for comparison with message length require.Equal(t, msgLen, uint32(n)) msg := new(TunnelMessage) err = proto.Unmarshal(msgBuf, msg) diff --git a/vpn/tunnel.go b/vpn/tunnel.go index e40732ae10e38..fdbe052cd38ca 100644 --- a/vpn/tunnel.go +++ b/vpn/tunnel.go @@ -302,6 +302,7 @@ func (t *Tunnel) Sync() { func sinkEntryToPb(e slog.SinkEntry) *Log { l := &Log{ + // #nosec G115 - Safe conversion for log levels which are small positive integers Level: Log_Level(e.Level), Message: e.Message, LoggerNames: e.LoggerNames,