mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -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 {
|
||||
|
||||
Generated
+1
-1
@@ -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"
|
||||
|
||||
Generated
+1
-1
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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.",
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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 == "" {
|
||||
|
||||
@@ -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
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{},
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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{
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -844,6 +844,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
|
||||
}
|
||||
|
||||
@@ -878,6 +879,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)
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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+)?"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user