cdk-insights
v1.42.4
Published
AWS CDK security and cost analysis CLI. Free static scans via npm — no account needed. Sign up free to add AI-powered insights.
Maintainers
Readme
CDK Insights 🔍
Catch security issues in your AWS CDK before they reach production.
Scan your CDK stacks for security vulnerabilities, cost waste, compliance violations, and best practice issues — across 100+ rules and 35+ AWS services. Your source code never leaves your machine.
👉 cdkinsights.dev | Full Documentation
Why CDK Insights?
Existing tools (Checkov, cfn-lint, cfn_nag) scan raw CloudFormation. They don't understand CDK constructs, L2/L3 patterns, or developer intent.
CDK Insights is purpose-built for CDK — it synthesizes your stacks and analyzes them with CDK context, integrating CDK Nag alongside 100+ custom rules.
Key differences:
- Local-first — static analysis runs entirely on your machine, no code uploaded
- No account needed for static scans — install via npm and run immediately
- Free forever — unlimited static analysis with 100+ rules, JSON/Table/Markdown output
- AI insights optional — sign up for a free account to get 500 AI-powered insights per month
- CDK-native — understands constructs and patterns, not just CloudFormation
- CI/CD ready — GitHub Action with PR comments and merge blocking
🚀 Quick Start
# Run instantly — no install needed
npx cdk-insights scanThat's it. CDK Insights will synthesize your stacks and scan them.
Install in your project
# Add to your project
npm install --save-dev cdk-insights
# Set up npm scripts automatically
npx cdk-insights init
# Then use familiar commands
npm run cdk-insightsWhat cdk-insights init adds
{
"scripts": {
"cdk-insights": "cdk-insights scan",
"cdk-insights:all": "cdk-insights scan --all",
"cdk-insights:watch": "cdk-insights scan --watch",
"cdk-insights:ci": "cdk-insights scan --all --output json --failOnCritical"
}
}Run npm run cdk-insights:watch for the dev loop — saves trigger a re-scan automatically. (The :watch script exists because npm run cdk-insights --watch would be silently consumed by npm; --watch is read as an npm flag, not a script arg. The named script avoids the -- --watch separator dance.)
Use npx cdk-insights init --all to include format-specific shortcuts (:json, :markdown, :summary, :issue).
🔍 What It Catches
CDK Insights scans for real problems across 35+ AWS services:
| Category | Examples | |----------|---------| | Security | Public S3 buckets, wildcard IAM policies, unencrypted RDS/DynamoDB/SQS, open security groups, self-locking S3 bucket / KMS key / SQS queue / SNS topic policies (Deny without root carveout) | | Cost | Over-provisioned Lambda memory, missing S3 lifecycle policies, unused resources | | Best Practices | Missing CloudWatch alarms, no VPC flow logs, missing point-in-time recovery | | Compliance | Encryption at rest, logging enabled, backup configuration |
Services covered: S3, IAM, Lambda, RDS, EC2, DynamoDB, SQS, SNS, CloudFront, ECS/Fargate, API Gateway, Cognito, KMS, Secrets Manager, Step Functions, CloudTrail, EventBridge, EBS, WAF, CloudWatch, Route53, ElastiCache, ECR, OpenSearch, VPC, EKS, and more.
📊 Output Formats
| Format | Use Case | Command |
|--------|----------|---------|
| Table | Terminal review (default) | npx cdk-insights scan |
| JSON | CI/CD pipelines, automation | --output json |
| Markdown | Reports, documentation | --output markdown |
| Summary | Quick overview | --output summary |
| SARIF | GitHub Code Scanning | --output sarif |
💡 Usage Examples
| Scenario | Command |
|----------|---------|
| Full project scan | npx cdk-insights scan --all --output summary |
| Security-only focus | npx cdk-insights scan --services IAM,S3,KMS --rule-filter Security |
| Markdown report | npx cdk-insights scan --output markdown > report.md |
| CI/CD with fail gate | npx cdk-insights scan --all --output json --fail-on-critical |
| Create GitHub issue | npx cdk-insights scan --output markdown --with-issue |
| Live feedback while editing | npx cdk-insights scan --watch |
👀 Watch mode
npx cdk-insights scan --watch
# or, after `npx cdk-insights init`:
npm run cdk-insights:watchRe-runs cdk synth + the static rule pack on every save and reprints the findings table — terminal stays focused on the latest result, vitest-watch style. The watch loop never deploys: it shells out to cdk synth only, never cdk deploy. Don't reach for cdk watch for this purpose; that's a shortcut for deploy --watch and will push real infra on every save.
npm gotcha: don't try
npm run cdk-insights --watch. npm consumes--watchas one of its own flags (you'll seeUnknown cli config "--watch"and a regular non-watch scan). The:watchnamed script — added bycdk-insights initsince 1.29.0 — exists so you don't have to remember the-- --watchseparator dance.
Reuses your existing cdk.json watch config
The watcher reads the same watch.include / watch.exclude block CDK uses for cdk watch, so a single config drives both:
{
"app": "node bin/my-app.js",
"watch": {
"include": ["lib/**/*.ts", "bin/**/*.ts"],
"exclude": ["**/*.test.ts", "**/handlers/**"]
}
}If the watch block is absent, defaults match aws-cdk's CdkToolkit exactly: include defaults to the project root (watch everything), and **/.*, **/.*/**, **/node_modules/**, plus the cloud-assembly output dir (output in cdk.json, default cdk.out) are always excluded — user-provided excludes are appended to this list, never replace it.
Watch-safe defaults
A few flags are forced or rejected because they don't make sense in a re-rendering live loop:
| Flag | Behaviour in --watch | Why |
|---|---|---|
| AI analysis | Forced off (--local) | Cache-miss = 1 credit per resource. Running on every save would burn credits with each edit. Run cdk-insights scan manually when you want AI feedback on a known-good intermediate state. |
| --writeBaseline | Rejected with error | Would silently overwrite the baseline file on every save. |
| --diff | Rejected with error | Filters findings against a moving target; meaningless when re-running continuously. |
| --github, --withIssue, --prComment | Forced off | Would otherwise create a GitHub issue / post a PR comment on every save. |
| failOnCritical | Forced off | Would kill the watcher on the first critical finding. |
| --output json / sarif / markdown / github-actions | Coerced to table (one-line warning) | Machine formats don't make sense for a re-rendering live loop. |
Loop ergonomics: 300 ms debounce on file events, queue-while-running so a save during an in-flight analysis triggers exactly one follow-up, synth failures preserve the last good results instead of crashing the watcher, Ctrl+C cleans up the chokidar watcher before exit.
🔄 CI/CD Integration
CDK Insights automatically detects CI environments (GitHub Actions, GitLab CI, Jenkins, CircleCI, AWS CodeBuild, and more) and adjusts behavior accordingly.
GitHub Action
- name: Run CDK Insights
uses: instance-labs/cdk-insights-action@v1
with:
license-key: ${{ secrets.CDK_INSIGHTS_LICENSE_KEY }}
fail-on-critical: trueThe GitHub Action posts findings as PR comments, uploads SARIF for Code Scanning, and supports configurable severity thresholds for merge blocking.
Manual CI Setup
- name: Run CDK Insights
run: npx cdk-insights scan --all --output json --fail-on-critical
env:
CDK_INSIGHTS_LICENSE_KEY: ${{ secrets.CDK_INSIGHTS_LICENSE_KEY }}In CI mode, CDK Insights will:
- Automatically analyze all stacks
- Output JSON format for easy parsing
- Skip interactive prompts
- Exit with code 1 on critical issues (with
--fail-on-critical)
⚙️ Configuration
Create a .cdk-insights.json in your project root, or run:
npx cdk-insights config setupIn-App Integration
CDK Insights ships two integration points; they are complementary, and most projects want both. Install them interactively with:
npx cdk-insights setup| Integration | When to use | What you get |
|-------------|-------------|--------------|
| Aspect | Always — pairs with cdk-insights scan | Precise file/line metadata, source-location tracking, richer findings |
| Validations plugin (aws-cdk-lib ≥ 2.251.0) | When you want CI to fail on synth | cdk synth blocks on findings; native CDK plugin surface; one less CI step |
Aspect — for precise findings
import { App, Aspects } from 'aws-cdk-lib';
import { createCdkInsightsAspect } from 'cdk-insights';
const app = new App();
Aspects.of(app).add(createCdkInsightsAspect());
// define stacks...
app.synth();The aspect runs cdk-nag's AwsSolutionsChecks rule pack alongside cdk-insights' own rules. As of 1.17.0, those findings are emitted as non-blocking Info annotations (cdk-insights::nagFinding::*) instead of cdk-nag's default Error/Warning annotations — the Validations Plugin (below) remains the actual deploy gate, configured per severity.
Auto-suppress CDK / AWS boilerplate
cdk-nag's AwsSolutionsChecks rule pack flags two patterns that are universally noisy for CDK consumers — they're either AWS-recommended defaults or stale rule heuristics:
AwsSolutions-IAM4flags any AWS-managed policy attached to a role, including the standard Lambda execution policies CDK auto-attaches based on event sources or tracing configuration. Each is narrowly scoped to a single AWS service — replacing them with customer-managed copies is busywork with no security gain.AwsSolutions-L1asserts that Lambda runtimes match cdk-nag's known "latest" list, which lags actual AWS LTS releases. Node 20.x / 22.x, Python 3.11–3.13, Java 17/21, .NET 8, Ruby 3.3/3.4, andprovided.al2023are all current AWS LTS at time of writing but get flagged depending on how recent youraws-cdk-libversion is.
Pass cdkBoilerplateSuppressions: true to short-circuit both:
Aspects.of(app).add(createCdkInsightsAspect({
cdkBoilerplateSuppressions: true,
}));This attaches NagSuppressions (with named justifications and appliesTo filters) before cdk-nag evaluates each construct, so the matching findings are never emitted. Anything outside the boilerplate set continues to surface — if a role has AWSLambdaBasicExecutionRole and a custom managed policy, only the boilerplate one is suppressed; the custom policy still produces a finding.
| Rule | What's suppressed | What still fires |
|---|---|---|
| AwsSolutions-IAM4 | 11 AWS-managed Lambda execution policy ARNs: AWSLambdaBasicExecutionRole, AWSLambdaVPCAccessExecutionRole, AWSLambdaENIManagementAccess, AWSLambdaSQSQueueExecutionRole, AWSLambdaDynamoDBExecutionRole, AWSLambdaKinesisExecutionRole, AWSLambdaMSKExecutionRole, AWSLambdaAMQExecutionRole, AWSLambdaSelfManagedKafkaExecutionRole, AWSLambdaInvocation-DynamoDB, AWSXRayDaemonWriteAccess | Any other managed policy attached to the same role |
| AwsSolutions-L1 | nodejs18.x / nodejs20.x / nodejs22.x, python3.11 / python3.12 / python3.13, java17 / java21, dotnet8, ruby3.3 / ruby3.4, provided.al2023 | Anything outside the LTS allowlist (older runtimes, unsupported ones, custom families) |
Default is false — opt-in is conservative and additive.
Suppress arbitrary cdk-nag rules
For rules outside the boilerplate set — context-dependent ones like AwsSolutions-COG2 (MFA), AwsSolutions-DDB3 (PITR), or any rule the project has reviewed and consciously accepted — pass suppressNagRules:
Aspects.of(app).add(createCdkInsightsAspect({
cdkBoilerplateSuppressions: true,
suppressNagRules: [
// String shorthand — gets a generic auto-reason. Fine for triage.
'AwsSolutions-APIG2',
// Object form — written justification. **Recommended for prod code.**
{
id: 'AwsSolutions-COG2',
reason: 'MFA opt-in by product policy; mandatory MFA blocks onboarding for new users. See RFC-0042.',
},
],
}));Both options compose. The aspect walks up to the construct-tree root on the first visit and applies the suppressions with applyToChildren: true, so the rule is honoured for every descendant resource regardless of stack.
For finer-grained control (per-resource, per-appliesTo-pattern), use NagSuppressions.addResourceSuppressions(...) from cdk-nag directly after constructing your stacks. Common patterns we don't auto-suppress because they need consumer context:
import { NagSuppressions } from 'cdk-nag';
// DDB grant{Read,Write}Data auto-attaches Resource::<TableArn>/index/*
// for GSI access. DynamoDB IAM has no index-level scope distinct from
// the table; the wildcard is unavoidable. Apply per-stack with reason.
NagSuppressions.addStackSuppressions(myStack, [
{
id: 'AwsSolutions-IAM5',
reason: 'DDB grantRead/Write expands to <TableArn>/index/* for GSI access — DynamoDB IAM has no index-level scope.',
},
]);
// COG2 (mandatory MFA) — product-policy decision, not a defect
NagSuppressions.addResourceSuppressionsByPath(myStack,
'/MyStack/MyUserPool/Resource',
[{ id: 'AwsSolutions-COG2', reason: 'MFA opt-in by product policy.' }],
);For accurate file:line source attribution, CDK needs to record per-construct stack traces during synth. Three ways to enable this, easiest first:
npx cdk-insights setupwrites"@aws-cdk/core:stackTrace": trueinto yourcdk.jsoncontextblock — durable across everycdk synthinvocation, no env-var dance. Recommended.cdk synth --context @aws-cdk/core:stackTrace=true— one-off, scoped to that command.CDK_DEBUG=true cdk synth— one-off, env-var form.
cdk-insights scan already sets CDK_DEBUG=true on its spawned synth process, so users on the CLI path get high-confidence attribution out of the box. The above is for users who run cdk synth themselves with the aspect attached. On aws-cdk-lib ≥ 2.252.0, findings on deferred or post-construction property assignments (lifecycle rules, env vars, role policies, Lazy.string/Lazy.any values) point at the property setter line — not the construct constructor — automatically. Older CDKs continue to work; you'll just get construct-level attribution.
Re-run findings on every save
If you have the aspect attached and run cdk synth yourself (manually, or via your own watcher), the rules re-evaluate every time synth runs — no extra wiring needed.
For an out-of-the-box live loop, use the cdk-insights scan --watch flag instead — it's documented in Watch mode below and runs synth-only (no deploy). Don't reach for cdk watch for this purpose: it's a shortcut for deploy --watch and will push real infra to AWS on every save.
Validations plugin — for synth-time enforcement
Requires aws-cdk-lib ≥ 2.251.0:
import { App, Validations } from 'aws-cdk-lib';
import { CdkInsightsPolicyValidationPlugin } from 'cdk-insights';
const app = new App();
Validations.of(app).addPlugins(new CdkInsightsPolicyValidationPlugin());
// define stacks...
app.synth();cdk synth will fail with a CDK Insights report if any rules trigger. Pass selectedServices, minimumSeverity, or customRules to scope what counts as a synth-time block:
new CdkInsightsPolicyValidationPlugin({
selectedServices: ['IAM', 'S3'],
minimumSeverity: 'HIGH',
});The plugin sees synthesized templates only — no construct tree. Pair it with the aspect (above) when you also want source-location capture in the cdk-insights scan report.
The plugin honours the same suppressions as cdk-insights scan — .cdk-insights.json (ignoreRules / ignorePaths) and inline Validations.of(scope).acknowledge(...) entries are both respected, so a finding suppressed in the CLI is also suppressed at synth time. Pass ignoreProjectConfig: true or ignoreInlineAcknowledgements: true to opt out of either source.
Suppressing Findings
Two channels, both feed into the same scan output, SARIF, severity counts, and PR comments:
Project-wide — add ignoreRules and ignorePaths to .cdk-insights.json. Trailing * wildcards supported.
{
"ignoreRules": ["CDK-INSIGHTS-SENSITIVE-*"],
"ignorePaths": ["MyStack/MarketingSite/*"]
}Inline (CDK ≥ 2.252.0) — acknowledge a finding next to the construct that triggered it, with a reason captured for audit:
import { Validations } from 'aws-cdk-lib';
Validations.of(myBucket).acknowledge({
id: 'cdk-insights::s3-bucket-public-access',
reason: 'Public-by-design marketing site',
});Acknowledgements cascade to descendant constructs, so scope them as narrowly as the situation allows. See Suppressing Findings for details.
💰 Pricing
| Plan | Price | What's Included | |------|-------|-----------------| | Free | £0 forever | Static analysis (100+ rules), JSON/Table/Markdown/SARIF output, multi-stack analysis, CLI access. Sign up for a free account to add 500 AI insights/month. | | Pro | £9.99/mo | Everything in Free + AI analysis (Bedrock), GitHub integration, dashboard, PDF reports, 5,000 AI insights/month | | Team | £12.99/seat/mo (2-seat minimum) | Everything in Pro + team management, shared configs, audit trails, 10,000 AI insights per seat |
Static analysis is free forever — no trial, no credit card, no signup required.
The AI tier adds deep analysis via AWS Bedrock: security analysis, findings categorised by Well-Architected Framework pillar, and context-aware recommendations.
🧰 Requirements
- Node.js 22 or later (to run the CLI itself)
- AWS CDK v2 project — in any CDK-supported language (TypeScript, JavaScript, Python, Java, C#/.NET, Go)
scan analyses the synthesized CloudFormation, so your CDK app's source language doesn't change what gets checked. A few features (in-process Aspect, source-line jump-to-file, cdk-insights fix) are TypeScript/JavaScript-only today — see CDK language support for the full feature matrix.
Quick Compatibility Check
node --version # Should be 22+
ls cdk.json # Should exist in CDK project🔧 Troubleshooting
Cache Management
npx cdk-insights clear-cache # Clear all caches
npx cdk-insights cache-status # Check cache status
npx cdk-insights scan --no-cache # Run without cacheAuthentication Issues
- Check your license key:
echo $CDK_INSIGHTS_LICENSE_KEY - Clear the auth cache:
npx cdk-insights clear-cache - Verify your internet connection
Sensitive Data Detection
CDK Insights detects potentially sensitive data in your CloudFormation templates:
npx cdk-insights scan --fail-on-critical # Fail on sensitive data (default)
npx cdk-insights scan --warn-sensitive # Warn but continue📚 Links
- Website: cdkinsights.dev
- Documentation: cdkinsights.dev/docs
- Pricing: cdkinsights.dev/pricing
- npm: npmjs.com/package/cdk-insights
- License: BSL 1.1 (converts to Apache 2.0 on 2030-04-12)
