diff --git a/ee/server/service/apple_mdm.go b/ee/server/service/apple_mdm.go index c80800fea05..28b5081f096 100644 --- a/ee/server/service/apple_mdm.go +++ b/ee/server/service/apple_mdm.go @@ -58,7 +58,7 @@ func (svc *Service) GetMDMAppleAccountEnrollmentProfile(ctx context.Context, enr fleet.MDMAssetSCEPChallenge, }, nil) if err != nil { - return nil, fmt.Errorf("loading SCEP challenge from the database: %w", err) + return nil, ctxerr.Wrap(ctx, err, "loading SCEP challenge from the database") } enrollURL := appConfig.MDMUrl() diff --git a/ee/server/service/calendar.go b/ee/server/service/calendar.go index 95463dd1cb5..cd23a19881a 100644 --- a/ee/server/service/calendar.go +++ b/ee/server/service/calendar.go @@ -24,7 +24,7 @@ func (svc *Service) CalendarWebhook(ctx context.Context, eventUUID string, chann appConfig, err := svc.ds.AppConfig(ctx) if err != nil { svc.authz.SkipAuthorization(ctx) - return fmt.Errorf("load app config: %w", err) + return ctxerr.Wrap(ctx, err, "load app config") } if len(appConfig.Integrations.GoogleCalendar) == 0 { @@ -88,7 +88,7 @@ func (svc *Service) CalendarWebhook(ctx context.Context, eventUUID string, chann if eventDetails.TeamID == nil { // Should not happen svc.authz.SkipAuthorization(ctx) - return fmt.Errorf("calendar event %s has no fleet ID", eventUUID) + return ctxerr.New(ctx, fmt.Sprintf("calendar event %s has no fleet ID", eventUUID)) } localConfig := &calendar.Config{ @@ -227,7 +227,7 @@ func (svc *Service) processCalendarEvent(ctx context.Context, eventDetails *flee return "", false, nil } if host.Email == "" { - err = fmt.Errorf("host %d has no associated email", host.HostID) + err = ctxerr.New(ctx, fmt.Sprintf("host %d has no associated email", host.HostID)) return "", false, err } diff --git a/ee/server/service/hostidentity/config.go b/ee/server/service/hostidentity/config.go index cac57d12281..c64328a8347 100644 --- a/ee/server/service/hostidentity/config.go +++ b/ee/server/service/hostidentity/config.go @@ -2,24 +2,24 @@ package hostidentity import ( "context" - "fmt" "github.com/fleetdm/fleet/v4/pkg/certificate" + "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/mdm/scep/depot" ) -func initAssets(ds fleet.Datastore) error { +func initAssets(ctx context.Context, ds fleet.Datastore) error { // Check if we have existing certs and keys expectedAssets := []fleet.MDMAssetName{ fleet.MDMAssetHostIdentityCACert, fleet.MDMAssetHostIdentityCAKey, } - savedAssets, err := ds.GetAllMDMConfigAssetsByName(context.Background(), expectedAssets, nil) + savedAssets, err := ds.GetAllMDMConfigAssetsByName(ctx, expectedAssets, nil) if err != nil { // allow not found errors as it means we're generating the assets for the first time. if !fleet.IsNotFound(err) { - return fmt.Errorf("loading existing host identity assets from the database: %w", err) + return ctxerr.Wrap(ctx, err, "loading existing host identity assets from the database") } } @@ -34,7 +34,7 @@ func initAssets(ds fleet.Datastore) error { ) scepCert, scepKey, err := depot.NewCACertKey(caCert) if err != nil { - return fmt.Errorf("generating host identity SCEP cert and key: %w", err) + return ctxerr.Wrap(ctx, err, "generating host identity SCEP cert and key") } // Store our config assets encrypted @@ -49,8 +49,8 @@ func initAssets(ds fleet.Datastore) error { }) } - if err := ds.InsertMDMConfigAssets(context.Background(), assets, nil); err != nil { - return fmt.Errorf("inserting host identity SCEP assets: %w", err) + if err := ds.InsertMDMConfigAssets(ctx, assets, nil); err != nil { + return ctxerr.Wrap(ctx, err, "inserting host identity SCEP assets") } } return nil diff --git a/ee/server/service/hostidentity/depot/depot.go b/ee/server/service/hostidentity/depot/depot.go index abb7039ff18..64b4e6664ad 100644 --- a/ee/server/service/hostidentity/depot/depot.go +++ b/ee/server/service/hostidentity/depot/depot.go @@ -5,8 +5,6 @@ import ( "crypto/ecdsa" "crypto/rsa" "crypto/x509" - "errors" - "fmt" "log/slog" "math/big" "time" @@ -52,12 +50,12 @@ func NewHostIdentitySCEPDepot(db *sqlx.DB, ds fleet.Datastore, logger *slog.Logg func (d *HostIdentitySCEPDepot) CA(_ []byte) ([]*x509.Certificate, *rsa.PrivateKey, error) { cert, err := assets.KeyPair(context.Background(), d.ds, fleet.MDMAssetHostIdentityCACert, fleet.MDMAssetHostIdentityCAKey) if err != nil { - return nil, nil, fmt.Errorf("getting assets: %w", err) + return nil, nil, ctxerr.Wrap(context.Background(), err, "getting assets") } pk, ok := cert.PrivateKey.(*rsa.PrivateKey) if !ok { - return nil, nil, errors.New("private key not in RSA format") + return nil, nil, ctxerr.New(context.Background(), "private key not in RSA format") } return []*x509.Certificate{cert.Leaf}, pk, nil @@ -86,10 +84,10 @@ func (d *HostIdentitySCEPDepot) HasCN(cn string, allowTime int, cert *x509.Certi // Put stores a certificate under the given name. func (d *HostIdentitySCEPDepot) Put(name string, crt *x509.Certificate) error { if crt.Subject.CommonName == "" || len(crt.Subject.CommonName) > maxCommonNameLength { - return errors.New("common name empty or too long") + return ctxerr.New(context.Background(), "common name empty or too long") } if !crt.SerialNumber.IsInt64() { - return errors.New("cannot represent serial number as int64") + return ctxerr.New(context.Background(), "cannot represent serial number as int64") } // Extract the ECC uncompressed point (04-prefixed X || Y); 0x04 means this is the raw representation @@ -98,11 +96,11 @@ func (d *HostIdentitySCEPDepot) Put(name string, crt *x509.Certificate) error { // - P-384: 97 bytes key, ok := crt.PublicKey.(*ecdsa.PublicKey) if !ok { - return errors.New("public key not in ECDSA format") + return ctxerr.New(context.Background(), "public key not in ECDSA format") } pubKeyRaw, err := types.CreateECDSAPublicKeyRaw(key) if err != nil { - return fmt.Errorf("creating public key raw: %w", err) + return ctxerr.Wrap(context.Background(), err, "creating public key raw") } certPEM := certificate.EncodeCertPEM(crt) @@ -112,7 +110,7 @@ func (d *HostIdentitySCEPDepot) Put(name string, crt *x509.Certificate) error { existingCert, err := d.ds.GetHostIdentityCertByName(context.Background(), name) switch { case err != nil && !fleet.IsNotFound(err): - return fmt.Errorf("checking existing certificate: %w", err) + return ctxerr.Wrap(context.Background(), err, "checking existing certificate") case err == nil: // Certificate exists, check if rate limit applies if time.Since(existingCert.CreatedAt) < cooldown { diff --git a/server/datastore/failing/common_store.go b/server/datastore/failing/common_store.go index 580aa75e9f2..4c23cf49c43 100644 --- a/server/datastore/failing/common_store.go +++ b/server/datastore/failing/common_store.go @@ -5,6 +5,8 @@ import ( "fmt" "io" "time" + + "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" ) // commonFailingStore is an implementation of CommonStore @@ -15,21 +17,22 @@ type commonFailingStore struct { } func (c commonFailingStore) Get(ctx context.Context, iconID string) (io.ReadCloser, int64, error) { - return nil, 0, fmt.Errorf("%s store not properly configured", c.Entity) + return nil, 0, ctxerr.New(ctx, fmt.Sprintf("%s store not properly configured", c.Entity)) } func (c commonFailingStore) Put(ctx context.Context, iconID string, content io.ReadSeeker) error { - return fmt.Errorf("%s store not properly configured", c.Entity) + return ctxerr.New(ctx, fmt.Sprintf("%s store not properly configured", c.Entity)) } func (c commonFailingStore) Exists(ctx context.Context, iconID string) (bool, error) { - return false, fmt.Errorf("%s store not properly configured", c.Entity) + return false, ctxerr.New(ctx, fmt.Sprintf("%s store not properly configured", c.Entity)) } func (c commonFailingStore) Cleanup(ctx context.Context, usedIconIDs []string, removeCreatedBefore time.Time) (int, error) { return 0, nil } -func (c commonFailingStore) Sign(_ context.Context, _ string, _ time.Duration) (string, error) { - return "", fmt.Errorf("%s store not properly configured", c.Entity) +func (c commonFailingStore) Sign(ctx context.Context, _ string, _ time.Duration) (string, error) { + return "", ctxerr.New(ctx, fmt.Sprintf("%s store not properly configured", c.Entity)) } + diff --git a/server/datastore/mysql/android_enterprises.go b/server/datastore/mysql/android_enterprises.go index d26145f555d..ec547ca69bb 100644 --- a/server/datastore/mysql/android_enterprises.go +++ b/server/datastore/mysql/android_enterprises.go @@ -63,7 +63,7 @@ func (ds *AndroidDatastore) GetEnterprise(ctx context.Context) (*android.Enterpr func (ds *AndroidDatastore) UpdateEnterprise(ctx context.Context, enterprise *android.EnterpriseDetails) error { if enterprise == nil || enterprise.ID == 0 { - return errors.New("missing enterprise ID") + return ctxerr.New(ctx, "missing enterprise ID") } stmt := `UPDATE android_enterprises SET signup_name = ?, diff --git a/server/datastore/mysql/host_certificate_templates.go b/server/datastore/mysql/host_certificate_templates.go index 844671d67cb..025ef1cea5c 100644 --- a/server/datastore/mysql/host_certificate_templates.go +++ b/server/datastore/mysql/host_certificate_templates.go @@ -372,12 +372,12 @@ func (ds *Datastore) UpsertCertificateStatus(ctx context.Context, update *fleet. WHERE host_uuid = :host_uuid AND certificate_template_id = :certificate_template_id` result, err := sqlx.NamedExecContext(ctx, ds.writer(ctx), updateStmt, update) if err != nil { - return err + return ctxerr.Wrap(ctx, err, "update host certificate template status") } rowsAffected, err := result.RowsAffected() if err != nil { - return err + return ctxerr.Wrap(ctx, err, "get rows affected for host certificate template status update") } // If no records were updated, then insert a new status. @@ -837,3 +837,4 @@ func (ds *Datastore) GetOrCreateFleetChallengeForCertificateTemplate( } return challenge, nil } + diff --git a/server/datastore/mysql/host_identity_scep.go b/server/datastore/mysql/host_identity_scep.go index b261a8581cd..8fb61c1aaea 100644 --- a/server/datastore/mysql/host_identity_scep.go +++ b/server/datastore/mysql/host_identity_scep.go @@ -11,6 +11,7 @@ import ( "fmt" "github.com/fleetdm/fleet/v4/ee/pkg/hostidentity/types" + "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" common_mysql "github.com/fleetdm/fleet/v4/server/platform/mysql" "github.com/jmoiron/sqlx" ) @@ -93,11 +94,11 @@ func (ds *Datastore) GetMDMSCEPCertBySerial(ctx context.Context, serialNumber ui // The hash is calculated from cert.Raw (DER-encoded bytes), not the PEM string block, _ := pem.Decode([]byte(certPEM)) if block == nil { - return "", errors.New("failed to decode PEM certificate") + return "", ctxerr.New(ctx, "failed to decode PEM certificate") } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - return "", fmt.Errorf("failed to parse certificate: %w", err) + return "", ctxerr.Wrap(ctx, err, "parse certificate") } hashed := sha256.Sum256(cert.Raw) hash := hex.EncodeToString(hashed[:]) diff --git a/server/mdm/acme/internal/mysql/challenge.go b/server/mdm/acme/internal/mysql/challenge.go index 9000d90b0a6..6aaacd6b310 100644 --- a/server/mdm/acme/internal/mysql/challenge.go +++ b/server/mdm/acme/internal/mysql/challenge.go @@ -56,7 +56,7 @@ func (ds *Datastore) GetChallengeByID(ctx context.Context, accountID, challengeI // UpdateChallenge handles updating the challenge status, and the authorization status as well as moving the order status. func (ds *Datastore) UpdateChallenge(ctx context.Context, challenge *types.Challenge) (*types.Challenge, error) { if challenge == nil { - return nil, errors.New("Challenge can not be nil for update") + return nil, ctxerr.New(ctx, "challenge can not be nil for update") } err := platform_mysql.WithRetryTxx(ctx, ds.writer(ctx), func(tx sqlx.ExtContext) error { diff --git a/server/mdm/lifecycle/lifecycle.go b/server/mdm/lifecycle/lifecycle.go index 48850d9a274..6cc0a98db29 100644 --- a/server/mdm/lifecycle/lifecycle.go +++ b/server/mdm/lifecycle/lifecycle.go @@ -130,7 +130,7 @@ func (t *HostLifecycle) doWithUUIDValidation(ctx context.Context, action uuidFn, users, acts, err := action(ctx, opts.UUID) if err != nil { - return err + return ctxerr.Wrap(ctx, err, "execute uuid action") } return t.createActivities(ctx, users, acts) } diff --git a/server/service/apple_mdm_batched.go b/server/service/apple_mdm_batched.go index 4213a90b3a8..73a6d19d3f4 100644 --- a/server/service/apple_mdm_batched.go +++ b/server/service/apple_mdm_batched.go @@ -3,7 +3,6 @@ package service import ( "context" "encoding/pem" - "fmt" "log/slog" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" @@ -36,7 +35,7 @@ func ReconcileAppleProfilesBatched( ) (err error) { appConfig, err := ds.AppConfig(ctx) if err != nil { - return fmt.Errorf("reading app config: %w", err) + return ctxerr.Wrap(ctx, err, "reading app config") } if !appConfig.MDM.EnabledAndConfigured { return nil diff --git a/server/vulnerabilities/macoffice/analyzer.go b/server/vulnerabilities/macoffice/analyzer.go index 8bb9ee0206e..b52d1a606e9 100644 --- a/server/vulnerabilities/macoffice/analyzer.go +++ b/server/vulnerabilities/macoffice/analyzer.go @@ -3,7 +3,6 @@ package macoffice import ( "context" "encoding/json" - "errors" "fmt" "os" "path/filepath" @@ -155,7 +154,7 @@ func Analyze( } } if !hasValid { - return nil, errors.New("MacOffice release notes contain no valid security updates (possible corrupted feed)") + return nil, ctxerr.New(ctx, "MacOffice release notes contain no valid security updates (possible corrupted feed)") } queryParams := fleet.SoftwareIterQueryOptions{IncludedSources: []string{"apps"}} diff --git a/server/vulnerabilities/msrc/analyzer.go b/server/vulnerabilities/msrc/analyzer.go index a276ceda9aa..09aac014f19 100644 --- a/server/vulnerabilities/msrc/analyzer.go +++ b/server/vulnerabilities/msrc/analyzer.go @@ -2,7 +2,6 @@ package msrc import ( "context" - "errors" "fmt" "log/slog" "strconv" @@ -34,12 +33,14 @@ func Analyze( return nil, err } + // >>> OPENFRAME(msrc-empty-bulletin-guard): reject corrupted empty MSRC feeds instead of remediating all vulns — openframe/docs/msrc-guard.md // Refuse to proceed if the loaded bulletin contains no vulnerability data — an empty // bulletin would cause every existing MSRC OS vulnerability for this OS to be marked as // remediated. This usually indicates the bulletin file was corrupted during download. if len(bulletin.Vulnerabilities) == 0 { - return nil, errors.New("MSRC bulletin contains no vulnerabilities (possible corrupted feed)") + return nil, ctxerr.New(ctx, "MSRC bulletin contains no vulnerabilities (possible corrupted feed)") } + // <<< OPENFRAME(msrc-empty-bulletin-guard) // Find matching products inside the bulletin matchingPIDs := make(map[string]bool) diff --git a/server/worker/zendesk.go b/server/worker/zendesk.go index f5a7cec7c1a..fb4bb8b9b50 100644 --- a/server/worker/zendesk.go +++ b/server/worker/zendesk.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "log/slog" "sort" @@ -267,7 +266,7 @@ func (z *Zendesk) Run(ctx context.Context, argsJSON json.RawMessage) error { func (z *Zendesk) runVuln(ctx context.Context, cli ZendeskClient, args zendeskArgs) error { vargs := args.Vulnerability if vargs == nil { - return errors.New("invalid job args") + return ctxerr.New(ctx, "invalid job args") } var hosts []fleet.HostVulnerabilitySummary