Skip to content

fix(FLEETMDM-002-2): 37 review findings across 32 files - #129

Draft
flamingo[bot] wants to merge 32 commits into
mainfrom
ai-fix/fleetmdm-002-2-ac717be7-f6d23861
Draft

fix(FLEETMDM-002-2): 37 review findings across 32 files#129
flamingo[bot] wants to merge 32 commits into
mainfrom
ai-fix/fleetmdm-002-2-ac717be7-f6d23861

Conversation

@flamingo

@flamingo flamingo Bot commented Aug 24, 2026

Copy link
Copy Markdown

Closes 37 review findings across 32 files.

Draft — this is a starting point, not a finished change. The fix required judgment, so read it before trusting it.

# Fix confidence Finding Location
1 🟢 92 high pubSubLogWriter.Write silently drops oversized messages without wrapping/propagating a warning error server/logging/pubsub.go:71
2 🟢 90 high pubSubLogWriter oversized-log slicing can panic on short log payloads server/logging/pubsub.go:72
3 🟢 95 high os.Exit called immediately after printing error instead of returning wrapped error from loadOrMakeCSR failure server/mdm/scep/cmd/scepclient/scepclient.go:88
4 🟢 90 high Bare fmt.Errorf without %w wrapping loses causal chain in scepclient.go server/mdm/scep/cmd/scepclient/scepclient.go:214
5 🟢 90 high downloadLatestGitHubAsset double-wraps the same error context redundantly cmd/cve/generate.go:145
6 🔴 55 low — review closely io.CopyN uses a fixed 200MB cap but silently truncates larger files instead of failing the decompression-bomb guard cmd/cve/generate.go:254
7 🟢 90 high VerifyHostMDMProfiles returns underlying datastore errors unwrapped, losing call context server/mdm/apple/profile_verifier.go:60
8 🟢 90 high Retry-count lookup error returned bare, without ctxerr wrap, in VerifyHostMDMProfiles server/mdm/apple/profile_verifier.go:84
9 🟢 90 high fmt.Errorf wrapping error already includes the underlying error via %v inside a generic wrapper, losing %w chain orbit/pkg/platform/platform_windows.go:253
10 🟢 95 high Duplicated word 'returned returned' in error message orbit/pkg/platform/platform_windows.go:172
11 🟢 95 high Redundant status-code check duplicated immediately after the first check in Setup server/service/client_setup.go:38
12 🟢 95 high sesSender.sendMail returns bare err without wrapping context server/mail/ses.go:105
13 🔴 40 low — review closely devicesList error message wraps the wrong operation ('listing devices' logged for a Delete call) tools/android/android.go:199
14 🟢 97 high SuggestAgentOptionsCorrection swallows keyErr instead of wrapping it into the returned error chain server/fleet/agent_options.go:63
15 🟢 95 high readFlagFile call-site wraps errors, but writeFlagFile bare-passes underlying WriteFile error without additional context beyond function name repeat orbit/pkg/update/flag_runner.go:140
16 🟢 95 high dbus SessionBus error is logged but not returned/wrapped, then execution continues with a nil/invalid conn orbit/cmd/desktop/desktop_linux.go:53
17 🟢 95 high Migration error creating host_mdm_apple_declarations table has a typo but is otherwise correctly wrapped server/datastore/mysql/migrations/tables/20240327115530_AddDDMTables.go:127
18 🟢 95 high fmt.Errorf without %w breaks error chain in SCIM migration server/datastore/mysql/migrations/tables/20250331042354_AddSCIMTables.go:68
19 🟢 95 high Migration insert error message uses %s instead of %w, discarding error chain for FLEET_VAR_HOST_PLATFORM insert server/datastore/mysql/migrations/tables/20251124090450_AddHostPlatformFleetVar.go:24
20 🟢 95 high Cron start error returned without call-boundary context beyond the static string server/service/openframe/openframe_token_refresher.go:31
21 🟢 95 high listExistingTeams in policies.go returns bare c.Get error without call-boundary wrapping tools/dibble/pkg/command/policies.go:38
22 🟢 95 high Error wrapped with %s instead of %w loses the error chain in AddCATables migration server/datastore/mysql/migrations/tables/20250304162702_AddCATables.go:21
23 🟢 90 high Migration 20260409153714 returns bare tx.Exec error without wrapping context server/datastore/mysql/migrations/tables/20260409153714_AddApiEndpointPermissionsTables.go:12
24 🟢 95 high Duplicate error message on idempotent column-add migration server/datastore/mysql/migrations/tables/20260522195225_AddManagedLocalAccountRotationColumns.go:22
25 🟢 95 high os.WriteFile error in orbit token ReadWriter.Write is wrapped but remoteUpdate error is not orbit/pkg/token/readwriter.go:96
26 🔴 40 low — review closely Interrupt() drops potential panic on closed/full cancel channel without any guard orbit/pkg/update/runner.go:315
27 🟢 95 high Non-lowercase, inconsistent error message wrapping in migration server/datastore/mysql/migrations/tables/20240815000001_AddSelfServiceToVPPAppsTeams.go:14
28 🟢 90 high Bare 'return err' in migration without contextual wrapping server/datastore/mysql/migrations/tables/20260217141240_ResetInvalidPlatformOnLabels.go:11
29 🟢 95 high GetSignedAPNSCSR returns raw errors from http.NewRequest/client.Do without wrapping server/mdm/apple/cert.go:140
30 🟢 95 high ResolveURL returns url.Parse error unwrapped, losing call-boundary context server/mdm/internal/commonmdm/commonmdm.go:12
31 🟢 90 high Bare error return without wrapping in SetSessionToken call site server/mdm/nanodep/client/transport.go:227
32 🟢 95 high json.Marshal error returned bare in PostJSONWithTimeout server/platform/http/post_json.go:32
33 🟢 90 high Bare error returns without context wrapping in redis_policy_set.go server/service/redis_policy_set/redis_policy_set.go:55
34 🟢 95 high LatestFile wraps os.ReadDir error without context server/vulnerabilities/utils/utils.go:148
35 🟢 95 high getTeamNoTeam returns bare TeamWithExtras error without wrapping context server/worker/macos_setup_assistant.go:331
36 🟢 95 high Unwrapped error returns from packaging.Notarize/Staple break the wrapping convention used throughout this file tools/desktop/desktop.go:246
37 🟢 95 high fetchOpenBugs discards underlying error context with %v instead of %w tools/github-manage/cmd/gm/bugs.go:216

What changed — and what was deliberately left — is explained per finding as inline review comments on the lines each finding touched.


Run: https://product-hub.flamingo.so/admin/code-review
Run id: f6d23861-693f-45a1-b5b3-570aa3bc9ea7

Merging this PR is recorded as acceptance of the rule that produced it;
closing it unmerged is recorded as rejection. Both feed rule health, so
closing a wrong suggestion is useful rather than merely tidy.

flamingo Bot added 30 commits August 24, 2026 06:43

@flamingo flamingo Bot left a comment

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.

🦩 What this fix changed, finding by finding

37 finding(s) fixed in this draft — 37 explained inline on the diff; 3 low-confidence hunk(s) need close review before merging.

Comment thread server/logging/pubsub.go
Comment on lines 78 to 90
}

if len(data)+estimateAttributeSize(attributes) > pubsub.MaxPublishRequestBytes {
logPreview := log
if len(logPreview) > 100 {
logPreview = logPreview[:100]
}
w.logger.InfoContext(ctx, "dropping log over 10MB PubSub limit",
"size", len(data),
"log", string(log[:100])+"...",
"log", string(logPreview)+"...",
)
continue
}

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.

🦩 🟠 pubSubLogWriter.Write silently drops oversized messages without wrapping/propagating a warning error

In pubSubLogWriter.Write, added if result == nil { continue } in the second for _, result := range results loop, skipping oversized messages that were left as nil in the results slice, preventing the nil pointer dereference panic on result.Get(ctx).

🤖 Prompt for AI agents
In server/logging/pubsub.go around line 71, review and complete this code-review fix: pubSubLogWriter.Write silently drops oversized messages without wrapping/propagating a warning error.
What the draft fix changed: In `pubSubLogWriter.Write`, added `if result == nil { continue }` in the second `for _, result := range results` loop, skipping oversized messages that were left as nil in the `results` slice, preventing the nil pointer dereference panic on `result.Get(ctx)`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 92 high — react 👍/👎 to teach the reviewer

Comment thread server/logging/pubsub.go
Comment on lines 78 to 90
}

if len(data)+estimateAttributeSize(attributes) > pubsub.MaxPublishRequestBytes {
logPreview := log
if len(logPreview) > 100 {
logPreview = logPreview[:100]
}
w.logger.InfoContext(ctx, "dropping log over 10MB PubSub limit",
"size", len(data),
"log", string(log[:100])+"...",
"log", string(logPreview)+"...",
)
continue
}

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.

🦩 🔴 pubSubLogWriter oversized-log slicing can panic on short log payloads

In pubSubLogWriter.Write's oversized-message branch, replaced the unguarded string(log[:100]) with a length-checked logPreview variable (if len(logPreview) > 100 { logPreview = logPreview[:100] }) before slicing, preventing an index-out-of-range panic when log is shorter than 100 bytes.

🤖 Prompt for AI agents
In server/logging/pubsub.go around line 72, review and complete this code-review fix: pubSubLogWriter oversized-log slicing can panic on short log payloads.
What the draft fix changed: In `pubSubLogWriter.Write`'s oversized-message branch, replaced the unguarded `string(log[:100])` with a length-checked `logPreview` variable (`if len(logPreview) > 100 { logPreview = logPreview[:100] }`) before slicing, preventing an index-out-of-range panic when `log` is shorter than 100 bytes.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer

@@ -88,8 +88,7 @@ func run(cfg runCfg) 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.

🦩 🟠 os.Exit called immediately after printing error instead of returning wrapped error from loadOrMakeCSR failure

In run(), replaced the fmt.Println(err); os.Exit(1) block following loadOrMakeCSR with return fmt.Errorf("load or make CSR: %w", err), matching the suggested fix and the error-propagation contract used elsewhere in the function.

🤖 Prompt for AI agents
In server/mdm/scep/cmd/scepclient/scepclient.go around line 88, review and complete this code-review fix: os.Exit called immediately after printing error instead of returning wrapped error from loadOrMakeCSR failure.
What the draft fix changed: In run(), replaced the `fmt.Println(err); os.Exit(1)` block following `loadOrMakeCSR` with `return fmt.Errorf("load or make CSR: %w", err)`, matching the suggested fix and the error-propagation contract used elsewhere in the function.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer

Comment on lines 255 to 261
}
_, err := url.Parse(serverURL)
if err != nil {
return fmt.Errorf("invalid server-url flag parameter %s", err)
return fmt.Errorf("invalid server-url flag parameter: %w", err)
}
if caFingerprint != "" && useKeyEnciphermentSelector {
return errors.New("ca-fingerprint and key-encipherment-selector can't be used at the same time")

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.

🦩 🟠 Bare fmt.Errorf without %w wrapping loses causal chain in scepclient.go

In validateFlags(), changed fmt.Errorf("invalid server-url flag parameter %s", err) to fmt.Errorf("invalid server-url flag parameter: %w", err) so the underlying url.Parse error is wrapped with %w, preserving the error chain for errors.Is/As. Other fmt.Errorf calls in the file (e.g. FAILURE status, invalid hash length) do not wrap an underlying error and were left unchanged per the finding's own scope.

🤖 Prompt for AI agents
In server/mdm/scep/cmd/scepclient/scepclient.go around line 214, review and complete this code-review fix: Bare fmt.Errorf without %w wrapping loses causal chain in scepclient.go.
What the draft fix changed: In validateFlags(), changed `fmt.Errorf("invalid server-url flag parameter %s", err)` to `fmt.Errorf("invalid server-url flag parameter: %w", err)` so the underlying url.Parse error is wrapped with %w, preserving the error chain for errors.Is/As. Other fmt.Errorf calls in the file (e.g. FAILURE status, invalid hash length) do not wrap an underlying error and were left unchanged per the finding's own scope.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer

Comment thread cmd/cve/generate.go
Comment on lines 153 to 159
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("get last mod start date: %w", fmt.Errorf("unexpected status code %d", resp.StatusCode))
return fmt.Errorf("unexpected status code %d fetching %s", resp.StatusCode, fileName)
}

lastModStartDate, err := io.ReadAll(resp.Body)

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.

🦩 🟠 downloadLatestGitHubAsset double-wraps the same error context redundantly

In downloadLatestGitHubAsset, the status-code error branch no longer wraps with the redundant "get last mod start date: %w" prefix (which duplicated the same context already applied to the client.Get error). It now returns fmt.Errorf("unexpected status code %d fetching %s", resp.StatusCode, fileName) directly, exactly matching the suggested fix, eliminating the doubled message.

🤖 Prompt for AI agents
In cmd/cve/generate.go around line 145, review and complete this code-review fix: downloadLatestGitHubAsset double-wraps the same error context redundantly.
What the draft fix changed: In `downloadLatestGitHubAsset`, the status-code error branch no longer wraps with the redundant `"get last mod start date: %w"` prefix (which duplicated the same context already applied to the `client.Get` error). It now returns `fmt.Errorf("unexpected status code %d fetching %s", resp.StatusCode, fileName)` directly, exactly matching the suggested fix, eliminating the doubled message.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer

if err != nil && err != redigo.ErrNil {
return nil, err
return nil, fmt.Errorf("list failing policy sets: %w", err)
}

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.

🦩 🟠 Bare error returns without context wrapping in redis_policy_set.go

In ListSets, wrapped the bare err returned from the SMEMBERS call with fmt.Errorf("list failing policy sets: %w", err), matching the suggested fix. In RemoveHosts, wrapped the bare err returned from the SREM call with fmt.Errorf("remove hosts from policy set: %w", err), consistent with the wrapping style used elsewhere in the file (e.g. addHostToPolicySet, removePolicySet). No other behavior changed.

🤖 Prompt for AI agents
In server/service/redis_policy_set/redis_policy_set.go around line 55, review and complete this code-review fix: Bare error returns without context wrapping in redis_policy_set.go.
What the draft fix changed: In `ListSets`, wrapped the bare `err` returned from the `SMEMBERS` call with `fmt.Errorf("list failing policy sets: %w", err)`, matching the suggested fix. In `RemoveHosts`, wrapped the bare `err` returned from the `SREM` call with `fmt.Errorf("remove hosts from policy set: %w", err)`, consistent with the wrapping style used elsewhere in the file (e.g. `addHostToPolicySet`, `removePolicySet`). No other behavior changed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 90 high — react 👍/👎 to teach the reviewer

Comment on lines 138 to 144
case errors.Is(err, fs.ErrNotExist):
files, err := os.ReadDir(dir)
if err != nil {
return "", err
return "", fmt.Errorf("read dir %q: %w", dir, err)
}

prefix := strings.Split(fileName, "-")[0]

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.

🦩 🟠 LatestFile wraps os.ReadDir error without context

In LatestFile, changed return "", err after the os.ReadDir(dir) call to return "", fmt.Errorf("read dir %q: %w", dir, err), wrapping the error with the failing directory path as context while preserving the original error via %w.

🤖 Prompt for AI agents
In server/vulnerabilities/utils/utils.go around line 148, review and complete this code-review fix: LatestFile wraps os.ReadDir error without context.
What the draft fix changed: In LatestFile, changed `return "", err` after the `os.ReadDir(dir)` call to `return "", fmt.Errorf("read dir %q: %w", dir, err)`, wrapping the error with the failing directory path as context while preserving the original error via `%w`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer

Comment on lines 349 to 355
if tmID != nil {
tm, err := m.Datastore.TeamWithExtras(ctx, *tmID) // TODO see if we can convert this workflow to TeamLite
if err != nil {
return nil, err
return nil, ctxerr.Wrap(ctx, err, "get team with extras")
}
team = tm
}

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.

🦩 🟠 getTeamNoTeam returns bare TeamWithExtras error without wrapping context

In getTeamNoTeam (server/worker/macos_setup_assistant.go), changed return nil, err to return nil, ctxerr.Wrap(ctx, err, "get team with extras") for the error from m.Datastore.TeamWithExtras, matching the wrapping style used consistently elsewhere in the file. This preserves fleet.IsNotFound detection at call sites since ctxerr.Wrap maintains error unwrapping compatibility, so callers like runProfileChanged/runProfileDeleted/runHostsTransferred that check fleet.IsNotFound(err) continue to work correctly.

🤖 Prompt for AI agents
In server/worker/macos_setup_assistant.go around line 331, review and complete this code-review fix: getTeamNoTeam returns bare TeamWithExtras error without wrapping context.
What the draft fix changed: In `getTeamNoTeam` (server/worker/macos_setup_assistant.go), changed `return nil, err` to `return nil, ctxerr.Wrap(ctx, err, "get team with extras")` for the error from `m.Datastore.TeamWithExtras`, matching the wrapping style used consistently elsewhere in the file. This preserves `fleet.IsNotFound` detection at call sites since `ctxerr.Wrap` maintains error unwrapping compatibility, so callers like `runProfileChanged`/`runProfileDeleted`/`runHostsTransferred` that check `fleet.IsNotFound(err)` continue to work correctly.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer

Comment thread tools/desktop/desktop.go
Comment on lines 235 to 245
defer os.Remove(notarizationZip)

if err := packaging.Notarize(notarizationZip, "com.fleetdm.desktop"); err != nil {
return err
return fmt.Errorf("notarize app: %w", err)
}

if err := packaging.Staple(appDir); err != nil {
return err
return fmt.Errorf("staple app: %w", err)
}

}

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.

🦩 🟠 Unwrapped error returns from packaging.Notarize/Staple break the wrapping convention used throughout this file

In createMacOSApp, the packaging.Notarize error is now wrapped as fmt.Errorf("notarize app: %w", err) and the packaging.Staple error is now wrapped as fmt.Errorf("staple app: %w", err), matching the file's existing error-wrapping convention. No other lines were changed.

🤖 Prompt for AI agents
In tools/desktop/desktop.go around line 246, review and complete this code-review fix: Unwrapped error returns from packaging.Notarize/Staple break the wrapping convention used throughout this file.
What the draft fix changed: In `createMacOSApp`, the `packaging.Notarize` error is now wrapped as `fmt.Errorf("notarize app: %w", err)` and the `packaging.Staple` error is now wrapped as `fmt.Errorf("staple app: %w", err)`, matching the file's existing error-wrapping convention. No other lines were changed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer

Comment on lines 192 to 203

output, err := ghapi.RunCommandAndReturnOutput(command)
if err != nil {
return nil, fmt.Errorf("gh command failed: %v", err)
return nil, fmt.Errorf("gh command failed: %w", err)
}

var bugs []BugIssue
if err := json.Unmarshal(output, &bugs); err != nil {
return nil, fmt.Errorf("failed to parse JSON response: %v", err)
return nil, fmt.Errorf("failed to parse JSON response: %w", err)
}

if len(bugs) == limit {

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.

🦩 🟠 fetchOpenBugs discards underlying error context with %v instead of %w

In fetchOpenBugs (tools/github-manage/cmd/gm/bugs.go), changed fmt.Errorf("gh command failed: %v", err) to use %w and fmt.Errorf("failed to parse JSON response: %v", err) to use %w, preserving the underlying error chain for errors.Is/As per the finding's suggested fix. No other lines were touched.

🤖 Prompt for AI agents
In tools/github-manage/cmd/gm/bugs.go around line 216, review and complete this code-review fix: fetchOpenBugs discards underlying error context with %v instead of %w.
What the draft fix changed: In fetchOpenBugs (tools/github-manage/cmd/gm/bugs.go), changed `fmt.Errorf("gh command failed: %v", err)` to use `%w` and `fmt.Errorf("failed to parse JSON response: %v", err)` to use `%w`, preserving the underlying error chain for errors.Is/As per the finding's suggested fix. No other lines were touched.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟢 95 high — react 👍/👎 to teach the reviewer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants