Save products you love by clicking the heart icon.
Comprehensive guide to testing Stripe payment integrations — test cards, webhook simulation, checkout flows, edge cases, and CI/CD strategies for bulletproof payment systems.
How an attacker chained pull_request_target cache poisoning with GitHub Actions memory extraction to publish 84 malicious npm packages — and what you must audit in your own CI/CD pipeline.
There is an asymmetry at the heart of cloud cost management: the decisions that create cost happen in pull requests, while the discovery of cost happens 30 days later in the bill. By the time a dashboard shows the overrun, the infrastructure has been provisioned, the resources have been paid for, and the fix is a cleanup project with a ticket backlog.
Provision-time FinOps eliminates this asymmetry. The developer sees the cost during the activity that produces it, with full context, in the tool where they are already making decisions. The available actions are now: accept the cost and merge, or modify the change and re-evaluate — both within the same work session.
This is the difference between a receipt and a guardrail. A dashboard is a receipt. A blocking CI check is a guardrail.
A complete shift-left FinOps toolchain looks at infrastructure from two perspectives:
| Layer | Tool | Evaluation vector | Primary value |
|---|---|---|---|
| Static (pre-deploy) | Infracost | Declared IaC specs (tfplan, HCL blocks) | Blocks budget overruns before resources are built |
| Runtime (in-cluster) | OpenCost / Kubecost | Real container metrics (vCPU, RAM, egress) | Exposes idle waste, guides accurate resizing |
The gate runs on every pull request that touches infrastructure code. Static analysis catches macro-level provisioning errors — an m5.large upgraded to an r5.4xlarge — while the runtime layer catches what static analysis cannot see.
Infracost is the workhorse of the static layer. It parses Terraform, OpenTofu, CloudFormation, and AWS CDK manifests, looks up real-time cloud pricing APIs, and computes exactly how much your bill changes if a PR merges. It supports over 1,100 resource types across AWS, Azure, and Google Cloud.
Two properties matter for security-conscious teams:
usage.yml file (Lambda invocations, S3 requests, data transfer), because static analysis cannot guess runtime usageInfracost produces a JSON breakdown with per-resource costs and an aggregate diff.totalMonthlyCost. Open Policy Agent (OPA) evaluates that JSON against Rego policies; conftest is the conventional runner in CI.
deny[msg] {
delta := to_number(input.projects[_].diff.totalMonthlyCost)
delta > threshold_monthly
msg := sprintf(
"Monthly cost delta is $%.2f, which exceeds the $%d limit. Break this into smaller changes or request an exception.",
[delta, threshold_monthly],
)
}
Aggregate caps miss outliers — a single 400 diff is invisible to the total. A per-resource rule catches it, with a documented exception path:
deny[msg] {
resource := input.projects[i].diff.resources[j]
cost := to_number(resource.monthlyCost)
cost > 200
not has_justification(resource)
msg := sprintf(
"Resource %s costs $%.2f/mo. Add a '# cost-justified: <reason>' comment or split the PR.",
[resource.name, cost],
)
}
has_justification(resource) {
startswith(resource.metadata.code_comment, "cost-justified:")
}
The has_justification predicate is the key design decision: exceptions are not bypasses, they become part of the code's audit trail. Every exception is visible in the PR comment history and reviewable.
name: FinOps Cost Gate
on:
pull_request:
paths:
- 'terraform/**'
jobs:
cost-gate:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.9.x
- name: Configure AWS credentials (read-only)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.PLAN_ROLE_ARN }}
aws-region: us-east-1
- name: Terraform plan
working-directory: terraform
run: |
terraform init -input=false
terraform plan -out=tfplan.binary -input=false
terraform show -json tfplan.binary > tfplan.json
- name: Setup Infracost
uses: infracost/actions/setup@v3
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Infracost breakdown
working-directory: terraform
run: |
infracost breakdown \
--path tfplan.json \
--format json \
--out-file ../infracost.json
- name: Enforce cost policy
run: conftest test --policy policy/ infracost.json
- name: Comment cost diff on PR
if: always()
uses: infracost/actions/comment@v3
with:
path: infracost.json
behavior: update
The IAM role for planning is read-only and scoped to the target account — the gate can inspect, never mutate. The PR check is enforced as required via branch protection, so a cost violation blocks the merge like a failed test would.
Static checks cannot predict what happens inside an elastic orchestration layer where costs change with traffic. For that, run OpenCost inside your clusters.
OpenCost is the open standard: a cloud-agnostic, community-driven API that standardizes cost metrics across platforms. Kubecost implements the OpenCost specification, providing real-time cost allocation at the namespace, deployment, and pod level, plus budget alerts when spending crosses thresholds.
The two layers work together. If your Infracost gate shows a microservice costs $200/month based on its declared CPU requests, but OpenCost telemetry shows real-world memory utilization never crossing 5%, you have found a downsizing target — with the numbers to justify it.
Don't enable hard-deny on day one. The pipeline has to earn trust first:
cost-justified pathThresholds are calibration, not guesswork. Analyze two to four weeks of Infracost data first:
A production reference implementation reports:
| Metric | Before | After |
|---|---|---|
| Cost surprises per month | 2–4 | 0 |
| Time from spend decision to detection | ~30 days | ~90 seconds |
| Waste prevented (monthly) | — | ~$2,400 (NAT gateways, oversized RDS, orphaned EIPs) |
| PR cycle time impact (p50) | — | +47 seconds |
| Adoption resistance | — | None after week 2 (exception path defused pushback) |
The +47 seconds per PR is negligible next to typical review latency — the gate costs less than a comment thread and prevents more than a budget review.
The gate pattern is not Terraform-specific. The same shape works wherever infrastructure is declared:
kubectl apply changes that bypass the PR gate entirelyiac-generation, scan, price-lookup) so agents generating infrastructure code check cost policies while they work, before the PR even existsThe unifying principle: cost policy lives as code next to the infrastructure it governs, evaluated in the same pipeline that reviews the change.
usage.yml modeling or estimates will be systematically wrong