# 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.

[![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 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

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

That's it. CDK Insights will synthesize your stacks and scan them.

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

---

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

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

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 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, and `provided.al2023` are all current AWS LTS at time of writing but get flagged depending on how recent your `aws-cdk-lib` version is.

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

```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 (100+ rules), JSON/Table/Markdown/SARIF output, multi-stack analysis, CLI access |
| **Pro** | £9.99/mo | Everything in Free + AI analysis (Bedrock), GitHub integration, dashboard, PDF reports, 10,000 resources/mo |
| **Team** | £7.99/member/mo | Everything in Pro + team management, shared configs, audit trails, 15,000 resources/member |

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.

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

---

## 🧰 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](./docs/cdk-language-support.md) for the full feature matrix.

### Quick Compatibility Check

```bash
node --version  # Should be 22+
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)
