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, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy