AI pair programmers love Action: "*" and Resource: "*" because demos work on the first try. Production does not forgive that. Encode CDK least-privilege as Cursor rules so generated stacks fail locally before they ever reach cdk diff on main.
⚡ TL;DR: Ship
.cursor/rulesthat ban IAM wildcards,0.0.0.0/0ingress, public S3, and overly broad managed policies; add a CDK-nag / custom aspect gate in CI; teach the agent to use grant helpers and specific ARNs from constructs. Pair with Cursor Rules for TypeScript Monorepos and AI Code Review Bots.
Project rules that name the sins
# AWS CDK IAM law
- Never emit IAM `Action: "*"` or `Resource: "*"` unless the file is under `/stacks/break-glass/` and labeled BREAK_GLASS.
- Prefer `table.grantReadData(fn)` / `bucket.grantRead(fn)` over hand-written PolicyStatements.
- SecurityGroup ingress: no `Peer.anyIpv4()` on prod stacks; use prefix lists or VPC CIDR.
- S3: `BlockPublicAccess.BLOCK_ALL`, encryption mandatory, no `BUCKET_OWNER_PREFERRED` without reason.
- When unsure of ARN, add a TODO + narrow placeholder — do not widen to `*`.
// .cursor/rules encoded as a lintable constant the agent must import
export const CDK_AGENT_LAW = {
forbidActionWildcard: true,
forbidResourceWildcard: true,
forbidAnyIpv4Ingress: true,
requireBucketBlockPublic: true,
} as const;
Aspects that fail the synth
// lib/aspects/no-wildcards.ts
import * as cdk from "aws-cdk-lib";
import { IConstruct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
export class NoIamWildcards implements cdk.IAspect {
visit(node: IConstruct) {
if (!(node instanceof iam.CfnPolicy) && !(node instanceof iam.CfnManagedPolicy)) return;
const doc = (node as any).policyDocument ?? (node as any).policyDocument;
// Normalize to JSON
const raw = JSON.stringify(node);
// ✅ Cheap static deny — refine with PolicyDocument parsing in real packs
if (/"Action"\s*:\s*"\*"/i.test(raw) || /"Action"\s*:\s*\[\s*"\*"/i.test(raw)) {
cdk.Annotations.of(node).addError("IAM Action wildcard forbidden by aspect");
}
if (/"Resource"\s*:\s*"\*"/i.test(raw)) {
cdk.Annotations.of(node).addError("IAM Resource wildcard forbidden by aspect");
}
}
}
// app.ts
const app = new cdk.App();
const stack = new CheckoutStack(app, "CheckoutProd");
cdk.Aspects.of(app).add(new NoIamWildcards());
Add cdk-nag AwsSolutions checks in the same synth. CI should run cdk synth and fail on errors — not warnings the agent can ignore.
Teach grant helpers, not PolicyStatement novels
// ✅ Agent-friendly pattern
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as lambda from "aws-cdk-lib/aws-lambda";
declare const table: dynamodb.Table;
declare const fn: lambda.Function;
table.grantReadData(fn); // precise actions + table ARN + indexes
// ❌ What models invent
fn.addToRolePolicy(new iam.PolicyStatement({
actions: ["dynamodb:*"],
resources: ["*"],
}));
// When you must hand-write, pin ARNs from constructs
fn.addToRolePolicy(new iam.PolicyStatement({
actions: ["dynamodb:GetItem", "dynamodb:Query"],
resources: [table.tableArn, `${table.tableArn}/index/*`],
}));
Security groups and buckets the agent must not “simplify”
// ❌ Prod anti-pattern the rule must ban
sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443), "temp open");
// ✅
sg.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(443));
// S3
new s3.Bucket(this, "Assets", {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
enforceSSL: true,
});
Review bots should flag wildcards the same way — see AI Code Review Bots: IAM, Secrets, and Least-Privilege Pipelines.
CI gate snippet
# .github/workflows/cdk.yml
- name: Synth with nag
run: |
npx cdk synth -q
# Annotations.addError fails synth when --strict / app throws via Aspects
- name: Diff attach policy wildcards
run: |
! git diff origin/main...HEAD | grep -E 'Action:\s*"\*"|"Action"\s*:\s*"\*"'
Closing checklist
✅ Dos
– ✅ Encode wildcard bans in Cursor rules + CDK Aspects
– ✅ Prefer grant* helpers from L2 constructs
– ✅ Run cdk-nag / custom aspects on every PR
– ✅ Block public buckets and anyIpv4 in prod stacks
– ✅ Keep a labeled break-glass path with expiry
❌ Don’ts
– ❌ Don’t accept dynamodb:* / s3:* “just for now”
– ❌ Don’t open 0.0.0.0/0 for “debugging ALB”
– ❌ Don’t silence cdk-nag with blanket suppressions
– ❌ Don’t let the agent edit IAM in the same PR as business logic without a label
– ❌ Don’t copy AWS managed AdministratorAccess into task roles
Related reading
- Cursor Rules for TypeScript Monorepos: Make AI Edits Stick
- AI Code Review Bots: IAM, Secrets, and Least-Privilege Pipelines
- Amazon Bedrock Agents: Tool Use, Memory, and Production Guardrails
- LLM Coding Agents on AWS: Safe Tool Sandboxes with Lambda
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.

Pingback: IAM Access Analyzer: Verify AI-Generated Policies Before Attach - CheatCoders