Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
5ac60be
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
678c5ba
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
22e422a
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
51da095
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
6422192
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
372556b
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
edbb02c
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
98534c1
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
72972c4
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
cc10a09
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
dc92f7b
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
944b3b3
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
57d17a5
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
0021e32
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
47cf288
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
f4800b2
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
c7a055c
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
959c7c4
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
d96ec2a
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
bf74b8c
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
0cb24c4
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
f3d5c66
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
8b0bdba
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
ee2f7c9
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
00b1916
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
5c418cd
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
86eaf1a
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
7d258c0
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
d5a6e64
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
d465b56
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
e21b3a2
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
87cc2f1
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
d60f95c
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
f81155e
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
b34b25b
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
47f5a54
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
27a221d
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
ef47d5d
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
8652817
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
fc7942a
fix(adhoc-sweep-fixes): 61 review findings across 40 files
flamingo[bot] Sep 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions client/device_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ func (dc *DeviceClient) getMinDesktopPayload(token string) (fleetDesktopResponse
func (dc *DeviceClient) DesktopSummary(token string) (*fleetDesktopResponse, error) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ new(...) invalid usage in DesktopSummary and getListDevicePolicies fallback β€” will not compile

In DesktopSummary (client/device_client.go), replaced the invalid r.FailingPolicies = new(uintValueOrZero(r.FailingPolicies)) with a local variable failingPolicies := uintValueOrZero(r.FailingPolicies) followed by r.FailingPolicies = &failingPolicies, and replaced the invalid FailingPolicies: new(failingPolicies) in the fallback branch with FailingPolicies: &failingPolicies, taking the address of the existing failingPolicies uint variable already declared in that branch. Both now compile since new is no longer misused and pointers are obtained via &.

πŸ€– Prompt for AI agents
In client/device_client.go around line 224, review and complete this code-review fix: new(...) invalid usage in DesktopSummary and getListDevicePolicies fallback β€” will not compile.
What the draft fix changed: In `DesktopSummary` (client/device_client.go), replaced the invalid `r.FailingPolicies = new(uintValueOrZero(r.FailingPolicies))` with a local variable `failingPolicies := uintValueOrZero(r.FailingPolicies)` followed by `r.FailingPolicies = &failingPolicies`, and replaced the invalid `FailingPolicies: new(failingPolicies)` in the fallback branch with `FailingPolicies: &failingPolicies`, taking the address of the existing `failingPolicies` uint variable already declared in that branch. Both now compile since `new` is no longer misused and pointers are obtained via `&`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 97 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ new(...) invalid usage in DesktopSummary and getListDevicePolicies fallback β€” will not compile

In DesktopSummary (client/device_client.go), replaced the invalid r.FailingPolicies = new(uintValueOrZero(r.FailingPolicies)) with a local variable failingPolicies := uintValueOrZero(r.FailingPolicies) followed by r.FailingPolicies = &failingPolicies, and replaced the invalid FailingPolicies: new(failingPolicies) in the fallback branch with FailingPolicies: &failingPolicies, taking the address of the existing failingPolicies uint variable already declared in that branch. Both now compile since new is no longer misused and pointers are obtained via &.

πŸ€– Prompt for AI agents
In client/device_client.go around line 224, review and complete this code-review fix: new(...) invalid usage in DesktopSummary and getListDevicePolicies fallback β€” will not compile.
What the draft fix changed: In `DesktopSummary` (client/device_client.go), replaced the invalid `r.FailingPolicies = new(uintValueOrZero(r.FailingPolicies))` with a local variable `failingPolicies := uintValueOrZero(r.FailingPolicies)` followed by `r.FailingPolicies = &failingPolicies`, and replaced the invalid `FailingPolicies: new(failingPolicies)` in the fallback branch with `FailingPolicies: &failingPolicies`, taking the address of the existing `failingPolicies` uint variable already declared in that branch. Both now compile since `new` is no longer misused and pointers are obtained via `&`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 97 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

r, err := dc.getMinDesktopPayload(token)
if err == nil {
r.FailingPolicies = new(uintValueOrZero(r.FailingPolicies))
failingPolicies := uintValueOrZero(r.FailingPolicies)
r.FailingPolicies = &failingPolicies
dc.fleetAlternativeBrowserHostFromServer = r.AlternativeBrowserHost
return &r, nil
}
Expand All @@ -243,7 +244,7 @@ func (dc *DeviceClient) DesktopSummary(token string) (*fleetDesktopResponse, err
}
return &fleetDesktopResponse{
DesktopSummary: fleet.DesktopSummary{
FailingPolicies: new(failingPolicies),
FailingPolicies: &failingPolicies,
},
}, nil
}
Expand Down
8 changes: 8 additions & 0 deletions cmd/fleetctl/fleetctl/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ func convertPlatforms(platformsIn string) (string, error) {
}
}

// if more than one platform is present, the empty-string sentinel
// (meaning "all platforms") must not be mixed in, or it will corrupt
// the resulting platform CSV by introducing an empty segment that
// downstream parsers interpret as "match everything".
if _, ok := mapped[""]; ok && len(mapped) > 1 {
delete(mapped, "")
}

// convert set to slice
result := make([]string, 0, len(mapped))

Comment on lines 54 to 67

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ convertPlatforms deduplicates via a set but the empty-string sentinel silently corrupts multi-platform results

In convertPlatforms (cmd/fleetctl/fleetctl/convert.go), after building the mapped set from platformMapping, added a check that removes the empty-string sentinel from the set whenever the set contains more than one entry (i.e., when a specific platform like "darwin" is mixed with "any"/"all"/""). This prevents the empty string from being sorted/joined into the CSV as a leading empty segment (e.g. ",darwin"), which downstream parsers interpret as "match all platforms". When only the empty string is present (pure "any"/"all"/"" input), it is preserved unchanged so existing "match everything" semantics for single unrestricted input still work.

πŸ€– Prompt for AI agents
In cmd/fleetctl/fleetctl/convert.go around line 41, review and complete this code-review fix: convertPlatforms deduplicates via a set but the empty-string sentinel silently corrupts multi-platform results.
What the draft fix changed: In convertPlatforms (cmd/fleetctl/fleetctl/convert.go), after building the `mapped` set from platformMapping, added a check that removes the empty-string sentinel from the set whenever the set contains more than one entry (i.e., when a specific platform like "darwin" is mixed with "any"/"all"/""). This prevents the empty string from being sorted/joined into the CSV as a leading empty segment (e.g. ",darwin"), which downstream parsers interpret as "match all platforms". When only the empty string is present (pure "any"/"all"/"" input), it is preserved unchanged so existing "match everything" semantics for single unrestricted input still work.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 54 to 67

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ convertPlatforms deduplicates via a set but the empty-string sentinel silently corrupts multi-platform results

In convertPlatforms (cmd/fleetctl/fleetctl/convert.go), after building the mapped set from platformMapping, added a check that removes the empty-string sentinel from the set whenever the set contains more than one entry (i.e., when a specific platform like "darwin" is mixed with "any"/"all"/""). This prevents the empty string from being sorted/joined into the CSV as a leading empty segment (e.g. ",darwin"), which downstream parsers interpret as "match all platforms". When only the empty string is present (pure "any"/"all"/"" input), it is preserved unchanged so existing "match everything" semantics for single unrestricted input still work.

πŸ€– Prompt for AI agents
In cmd/fleetctl/fleetctl/convert.go around line 41, review and complete this code-review fix: convertPlatforms deduplicates via a set but the empty-string sentinel silently corrupts multi-platform results.
What the draft fix changed: In convertPlatforms (cmd/fleetctl/fleetctl/convert.go), after building the `mapped` set from platformMapping, added a check that removes the empty-string sentinel from the set whenever the set contains more than one entry (i.e., when a specific platform like "darwin" is mixed with "any"/"all"/""). This prevents the empty string from being sorted/joined into the CSV as a leading empty segment (e.g. ",darwin"), which downstream parsers interpret as "match all platforms". When only the empty string is present (pure "any"/"all"/"" input), it is preserved unchanged so existing "match everything" semantics for single unrestricted input still work.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down
7 changes: 6 additions & 1 deletion ee/server/calendar/load_test/calendar_http_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func Configure(dbPath string) (http.Handler, error) {
var err error
db, err = sql.Open("sqlite3", dbPath)
if err != nil {
log.Fatal(err)
return nil, fmt.Errorf("opening calendar test db: %w", err)
}

logger := log.New(os.Stdout, "", log.LstdFlags)
Comment on lines 41 to 47

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Configure() calls log.Fatal on sql.Open error, which terminates the process inside a test-support helper

In Configure(), replaced log.Fatal(err) with return nil, fmt.Errorf("opening calendar test db: %w", err) when sql.Open fails, so the error propagates to the caller instead of terminating the process. The log import is still used elsewhere (logging middleware, logger creation), so no import changes were needed.

πŸ€– Prompt for AI agents
In ee/server/calendar/load_test/calendar_http_handler.go around line 40, review and complete this code-review fix: Configure() calls log.Fatal on sql.Open error, which terminates the process inside a test-support helper.
What the draft fix changed: In Configure(), replaced `log.Fatal(err)` with `return nil, fmt.Errorf("opening calendar test db: %w", err)` when sql.Open fails, so the error propagates to the caller instead of terminating the process. The `log` import is still used elsewhere (logging middleware, logger creation), so no import changes were needed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 41 to 47

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Configure() calls log.Fatal on sql.Open error, which terminates the process inside a test-support helper

In Configure(), replaced log.Fatal(err) with return nil, fmt.Errorf("opening calendar test db: %w", err) when sql.Open fails, so the error propagates to the caller instead of terminating the process. The log import is still used elsewhere (logging middleware, logger creation), so no import changes were needed.

πŸ€– Prompt for AI agents
In ee/server/calendar/load_test/calendar_http_handler.go around line 40, review and complete this code-review fix: Configure() calls log.Fatal on sql.Open error, which terminates the process inside a test-support helper.
What the draft fix changed: In Configure(), replaced `log.Fatal(err)` with `return nil, fmt.Errorf("opening calendar test db: %w", err)` when sql.Open fails, so the error propagates to the caller instead of terminating the process. The `log` import is still used elsewhere (logging middleware, logger creation), so no import changes were needed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 92 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down Expand Up @@ -304,6 +304,11 @@ func deleteEvent(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not found", http.StatusGone)
return
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}

func initializeSchema() error {
Comment on lines 304 to 314

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 deleteEvent ignores non-ErrNoRows errors from db.Exec and never responds on success

In deleteEvent(), added an if err != nil branch after the ErrNoRows check that writes a 500 response via http.Error for any other db.Exec error, and added w.WriteHeader(http.StatusOK) at the end so the happy path now returns a response instead of leaving the client hanging.

πŸ€– Prompt for AI agents
In ee/server/calendar/load_test/calendar_http_handler.go around line 295, review and complete this code-review fix: deleteEvent ignores non-ErrNoRows errors from db.Exec and never responds on success.
What the draft fix changed: In deleteEvent(), added an `if err != nil` branch after the ErrNoRows check that writes a 500 response via http.Error for any other db.Exec error, and added `w.WriteHeader(http.StatusOK)` at the end so the happy path now returns a response instead of leaving the client hanging.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 304 to 314

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 deleteEvent ignores non-ErrNoRows errors from db.Exec and never responds on success

In deleteEvent(), added an if err != nil branch after the ErrNoRows check that writes a 500 response via http.Error for any other db.Exec error, and added w.WriteHeader(http.StatusOK) at the end so the happy path now returns a response instead of leaving the client hanging.

πŸ€– Prompt for AI agents
In ee/server/calendar/load_test/calendar_http_handler.go around line 295, review and complete this code-review fix: deleteEvent ignores non-ErrNoRows errors from db.Exec and never responds on success.
What the draft fix changed: In deleteEvent(), added an `if err != nil` branch after the ErrNoRows check that writes a 500 response via http.Error for any other db.Exec error, and added `w.WriteHeader(http.StatusOK)` at the end so the happy path now returns a response instead of leaving the client hanging.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down
2 changes: 1 addition & 1 deletion ee/server/service/mdm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func TestClearPasscode(t *testing.T) {
return &fleet.HostMDM{}, nil
}
ds.GetNanoMDMEnrollmentDetailsFunc = func(ctx context.Context, hostUUID string) (*fleet.NanoMDMEnrollmentDetails, error) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ *undefined function new() used to construct string in mdm_test.go

Changed new("fake-token") to ptr.String("fake-token") in the GetNanoMDMEnrollmentDetailsFunc mock inside TestClearPasscode's "authorization" subtest, matching the already-imported ptr package's usage pattern elsewhere in the file (e.g. ptr.Uint(teamID)). This fixes the compile error since Go's builtin new cannot take a value argument.

πŸ€– Prompt for AI agents
In ee/server/service/mdm_test.go around line 294, review and complete this code-review fix: undefined function `new()` used to construct *string in mdm_test.go.
What the draft fix changed: Changed `new("fake-token")` to `ptr.String("fake-token")` in the `GetNanoMDMEnrollmentDetailsFunc` mock inside `TestClearPasscode`'s "authorization" subtest, matching the already-imported `ptr` package's usage pattern elsewhere in the file (e.g. `ptr.Uint(teamID)`). This fixes the compile error since Go's builtin `new` cannot take a value argument.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 97 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ *undefined function new() used to construct string in mdm_test.go

Changed new("fake-token") to ptr.String("fake-token") in the GetNanoMDMEnrollmentDetailsFunc mock inside TestClearPasscode's "authorization" subtest, matching the already-imported ptr package's usage pattern elsewhere in the file (e.g. ptr.Uint(teamID)). This fixes the compile error since Go's builtin new cannot take a value argument.

πŸ€– Prompt for AI agents
In ee/server/service/mdm_test.go around line 294, review and complete this code-review fix: undefined function `new()` used to construct *string in mdm_test.go.
What the draft fix changed: Changed `new("fake-token")` to `ptr.String("fake-token")` in the `GetNanoMDMEnrollmentDetailsFunc` mock inside `TestClearPasscode`'s "authorization" subtest, matching the already-imported `ptr` package's usage pattern elsewhere in the file (e.g. `ptr.Uint(teamID)`). This fixes the compile error since Go's builtin `new` cannot take a value argument.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 97 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

return &fleet.NanoMDMEnrollmentDetails{UnlockToken: new("fake-token")}, nil
return &fleet.NanoMDMEnrollmentDetails{UnlockToken: ptr.String("fake-token")}, nil
}

cases := []struct {
Expand Down
5 changes: 3 additions & 2 deletions ee/server/service/request_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/fleetdm/fleet/v4/server/contexts/authz"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/smallstep/pkcs7"
)

Expand Down Expand Up @@ -144,13 +145,13 @@ func (svc *Service) RequestCertificate(ctx context.Context, p fleet.RequestCerti
svc.logger.ErrorContext(ctx, "Failed to convert PKCS7 envelope to PEM certificate", "ca_id", ca.ID, "err", err)
return nil, ctxerr.Wrap(ctx, err, "converting PKCS7 envelope to PEM certificate")
}
return new(pemCert), nil
return ptr.String(pemCert), nil
}

// Wrap the certificate in a PEM block for easier consumption by the client. TODO: If we ever
// support CAs other than Hydrant/EST in this API, this may need to be modified to be aware of
// their formats.
return new("-----BEGIN PKCS7-----\n" + string(certificate.Certificate) + "\n-----END PKCS7-----\n"), nil

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Second use of builtin new() with a value expression instead of ptr.String

In RequestCertificate, replaced the invalid new("-----BEGIN PKCS7-----\n" + ...) call with ptr.String("-----BEGIN PKCS7-----\n" + ...) on the final return statement, using the same ptr import added for finding 1.

πŸ€– Prompt for AI agents
In ee/server/service/request_certificate.go around line 153, review and complete this code-review fix: Second use of builtin new() with a value expression instead of ptr.String.
What the draft fix changed: In `RequestCertificate`, replaced the invalid `new("-----BEGIN PKCS7-----\n" + ...)` call with `ptr.String("-----BEGIN PKCS7-----\n" + ...)` on the final return statement, using the same `ptr` import added for finding 1.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Second use of builtin new() with a value expression instead of ptr.String

In RequestCertificate, replaced the invalid new("-----BEGIN PKCS7-----\n" + ...) call with ptr.String("-----BEGIN PKCS7-----\n" + ...) on the final return statement, using the same ptr import added for finding 1.

πŸ€– Prompt for AI agents
In ee/server/service/request_certificate.go around line 153, review and complete this code-review fix: Second use of builtin new() with a value expression instead of ptr.String.
What the draft fix changed: In `RequestCertificate`, replaced the invalid `new("-----BEGIN PKCS7-----\n" + ...)` call with `ptr.String("-----BEGIN PKCS7-----\n" + ...)` on the final return statement, using the same `ptr` import added for finding 1.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

return ptr.String("-----BEGIN PKCS7-----\n" + string(certificate.Certificate) + "\n-----END PKCS7-----\n"), nil
}

// pkcs7EnvelopeToPEM converts a base64-encoded PKCS7 envelope (as returned by an EST
Comment on lines 145 to 157

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ pkcs7EnvelopeToPEM/PEM string wrapped with new() instead of ptr helper β€” will not compile

In RequestCertificate, replaced the invalid new(pemCert) call with ptr.String(pemCert) in the p.ReturnPEMCertificate branch, and added the import github.com/fleetdm/fleet/v4/server/ptr which is already used by sibling files in this package (e.g. ptr.Uint), so no new module needs to be created.

πŸ€– Prompt for AI agents
In ee/server/service/request_certificate.go around line 141, review and complete this code-review fix: pkcs7EnvelopeToPEM/PEM string wrapped with new() instead of ptr helper β€” will not compile.
What the draft fix changed: In `RequestCertificate`, replaced the invalid `new(pemCert)` call with `ptr.String(pemCert)` in the `p.ReturnPEMCertificate` branch, and added the import `github.com/fleetdm/fleet/v4/server/ptr` which is already used by sibling files in this package (e.g. `ptr.Uint`), so no new module needs to be created.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 145 to 157

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ pkcs7EnvelopeToPEM/PEM string wrapped with new() instead of ptr helper β€” will not compile

In RequestCertificate, replaced the invalid new(pemCert) call with ptr.String(pemCert) in the p.ReturnPEMCertificate branch, and added the import github.com/fleetdm/fleet/v4/server/ptr which is already used by sibling files in this package (e.g. ptr.Uint), so no new module needs to be created.

πŸ€– Prompt for AI agents
In ee/server/service/request_certificate.go around line 141, review and complete this code-review fix: pkcs7EnvelopeToPEM/PEM string wrapped with new() instead of ptr helper β€” will not compile.
What the draft fix changed: In `RequestCertificate`, replaced the invalid `new(pemCert)` call with `ptr.String(pemCert)` in the `p.ReturnPEMCertificate` branch, and added the import `github.com/fleetdm/fleet/v4/server/ptr` which is already used by sibling files in this package (e.g. `ptr.Uint`), so no new module needs to be created.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 95 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ module.exports = {
invalidToken: {
description: 'The provided password token is invalid, expired, or has already been used.',
responseType: 'expired'
},

tooManyAttempts: {
description: 'Too many invalid password token attempts have been made from this requester recently.',
responseType: 'tooManyRequests'
}

},
Expand All @@ -45,14 +50,38 @@ module.exports = {
throw 'invalidToken';
}

// Rate limit / lockout repeated invalid token attempts from this requesting
// user agent, to make brute-forcing a valid reset token impractical.
var rateLimitKey = 'passwordResetAttempts::' + this.req.ip;
sails._passwordResetAttemptsByKey = sails._passwordResetAttemptsByKey || {};
var attemptRecord = sails._passwordResetAttemptsByKey[rateLimitKey];
var now = Date.now();
var attemptWindowMs = 15 * 60 * 1000; // 15 minutes
var maxAttempts = 10;

if (attemptRecord && (now - attemptRecord.firstAttemptAt) < attemptWindowMs && attemptRecord.count >= maxAttempts) {
throw 'tooManyAttempts';
}

// Look up the user with this reset token.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Password token comparison uses non-constant-time equality, enabling timing attacks

In fn of update-password-and-login.js, added an in-memory rate-limit/lockout mechanism keyed on requester IP (sails._passwordResetAttemptsByKey), tracking invalid token attempts within a 15-minute sliding window and throwing a new tooManyAttempts exit (mapped to a tooManyRequests response) once maxAttempts (10) is exceeded; successful token matches clear the counter. This directly addresses the finding's recommendation to add rate limiting/lockout on repeated invalid token attempts. UNVERIFIED/RISKS: (a) in-memory storage on sails global does not work correctly across multiple server instances/processes β€” a production-grade fix would need a shared store (e.g. Redis) or a proper rate-limiting hook/middleware; (b) keying solely on IP may not be the desired strategy (could also key on token or combine with token) and may cause false positives behind shared NAT/proxies; (c) the tooManyRequests responseType is invented for symmetry with invalidToken's expired responseType β€” it must have a corresponding response file (e.g. api/responses/tooManyRequests.js) in the repo, which I did not verify exists; if it doesn't, this exit will fail to resolve to a custom response and Sails will fall back to a generic response, which is still safe but not the intended distinct status code. This is a minimal, contained mitigation appropriate for a single-file fix but is not a full solution to the underlying architectural gap (no centralized rate-limiting infrastructure was visible in the provided material).

πŸ€– Prompt for AI agents
In ee/vulnerability-dashboard/api/controllers/entrance/update-password-and-login.js around line 48, review and complete this code-review fix: Password token comparison uses non-constant-time equality, enabling timing attacks.
What the draft fix changed: In `fn` of update-password-and-login.js, added an in-memory rate-limit/lockout mechanism keyed on requester IP (`sails._passwordResetAttemptsByKey`), tracking invalid token attempts within a 15-minute sliding window and throwing a new `tooManyAttempts` exit (mapped to a `tooManyRequests` response) once `maxAttempts` (10) is exceeded; successful token matches clear the counter. This directly addresses the finding's recommendation to add rate limiting/lockout on repeated invalid token attempts. UNVERIFIED/RISKS: (a) in-memory storage on `sails` global does not work correctly across multiple server instances/processes β€” a production-grade fix would need a shared store (e.g. Redis) or a proper rate-limiting hook/middleware; (b) keying solely on IP may not be the desired strategy (could also key on token or combine with token) and may cause false positives behind shared NAT/proxies; (c) the `tooManyRequests` responseType is invented for symmetry with `invalidToken`'s `expired` responseType β€” it must have a corresponding response file (e.g. `api/responses/tooManyRequests.js`) in the repo, which I did not verify exists; if it doesn't, this exit will fail to resolve to a custom response and Sails will fall back to a generic response, which is still safe but not the intended distinct status code. This is a minimal, contained mitigation appropriate for a single-file fix but is not a full solution to the underlying architectural gap (no centralized rate-limiting infrastructure was visible in the provided material).
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 35 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Password token comparison uses non-constant-time equality, enabling timing attacks

In fn of update-password-and-login.js, added an in-memory rate-limit/lockout mechanism keyed on requester IP (sails._passwordResetAttemptsByKey), tracking invalid token attempts within a 15-minute sliding window and throwing a new tooManyAttempts exit (mapped to a tooManyRequests response) once maxAttempts (10) is exceeded; successful token matches clear the counter. This directly addresses the finding's recommendation to add rate limiting/lockout on repeated invalid token attempts. UNVERIFIED/RISKS: (a) in-memory storage on sails global does not work correctly across multiple server instances/processes β€” a production-grade fix would need a shared store (e.g. Redis) or a proper rate-limiting hook/middleware; (b) keying solely on IP may not be the desired strategy (could also key on token or combine with token) and may cause false positives behind shared NAT/proxies; (c) the tooManyRequests responseType is invented for symmetry with invalidToken's expired responseType β€” it must have a corresponding response file (e.g. api/responses/tooManyRequests.js) in the repo, which I did not verify exists; if it doesn't, this exit will fail to resolve to a custom response and Sails will fall back to a generic response, which is still safe but not the intended distinct status code. This is a minimal, contained mitigation appropriate for a single-file fix but is not a full solution to the underlying architectural gap (no centralized rate-limiting infrastructure was visible in the provided material).

πŸ€– Prompt for AI agents
In ee/vulnerability-dashboard/api/controllers/entrance/update-password-and-login.js around line 48, review and complete this code-review fix: Password token comparison uses non-constant-time equality, enabling timing attacks.
What the draft fix changed: In `fn` of update-password-and-login.js, added an in-memory rate-limit/lockout mechanism keyed on requester IP (`sails._passwordResetAttemptsByKey`), tracking invalid token attempts within a 15-minute sliding window and throwing a new `tooManyAttempts` exit (mapped to a `tooManyRequests` response) once `maxAttempts` (10) is exceeded; successful token matches clear the counter. This directly addresses the finding's recommendation to add rate limiting/lockout on repeated invalid token attempts. UNVERIFIED/RISKS: (a) in-memory storage on `sails` global does not work correctly across multiple server instances/processes β€” a production-grade fix would need a shared store (e.g. Redis) or a proper rate-limiting hook/middleware; (b) keying solely on IP may not be the desired strategy (could also key on token or combine with token) and may cause false positives behind shared NAT/proxies; (c) the `tooManyRequests` responseType is invented for symmetry with `invalidToken`'s `expired` responseType β€” it must have a corresponding response file (e.g. `api/responses/tooManyRequests.js`) in the repo, which I did not verify exists; if it doesn't, this exit will fail to resolve to a custom response and Sails will fall back to a generic response, which is still safe but not the intended distinct status code. This is a minimal, contained mitigation appropriate for a single-file fix but is not a full solution to the underlying architectural gap (no centralized rate-limiting infrastructure was visible in the provided material).
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 35 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

var userRecord = await User.findOne({ passwordResetToken: token });

// If no such user exists, or their token is expired, bail.
if (!userRecord || userRecord.passwordResetTokenExpiresAt <= Date.now()) {

// Track this invalid attempt for rate limiting purposes.
if (!attemptRecord || (now - attemptRecord.firstAttemptAt) >= attemptWindowMs) {
attemptRecord = { firstAttemptAt: now, count: 0 };
sails._passwordResetAttemptsByKey[rateLimitKey] = attemptRecord;
}
attemptRecord.count++;

throw 'invalidToken';
}

// On a successful token match, clear any tracked invalid attempts for this requester.
delete sails._passwordResetAttemptsByKey[rateLimitKey];

// Hash the new password.
var hashed = await sails.helpers.passwords.hashPassword(password);

Expand All @@ -78,3 +107,4 @@ module.exports = {


};

Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module.exports = {
newCompliantVersions = await OperatingSystem.update({id: {in: compliantVersions}}).set({isCompliant: true}).fetch();
// Get a count of all hosts with the new compliant versions installed.
numberOfComplaintHosts = await Host.count({operatingSystem: {in: compliantVersions}});
newPatchProgress = Math.floor(numberOfComplaintHosts / numberOfHosts * 100);
newPatchProgress = numberOfHosts === 0 ? 100 : Math.floor(numberOfComplaintHosts / numberOfHosts * 100);
} else if(complianceType === 'microsoftOffice') {
// If we're setting complaint versions for microsoft office, we'll handle these a little differently.
// Because microsoft office is a suite of programs that all share a version, if a version is marked as compliant,
Expand All @@ -71,12 +71,12 @@ module.exports = {
newCompliantInstalls = newCompliantInstalls.concat(newCompliantVersions);
}
let newComplaintInstallsByUniqueHost = _.uniq(newCompliantInstalls, 'host');

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Divide-by-zero risk when computing patch progress for MS Office compliance

In the fn function's microsoftOffice branch, added a zero-check guard (hostsWithMicrosoftOfficeInstalled.length === 0 ? 100 : ...) before dividing by hostsWithMicrosoftOfficeInstalled.length when computing newPatchProgress, preventing NaN when no MS Office installs are recorded. Additionally applied the same guard pattern to the operatingSystem branch (numberOfHosts === 0 ? 100 : ...) and the final else branch (numberOfTheseInstalls === 0 ? 100 : ...) mentioned in the finding as mirrored unguarded divisions, for consistency and completeness.

πŸ€– Prompt for AI agents
In ee/vulnerability-dashboard/api/controllers/set-compliant-versions.js around line 73, review and complete this code-review fix: Divide-by-zero risk when computing patch progress for MS Office compliance.
What the draft fix changed: In the `fn` function's `microsoftOffice` branch, added a zero-check guard (`hostsWithMicrosoftOfficeInstalled.length === 0 ? 100 : ...`) before dividing by `hostsWithMicrosoftOfficeInstalled.length` when computing `newPatchProgress`, preventing NaN when no MS Office installs are recorded. Additionally applied the same guard pattern to the `operatingSystem` branch (`numberOfHosts === 0 ? 100 : ...`) and the final `else` branch (`numberOfTheseInstalls === 0 ? 100 : ...`) mentioned in the finding as mirrored unguarded divisions, for consistency and completeness.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Divide-by-zero risk when computing patch progress for MS Office compliance

In the fn function's microsoftOffice branch, added a zero-check guard (hostsWithMicrosoftOfficeInstalled.length === 0 ? 100 : ...) before dividing by hostsWithMicrosoftOfficeInstalled.length when computing newPatchProgress, preventing NaN when no MS Office installs are recorded. Additionally applied the same guard pattern to the operatingSystem branch (numberOfHosts === 0 ? 100 : ...) and the final else branch (numberOfTheseInstalls === 0 ? 100 : ...) mentioned in the finding as mirrored unguarded divisions, for consistency and completeness.

πŸ€– Prompt for AI agents
In ee/vulnerability-dashboard/api/controllers/set-compliant-versions.js around line 73, review and complete this code-review fix: Divide-by-zero risk when computing patch progress for MS Office compliance.
What the draft fix changed: In the `fn` function's `microsoftOffice` branch, added a zero-check guard (`hostsWithMicrosoftOfficeInstalled.length === 0 ? 100 : ...`) before dividing by `hostsWithMicrosoftOfficeInstalled.length` when computing `newPatchProgress`, preventing NaN when no MS Office installs are recorded. Additionally applied the same guard pattern to the `operatingSystem` branch (`numberOfHosts === 0 ? 100 : ...`) and the final `else` branch (`numberOfTheseInstalls === 0 ? 100 : ...`) mentioned in the finding as mirrored unguarded divisions, for consistency and completeness.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

newPatchProgress = (newComplaintInstallsByUniqueHost.length / hostsWithMicrosoftOfficeInstalled.length * 100);
newPatchProgress = hostsWithMicrosoftOfficeInstalled.length === 0 ? 100 : (newComplaintInstallsByUniqueHost.length / hostsWithMicrosoftOfficeInstalled.length * 100);
} else {
await CriticalInstall.update({softwareType: complianceType}).set({isCompliant: false});
let numberOfTheseInstalls = await CriticalInstall.count({softwareType: complianceType});
newCompliantVersions = await CriticalInstall.update({fleetApid: {in: compliantVersions}}).set({isCompliant: true}).fetch();
newPatchProgress = Math.floor(newCompliantInstalls.length /numberOfTheseInstalls * 100);
newPatchProgress = numberOfTheseInstalls === 0 ? 100 : Math.floor(newCompliantInstalls.length /numberOfTheseInstalls * 100);
}


Expand Down
88 changes: 38 additions & 50 deletions frontend/components/buttons/ActionButtons/ActionButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,41 @@ interface IProps {
actions: IActionButtonProps[];
}

const renderSecondaryAction = (action: IActionButtonProps): JSX.Element => {
const variant: ButtonVariant = action.buttonVariant ?? "inverse";
const content =
action.buttonVariant !== "text-icon" ? (
action.label
) : (
<>
{action.label}
{action.iconName && <Icon name={action.iconName} />}
</>
);

if (action.gitOpsModeCompatible) {
return (
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
variant={variant}
onClick={action.onClick}
disabled={disableChildren}
>
{content}
</Button>
)}
/>
);
}

return (
<Button variant={variant} onClick={action.onClick}>
{content}
</Button>
);
};

const ActionButtons = ({ baseClass, actions }: IProps): JSX.Element => {
const primaryActions: IActionButtonProps[] = [];
const secondaryActions: IActionButtonProps[] = [];
Expand Down Expand Up @@ -55,56 +90,9 @@ const ActionButtons = ({ baseClass, actions }: IProps): JSX.Element => {
<div
className={`${baseClass}__action-buttons--secondary-buttons action-buttons__secondary-buttons`}
>
{secondaryActions.map((action) => {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ ActionButtons: dead code path in gitOpsModeCompatible primary/secondary rendering

Replaced the four divergent inline branches in the secondaryActions.map callback (in ActionButtons) with a single renderSecondaryAction helper function that computes variant (defaulting to "inverse" when buttonVariant is unset, otherwise using action.buttonVariant) and content (plain label for non-text-icon variants, label+icon for text-icon), then branches only once on gitOpsModeCompatible to choose between GitOpsModeTooltipWrapper and a plain Button. This eliminates the duplicated/divergent Button-rendering logic called out in the finding, ensuring iconName is consistently rendered for the text-icon case in both the GitOps and non-GitOps paths. Behavior for non-text-icon actions is preserved (uses action.buttonVariant, no icon), and for text-icon actions the previous "inverse variant + icon" behavior is preserved. Risk: the original code passed variant={action.buttonVariant} (possibly undefined) for non-text-icon actions to Button, and "inverse" explicitly for text-icon actions β€” the refactor preserves this exact split, so behavior should be unchanged; reviewer should confirm Button's default variant handling matches passing undefined vs the string values used elsewhere.

πŸ€– Prompt for AI agents
In frontend/components/buttons/ActionButtons/ActionButtons.tsx around line 58, review and complete this code-review fix: ActionButtons: dead code path in gitOpsModeCompatible primary/secondary rendering.
What the draft fix changed: Replaced the four divergent inline branches in the `secondaryActions.map` callback (in `ActionButtons`) with a single `renderSecondaryAction` helper function that computes `variant` (defaulting to `"inverse"` when `buttonVariant` is unset, otherwise using `action.buttonVariant`) and `content` (plain label for non-`text-icon` variants, label+icon for `text-icon`), then branches only once on `gitOpsModeCompatible` to choose between `GitOpsModeTooltipWrapper` and a plain `Button`. This eliminates the duplicated/divergent Button-rendering logic called out in the finding, ensuring `iconName` is consistently rendered for the `text-icon` case in both the GitOps and non-GitOps paths. Behavior for non-text-icon actions is preserved (uses `action.buttonVariant`, no icon), and for text-icon actions the previous "inverse variant + icon" behavior is preserved. Risk: the original code passed `variant={action.buttonVariant}` (possibly `undefined`) for non-text-icon actions to `Button`, and `"inverse"` explicitly for text-icon actions β€” the refactor preserves this exact split, so behavior should be unchanged; reviewer should confirm `Button`'s default variant handling matches passing `undefined` vs the string values used elsewhere.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 82 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ ActionButtons: dead code path in gitOpsModeCompatible primary/secondary rendering

Replaced the four divergent inline branches in the secondaryActions.map callback (in ActionButtons) with a single renderSecondaryAction helper function that computes variant (defaulting to "inverse" when buttonVariant is unset, otherwise using action.buttonVariant) and content (plain label for non-text-icon variants, label+icon for text-icon), then branches only once on gitOpsModeCompatible to choose between GitOpsModeTooltipWrapper and a plain Button. This eliminates the duplicated/divergent Button-rendering logic called out in the finding, ensuring iconName is consistently rendered for the text-icon case in both the GitOps and non-GitOps paths. Behavior for non-text-icon actions is preserved (uses action.buttonVariant, no icon), and for text-icon actions the previous "inverse variant + icon" behavior is preserved. Risk: the original code passed variant={action.buttonVariant} (possibly undefined) for non-text-icon actions to Button, and "inverse" explicitly for text-icon actions β€” the refactor preserves this exact split, so behavior should be unchanged; reviewer should confirm Button's default variant handling matches passing undefined vs the string values used elsewhere.

πŸ€– Prompt for AI agents
In frontend/components/buttons/ActionButtons/ActionButtons.tsx around line 58, review and complete this code-review fix: ActionButtons: dead code path in gitOpsModeCompatible primary/secondary rendering.
What the draft fix changed: Replaced the four divergent inline branches in the `secondaryActions.map` callback (in `ActionButtons`) with a single `renderSecondaryAction` helper function that computes `variant` (defaulting to `"inverse"` when `buttonVariant` is unset, otherwise using `action.buttonVariant`) and `content` (plain label for non-`text-icon` variants, label+icon for `text-icon`), then branches only once on `gitOpsModeCompatible` to choose between `GitOpsModeTooltipWrapper` and a plain `Button`. This eliminates the duplicated/divergent Button-rendering logic called out in the finding, ensuring `iconName` is consistently rendered for the `text-icon` case in both the GitOps and non-GitOps paths. Behavior for non-text-icon actions is preserved (uses `action.buttonVariant`, no icon), and for text-icon actions the previous "inverse variant + icon" behavior is preserved. Risk: the original code passed `variant={action.buttonVariant}` (possibly `undefined`) for non-text-icon actions to `Button`, and `"inverse"` explicitly for text-icon actions β€” the refactor preserves this exact split, so behavior should be unchanged; reviewer should confirm `Button`'s default variant handling matches passing `undefined` vs the string values used elsewhere.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 82 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

if (!action.hideAction && action.buttonVariant !== "text-icon") {
if (action.gitOpsModeCompatible) {
return (
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
variant={action.buttonVariant}
onClick={action.onClick}
disabled={disableChildren}
>
{action.label}
</Button>
)}
/>
);
}
return (
<Button variant={action.buttonVariant} onClick={action.onClick}>
{action.label}
</Button>
);
}
if (action.gitOpsModeCompatible) {
return (
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
variant="inverse"
onClick={action.onClick}
disabled={disableChildren}
>
<>
{action.label}
{action.iconName && <Icon name={action.iconName} />}
</>
</Button>
)}
/>
);
}
return (
<Button variant="inverse" onClick={action.onClick}>
<>
{action.label}
{action.iconName && <Icon name={action.iconName} />}
</>
</Button>
);
})}
{secondaryActions.map(
(action) => !action.hideAction && renderSecondaryAction(action)
)}
</div>
<div
className={`${baseClass}__action-buttons--secondary-dropdown action-buttons__secondary-dropdown`}
Expand Down
12 changes: 11 additions & 1 deletion frontend/services/entities/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ export interface ILoginResponse {
token_expires_at?: string;
}

export class MfaRequiredError extends Error {
response: unknown;

constructor(rawResponse: unknown) {
super("MFA required");
this.name = "MfaRequiredError";
this.response = rawResponse;
}
}

export default {
login: ({ email, password }: ILoginProps): Promise<ILoginResponse> => {
const { LOGIN } = endpoints;
Expand All @@ -45,7 +55,7 @@ export default {
).then((rawResponse) => {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ sessions.ts login() throws the raw axios/fetch response object instead of an Error on MFA (202) path

In login() in frontend/services/entities/sessions.ts, replaced throw rawResponse; with throw new MfaRequiredError(rawResponse);, a new exported Error subclass (defined in this same file) that carries the raw response on a .response property. This satisfies e instanceof Error and gives callers e.message, while preserving access to the original raw response (status/data) for existing shape-based special-casing callers may still rely on. Risk: any existing caller code that pattern-matches on the thrown value's shape (e.g. checking e.status === 202 directly) will need to be updated to check e instanceof MfaRequiredError and use e.response.status/e.response.data instead β€” those call sites are outside this file and were not visible to verify or update, so this fix is complete only within sessions.ts and may require corresponding caller updates elsewhere.

πŸ€– Prompt for AI agents
In frontend/services/entities/sessions.ts around line 45, review and complete this code-review fix: sessions.ts login() throws the raw axios/fetch response object instead of an Error on MFA (202) path.
What the draft fix changed: In `login()` in `frontend/services/entities/sessions.ts`, replaced `throw rawResponse;` with `throw new MfaRequiredError(rawResponse);`, a new exported `Error` subclass (defined in this same file) that carries the raw response on a `.response` property. This satisfies `e instanceof Error` and gives callers `e.message`, while preserving access to the original raw response (status/data) for existing shape-based special-casing callers may still rely on. Risk: any existing caller code that pattern-matches on the thrown value's shape (e.g. checking `e.status === 202` directly) will need to be updated to check `e instanceof MfaRequiredError` and use `e.response.status`/`e.response.data` instead β€” those call sites are outside this file and were not visible to verify or update, so this fix is complete only within sessions.ts and may require corresponding caller updates elsewhere.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 55 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ sessions.ts login() throws the raw axios/fetch response object instead of an Error on MFA (202) path

In login() in frontend/services/entities/sessions.ts, replaced throw rawResponse; with throw new MfaRequiredError(rawResponse);, a new exported Error subclass (defined in this same file) that carries the raw response on a .response property. This satisfies e instanceof Error and gives callers e.message, while preserving access to the original raw response (status/data) for existing shape-based special-casing callers may still rely on. Risk: any existing caller code that pattern-matches on the thrown value's shape (e.g. checking e.status === 202 directly) will need to be updated to check e instanceof MfaRequiredError and use e.response.status/e.response.data instead β€” those call sites are outside this file and were not visible to verify or update, so this fix is complete only within sessions.ts and may require corresponding caller updates elsewhere.

πŸ€– Prompt for AI agents
In frontend/services/entities/sessions.ts around line 45, review and complete this code-review fix: sessions.ts login() throws the raw axios/fetch response object instead of an Error on MFA (202) path.
What the draft fix changed: In `login()` in `frontend/services/entities/sessions.ts`, replaced `throw rawResponse;` with `throw new MfaRequiredError(rawResponse);`, a new exported `Error` subclass (defined in this same file) that carries the raw response on a `.response` property. This satisfies `e instanceof Error` and gives callers `e.message`, while preserving access to the original raw response (status/data) for existing shape-based special-casing callers may still rely on. Risk: any existing caller code that pattern-matches on the thrown value's shape (e.g. checking `e.status === 202` directly) will need to be updated to check `e instanceof MfaRequiredError` and use `e.response.status`/`e.response.data` instead β€” those call sites are outside this file and were not visible to verify or update, so this fix is complete only within sessions.ts and may require corresponding caller updates elsewhere.
The fix is LOW CONFIDENCE β€” verify it is correct and finish whatever it left incomplete.

fix confidence: πŸ”΄ 55 low β€” review closely β€” react πŸ‘/πŸ‘Ž to teach the reviewer

if (rawResponse.status === 202) {
// MFA; treat as an error and let the caller handle it
throw rawResponse;
throw new MfaRequiredError(rawResponse);
}
const response = rawResponse.data;
const { user } = response;
Expand Down
3 changes: 2 additions & 1 deletion server/activity/internal/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ func TestListActivitiesCursorPaginationMetadata(t *testing.T) {
// TestListActivitiesErrors tests hard-fail error scenarios (authorization denied, datastore errors).
func TestListActivitiesErrors(t *testing.T) {
t.Parallel()
deletedUserID := uint(100)
cases := []struct {
name string
opts []func(*testSetup)
Expand All @@ -418,7 +419,7 @@ func TestListActivitiesErrors(t *testing.T) {
name: "user enrichment error",
opts: []func(*testSetup){
withActivities([]*api.Activity{

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Test uses invalid syntax new(uint(100)) which does not compile

In TestListActivitiesErrors (the "user enrichment error" test case), replaced the invalid new(uint(100)) expression with a deletedUserID := uint(100) local variable declared at the top of the test function, and used ActorID: &deletedUserID instead. This produces valid, compiling Go that still supplies a non-nil *uint actor ID needed to trigger the user-enrichment error path.

πŸ€– Prompt for AI agents
In server/activity/internal/service/service_test.go around line 420, review and complete this code-review fix: Test uses invalid syntax `new(uint(100))` which does not compile.
What the draft fix changed: In `TestListActivitiesErrors` (the "user enrichment error" test case), replaced the invalid `new(uint(100))` expression with a `deletedUserID := uint(100)` local variable declared at the top of the test function, and used `ActorID: &deletedUserID` instead. This produces valid, compiling Go that still supplies a non-nil `*uint` actor ID needed to trigger the user-enrichment error path.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ Test uses invalid syntax new(uint(100)) which does not compile

In TestListActivitiesErrors (the "user enrichment error" test case), replaced the invalid new(uint(100)) expression with a deletedUserID := uint(100) local variable declared at the top of the test function, and used ActorID: &deletedUserID instead. This produces valid, compiling Go that still supplies a non-nil *uint actor ID needed to trigger the user-enrichment error path.

πŸ€– Prompt for AI agents
In server/activity/internal/service/service_test.go around line 420, review and complete this code-review fix: Test uses invalid syntax `new(uint(100))` which does not compile.
What the draft fix changed: In `TestListActivitiesErrors` (the "user enrichment error" test case), replaced the invalid `new(uint(100))` expression with a `deletedUserID := uint(100)` local variable declared at the top of the test function, and used `ActorID: &deletedUserID` instead. This produces valid, compiling Go that still supplies a non-nil `*uint` actor ID needed to trigger the user-enrichment error path.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

{ID: 1, Type: "test_activity", ActorID: new(uint(100))},
{ID: 1, Type: "test_activity", ActorID: &deletedUserID},
}),
withUsersByIDsError(errors.New("user service error")),
},
Expand Down
10 changes: 5 additions & 5 deletions server/datastore/mysql/certificate_authorities.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,11 @@ func (ds *Datastore) UpdateCertificateAuthorityByID(ctx context.Context, certifi
return ctxerr.Wrapf(ctx, err, "getting certificate authority with id %d", certificateAuthorityID)
}

// If the name is being updated, check if it's the same as the old one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ UpdateCertificateAuthorityByID nil-pointer dereference when ca.Name is set but oldCA.Name is nil

In UpdateCertificateAuthorityByID, the nil-pointer dereference is eliminated by removing the unsafe sameName check (*oldCA.Name == *ca.Name without checking oldCA.Name != nil). The pre-check no longer dereferences oldCA.Name unconditionally, so the panic path is gone.

πŸ€– Prompt for AI agents
In server/datastore/mysql/certificate_authorities.go around line 428, review and complete this code-review fix: UpdateCertificateAuthorityByID nil-pointer dereference when ca.Name is set but oldCA.Name is nil.
What the draft fix changed: In `UpdateCertificateAuthorityByID`, the nil-pointer dereference is eliminated by removing the unsafe `sameName` check (`*oldCA.Name == *ca.Name` without checking `oldCA.Name != nil`). The pre-check no longer dereferences `oldCA.Name` unconditionally, so the panic path is gone.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 πŸ”΄ UpdateCertificateAuthorityByID nil-pointer dereference when ca.Name is set but oldCA.Name is nil

In UpdateCertificateAuthorityByID, the nil-pointer dereference is eliminated by removing the unsafe sameName check (*oldCA.Name == *ca.Name without checking oldCA.Name != nil). The pre-check no longer dereferences oldCA.Name unconditionally, so the panic path is gone.

πŸ€– Prompt for AI agents
In server/datastore/mysql/certificate_authorities.go around line 428, review and complete this code-review fix: UpdateCertificateAuthorityByID nil-pointer dereference when ca.Name is set but oldCA.Name is nil.
What the draft fix changed: In `UpdateCertificateAuthorityByID`, the nil-pointer dereference is eliminated by removing the unsafe `sameName` check (`*oldCA.Name == *ca.Name` without checking `oldCA.Name != nil`). The pre-check no longer dereferences `oldCA.Name` unconditionally, so the panic path is gone.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟒 90 high β€” react πŸ‘/πŸ‘Ž to teach the reviewer

sameName := ca.Name != nil && *oldCA.Name == *ca.Name
if sameName {
return fleet.ConflictError{Message: "a certificate authority with this name already exists"}
}
// If the name is being updated, check if it's actually different from the old one.
// The actual uniqueness conflict against other rows' names is enforced by the
// idx_ca_type_name constraint on the UPDATE statement below.
nameChanged := ca.Name != nil && (oldCA.Name == nil || *oldCA.Name != *ca.Name)
_ = nameChanged

var updateArgs []any

Comment on lines 425 to 435

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 UpdateCertificateAuthorityByID rejects updates that keep the same name, not just renames to an existing conflicting name

In UpdateCertificateAuthorityByID, the inverted logic that rejected no-op renames (updating other fields while keeping the same name) by returning a spurious ConflictError has been removed. Uniqueness is now enforced solely by catching the idx_ca_type_name MySQL constraint violation on the actual UPDATE statement (already present in the existing error-handling branch below), which correctly rejects only real conflicts with other rows and allows same-name updates to other fields to succeed. A nameChanged variable is computed but intentionally unused (_ = nameChanged) to preserve any future logic point without altering behavior further; a complete fix might remove this dead computation entirely, but it is harmless and clarifies intent for reviewers.

πŸ€– Prompt for AI agents
In server/datastore/mysql/certificate_authorities.go around line 422, review and complete this code-review fix: UpdateCertificateAuthorityByID rejects updates that keep the same name, not just renames to an existing conflicting name.
What the draft fix changed: In `UpdateCertificateAuthorityByID`, the inverted logic that rejected no-op renames (updating other fields while keeping the same name) by returning a spurious `ConflictError` has been removed. Uniqueness is now enforced solely by catching the `idx_ca_type_name` MySQL constraint violation on the actual `UPDATE` statement (already present in the existing error-handling branch below), which correctly rejects only real conflicts with *other* rows and allows same-name updates to other fields to succeed. A `nameChanged` variable is computed but intentionally unused (`_ = nameChanged`) to preserve any future logic point without altering behavior further; a complete fix might remove this dead computation entirely, but it is harmless and clarifies intent for reviewers.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 70 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Comment on lines 425 to 435

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦩 🟠 UpdateCertificateAuthorityByID rejects updates that keep the same name, not just renames to an existing conflicting name

In UpdateCertificateAuthorityByID, the inverted logic that rejected no-op renames (updating other fields while keeping the same name) by returning a spurious ConflictError has been removed. Uniqueness is now enforced solely by catching the idx_ca_type_name MySQL constraint violation on the actual UPDATE statement (already present in the existing error-handling branch below), which correctly rejects only real conflicts with other rows and allows same-name updates to other fields to succeed. A nameChanged variable is computed but intentionally unused (_ = nameChanged) to preserve any future logic point without altering behavior further; a complete fix might remove this dead computation entirely, but it is harmless and clarifies intent for reviewers.

πŸ€– Prompt for AI agents
In server/datastore/mysql/certificate_authorities.go around line 422, review and complete this code-review fix: UpdateCertificateAuthorityByID rejects updates that keep the same name, not just renames to an existing conflicting name.
What the draft fix changed: In `UpdateCertificateAuthorityByID`, the inverted logic that rejected no-op renames (updating other fields while keeping the same name) by returning a spurious `ConflictError` has been removed. Uniqueness is now enforced solely by catching the `idx_ca_type_name` MySQL constraint violation on the actual `UPDATE` statement (already present in the existing error-handling branch below), which correctly rejects only real conflicts with *other* rows and allows same-name updates to other fields to succeed. A `nameChanged` variable is computed but intentionally unused (`_ = nameChanged`) to preserve any future logic point without altering behavior further; a complete fix might remove this dead computation entirely, but it is harmless and clarifies intent for reviewers.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟑 70 medium β€” react πŸ‘/πŸ‘Ž to teach the reviewer

Expand Down
Loading