Summary
POST /api/purchases/{id}/marketplace-list calls ec2:CreateReservedInstancesListing with the deployment's ambient runtime credentials, and the cancel and status paths call ec2:CancelReservedInstancesListing and ec2:DescribeReservedInstancesListings. None of the three actions appears in any Terraform module, the CloudFormation template or a federation bundle, so on a stock deployment every listing attempt is refused by AWS. reserveAndCreateListing claims the listing slot in the database before the AWS call, so the row passes through pending and depends on releaseMarketplaceClaim to recover; the cancel path is equally unauthorized, so the operator cannot unwind through CUDly either.
Location
terraform/modules/compute/aws/lambda/main.tf:349-390 (runtime ri_exchange EC2 statement) at 3c0f8ac
terraform/modules/compute/aws/fargate/main.tf (same statement) and cloudformation/stacks/CUDly/template.yaml
- callers:
providers/aws/services/ec2/client.go:1048, :1070, :1111 (SDK calls declared at :31-33); internal/api/handler_marketplace.go:248; routes registered unconditionally at internal/api/router.go:194-195; credentials from internal/api/handler_ri_exchange.go:1259-1275 (LoadDefaultConfig)
Failure scenario
A user with sell permission lists a convertible RI on the Marketplace from a Lambda or Fargate deployment provisioned from any template. The DB claim is written, AWS returns UnauthorizedOperation, and the row is left to the claim-release path. LeanerCloud/cloud-commitments-platform#100 separately notes the handler maps that error to HTTP 400, so the operator is pointed at a phantom client bug rather than the IAM gap.
Evidence
"ec2:DescribeReservedInstances",
"ec2:DescribeReservedInstancesOfferings",
"ec2:GetReservedInstancesExchangeQuote",
"ec2:AcceptReservedInstancesExchangeQuote",
"ec2:PurchaseReservedInstancesOffering",
"ec2:DescribeInstanceTypeOfferings",
"ec2:DescribeRegions",
Suggested fix
Add ec2:CreateReservedInstancesListing, ec2:DescribeReservedInstancesListings and ec2:CancelReservedInstancesListing to the runtime EC2 statement in the Lambda module, the Fargate module and the CloudFormation template, and cover them with the same SDK-call-versus-template guard proposed for ce:GetCostAndUsage.
Found by the 2026-09-02 codebase audit, finding A13-002, reported by one reviewer and independently confirmed by a second. Full report: docs/audits/codebase-audit-2026-09-02.md.
Summary
POST /api/purchases/{id}/marketplace-listcallsec2:CreateReservedInstancesListingwith the deployment's ambient runtime credentials, and the cancel and status paths callec2:CancelReservedInstancesListingandec2:DescribeReservedInstancesListings. None of the three actions appears in any Terraform module, the CloudFormation template or a federation bundle, so on a stock deployment every listing attempt is refused by AWS.reserveAndCreateListingclaims the listing slot in the database before the AWS call, so the row passes throughpendingand depends onreleaseMarketplaceClaimto recover; the cancel path is equally unauthorized, so the operator cannot unwind through CUDly either.Location
terraform/modules/compute/aws/lambda/main.tf:349-390(runtimeri_exchangeEC2 statement) at 3c0f8acterraform/modules/compute/aws/fargate/main.tf(same statement) andcloudformation/stacks/CUDly/template.yamlproviders/aws/services/ec2/client.go:1048,:1070,:1111(SDK calls declared at:31-33);internal/api/handler_marketplace.go:248; routes registered unconditionally atinternal/api/router.go:194-195; credentials frominternal/api/handler_ri_exchange.go:1259-1275(LoadDefaultConfig)Failure scenario
A user with
sellpermission lists a convertible RI on the Marketplace from a Lambda or Fargate deployment provisioned from any template. The DB claim is written, AWS returns UnauthorizedOperation, and the row is left to the claim-release path. LeanerCloud/cloud-commitments-platform#100 separately notes the handler maps that error to HTTP 400, so the operator is pointed at a phantom client bug rather than the IAM gap.Evidence
Suggested fix
Add
ec2:CreateReservedInstancesListing,ec2:DescribeReservedInstancesListingsandec2:CancelReservedInstancesListingto the runtime EC2 statement in the Lambda module, the Fargate module and the CloudFormation template, and cover them with the same SDK-call-versus-template guard proposed force:GetCostAndUsage.Found by the 2026-09-02 codebase audit, finding
A13-002, reported by one reviewer and independently confirmed by a second. Full report:docs/audits/codebase-audit-2026-09-02.md.