chore: update golang to 1.24.1 (#17035)

- Update go.mod to use Go 1.24.1
- Update GitHub Actions setup-go action to use Go 1.24.1
- Fix linting issues with golangci-lint by:
  - Updating to golangci-lint v1.57.1 (more compatible with Go 1.24.1)

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
This commit is contained in:
Jon Ayers
2025-03-26 01:56:39 -05:00
committed by GitHub
parent c131d01cfd
commit 17ddee05e5
187 changed files with 650 additions and 531 deletions
+1 -1
View File
@@ -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:
+11 -4
View File
@@ -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.
+12 -6
View File
@@ -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
}
@@ -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)
+1
View File
@@ -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())
}
+3 -2
View File
@@ -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) {
+6 -1
View File
@@ -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)
+2 -2
View File
@@ -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
+4 -3
View File
@@ -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()))
}
}
+3 -2
View File
@@ -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)
}
+2
View File
@@ -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
+2 -2
View File
@@ -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
+6 -4
View File
@@ -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 {
+1
View File
@@ -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"
+1
View File
@@ -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 {
+1 -1
View File
@@ -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
+4 -3
View File
@@ -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)
+2 -2
View File
@@ -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
}
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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() {
+2 -2
View File
@@ -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
+6 -3
View File
@@ -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:] {
+1 -1
View File
@@ -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.
+4 -3
View File
@@ -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,
})
+4 -4
View File
@@ -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")
}),
},
+1 -1
View File
@@ -91,7 +91,7 @@ fi
if err != nil {
return err
}
return cliui.Canceled
return cliui.ErrCanceled
}
if extra != "" {
if extAuth.TokenExtra == nil {
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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!")
})
+1 -1
View File
@@ -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)
+4 -7
View File
@@ -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
},
+6 -8
View File
@@ -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
+2 -2
View File
@@ -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 {
+3 -3
View File
@@ -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 {
+3 -5
View File
@@ -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)
+5 -5
View File
@@ -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
+2 -2
View File
@@ -1764,9 +1764,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
}
+1
View File
@@ -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)
+1 -1
View File
@@ -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
}
+1 -1
View File
@@ -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()
+4 -3
View File
@@ -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{}
}
+4
View File
@@ -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",
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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
}
}
+3 -3
View File
@@ -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",
+1 -11
View File
@@ -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())
}
+6 -5
View File
@@ -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 {
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+3 -3
View File
@@ -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
+8 -6
View File
@@ -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)
}
+2
View File
@@ -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" {
+1 -1
View File
@@ -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 {
+3 -3
View File
@@ -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)
}
+35 -30
View File
@@ -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 {
@@ -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)
}
}
+4 -4
View File
@@ -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.",
})
+1 -1
View File
@@ -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"
+4 -4
View File
@@ -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)
})
+1 -1
View File
@@ -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]
+19 -17
View File
@@ -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
@@ -1667,8 +1667,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) {
@@ -3050,11 +3050,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) {
@@ -3245,6 +3245,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 {
@@ -3397,7 +3398,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)
@@ -3830,6 +3831,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)
@@ -3930,7 +3932,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")
+1 -1
View File
@@ -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")
})
}
+1
View File
@@ -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
}
+20 -10
View File
@@ -6057,6 +6057,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]
@@ -6691,6 +6692,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]
@@ -7618,6 +7620,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]
@@ -9280,6 +9283,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 {
@@ -12415,17 +12419,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))
+1
View File
@@ -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())
}
+1 -1
View File
@@ -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 {
+1
View File
@@ -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
}
+1 -1
View File
@@ -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)
})
+20 -16
View File
@@ -2119,10 +2119,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,
})
}
@@ -3182,21 +3183,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)
}
}
@@ -3337,18 +3339,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)
}
}
@@ -3441,11 +3444,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)
}
}
+2 -2
View File
@@ -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
}
+4 -3
View File
@@ -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.
+2 -4
View File
@@ -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
+3 -5
View File
@@ -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 {
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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!")
+2 -1
View File
@@ -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) {
+4 -2
View File
@@ -202,8 +202,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)
+8 -8
View File
@@ -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
}
+1
View File
@@ -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)
+2
View File
@@ -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)
+1 -1
View File
@@ -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())
}
+4 -1
View File
@@ -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 {
+4 -3
View File
@@ -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))
}
@@ -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
}
}
@@ -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",
@@ -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 {
@@ -2197,9 +2199,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)
+1
View File
@@ -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)
+3
View File
@@ -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<<uint(i)) != 0
}
return days
@@ -88,6 +89,7 @@ func VerifyTemplateAutostopRequirement(days uint8, weeks int64) error {
if days&0b10000000 != 0 {
return xerrors.New("invalid autostop requirement days, last bit is set")
}
//nolint:staticcheck
if days > 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")
}
+3 -1
View File
@@ -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 == "" {
+8 -6
View File
@@ -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 != "",
}
}
+1 -1
View File
@@ -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{
+7 -7
View File
@@ -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{
+1 -1
View File
@@ -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{},
+3
View File
@@ -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:
+2
View File
@@ -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()
+1 -1
View File
@@ -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())
+4 -3
View File
@@ -1509,7 +1509,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))
@@ -1524,14 +1525,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.
+1 -1
View File
@@ -1453,7 +1453,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
}))
+4 -2
View File
@@ -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{

Some files were not shown because too many files have changed in this diff Show More