Skip to content

fix(adhoc-sweep-fixes): CU-86akbhhtv 66 review findings across 40 files - #143

Draft
flamingo[bot] wants to merge 40 commits into
mainfrom
ai-fix/adhoc-sweep-fixes-1204277a-3ba0fd37
Draft

fix(adhoc-sweep-fixes): CU-86akbhhtv 66 review findings across 40 files#143
flamingo[bot] wants to merge 40 commits into
mainfrom
ai-fix/adhoc-sweep-fixes-1204277a-3ba0fd37

Conversation

@flamingo

@flamingo flamingo Bot commented Sep 7, 2026

Copy link
Copy Markdown

Closes 66 review findings across 40 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 🟢 90 high loadtest.go uses uninitialized index t against hosts slice in loop bounded by teamExtraCount, risking index-out-of-range panic tools/mdm/apple/loadtest/loadtest.go:204
2 🟡 80 medium cron_test.go calls new(...) as if it were a helper function for creating pointers, but this is not valid Go without a locally-defined helper cmd/fleet/cron_test.go:363
3 🟡 85 medium Entra client ID de-duplication normalizes input to lowercase but does not validate case-insensitive duplicates against the raw stored value consistently on delete frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/DeleteEntraClientIDModal/DeleteEntraClientIDModal.tsx:30
4 🟡 80 medium console.error used to swallow fetch-current-user failure instead of surfacing to the user frontend/pages/ApiOnlyUser/ApiOnlyUser.tsx:31
5 🟡 85 medium UsersForm resets lockEndUserInfo to computed canLockEndUserInfo after save even when Apple MDM is not configured frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tsx:102
6 🟡 75 medium AutomationsModal team-update payload silently discards calendar/CA data when otherData is falsy frontend/pages/policies/ManagePoliciesPage/components/AutomationsModal/AutomationsModal.tsx:180
7 🟡 72 medium Large unbounded backfill INSERT...SELECT with JOIN and GROUP BY runs synchronously inside the schema migration transaction server/datastore/mysql/migrations/tables/20251028140000_CreateTableOSVersionVulnerabilities.go:51
8 🔴 35 low — review closely Regression test documents a real upstream race condition in DB migration versioning server/goose/migrate_openframe_test.go:1
9 🟢 90 high RetrieveMigrationCheckins swallows errors from fileExists by sending to channel but continuing execution server/mdm/nanomdm/storage/file/migrate.go:35
10 🟡 82 medium multiMatcher.Match mutates shared depth counter without matching lock scope, risking incorrect nil-match culling under concurrent use server/vulnerabilities/nvd/tools/wfn/matcher.go:77
11 🟡 85 medium UUID fallback in randomUUIDv4 produces a fixed non-unique nil UUID on crypto/rand failure, defeating the per-profile uniqueness guarantee tools/dibble/pkg/seed/profiles.go:122
12 🟡 65 medium AddAbmModal title truncated to 'Add AB' and button text 'Add AB' — appears to be a search/replace error dropping 'M' from 'ABM' frontend/pages/admin/IntegrationsPage/cards/MdmSettings/AppleBusinessManagerPage/components/AddAbmModal/AddAbmModal.tsx:54
13 🟡 85 medium requireXcodeSpecialCase computed inside per-install loop but only meaningfully consumed for the last processed install server/service/apple_mdm_cmd_results.go:155
14 🔴 45 low — review closely Fresh cache-column backfill assumes 'currently active' at migration time equals correctness for all history server/datastore/mysql/migrations/tables/20260218175704_FMAActiveInstallers.go:26
15 🟢 90 high Constant-time bearer auth compares strings of differing lengths, defeating the timing-attack protection tools/fleet-mcp/auth.go:14
16 🔴 55 low — review closely finishMFA swallows all errors from the MFA completion API into a single 'expired' state, masking other failure causes frontend/pages/MfaPage/MfaPage.tsx:75
17 🟡 65 medium UpdateActivityTable migration combines column add, rename, and drop in a single non-idempotent guarded ALTER, risking partial-failure inconsistency server/datastore/mysql/migrations/tables/20250807140441_UpdateActivityTable.go:15
18 🔴 55 low — review closely New unique-key migration on mdm_configuration_profile_update_settings has no data uniqueness guarantee before constraint creation server/datastore/mysql/migrations/tables/20260529091823_AddUpdateProfileSettingsTrackingTable.go:50
19 🔴 35 low — review closely has_pending_commands backfill can silently miss commands inserted between ADD COLUMN and UPDATE server/datastore/mysql/migrations/tables/20260603120000_AddPollScheduleRelaxedToMDMWindowsEnrollments.go:37
20 🔴 40 low — review closely get-stripe-checkout-session-url retries billing helper with .retry() but no idempotency key, risking duplicate Stripe customers website/api/controllers/customers/get-stripe-checkout-session-url.js:46
21 🟡 80 medium linux_wipe.sh unmounts network filesystems with a blocklist that misses many other essential paths ee/server/service/embedded_scripts/linux_wipe.sh:32
22 🔴 25 low — review closely PastActivityFeed casts pastActivityComponentMap lookup to a component accepting show-details props without a type guard, but not all entries use IHostActivityItemComponentPropsWithShowDetails frontend/pages/hosts/details/cards/Activity/PastActivityFeed/PastActivityFeed.tsx:62
23 🔴 35 low — review closely ListOptions.GetSecondaryOrderKey/IsSecondaryDescending hardcode 'not used' but interface may be shared with generic pagination code expecting real values server/activity/internal/types/activity.go:72
24 🔴 40 low — review closely Software checksum migration hashes over bundle_identifier without COALESCE, unlike host_mdm_apple_profiles pattern used elsewhere server/datastore/mysql/migrations/tables/20231212094238_AddUniqueHashToSoftware.go:39
25 🟡 65 medium Batch script execution host results FK references execution_id (string) not batch id, but no uniqueness/index guard against orphaned host_execution_id duplicates server/datastore/mysql/migrations/tables/20250424153059_AddBatchScriptExecutionTables.go:27
26 🔴 45 low — review closely New cross-team policy authorization guard tested but underlying behavior seems fragile in default svc setup server/service/global_policies_test.go:162
27 🔴 55 low — review closely License key generation JWT does not validate numberOfHosts or expiresAt bounds before signing website/api/helpers/create-license-key.js:46
28 🔴 35 low — review closely Migration backfill sets updated_at to itself but MySQL ON UPDATE CURRENT_TIMESTAMP columns may still bump depending on strict mode / column definition server/datastore/mysql/migrations/tables/20260610172952_AddHasACMEPayloadToHostMDMAppleProfiles.go:25
29 🟢 90 high writeAPIKeys returns wrong wrapped error message on MkdirAll failure orbit/pkg/packaging/macos_rcodesign.go:75
30 🟢 90 high rSign uses %s instead of %w when wrapping the cert-write error orbit/pkg/packaging/macos_rcodesign.go:17
31 🟡 85 medium writeAPIKeys wraps errors with %s instead of %w in three places orbit/pkg/packaging/macos_rcodesign.go:67
32 🟡 75 medium secure.MkdirAll called with file-permission bits (0o600) instead of directory permission bits orbit/pkg/packaging/macos_rcodesign.go:75
33 🟢 90 high cmd.Run() error return is silently discarded via output re-read with CombinedOutput after Run already consumed the pipes tools/snapshot/snapshot.go:163
34 🟢 90 high Same invalid Run()+CombinedOutput() pattern repeated in snapshot() tools/snapshot/snapshot.go:252
35 🟡 60 medium Snapshot restore prompt error not wrapped with context tools/snapshot/snapshot.go:148
36 🔴 20 low — review closely getRepoRoot() naive relative-path join may break if binary is not directly under a top-level bin dir tools/snapshot/snapshot.go:285
37 🟡 85 medium logging.WithExtras call result is discarded — likely missing the actual log emission server/service/sessions.go:200
38 🟡 80 medium Service.Login swallows NewActivity logging failure without wrapping or surfacing it server/service/sessions.go:195
39 🔴 55 low — review closely InitiateSSO redirect-scheme blocklist is an incomplete denylist rather than an allowlist server/service/sessions.go:470
40 🔴 20 low — review closely Duplicated token-expiry / available-teams block between loginEndpoint and sessionCreateEndpoint server/service/sessions.go:144
41 🟡 75 medium Empty hostIDs list vs nil hostIDs semantics conflated in ModifyLabel server/service/labels.go:210
42 🟡 60 medium labelResponseForLabel and labelResponseForLabelWithTeamName never return an error but keep an (X, error) signature server/service/labels.go:341
43 🔴 45 low — review closely Bare error returns without wrapping in NewLabel across multiple call sites server/service/labels.go:115
44 🟢 95 high Typo 'empry' in production comment server/service/labels.go:211
45 🟢 95 high Wrong log message on device-listing error in devicesDelete tools/android/android.go:220
46 🟢 95 high Wrong log message on empty devices result in devicesList tools/android/android.go:200
47 🟡 85 medium Redundant duplicate validation of command in main() tools/android/android.go:75
48 🟢 95 high MakeMacOSFatExecutable does not validate inPaths is non-empty before indexing inputs[len(inputs)-1] pkg/buildpkg/buildpkg.go:54
49 🟡 65 medium Output file left on disk with wrong permissions/partial content if binary.Write or out.Write fails after os.Create pkg/buildpkg/buildpkg.go:64
50 🟡 60 medium Bare error returns without wrapping throughout MakeMacOSFatExecutable pkg/buildpkg/buildpkg.go:64
51 🔴 55 low — review closely LUKS escrow passphrase hardcoded as plaintext constant tools/luks/luks/main.go:37
52 🟡 70 medium panic(err) instead of wrapped error return in LUKS escrow tool tools/luks/luks/main.go:18
53 🔴 45 low — review closely Infinite retry loop with no maximum keySlot exit besides slot exhaustion, no user-abort path tools/luks/luks/main.go:50
54 🟡 85 medium doneReadingChan is closed while its producer goroutine may still be running, risking a send-on-closed-channel panic server/service/client_live_query.go:137
55 🔴 55 low — review closely Unbuffered error/results channels can deadlock the read-goroutine in LiveQueryWithContext server/service/client_live_query.go:143
56 🟡 60 medium Bare ctxerr.Errorf without additional call-site context wrapping in LiveQueryWithContext server/service/client_live_query.go:79
57 🟢 90 high RetrieveTokenUpdateTally / ExpandEmbeddedSecrets panic on nil interface type assertion when first store errors without a value server/mdm/nanomdm/storage/allmulti/allmulti.go:80
58 🔴 55 low — review closely execStores race: goroutines return before finalErr/finalValue assigned in deterministic order but logic assumes store 0's channel message is read distinctly server/mdm/nanomdm/storage/allmulti/allmulti.go:51
59 🟡 70 medium ExpandHostSecrets and SetRecoveryLockFailed silently no-op instead of erroring in multi-store dispatcher server/mdm/nanomdm/storage/allmulti/allmulti.go:108
60 🟡 75 medium Package-level cachedCVEFeeds map used without synchronization, risking data races under concurrent VulnCheck sync server/vulnerabilities/nvd/sync/cve_syncer.go:260
61 🔴 55 low — review closely Legacy feed year clamping logic duplicated verbatim between updateYearFile and updateVulnCheckYearFile server/vulnerabilities/nvd/sync/cve_syncer.go:204
62 🟢 90 high writeLastModStartDateFile propagates raw error from parseAndFormatForNVD without wrapping server/vulnerabilities/nvd/sync/cve_syncer.go:333
63 🟡 80 medium needsUpdate() only checks Etag file, not the actual presence of the data file itself server/vulnerabilities/nvd/tools/providers/nvd/cpe.go:169
64 🔴 35 low — review closely cpe.go Sync uses non-atomic rename-swap that can leave data file missing on crash between renames server/vulnerabilities/nvd/tools/providers/nvd/cpe.go:157
65 🟡 85 medium cpe.go download() ignores non-200 error body / does not clean up temp file on later errors within Sync server/vulnerabilities/nvd/tools/providers/nvd/cpe.go:201

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: 3ba0fd37-c226-436b-ba81-b544a7d0a515

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.

ClickUp task: CU-86akbhhtv FleetMDM bulk review findings sweep (10 PRs)

flamingo Bot added 30 commits September 7, 2026 07:48

@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

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

Comment on lines 82 to 91
log.Fatalf("host count (%d) must match expected team count (%d)", len(hosts), *teamCount)
}

if *teamExtraCount > len(hosts) {
log.Fatalf("team_extra_count (%d) exceeds available hosts (%d)", *teamExtraCount, len(hosts))
}

printfAndPrompt("1. Creating %d teams...", *teamCount)
start := time.Now()

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.

🦩 🔴 loadtest.go uses uninitialized index t against hosts slice in loop bounded by teamExtraCount, risking index-out-of-range panic

Added a validation check if *teamExtraCount > len(hosts) { log.Fatalf(...) } immediately after the existing if len(hosts) != *teamCount check in main(), before any teams/hosts processing begins. This ensures the program fails fast with a clear error message instead of panicking later with an index-out-of-range error in the extra-teams loop (for t := 0; t < *teamExtraCount; t++ { ... hosts[t] ... }), matching the suggested fix exactly.

🤖 Prompt for AI agents
In tools/mdm/apple/loadtest/loadtest.go around line 204, review and complete this code-review fix: loadtest.go uses uninitialized index t against hosts slice in loop bounded by teamExtraCount, risking index-out-of-range panic.
What the draft fix changed: Added a validation check `if *teamExtraCount > len(hosts) { log.Fatalf(...) }` immediately after the existing `if len(hosts) != *teamCount` check in `main()`, before any teams/hosts processing begins. This ensures the program fails fast with a clear error message instead of panicking later with an index-out-of-range error in the extra-teams loop (`for t := 0; t < *teamExtraCount; t++ { ... hosts[t] ... }`), matching the suggested fix exactly.
Verify the change is correct and complete; do not refactor unrelated code.

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

Comment thread cmd/fleet/cron_test.go
for i := range 3 {
osqueryHostID := fmt.Sprintf("idp-cron-%d", i)
nodeKey := fmt.Sprintf("idp-cron-%d", i)
h, err := ds.NewHost(ctx, &fleet.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.

🦩 🔴 cron_test.go calls new(...) as if it were a helper function for creating pointers, but this is not valid Go without a locally-defined helper

In TestHostVitalsLabelMembershipCronIDP, replaced all invalid new(value) calls with standard Go pointer idioms: introduced local variables (osqueryHostID, nodeKey, active, vital, value, criteriaRawMessage) and took their addresses (&osqueryHostID, etc.) for the OsqueryHostID, NodeKey, Active, Vital, Value, and HostVitalsCriteria struct fields, so the file now compiles without relying on any nonexistent generic new[T any](v T) *T helper. Risk: since no such helper is defined anywhere in the visible file, this assumes the original intent was simple pointer-to-value construction identical to what a ptr.String/ptr.Bool-style helper would produce; the reviewer should confirm the field types (*string, *bool, *json.RawMessage) match, and check whether the project has a conventional ptr package that should be used instead for consistency with the rest of the codebase.

🤖 Prompt for AI agents
In cmd/fleet/cron_test.go around line 363, review and complete this code-review fix: cron_test.go calls new(...) as if it were a helper function for creating pointers, but this is not valid Go without a locally-defined helper.
What the draft fix changed: In `TestHostVitalsLabelMembershipCronIDP`, replaced all invalid `new(value)` calls with standard Go pointer idioms: introduced local variables (`osqueryHostID`, `nodeKey`, `active`, `vital`, `value`, `criteriaRawMessage`) and took their addresses (`&osqueryHostID`, etc.) for the `OsqueryHostID`, `NodeKey`, `Active`, `Vital`, `Value`, and `HostVitalsCriteria` struct fields, so the file now compiles without relying on any nonexistent generic `new[T any](v T) *T` helper. Risk: since no such helper is defined anywhere in the visible file, this assumes the original intent was simple pointer-to-value construction identical to what a `ptr.String`/`ptr.Bool`-style helper would produce; the reviewer should confirm the field types (`*string`, `*bool`, `*json.RawMessage`) match, and check whether the project has a conventional `ptr` package that should be used instead for consistency with the rest of the codebase.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 80 medium — react 👍/👎 to teach the reviewer

@@ -28,7 +28,9 @@ const DeleteEntraClientIdModal = ({

try {
const currentClientIds = config?.mdm.windows_entra_client_ids ?? [];

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.

🦩 🔴 Entra client ID de-duplication normalizes input to lowercase but does not validate case-insensitive duplicates against the raw stored value consistently on delete

Changed the filter predicate in onDeleteClientId (DeleteEntraClientIdModal component) from strict equality id !== clientId to case-insensitive comparison id.toLowerCase() !== clientId.toLowerCase(), ensuring that a stored client ID differing only in case from the passed-in clientId prop is still matched and removed, consistent with the case-insensitive duplicate check used in AddEntraClientIDModal.

🤖 Prompt for AI agents
In frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/DeleteEntraClientIDModal/DeleteEntraClientIDModal.tsx around line 30, review and complete this code-review fix: Entra client ID de-duplication normalizes input to lowercase but does not validate case-insensitive duplicates against the raw stored value consistently on delete.
What the draft fix changed: Changed the filter predicate in `onDeleteClientId` (DeleteEntraClientIdModal component) from strict equality `id !== clientId` to case-insensitive comparison `id.toLowerCase() !== clientId.toLowerCase()`, ensuring that a stored client ID differing only in case from the passed-in `clientId` prop is still matched and removed, consistent with the case-insensitive duplicate check used in AddEntraClientIDModal.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer

@@ -30,6 +30,7 @@ const ApiOnlyUser = ({ router }: IApiOnlyUserProps): JSX.Element => {
}
} catch (response) {

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.

🦩 🔴 console.error used to swallow fetch-current-user failure instead of surfacing to the user

In the fetchCurrentUser function's catch block inside the useEffect hook, added router.push(LOGIN) after the existing console.error(response) call, so that a fetch failure (e.g., network error or unhandled 401) now redirects the user to the LOGIN page instead of leaving them stuck on the 'Access denied' page, matching the behavior of the !user branch.

🤖 Prompt for AI agents
In frontend/pages/ApiOnlyUser/ApiOnlyUser.tsx around line 31, review and complete this code-review fix: console.error used to swallow fetch-current-user failure instead of surfacing to the user.
What the draft fix changed: In the `fetchCurrentUser` function's `catch` block inside the `useEffect` hook, added `router.push(LOGIN)` after the existing `console.error(response)` call, so that a fetch failure (e.g., network error or unhandled 401) now redirects the user to the LOGIN page instead of leaving them stuck on the 'Access denied' page, matching the behavior of the `!user` branch.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 80 medium — react 👍/👎 to teach the reviewer

e.preventDefault();

setIsUpdating(true);
const canLockEndUserInfo =

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.

🦩 🔴 UsersForm resets lockEndUserInfo to computed canLockEndUserInfo after save even when Apple MDM is not configured

In onSubmit, replaced the unconditional canLockEndUserInfo = formData.endUserAuthEnabled && formData.lockEndUserInfo computation with lockEndUserInfoToSend, which only applies that collapsing logic when isMacMdmEnabledAndConfigured is true; otherwise it passes through formData.lockEndUserInfo unchanged (preserving the backend-derived value when Apple MDM isn't configured, matching the read-only-field semantics already established in onEndUserAuthChange). The payload's lock_end_user_info field and the post-save setFormData sync now both use this same value, and since that field is only included in the payload when isMacMdmEnabledAndConfigured is true anyway, behavior for the Apple-MDM-configured path is unchanged while the non-configured path no longer silently corrupts formData.lockEndUserInfo.

🤖 Prompt for AI agents
In frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tsx around line 102, review and complete this code-review fix: UsersForm resets lockEndUserInfo to computed canLockEndUserInfo after save even when Apple MDM is not configured.
What the draft fix changed: In `onSubmit`, replaced the unconditional `canLockEndUserInfo = formData.endUserAuthEnabled && formData.lockEndUserInfo` computation with `lockEndUserInfoToSend`, which only applies that collapsing logic when `isMacMdmEnabledAndConfigured` is true; otherwise it passes through `formData.lockEndUserInfo` unchanged (preserving the backend-derived value when Apple MDM isn't configured, matching the read-only-field semantics already established in `onEndUserAuthChange`). The payload's `lock_end_user_info` field and the post-save `setFormData` sync now both use this same value, and since that field is only included in the payload when `isMacMdmEnabledAndConfigured` is true anyway, behavior for the Apple-MDM-configured path is unchanged while the non-configured path no longer silently corrupts `formData.lockEndUserInfo`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer

return nil
}

func (s *CVE) updateYearFile(ctx context.Context, year int, cves []nvdapi.CVEItem) 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.

🦩 🟠 Legacy feed year clamping logic duplicated verbatim between updateYearFile and updateVulnCheckYearFile

Extracted the duplicated "clamp year to 2002" logic into a new small helper legacyFeedYear(year int) int, used by both updateYearFile and updateVulnCheckYearFile, so future changes to the year floor only need to be made in one place. The finding also mentions broader duplication in the read/convert/merge/store pattern between the two functions, which was NOT deduplicated (doing so would be a larger structural refactor touching both functions' distinct merge semantics — VulnCheck merge preserves existing configurations and tracks mod/add counts differently than the plain NVD merge). This is a partial fix: it resolves the specific "year < 2002" duplication cited in the evidence but leaves the larger duplicated read/merge/store pattern as-is, which the finding also flags as a risk.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/sync/cve_syncer.go around line 204, review and complete this code-review fix: Legacy feed year clamping logic duplicated verbatim between updateYearFile and updateVulnCheckYearFile.
What the draft fix changed: Extracted the duplicated "clamp year to 2002" logic into a new small helper `legacyFeedYear(year int) int`, used by both `updateYearFile` and `updateVulnCheckYearFile`, so future changes to the year floor only need to be made in one place. The finding also mentions broader duplication in the read/convert/merge/store pattern between the two functions, which was NOT deduplicated (doing so would be a larger structural refactor touching both functions' distinct merge semantics — VulnCheck merge preserves existing configurations and tracks mod/add counts differently than the plain NVD merge). This is a partial fix: it resolves the specific "year < 2002" duplication cited in the evidence but leaves the larger duplicated read/merge/store pattern as-is, which the finding also flags as a risk.
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

}

// writeLastModStartDateFile writes the lastModStartDate to a file in the local DB directory.
func (s *CVE) writeLastModStartDateFile(lastModStartDate string) 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.

🦩 🟠 writeLastModStartDateFile propagates raw error from parseAndFormatForNVD without wrapping

In writeLastModStartDateFile, changed return err to return fmt.Errorf("writeLastModStartDateFile: %w", err) when parseAndFormatForNVD fails, wrapping the error with call-site context per the cited convention, using %w to preserve error chain unwrapping.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/sync/cve_syncer.go around line 333, review and complete this code-review fix: writeLastModStartDateFile propagates raw error from parseAndFormatForNVD without wrapping.
What the draft fix changed: In `writeLastModStartDateFile`, changed `return err` to `return fmt.Errorf("writeLastModStartDateFile: %w", err)` when `parseAndFormatForNVD` fails, wrapping the error with call-site context per the cited convention, using `%w` to preserve error chain unwrapping.
Verify the change is correct and complete; do not refactor unrelated code.

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

@@ -168,6 +168,10 @@ func (cf cpeFile) Sync(ctx context.Context, src SourceConfig, localdir string) e

func (cf cpeFile) needsUpdate(ctx context.Context, targetURL, localdir string) (bool, 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.

🦩 🟠 needsUpdate() only checks Etag file, not the actual presence of the data file itself

In needsUpdate() (cpe.go), added an os.Stat check on filepath.Join(localdir, cf.DataFile) at the top of the function; if the data file is missing, the function now immediately returns (true, nil) to force a re-sync, instead of relying solely on the etag file comparison. This directly addresses the finding that a matching etag with a missing/deleted data file previously caused needsUpdate to wrongly report no update needed.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/tools/providers/nvd/cpe.go around line 169, review and complete this code-review fix: needsUpdate() only checks Etag file, not the actual presence of the data file itself.
What the draft fix changed: In needsUpdate() (cpe.go), added an os.Stat check on filepath.Join(localdir, cf.DataFile) at the top of the function; if the data file is missing, the function now immediately returns (true, nil) to force a re-sync, instead of relying solely on the etag file comparison. This directly addresses the finding that a matching etag with a missing/deleted data file previously caused needsUpdate to wrongly report no update needed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 80 medium — react 👍/👎 to teach the reviewer

Comment on lines 168 to 177

func (cf cpeFile) needsUpdate(ctx context.Context, targetURL, localdir string) (bool, error) {
flog.V(1).Infof("checking etag for %q", targetURL)
if _, err := os.Stat(filepath.Join(localdir, cf.DataFile)); err != nil {
flog.V(1).Infof("data file %q does not exist in %q, needs sync", cf.DataFile, localdir)
return true, nil
}
req, err := httpNewRequestContext(ctx, "HEAD", targetURL)
if err != nil {
return false, 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.

🦩 🟠 cpe.go Sync uses non-atomic rename-swap that can leave data file missing on crash between renames

No rename/recovery logic was changed in Sync(); this finding is only partially mitigated as a side effect of fix #1 (needsUpdate now detects a missing data file and forces a re-download on the next run, so the crash window no longer causes a permanent stale state). This does NOT make the rename-swap atomic and does NOT add a startup check to recover from a stray .bak file — a complete fix would require detecting/restoring the .bak file explicitly (e.g., at the start of Sync, if dataFilename is missing but bakDataFilename exists, rename it back) or using a truly atomic replace mechanism. Risk: between the two renames, the file is still briefly missing on disk, and any concurrent reader in that instant sees no file; only the next Sync invocation self-heals now, via needsUpdate's new existence check.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/tools/providers/nvd/cpe.go around line 157, review and complete this code-review fix: cpe.go Sync uses non-atomic rename-swap that can leave data file missing on crash between renames.
What the draft fix changed: No rename/recovery logic was changed in Sync(); this finding is only partially mitigated as a side effect of fix #1 (needsUpdate now detects a missing data file and forces a re-download on the next run, so the crash window no longer causes a *permanent* stale state). This does NOT make the rename-swap atomic and does NOT add a startup check to recover from a stray .bak file — a complete fix would require detecting/restoring the .bak file explicitly (e.g., at the start of Sync, if dataFilename is missing but bakDataFilename exists, rename it back) or using a truly atomic replace mechanism. Risk: between the two renames, the file is still briefly missing on disk, and any concurrent reader in that instant sees no file; only the next Sync invocation self-heals now, via needsUpdate's new existence check.
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

Comment on lines 220 to 226
if err != nil {
return "", "", err
}
defer dataFile.Close()
_, err = io.Copy(dataFile, resp.Body)
if err != nil {
return "", "", 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.

🦩 🔵 cpe.go download() ignores non-200 error body / does not clean up temp file on later errors within Sync

In download() (cpe.go), added defer dataFile.Close() immediately after the successful ioutil.TempFile call, ensuring the file descriptor is always closed on both the success path and the early-return error path when io.Copy fails, fixing the file descriptor leak. The partially-written temp file itself is still cleaned up by the caller's existing defer os.Remove(tempDataFilename) in Sync, which was already correct and untouched.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/tools/providers/nvd/cpe.go around line 201, review and complete this code-review fix: cpe.go download() ignores non-200 error body / does not clean up temp file on later errors within Sync.
What the draft fix changed: In download() (cpe.go), added `defer dataFile.Close()` immediately after the successful ioutil.TempFile call, ensuring the file descriptor is always closed on both the success path and the early-return error path when io.Copy fails, fixing the file descriptor leak. The partially-written temp file itself is still cleaned up by the caller's existing `defer os.Remove(tempDataFilename)` in Sync, which was already correct and untouched.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer

@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

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

Comment on lines 82 to 91
log.Fatalf("host count (%d) must match expected team count (%d)", len(hosts), *teamCount)
}

if *teamExtraCount > len(hosts) {
log.Fatalf("team_extra_count (%d) exceeds available hosts (%d)", *teamExtraCount, len(hosts))
}

printfAndPrompt("1. Creating %d teams...", *teamCount)
start := time.Now()

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.

🦩 🔴 loadtest.go uses uninitialized index t against hosts slice in loop bounded by teamExtraCount, risking index-out-of-range panic

Added a validation check if *teamExtraCount > len(hosts) { log.Fatalf(...) } immediately after the existing if len(hosts) != *teamCount check in main(), before any teams/hosts processing begins. This ensures the program fails fast with a clear error message instead of panicking later with an index-out-of-range error in the extra-teams loop (for t := 0; t < *teamExtraCount; t++ { ... hosts[t] ... }), matching the suggested fix exactly.

🤖 Prompt for AI agents
In tools/mdm/apple/loadtest/loadtest.go around line 204, review and complete this code-review fix: loadtest.go uses uninitialized index t against hosts slice in loop bounded by teamExtraCount, risking index-out-of-range panic.
What the draft fix changed: Added a validation check `if *teamExtraCount > len(hosts) { log.Fatalf(...) }` immediately after the existing `if len(hosts) != *teamCount` check in `main()`, before any teams/hosts processing begins. This ensures the program fails fast with a clear error message instead of panicking later with an index-out-of-range error in the extra-teams loop (`for t := 0; t < *teamExtraCount; t++ { ... hosts[t] ... }`), matching the suggested fix exactly.
Verify the change is correct and complete; do not refactor unrelated code.

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

Comment thread cmd/fleet/cron_test.go
for i := range 3 {
osqueryHostID := fmt.Sprintf("idp-cron-%d", i)
nodeKey := fmt.Sprintf("idp-cron-%d", i)
h, err := ds.NewHost(ctx, &fleet.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.

🦩 🔴 cron_test.go calls new(...) as if it were a helper function for creating pointers, but this is not valid Go without a locally-defined helper

In TestHostVitalsLabelMembershipCronIDP, replaced all invalid new(value) calls with standard Go pointer idioms: introduced local variables (osqueryHostID, nodeKey, active, vital, value, criteriaRawMessage) and took their addresses (&osqueryHostID, etc.) for the OsqueryHostID, NodeKey, Active, Vital, Value, and HostVitalsCriteria struct fields, so the file now compiles without relying on any nonexistent generic new[T any](v T) *T helper. Risk: since no such helper is defined anywhere in the visible file, this assumes the original intent was simple pointer-to-value construction identical to what a ptr.String/ptr.Bool-style helper would produce; the reviewer should confirm the field types (*string, *bool, *json.RawMessage) match, and check whether the project has a conventional ptr package that should be used instead for consistency with the rest of the codebase.

🤖 Prompt for AI agents
In cmd/fleet/cron_test.go around line 363, review and complete this code-review fix: cron_test.go calls new(...) as if it were a helper function for creating pointers, but this is not valid Go without a locally-defined helper.
What the draft fix changed: In `TestHostVitalsLabelMembershipCronIDP`, replaced all invalid `new(value)` calls with standard Go pointer idioms: introduced local variables (`osqueryHostID`, `nodeKey`, `active`, `vital`, `value`, `criteriaRawMessage`) and took their addresses (`&osqueryHostID`, etc.) for the `OsqueryHostID`, `NodeKey`, `Active`, `Vital`, `Value`, and `HostVitalsCriteria` struct fields, so the file now compiles without relying on any nonexistent generic `new[T any](v T) *T` helper. Risk: since no such helper is defined anywhere in the visible file, this assumes the original intent was simple pointer-to-value construction identical to what a `ptr.String`/`ptr.Bool`-style helper would produce; the reviewer should confirm the field types (`*string`, `*bool`, `*json.RawMessage`) match, and check whether the project has a conventional `ptr` package that should be used instead for consistency with the rest of the codebase.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 80 medium — react 👍/👎 to teach the reviewer

@@ -28,7 +28,9 @@ const DeleteEntraClientIdModal = ({

try {
const currentClientIds = config?.mdm.windows_entra_client_ids ?? [];

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.

🦩 🔴 Entra client ID de-duplication normalizes input to lowercase but does not validate case-insensitive duplicates against the raw stored value consistently on delete

Changed the filter predicate in onDeleteClientId (DeleteEntraClientIdModal component) from strict equality id !== clientId to case-insensitive comparison id.toLowerCase() !== clientId.toLowerCase(), ensuring that a stored client ID differing only in case from the passed-in clientId prop is still matched and removed, consistent with the case-insensitive duplicate check used in AddEntraClientIDModal.

🤖 Prompt for AI agents
In frontend/pages/admin/IntegrationsPage/cards/MdmSettings/components/DeleteEntraClientIDModal/DeleteEntraClientIDModal.tsx around line 30, review and complete this code-review fix: Entra client ID de-duplication normalizes input to lowercase but does not validate case-insensitive duplicates against the raw stored value consistently on delete.
What the draft fix changed: Changed the filter predicate in `onDeleteClientId` (DeleteEntraClientIdModal component) from strict equality `id !== clientId` to case-insensitive comparison `id.toLowerCase() !== clientId.toLowerCase()`, ensuring that a stored client ID differing only in case from the passed-in `clientId` prop is still matched and removed, consistent with the case-insensitive duplicate check used in AddEntraClientIDModal.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer

@@ -30,6 +30,7 @@ const ApiOnlyUser = ({ router }: IApiOnlyUserProps): JSX.Element => {
}
} catch (response) {

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.

🦩 🔴 console.error used to swallow fetch-current-user failure instead of surfacing to the user

In the fetchCurrentUser function's catch block inside the useEffect hook, added router.push(LOGIN) after the existing console.error(response) call, so that a fetch failure (e.g., network error or unhandled 401) now redirects the user to the LOGIN page instead of leaving them stuck on the 'Access denied' page, matching the behavior of the !user branch.

🤖 Prompt for AI agents
In frontend/pages/ApiOnlyUser/ApiOnlyUser.tsx around line 31, review and complete this code-review fix: console.error used to swallow fetch-current-user failure instead of surfacing to the user.
What the draft fix changed: In the `fetchCurrentUser` function's `catch` block inside the `useEffect` hook, added `router.push(LOGIN)` after the existing `console.error(response)` call, so that a fetch failure (e.g., network error or unhandled 401) now redirects the user to the LOGIN page instead of leaving them stuck on the 'Access denied' page, matching the behavior of the `!user` branch.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 80 medium — react 👍/👎 to teach the reviewer

e.preventDefault();

setIsUpdating(true);
const canLockEndUserInfo =

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.

🦩 🔴 UsersForm resets lockEndUserInfo to computed canLockEndUserInfo after save even when Apple MDM is not configured

In onSubmit, replaced the unconditional canLockEndUserInfo = formData.endUserAuthEnabled && formData.lockEndUserInfo computation with lockEndUserInfoToSend, which only applies that collapsing logic when isMacMdmEnabledAndConfigured is true; otherwise it passes through formData.lockEndUserInfo unchanged (preserving the backend-derived value when Apple MDM isn't configured, matching the read-only-field semantics already established in onEndUserAuthChange). The payload's lock_end_user_info field and the post-save setFormData sync now both use this same value, and since that field is only included in the payload when isMacMdmEnabledAndConfigured is true anyway, behavior for the Apple-MDM-configured path is unchanged while the non-configured path no longer silently corrupts formData.lockEndUserInfo.

🤖 Prompt for AI agents
In frontend/pages/ManageControlsPage/SetupExperience/cards/Users/components/UsersForm/UsersForm.tsx around line 102, review and complete this code-review fix: UsersForm resets lockEndUserInfo to computed canLockEndUserInfo after save even when Apple MDM is not configured.
What the draft fix changed: In `onSubmit`, replaced the unconditional `canLockEndUserInfo = formData.endUserAuthEnabled && formData.lockEndUserInfo` computation with `lockEndUserInfoToSend`, which only applies that collapsing logic when `isMacMdmEnabledAndConfigured` is true; otherwise it passes through `formData.lockEndUserInfo` unchanged (preserving the backend-derived value when Apple MDM isn't configured, matching the read-only-field semantics already established in `onEndUserAuthChange`). The payload's `lock_end_user_info` field and the post-save `setFormData` sync now both use this same value, and since that field is only included in the payload when `isMacMdmEnabledAndConfigured` is true anyway, behavior for the Apple-MDM-configured path is unchanged while the non-configured path no longer silently corrupts `formData.lockEndUserInfo`.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer

return nil
}

func (s *CVE) updateYearFile(ctx context.Context, year int, cves []nvdapi.CVEItem) 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.

🦩 🟠 Legacy feed year clamping logic duplicated verbatim between updateYearFile and updateVulnCheckYearFile

Extracted the duplicated "clamp year to 2002" logic into a new small helper legacyFeedYear(year int) int, used by both updateYearFile and updateVulnCheckYearFile, so future changes to the year floor only need to be made in one place. The finding also mentions broader duplication in the read/convert/merge/store pattern between the two functions, which was NOT deduplicated (doing so would be a larger structural refactor touching both functions' distinct merge semantics — VulnCheck merge preserves existing configurations and tracks mod/add counts differently than the plain NVD merge). This is a partial fix: it resolves the specific "year < 2002" duplication cited in the evidence but leaves the larger duplicated read/merge/store pattern as-is, which the finding also flags as a risk.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/sync/cve_syncer.go around line 204, review and complete this code-review fix: Legacy feed year clamping logic duplicated verbatim between updateYearFile and updateVulnCheckYearFile.
What the draft fix changed: Extracted the duplicated "clamp year to 2002" logic into a new small helper `legacyFeedYear(year int) int`, used by both `updateYearFile` and `updateVulnCheckYearFile`, so future changes to the year floor only need to be made in one place. The finding also mentions broader duplication in the read/convert/merge/store pattern between the two functions, which was NOT deduplicated (doing so would be a larger structural refactor touching both functions' distinct merge semantics — VulnCheck merge preserves existing configurations and tracks mod/add counts differently than the plain NVD merge). This is a partial fix: it resolves the specific "year < 2002" duplication cited in the evidence but leaves the larger duplicated read/merge/store pattern as-is, which the finding also flags as a risk.
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

}

// writeLastModStartDateFile writes the lastModStartDate to a file in the local DB directory.
func (s *CVE) writeLastModStartDateFile(lastModStartDate string) 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.

🦩 🟠 writeLastModStartDateFile propagates raw error from parseAndFormatForNVD without wrapping

In writeLastModStartDateFile, changed return err to return fmt.Errorf("writeLastModStartDateFile: %w", err) when parseAndFormatForNVD fails, wrapping the error with call-site context per the cited convention, using %w to preserve error chain unwrapping.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/sync/cve_syncer.go around line 333, review and complete this code-review fix: writeLastModStartDateFile propagates raw error from parseAndFormatForNVD without wrapping.
What the draft fix changed: In `writeLastModStartDateFile`, changed `return err` to `return fmt.Errorf("writeLastModStartDateFile: %w", err)` when `parseAndFormatForNVD` fails, wrapping the error with call-site context per the cited convention, using `%w` to preserve error chain unwrapping.
Verify the change is correct and complete; do not refactor unrelated code.

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

@@ -168,6 +168,10 @@ func (cf cpeFile) Sync(ctx context.Context, src SourceConfig, localdir string) e

func (cf cpeFile) needsUpdate(ctx context.Context, targetURL, localdir string) (bool, 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.

🦩 🟠 needsUpdate() only checks Etag file, not the actual presence of the data file itself

In needsUpdate() (cpe.go), added an os.Stat check on filepath.Join(localdir, cf.DataFile) at the top of the function; if the data file is missing, the function now immediately returns (true, nil) to force a re-sync, instead of relying solely on the etag file comparison. This directly addresses the finding that a matching etag with a missing/deleted data file previously caused needsUpdate to wrongly report no update needed.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/tools/providers/nvd/cpe.go around line 169, review and complete this code-review fix: needsUpdate() only checks Etag file, not the actual presence of the data file itself.
What the draft fix changed: In needsUpdate() (cpe.go), added an os.Stat check on filepath.Join(localdir, cf.DataFile) at the top of the function; if the data file is missing, the function now immediately returns (true, nil) to force a re-sync, instead of relying solely on the etag file comparison. This directly addresses the finding that a matching etag with a missing/deleted data file previously caused needsUpdate to wrongly report no update needed.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 80 medium — react 👍/👎 to teach the reviewer

Comment on lines 168 to 177

func (cf cpeFile) needsUpdate(ctx context.Context, targetURL, localdir string) (bool, error) {
flog.V(1).Infof("checking etag for %q", targetURL)
if _, err := os.Stat(filepath.Join(localdir, cf.DataFile)); err != nil {
flog.V(1).Infof("data file %q does not exist in %q, needs sync", cf.DataFile, localdir)
return true, nil
}
req, err := httpNewRequestContext(ctx, "HEAD", targetURL)
if err != nil {
return false, 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.

🦩 🟠 cpe.go Sync uses non-atomic rename-swap that can leave data file missing on crash between renames

No rename/recovery logic was changed in Sync(); this finding is only partially mitigated as a side effect of fix #1 (needsUpdate now detects a missing data file and forces a re-download on the next run, so the crash window no longer causes a permanent stale state). This does NOT make the rename-swap atomic and does NOT add a startup check to recover from a stray .bak file — a complete fix would require detecting/restoring the .bak file explicitly (e.g., at the start of Sync, if dataFilename is missing but bakDataFilename exists, rename it back) or using a truly atomic replace mechanism. Risk: between the two renames, the file is still briefly missing on disk, and any concurrent reader in that instant sees no file; only the next Sync invocation self-heals now, via needsUpdate's new existence check.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/tools/providers/nvd/cpe.go around line 157, review and complete this code-review fix: cpe.go Sync uses non-atomic rename-swap that can leave data file missing on crash between renames.
What the draft fix changed: No rename/recovery logic was changed in Sync(); this finding is only partially mitigated as a side effect of fix #1 (needsUpdate now detects a missing data file and forces a re-download on the next run, so the crash window no longer causes a *permanent* stale state). This does NOT make the rename-swap atomic and does NOT add a startup check to recover from a stray .bak file — a complete fix would require detecting/restoring the .bak file explicitly (e.g., at the start of Sync, if dataFilename is missing but bakDataFilename exists, rename it back) or using a truly atomic replace mechanism. Risk: between the two renames, the file is still briefly missing on disk, and any concurrent reader in that instant sees no file; only the next Sync invocation self-heals now, via needsUpdate's new existence check.
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

Comment on lines 220 to 226
if err != nil {
return "", "", err
}
defer dataFile.Close()
_, err = io.Copy(dataFile, resp.Body)
if err != nil {
return "", "", 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.

🦩 🔵 cpe.go download() ignores non-200 error body / does not clean up temp file on later errors within Sync

In download() (cpe.go), added defer dataFile.Close() immediately after the successful ioutil.TempFile call, ensuring the file descriptor is always closed on both the success path and the early-return error path when io.Copy fails, fixing the file descriptor leak. The partially-written temp file itself is still cleaned up by the caller's existing defer os.Remove(tempDataFilename) in Sync, which was already correct and untouched.

🤖 Prompt for AI agents
In server/vulnerabilities/nvd/tools/providers/nvd/cpe.go around line 201, review and complete this code-review fix: cpe.go download() ignores non-200 error body / does not clean up temp file on later errors within Sync.
What the draft fix changed: In download() (cpe.go), added `defer dataFile.Close()` immediately after the successful ioutil.TempFile call, ensuring the file descriptor is always closed on both the success path and the early-return error path when io.Copy fails, fixing the file descriptor leak. The partially-written temp file itself is still cleaned up by the caller's existing `defer os.Remove(tempDataFilename)` in Sync, which was already correct and untouched.
Verify the change is correct and complete; do not refactor unrelated code.

fix confidence: 🟡 85 medium — react 👍/👎 to teach the reviewer

@flamingo flamingo Bot changed the title fix(adhoc-sweep-fixes): 66 review findings across 40 files fix(adhoc-sweep-fixes): CU-86akbhhtv 66 review findings across 40 files Sep 8, 2026
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