Skip to content

fix(azure): a reservation with nil purchaseDate bypasses the duplicate-purchase guard #454

Closed

Description

@cristim

Problem

providers/azure/services/internal/reservations/inventory.go (~:266, as of #453) fills StartDate from PurchaseDate. When Azure returns a reservation with a nil PurchaseDate, StartDate stays zero and StartDateIsDay stays false. The duplicate guard (pkg/recfilter/dedupe.go:116-119) then treats the reservation as ancient, so it does not suppress a re-purchase.

This fails open on a money path. Azure normally always returns purchaseDate, so the risk is low, but the guard should not depend on that.

Expected

A reservation with no purchase date is treated as possibly recent, so the guard suppresses a duplicate. The alternative is an error that stops the purchase, never a silent skip.

Evidence

Found during the independent review of #453 (a non-blocking note, and not introduced by #453).

Fix sketch

When PurchaseDate is nil, either set StartDate to now with StartDateIsDay set (counts as recent), or return an error from populateCommitmentFields. Add a regression test for a nil PurchaseDate that must suppress the duplicate.

Activity

  1. self-assigned this
    on Oct 16, 2026
  2. cristim commented on Oct 16, 2026

    @cristim
    MemberAuthor

    Claiming: working on this.

  3. cristim commented on Oct 16, 2026

    @cristim
    MemberAuthor

    Checked against main: the production path already fails loud.

    What remains is that the exported, lenient CommitmentFromReservation (inventory.go:266-270) still returns a live commitment with a zero start if a future caller uses it directly. Proposed rephrase: "make CommitmentFromReservation itself reject a live row without purchaseDate, and drop the duplicate check in strict.go" (provider-only, about 100 lines), or close as fixed by #317/#328. Owner's call; marking status/needs-info and releasing my claim meanwhile.

    The same fail-open does exist for Azure savings plans: filed as #481 (the AWS savings-plan variant is in #53).

  4. removed their assignment
    on Oct 16, 2026
  5. cristim commented on Oct 20, 2026

    @cristim
    MemberAuthor

    Verified on current main. The only production caller of CommitmentFromReservation is StrictCommitment (providers/azure/services/internal/reservations/strict.go:32, used by compute GetExistingCommitments at compute/client.go:299), and validateLiveReservationFields errors on a nil purchaseDate for a live row (strict.go:91-93) so inventory fails and the purchase is stopped; the SDK-JSON test 'purchaseDate nil' pins it (compute/inventory_completeness_sdk_test.go:61, #317/#328). The exported lenient CommitmentFromReservation (inventory.go:232) still leaves a zero StartDate if a future caller bypasses StrictCommitment; that is a hardening option, not the reported bug. [claude-cc-libs]

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions