UNPKG

@k9securityio/k9-cdk

Version:

Provision strong AWS security policies easily using the AWS CDK.

114 lines (83 loc) 6.9 kB
# AGENTS.md This file provides guidance to coding agents such as Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview k9-cdk (`@k9securityio/k9-cdk`) is an AWS CDK construct library that generates least-privilege AWS resource policies using the k9 Security access capability model. It maps high-level access capabilities (ADMINISTER_RESOURCE, READ_CONFIG, READ_DATA, WRITE_DATA, DELETE_DATA) to specific IAM actions for each supported AWS service. Supported services: S3, KMS, DynamoDB, SQS, EventBridge. ## Build & Test Commands This project uses **projen** (configured in `.projenrc.js`) to manage build tooling. Do not edit `package.json`, `tsconfig.json`, or `.eslintrc.json` directly — edit `.projenrc.js` and run `npx projen`. ```bash # Full build (compile + lint + test) npx projen build # Compile TypeScript only npx projen compile # Run all tests npx projen test # Run tests in watch mode npx projen test:watch # Run a single test file npx jest test/k9policy.test.ts # Run a single test by name npx jest -t "test name pattern" # Lint npx projen eslint # Update snapshots after intentional changes npx jest -u ``` The `Makefile` provides: * `make init` — install dependencies via `yarn install` (requires yarn installed globally) * `make build` — same as `npx projen build` * `make examples` — builds the library then builds and synthesizes the examples app * `make all` — init + build + deploy The `examples/Makefile` provides `make examples` which compiles the parent library, installs example dependencies, builds, and synthesizes. ## Architecture ### Core Module: `src/k9policy.ts` The foundation of the library. Contains: - `AccessCapability` enum — the five k9 access capabilities - `IAccessSpec` interface — maps principal ARNs to capabilities - `K9PolicyFactory` class — generates IAM Allow/Deny statements from access specs - `resources/capability_summary.json` — maps each AWS service's IAM actions to access capabilities ### Service Modules: `src/s3.ts`, `src/kms.ts`, `src/dynamodb.ts`, `src/sqs.ts`, `src/events.ts` Each adapts `K9PolicyFactory` to its service's CDK API: - **S3/SQS/EventBridge**: Use `grantAccessViaResourcePolicy()` — adds statements to existing resource policy via `addToResourcePolicy()` - **KMS/DynamoDB**: Use `makeResourcePolicy()` — returns a `PolicyDocument` to pass as a construct prop - **EventBridge**: Supports only ADMINISTER_RESOURCE, READ_CONFIG, WRITE_DATA (not READ_DATA or DELETE_DATA). Uses PascalCase SIDs (like DynamoDB). ### Utility: `src/aws-iam-utils.ts` `getAllowedPrincipalArns()` extracts existing allowed principals from a PolicyDocument, used when merging with existing policies. ### Policy Generation Pattern Every generated policy follows the same structure: 1. Allow statements per capability (principals × actions for that capability) 2. Service-specific enforcement (e.g., DenyInsecureCommunications, DenyUnencryptedStorage for S3) 3. DenyEveryoneElse statement — explicit deny for principals not in allow list 4. DenyUntrustedOrgs statement — explicit deny for principals outside specified orgs (only when `restrictToPrincipalOrgIDs` is set on any access spec) ### Key Design Details - S3 module preserves existing bucket policy statements (important for CDK features like `autoDeleteObjects`) - SQS has a 7-action-per-statement limit; the code partitions large allow statements into numbered sub-statements - KMS supports two modes: `trustAccountIdentities` (true/false) controlling whether account root gets full key access - DenyEveryoneElse uses `aws:PrincipalArn` conditions (SQS/DynamoDB) to avoid blocking AWS service principals - When wildcard `*` principal is used with `restrictToPrincipalOrgIDs`, DenyEveryoneElse is skipped (cannot meaningfully except `*` from a deny). Currently only EventBridge implements this check. - `restrictToPrincipalOrgIDs` on `IAccessSpec` adds `StringEquals: aws:PrincipalOrgID` to Allow statements and generates a `DenyUntrustedOrgs` deny statement with `StringNotEquals: aws:PrincipalOrgID` + `Bool: aws:PrincipalIsAWSService=false` - DenyUntrustedOrgs is generated by `K9PolicyFactory._makeDenyUntrustedOrgsStatement()` (internal method) and is independent of DenyEveryoneElse (e.g., KMS generates it even when `trustAccountIdentities: false` omits DenyEveryoneElse) ## Testing Patterns - Tests import from `../lib` (compiled output), not `../src` - `test/k9.test.ts` — integration tests covering S3, KMS, DynamoDB via the public API - `test/k9policy.test.ts` — unit tests for K9PolicyFactory internals - `test/sqs.test.ts` — SQS-specific tests (statement partitioning, etc.) - `test/events.test.ts` — EventBridge-specific tests (org-scoping, wildcard principals, multi-org) - `test/helpers.ts``stringifyStatement()`, `stringifyPolicy()` for readable policy output in tests - Snapshot tests via `SynthUtils.toCloudFormation(stack)` — update with `npx jest -u` - CDK assertions via `@aws-cdk/assert` (`haveResource`, `expectCDK`) ## Important Conventions - Default release branch is `v2-main` (not `main`) - JSII compatibility required — this library is compiled with jsii for cross-language support. Avoid TypeScript features not supported by jsii (mapped types, conditional types, `Partial<T>`, `Map<K,V>` as public params, union types like `T | null`). For internal methods that need these types, use `/** @internal */` and prefix with `_`. - ESLint import ordering: warns on misordered imports but does not enforce alphabetical sorting within groups - Example CDK app in `bin/k9-cdk.ts` demonstrates library usage and is deployed for integration testing via `cdk deploy` - Examples app in `examples/example.ts` demonstrates all service modules; depends on the local build via `"file:.."` in `examples/package.json`. The examples directory must NOT have its own `aws-cdk-lib` dependency — it resolves from the parent's `node_modules` to avoid duplicate type conflicts. ## Node.js and Toolchain - Node.js version is pinned to v22 LTS (`lts/jod`) via `.nvmrc` (local) and `workflowNodeVersion` in `.projenrc.js` (CI) - jsii version is `~5.9.0` — the current supported release line. jsii 5.9 officially supports Node ^20 and ^22. - Package manager is **yarn** (v1/Classic). `yarn.lock` is tracked in git. Do not use `npm install` in the project root — it creates a conflicting `package-lock.json`. - `make clean` removes `node_modules`; run `make init` (which requires globally-installed yarn) to bootstrap. ## Releases - Releases are automated via the `release` GitHub Actions workflow, triggered on push to `v2-main` - Version bumps are determined automatically by projen/standard-version from conventional commit prefixes (`feat:` → minor, `fix:`/`build:` → patch) - The workflow publishes to npm (requires `NPM_TOKEN` secret) and creates a GitHub Release