# 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 145 rules and 36 AWS services. Static analysis stays on your machine; licensed AI sends privacy-filtered, synthesized resource context to the configured AWS Bedrock model.

[![npm version](https://img.shields.io/npm/v/cdk-insights.svg)](https://www.npmjs.com/package/cdk-insights)
[![npm downloads](https://img.shields.io/npm/dw/cdk-insights.svg)](https://www.npmjs.com/package/cdk-insights)

👉 **[cdkinsights.dev](https://cdkinsights.dev)** | **[Full Documentation](https://cdkinsights.dev/docs)**

---

## 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 145 static 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 145 rules and all six CLI output views
- **AI insights optional** - sign up for a free account to get 500 AI credits per month
- **CDK-native** - understands constructs and patterns, not just CloudFormation
- **CI/CD ready** - GitHub Action with PR comments and merge blocking

---

## 🚀 Quick Start

```bash
# Run instantly - no install needed
npx cdk-insights scan
```

That's it. CDK Insights scans the existing `cdk.out` assembly, or runs `cdk synth` automatically when no assembly exists. Pass `--synth` when you want to force a fresh synthesis first.

### Install in your project

```bash
# 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-insights
```

### What `cdk-insights init` adds

```json
{
  "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 --fail-on-critical"
  }
}
```

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 **36 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` |
| **GitHub Actions** | Inline annotations and job summary | `--output github-actions` |

JSON, Markdown, and SARIF are emitted to stdout and saved as report files.
Human progress is routed to stderr, so pipes and redirects remain clean. Generate
all three file formats from one scan with `--reports json sarif markdown`.

---

## 💡 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` |
| All report artifacts in one pass | `npx cdk-insights scan --reports json sarif markdown` |
| 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` |
| Deployment risk preview | `npx cdk-insights preview --write-baseline` then `npx cdk-insights preview` |
| Architecture and attack paths | `npx cdk-insights graph` (terminal summary) |
| Mermaid architecture diagram | `npx cdk-insights graph --output mermaid --file architecture.mmd` |
| Reliability simulation | `npx cdk-insights simulate` |
| Plan recommendation from Git history | `npx cdk-insights plan` |
| Compare with deployed CloudFormation | `AWS_PROFILE=audit npx cdk-insights live --stack MyStack` |

---

## Infrastructure intelligence

The CLI includes local-first tools that work from synthesized CloudFormation and do not consume AI credits:

| Command | What it provides |
| --- | --- |
| `preview` | Release risk score and GO / REVIEW / BLOCK recommendation against a reviewed template baseline |
| `graph` | Readable resource and relationship summary plus public-to-sensitive attack paths; Mermaid and JSON are available with `--output` |
| `simulate` | Availability Zone, dependency, quota-pressure and regional-recovery review |
| `document` | Markdown architecture documentation with a Mermaid diagram |
| `policy` | Project-specific limits for tags, resource types, public entry points and deployment risk |
| `live` | Read-only comparison with deployed CloudFormation templates and existing drift results |
| `learn` | Converts a structured incident report into an auditable local custom rule |
| `plan` | Uses estate size and infrastructure Git history to estimate monthly credits and recommend a plan |

Use the default terminal summary for a quick architecture review, or export a Mermaid diagram for documentation:

```bash
npx cdk-insights graph
npx cdk-insights graph --output mermaid --file architecture.mmd
```

For a fast static-only pass over an existing assembly, skip both AI and synthesis explicitly:

```bash
npx cdk-insights scan --local --no-synth
```

`live` only calls CloudFormation read APIs. It never starts drift detection or changes AWS resources. Use a read-only AWS profile:

```bash
AWS_PROFILE=audit npx cdk-insights live --stack ProductionStack
```

### MCP and editor integration

Expose the same read-only architecture, reliability, documentation and plan-advisor tools to MCP-compatible editors and AI assistants:

```json
{
  "mcpServers": {
    "cdk-insights": {
      "command": "npx",
      "args": ["-y", "cdk-insights@latest", "mcp"]
    }
  }
}
```

The MCP server reads an existing `cdk.out`; it does not synthesize or deploy. For native editor diagnostics, generate SARIF with `cdk-insights scan --output sarif` and open it in any SARIF-compatible viewer.

Detailed guides:

- [Git history plan advisor](https://cdkinsights.dev/docs/features/plan-advisor)
- [Generated architecture documentation](https://cdkinsights.dev/docs/features/architecture-documentation)
- [Incident-to-rule learning](https://cdkinsights.dev/docs/features/incident-learning)
- [MCP integration](https://cdkinsights.dev/docs/features/mcp-integration)

---

<a id="watch-mode"></a>

## 👀 Watch mode

```bash
npx cdk-insights scan --watch
# or, after `npx cdk-insights init`:
npm run cdk-insights:watch
```

Re-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 `--watch` as one of its own flags (you'll see `Unknown cli config "--watch"` and a regular non-watch scan). The `:watch` named script - added by `cdk-insights init` since 1.29.0 - exists so you don't have to remember the `-- --watch` separator 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:

```json
{
  "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`) | A cache miss spends model-weighted credits per resource (0.5–16). Running on every save could burn credits with each edit. Run `cdk-insights scan` manually when you want AI feedback on a known-good intermediate state. |
| `--write-baseline` | 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`, `--with-issue`, `--pr-comment` | Forced off | Would otherwise create a GitHub issue / post a PR comment on every save. |
| `--fail-on-critical` | 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

```yaml
- name: Run CDK Insights
  uses: instancelabs/cdk-insights-action@v1
  with:
    license-key: ${{ secrets.CDK_INSIGHTS_LICENSE_KEY }}
    fail-on: critical
```

The GitHub Action posts findings as **PR comments**, uploads **SARIF for Code Scanning**, and supports **configurable severity thresholds** for merge blocking.

### Manual CI Setup

```yaml
- 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:

```bash
npx cdk-insights config setup
```

### In-App Integration

CDK Insights ships two integration points; they are complementary, and most projects want both. Install them interactively with:

```bash
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

```ts
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-IAM4`** flags 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-L1`** asserts that Lambda runtimes match cdk-nag's known "latest" list, which can lag actual AWS releases. CDK Insights only suppresses runtimes that AWS currently supports: Node 22.x / 24.x, Python 3.11–3.14, Java 17/21/25, .NET 8/10, Ruby 3.3/3.4/4.0, and `provided.al2023`. Deprecated Node 18.x and 20.x runtimes remain visible.

Pass `cdkBoilerplateSuppressions: true` to short-circuit both:

```ts
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` | `nodejs22.x` / `nodejs24.x`, `python3.11`–`python3.14`, `java17` / `java21` / `java25`, `dotnet8` / `dotnet10`, `ruby3.3` / `ruby3.4` / `ruby4.0`, `provided.al2023` | Anything outside the supported-runtime allowlist (deprecated 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`:

```ts
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:

```ts
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 setup`** writes `"@aws-cdk/core:stackTrace": true` into your `cdk.json` `context` block - durable across every `cdk synth` invocation, 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](#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:

```ts
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:

```ts
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.

```json
{
  "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:

```ts
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](https://github.com/instancelabs/cdk-insights/blob/main/docs/configuration.md#suppressing-findings) for details.

---

## 💰 Pricing

| Plan | Price | What's Included |
|------|-------|-----------------|
| **Free** | £0 forever | Static analysis (145 rules), all six CLI output views, multi-stack analysis, CLI access. Sign up for a free account to add **500 AI credits/month**. |
| **Pro** | £9.99/mo | Everything in Free + all five Bedrock models, GitHub integration, dashboard, PDF reports, **10,000 AI credits/month** |
| **Team** | £12.99/seat/mo (2-seat minimum) | Everything in Pro + team management, shared configs, audit trails, **20,000 AI credits per seat** |

Static analysis is **free forever** - no trial, no credit card, no signup required.

One-time, non-expiring credit packs are available from Billing when a deeper model review or an unusually active month would exceed the included allowance. Credits are shown before a scan starts; there are no automatic overage charges.

The AI tier adds deep analysis via AWS Bedrock: security analysis, findings categorised by Well-Architected Framework pillar, and context-aware recommendations.

👉 [View full pricing](https://cdkinsights.dev/pricing)

---

## 🧰 Requirements

- Node.js 22 LTS 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](./docs/cdk-language-support.md) for the full feature matrix.

### Quick Compatibility Check

```bash
node --version  # Should be 24+
ls cdk.json     # Should exist in CDK project
```

---

## 🔧 Troubleshooting

### Cache Management

```bash
npx cdk-insights clear-cache    # Clear all caches
npx cdk-insights cache-status   # Check cache status
npx cdk-insights scan --no-cache # Run without cache
```

### Authentication Issues

1. Check your license key: `echo $CDK_INSIGHTS_LICENSE_KEY`
2. Clear the auth cache: `npx cdk-insights clear-cache`
3. Verify your internet connection

### Sensitive Data Detection

CDK Insights detects potentially sensitive data in your CloudFormation templates:

```bash
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](https://cdkinsights.dev)
- **Documentation:** [cdkinsights.dev/docs](https://cdkinsights.dev/docs)
- **Pricing:** [cdkinsights.dev/pricing](https://cdkinsights.dev/pricing)
- **npm:** [npmjs.com/package/cdk-insights](https://www.npmjs.com/package/cdk-insights)
- **License:** [BSL 1.1](./LICENSE) (converts to Apache 2.0 on 2030-04-12)
