Summary
Purchase repricing, scheduler IDs and stored recommendation identity omit purchase-specific detail fields. A term/payment change can therefore resolve to a recommendation with a different purchase configuration, such as EC2 tenancy, while preserving the visible account/service/region/instance-type tuple.
Current behavior and evidence
Source inspected at main 5405fd1e90dffdb701a46d4b93868b947e1fbf05 while reviewing LeanerCloud/cloud-commitments-cli#2071. internal/api/purchase_pricing.go:96 keys stored prices by provider, account, service, region, resource type, engine, term and payment. It omits detail fields; priceFromStored then copies stored details into the request.
AWS term/payment combinations are collected independently (providers/aws/recommendations/client.go:310). EC2 details carry platform, tenancy and scope from each Cost Explorer response (providers/aws/recommendations/parser_services.go:159). Rows with different terms and tenancy can coexist because term/payment are in the stored unique key. The EC2 offering query consumes tenancy (providers/aws/services/ec2/client.go:400).
An independent Astra in-memory frontend probe on both base and LeanerCloud/cloud-commitments-cli#2071 candidate used a 3-year/default-tenancy row and a 1-year/dedicated-tenancy row sharing the other cell fields. The candidate directly selected the dedicated sibling. The base retained default tenancy in its submitted details, but the source-traced backend lookup resolves that changed term to the stored dedicated row and copies those details. This broader backend identity gap therefore predates LeanerCloud/cloud-commitments-cli#2071.
The frontend guard being planned in LeanerCloud/cloud-commitments-cli#2071 narrows modal alternatives, but cannot make a shared backend identity contract complete for other callers. No live database, cloud purchase, production occurrence, or backend end-to-end execution is claimed by this evidence.
Steps to reproduce safely
- In backend fixtures, store two EC2 recommendations with the same account/service/region/instance type/engine: 3-year/default tenancy and 1-year/dedicated tenancy, with distinct corresponding prices.
- Submit a request derived from the default-tenancy row with term changed to one year while retaining its original purchase details.
- Exercise
priceAndEnforcePurchaseConstraints through the real request-validation path with cloud execution stubbed.
- Assert that the request must not silently acquire dedicated tenancy. Repeat for platform/scope and RDS AZ configuration after verifying their typed purchase contracts.
Expected behavior and proposed fix
Define a purchase-complete stable identity shared by collection/storage and repricing, and reject ambiguous or mismatched configurations. Inspect scheduler ID generation, recommendation storage uniqueness, internal/api/purchase_pricing.go, and provider offering filters together before deciding migration scope.
Do not compare all details wholesale: Savings Plans hourly_commitment and offering_id legitimately vary with priced alternatives, and enrichment fields are not necessarily purchase discriminators. Add tests rejecting different stable purchase configurations while accepting legitimate term/payment variants and preserving capacity scaling, account isolation and fail-closed pricing.
References
Severity: high, because a mismatched commitment configuration can reach the purchase path. Priority P1, this sprint; currently demonstrated with fixtures/source tracing and a limited configuration-dependent audience, not a confirmed production incident. Estimated effort M pending identity/storage migration analysis.
Summary
Purchase repricing, scheduler IDs and stored recommendation identity omit purchase-specific detail fields. A term/payment change can therefore resolve to a recommendation with a different purchase configuration, such as EC2 tenancy, while preserving the visible account/service/region/instance-type tuple.
Current behavior and evidence
Source inspected at main
5405fd1e90dffdb701a46d4b93868b947e1fbf05while reviewing LeanerCloud/cloud-commitments-cli#2071.internal/api/purchase_pricing.go:96keys stored prices by provider, account, service, region, resource type, engine, term and payment. It omits detail fields;priceFromStoredthen copies stored details into the request.AWS term/payment combinations are collected independently (
providers/aws/recommendations/client.go:310). EC2 details carry platform, tenancy and scope from each Cost Explorer response (providers/aws/recommendations/parser_services.go:159). Rows with different terms and tenancy can coexist because term/payment are in the stored unique key. The EC2 offering query consumes tenancy (providers/aws/services/ec2/client.go:400).An independent Astra in-memory frontend probe on both base and LeanerCloud/cloud-commitments-cli#2071 candidate used a 3-year/default-tenancy row and a 1-year/dedicated-tenancy row sharing the other cell fields. The candidate directly selected the dedicated sibling. The base retained default tenancy in its submitted details, but the source-traced backend lookup resolves that changed term to the stored dedicated row and copies those details. This broader backend identity gap therefore predates LeanerCloud/cloud-commitments-cli#2071.
The frontend guard being planned in LeanerCloud/cloud-commitments-cli#2071 narrows modal alternatives, but cannot make a shared backend identity contract complete for other callers. No live database, cloud purchase, production occurrence, or backend end-to-end execution is claimed by this evidence.
Steps to reproduce safely
priceAndEnforcePurchaseConstraintsthrough the real request-validation path with cloud execution stubbed.Expected behavior and proposed fix
Define a purchase-complete stable identity shared by collection/storage and repricing, and reject ambiguous or mismatched configurations. Inspect scheduler ID generation, recommendation storage uniqueness,
internal/api/purchase_pricing.go, and provider offering filters together before deciding migration scope.Do not compare all
detailswholesale: Savings Planshourly_commitmentandoffering_idlegitimately vary with priced alternatives, and enrichment fields are not necessarily purchase discriminators. Add tests rejecting different stable purchase configurations while accepting legitimate term/payment variants and preserving capacity scaling, account isolation and fail-closed pricing.References
Severity: high, because a mismatched commitment configuration can reach the purchase path. Priority P1, this sprint; currently demonstrated with fixtures/source tracing and a limited configuration-dependent audience, not a confirmed production incident. Estimated effort M pending identity/storage migration analysis.