Skip to content

fix(aws/recommendations): reject NaN/Inf in SP money parsing (follow-up to #1455) - #1461

Merged
cristim merged 1 commit into
mainfrom
fix/1455-followup-nan-inf-money
Jul 19, 2026
Merged

cristim merged 1 commit into
mainfrom
fix/1455-followup-nan-inf-money

Conversation

@cristim

@cristim cristim commented Jul 19, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #1455 — two unaddressed CodeRabbit Major threads

Part of the adversarial-sweep over recently-merged PRs. #1455 merged with two unresolved CodeRabbit Major threads on parser_sp.go, both money-integrity.

parseOptionalFloat used strconv.ParseFloat, which accepts "NaN", "Inf", "+Inf", "-Inf" (and Infinity variants) as valid with a nil error. A Cost Explorer money field carrying one of these flowed through as a corrupt non-finite value into the purchase money fields (HourlyCommitmentToPurchase, EstimatedMonthlySavingsAmount, UpfrontCost) and into the OnDemandCost baseline.

Fix (root cause, one guard)

Reject non-finite values at the parse boundary (math.IsNaN / math.IsInf -> error):

  • Money fields (thread @ parser_sp.go:203): NaN/Inf now error -> the recommendation is dropped; no corrupt figure reaches the scheduler/frontend.
  • OnDemandCost (thread @ parser_sp.go:308): a non-finite CurrentAverageHourlyOnDemandSpend previously survived parseOptionalFloatOrWarn as NaN; it now degrades to 0 -> nil (frontend reconstruction), matching the documented "unavailable" path.

Verification

  • Extended TestParseSavingsPlanDetail_MoneyFieldUnparseable with NaN/+Inf/-Inf money cases + new TestParseOptionalFloat_RejectsNonFinite.
  • Verified both fail without the guard and pass with it.
  • go build ./..., go vet, full recommendations package (424 tests) green.

Summary by CodeRabbit

  • Bug Fixes

    • Invalid non-finite numeric values such as NaN and infinity are now rejected when processing AWS recommendation data.
    • Malformed financial values no longer pass through as valid recommendations.
  • Tests

    • Added coverage for non-finite and valid numeric input scenarios.

…up to #1455)

parseOptionalFloat used strconv.ParseFloat, which accepts "NaN", "Inf", "+Inf",
"-Inf" (and Infinity variants) as valid with a nil error. A Cost Explorer money
field carrying one of these would therefore flow straight through as a corrupt
non-finite value into the purchase money fields (HourlyCommitmentToPurchase,
EstimatedMonthlySavingsAmount, UpfrontCost) and, via parseOptionalFloatOrWarn,
into the OnDemandCost baseline. Reject non-finite values at the parse boundary
the same way as unparseable ones (math.IsNaN / math.IsInf -> error).

This is the root-cause fix for both unresolved CodeRabbit Major threads on #1455:
- money fields: NaN/Inf now error -> the recommendation is dropped, no corrupt
  figure reaches the scheduler/frontend.
- OnDemandCost (CurrentAverageHourlyOnDemandSpend): NaN previously survived
  parseOptionalFloatOrWarn as NaN; it now degrades to 0 -> nil (frontend
  reconstruction), matching the documented "unavailable" path.

Tests:
- Extended TestParseSavingsPlanDetail_MoneyFieldUnparseable with NaN/+Inf/-Inf
  money-field cases.
- New TestParseOptionalFloat_RejectsNonFinite unit guard.
Verified both FAIL without the guard and PASS with it.
@cristim cristim added triaged Item has been triaged priority/p1 Next up; this sprint severity/high Significant harm urgency/now Drop other things impact/all-users Affects every user effort/xs Trivial / one-liner type/bug Defect labels Jul 19, 2026
@cristim

cristim commented Jul 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 89258b76-d1d5-4629-983f-3dc51ba937b6

📥 Commits

Reviewing files that changed from the base of the PR and between 5c267c5 and 5742a87.

📒 Files selected for processing (2)
  • providers/aws/recommendations/parser_sp.go
  • providers/aws/recommendations/parser_sp_test.go

📝 Walkthrough

Walkthrough

Savings Plans float parsing now rejects NaN and infinite values. Tests cover invalid non-finite money fields, direct optional-float parsing, and continued acceptance of finite numbers.

Changes

Savings Plan float validation

Layer / File(s) Summary
Reject non-finite parsed values
providers/aws/recommendations/parser_sp.go, providers/aws/recommendations/parser_sp_test.go
parseOptionalFloat returns errors for NaN and infinite values, while Savings Plans parsing tests verify invalid money fields produce errors and no recommendation.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • LeanerCloud/CUDly#1346: Updates related Savings Plans numeric parsing to reject non-finite floats with corresponding tests.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1455-followup-nan-inf-money

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
cristim merged commit 0479b99 into main Jul 19, 2026
19 checks passed
@cristim

cristim commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

Addressed the Fable adversarial-review finding: the RI sibling parser (parseCostInformation, parseAWSCostDetails) had the same NaN/Inf gap and now routes through the shared guarded parseOptionalFloat (restoring genuine SP/RI parity). Also: parseOptionalFloat rejects negatives (matches parseSPFloat), and utilization.go parseFloat degrades non-finite to 0 so a NaN can't poison utilization aggregates. Added TestRICostParsers_RejectNonFiniteAndNegative (verified fails when reverted).

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

@cristim, reviewing the updated SP/RI parsing and utilization hardening now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

cristim added a commit that referenced this pull request Jul 20, 2026
…_ri, ondemand_series, RI-utilization (follow-up to #1461) (#1473)

* fix(aws/recommendations): close NaN/Inf/negative gap on the RI parser too

Addresses the Fable adversarial-review finding on this PR: the SP-path
non-finite guard did not fully close the bug class. The RI sibling parser
(parseCostInformation, parseAWSCostDetails) parsed the same CE money fields
(EstimatedMonthlySavingsAmount/Percentage, UpfrontCost,
EstimatedMonthlyOnDemandCost, RecurringStandardMonthlyCost) with bare
strconv.ParseFloat and no guard, so a "NaN"/"Inf" value still flowed into
CommitmentCost/OnDemandCost/savings/RecurringMonthlyCost. The doc comments also
claimed the SP path "mirrors the RI path", which was false after the first commit.

Changes:
- Route both RI money parsers through the shared parseOptionalFloat, restoring
  genuine SP/RI parity (comments now hold).
- parseOptionalFloat also rejects negative values (every field it parses is a
  non-negative financial metric; mirrors the repo's stricter parseSPFloat).
- utilization.go parseFloat degrades NaN/Inf to 0 with a warning, so a single
  non-finite hours value can't poison every downstream utilization aggregate.

Tests:
- New TestRICostParsers_RejectNonFiniteAndNegative (verified it fails when the
  RI routing is reverted).
- Extended TestParseOptionalFloat_RejectsNonFinite with negative cases + a
  zero-is-valid sanity check.
- Updated TestParseRecommendationDetail_MalformedCostFields error-string
  assertions to the now-unified "failed to parse <Field> ..." wording.

* fix(aws/recommendations): guard remaining non-finite float parses in package

Second round of the Fable adversarial review on this PR: it found the same
non-finite bug class still open at three more parse sites in the recommendations
package that the first two commits missed.

- ondemand_series.go accumulateDailyResults: a "NaN"/"Inf" CE daily amount
  parsed with a nil error and slipped past the downstream all-zero fail-loud
  check (NaN != 0), poisoning the ladder baseline the engine sizes purchases
  from. Now fails loud on non-finite. NO negative guard here -- CE unblended
  cost is legitimately negative on credit/refund days.
- parser_ri.go parseRIUtilizationSignals: NaN/Inf were stored into
  AverageInstancesUsedPerHour / RecommendedUtilization; the --target-coverage
  guards are all `<= 0` and NaN <= 0 is false, so a stored NaN was treated as a
  live signal and produced NaN purchase counts. Now routed through
  parseOptionalFloatOrWarn (warn + 0).
- parser_ri.go parseRecommendedQuantity: fmt.Sscanf %f also accepts NaN/Inf;
  a non-finite quantity would corrupt the purchase count. Fail loud.

Tests:
- ondemand_series_test.go: TestGetOnDemandSeries_NonFiniteAmountFails.
- parser_ri_test.go: non-finite-degrades-to-zero case in the utilization-signals table.
- parser_sp_test.go: TestUtilizationParseFloat_DegradesNonFinite for the
  utilization.go parseFloat guard added in the previous commit.

* fix(aws): guard non-finite/negative in ladder SP commitment + RI quantity (Fable follow-ups)

Addresses the Fable open-PR review findings on this PR:

- ladder/adapters.go mapActiveSP (MED, live money path): SavingsPlan.Commitment
  parsed with bare strconv.ParseFloat and no non-finite guard, despite the doc
  claiming it "fails loud on a non-numeric Commitment (money path)". A NaN/Inf
  commitment flowed through sumSPHourlyCost / sumExpiringSPHourlyCost into the
  ladder layer-state totals the engine sizes purchases from. Now rejects
  NaN/Inf/negative (a commitment is a non-negative money rate).
- parser_ri.go parseRecommendedQuantity (LOW): rejected non-finite but still
  accepted a negative purchase count ("-3" via Sscanf/Atoi). Now rejects negatives.
- parser_ri.go parseRIUtilizationSignals (LOW): the refactor dropped
  service/account context from the parse-failure warning; the field label now
  carries "service=.. account=.." so an operator can identify the corrupt row.

Regression tests: TestSPLister_NonFiniteOrNegativeCommitmentFails (verified fails
when the guard is reverted), plus NaN/Inf/negative cases in the quantity table.
cristim added a commit that referenced this pull request Jul 21, 2026
The prior commit's code comments cited "#1461" as the tracking issue
for the RI utilization filter bug. #1461 is an unrelated PR (NaN/Inf
rejection in SP money parsing). The bug this fix addresses was
introduced by PR #1361, per the review that surfaced it; correct all
comment references accordingly.
cristim added a commit that referenced this pull request Jul 22, 2026
The prior commit's code comments cited "#1461" as the tracking issue
for the RI utilization filter bug. #1461 is an unrelated PR (NaN/Inf
rejection in SP money parsing). The bug this fix addresses was
introduced by PR #1361, per the review that surfaced it; correct all
comment references accordingly.
cristim added a commit that referenced this pull request Jul 22, 2026
… decisions (follow-up to #1361) (#1479)

* fix(aws/ladder): scope RI utilization query to EC2+region for reshape decisions

GetRIUtilization built GetReservationUtilization with no Filter at all,
so it blended utilization across every reserved-resource type in the
account (RDS, ElastiCache, OpenSearch, Redshift, standard EC2 RIs) and
every region into one SUBSCRIPTION_ID-grouped number. The ladder
ConvertibleRI (buffer) layer reads this as if it were EC2-convertible
utilization for one account/region, then uses it to decide whether to
trigger a real RI reshape/exchange -- an underutilized RI in an
unrelated service or region could trigger a reshape, or mask a
genuinely poorly-utilized convertible RI.

Add a SERVICE+REGION Filter to the CE call, mirroring the same pattern
already used by GetRICoverageMap in coverage.go. Also intersect the
(now EC2+region-scoped) response against the account's own convertible
RI IDs before aggregating in GetLayerStates, since a standard
(non-convertible) EC2 RI in the same account/region would otherwise
still pass the SERVICE+REGION filter and blend into the layer's
UtilizationPct.

Threads a region parameter through GetRIUtilization's callers
(providers/aws/ladder, providers/aws/service_client.go,
internal/server, internal/api). Follow-up to #1361.

* fix(aws/ladder): correct comment reference from #1461 to #1361

The prior commit's code comments cited "#1461" as the tracking issue
for the RI utilization filter bug. #1461 is an unrelated PR (NaN/Inf
rejection in SP money parsing). The bug this fix addresses was
introduced by PR #1361, per the review that surfaced it; correct all
comment references accordingly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/xs Trivial / one-liner impact/all-users Affects every user priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/bug Defect urgency/now Drop other things

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant