diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c5d4ff..2afed0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,10 +165,9 @@ jobs: # The '**' glob is required to descend into Stage-nested stacks. Without # it, `cdk synth` only synthesizes the top-level App (which contains the - # Stage but no stacks directly), so cdk-nag never runs against the actual - # WAF/backend/frontend stacks and any unsuppressed findings silently pass - # CI. `make cdk-synth` already uses the same glob — this aligns the - # CI gate with what runs locally. + # Stage but no stacks directly), so asset bundling never runs against the + # actual WAF/backend/frontend stacks. `make cdk-synth` uses the same glob + # — this aligns the CI gate with what runs locally. - name: Run cdk synth env: CDK_DEFAULT_ACCOUNT: "123456789012" @@ -176,6 +175,15 @@ jobs: AWS_DEFAULT_REGION: us-east-1 run: npx cdk synth '**' --quiet + # cdk-nag v3 hard gate: CDK signals a failed policy validation by setting + # process.exitCode in the NODE process — for a Python app that's jsii's + # throwaway kernel, so the synth above exits 0 even with findings + # (verified live). The checker fails on any violation AND on a missing + # report (packs not attached = broken gate, not a pass); see + # scripts/check_validation_report.py. + - name: Check cdk-nag validation report + run: uv run python scripts/check_validation_report.py cdk.out + - name: Run CDK stack assertion tests env: AWS_DEFAULT_REGION: us-east-1 diff --git a/CLAUDE.md b/CLAUDE.md index b72ef4a..78f4da2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,20 +17,22 @@ CDK and Powertools require incompatible `attrs` versions (CDK pulls `attrs<26` v Run `make doctor` after `make install` to verify both venvs picked up the expected groups, `npx cdk`/`drawio` resolve, and pre-commit is wired. `make clean-venvs && make install` is the recovery path for a corrupted venv. `make pr` runs every CI gate locally in CI's order. -## `cdk synth` must use `'**'` +## `cdk synth` must use `'**'` — and the nag gate is the report check, not the exit code -All five stacks live inside `AppStage` (a `cdk.Stage`). Bare `cdk synth` walks only the App's direct children, finds the Stage, doesn't recurse, and emits an empty synthesis that succeeds *without* running cdk-nag against the real stacks. `make cdk-synth` and the CI `cdk-check` job both invoke `cdk synth '**'`. If you run `cdk synth` directly during development, include the glob — otherwise the gate passes silently regardless of what cdk-nag would find. +All five stacks live inside `AppStage` (a `cdk.Stage`). Bare `cdk synth` walks only the App's direct children, finds the Stage, doesn't recurse, and emits an empty synthesis — asset bundling never runs against the real stacks. `make cdk-synth` and the CI `cdk-check` job both invoke `cdk synth '**'`. -## cdk-nag is a hard gate +**The CLI's exit code is NOT the cdk-nag gate.** cdk-nag v3 packs are policy-validation plugins, and CDK signals validation failure by setting `process.exitCode` in the **Node** process — for this Python app that's jsii's throwaway kernel, so `cdk synth` exits 0 even with findings (verified live). The hard gate is `scripts/check_validation_report.py`, run over `cdk.out/validation-report.json` by both `make cdk-synth` and the CI step right after synth; it fails on any violation and on a *missing* report (packs not attached = broken gate, not a pass). -Five rule packs run on every synth: AwsSolutions, Serverless, NIST 800-53 R5, HIPAA Security, PCI DSS 3.2.1. Findings fail CI. Resolve by: +## cdk-nag is a hard gate (v3: policy-validation plugins, not Aspects) + +Five rule packs — AwsSolutions, Serverless, NIST 800-53 R5, HIPAA Security, PCI DSS 3.2.1 — run as cdk-nag **v3 policy-validation plugins**, attached ONCE at the App root (`attach_nag_packs` in `nag_utils.py`, called from `app.py` and the nag-gating test fixtures). They evaluate the synthesized assembly, not per-stack Aspects. Findings fail CI. Resolve by: 1. **Fix the underlying issue** (preferred). README "Design decisions and known limitations" documents recurring patterns. -2. **Suppress with rationale**. Every suppression carries a `reason=` string. For `AwsSolutions-IAM5` wildcards, scope with `applies_to=["Resource::*"]` or a specific pattern and explain *why* the wildcard is unavoidable. +2. **Acknowledge with rationale** via `acknowledge_rules(construct, [{"id": ..., "reason": ..., "applies_to": [...]}])` — the project-wide adapter in `nag_utils.py` that keeps the v2-era data shape and maps it onto v3's `Validations.of().acknowledge()`. Three v3 rules to know: **granular rules (IAM4/IAM5) match individual `Rule[Finding]` ids only** — a bare `AwsSolutions-IAM5` acknowledgment matches nothing, so every wildcard needs its exact `applies_to` finding (the gate's failure output prints them); **acknowledgments cover the whole construct subtree** (v2's `apply_to_children` is gone); and **finding ids containing more than one `::`** (IAM4's `Policy::arn::...`) are rejected by CDK's acknowledge API — `acknowledge_rules` routes those through the `aws:cdk:acknowledged-rules` metadata fallback (documented in its docstring; drop when fixed upstream). -A bespoke validation Aspect rides alongside the packs (also wired by `apply_compliance_aspects`): `TemplateConventionChecks` in `infrastructure/validation_aspects.py` enforces two project conventions no rule pack covers — every log group declares an explicit retention (never-expire is the CloudWatch default), and every stateful resource (`CfnBucket`, DynamoDB `CfnTable`/`CfnGlobalTable`, `CfnKey`) declares an explicit removal policy. Its violations are error-level annotations, so they fail the same gates as nag findings (and `TestNagCompliance`); it's unit-tested in `tests/cdk/test_validation_aspects.py`. +A bespoke validation Aspect stays per-stack (wired by `apply_compliance_aspects`): `TemplateConventionChecks` in `infrastructure/validation_aspects.py` enforces two project conventions no rule pack covers — every log group declares an explicit retention, and every stateful resource declares an explicit removal policy. Its violations are error-level annotations (asserted empty by `TestNagCompliance`); it's unit-tested in `tests/cdk/test_validation_aspects.py`. `apply_compliance_aspects` also registers cdk-nag's `WriteNagSuppressionsToCloudFormationAspect` per stack — the v2-style `cdk_nag` Metadata audit trail in templates — because Aspects don't cross `cdk.Stage` boundaries, so registering it at the App root would silently skip every Stage-nested stack (verified live). -**Local nag gate**: `Template.from_stack()` does **NOT** raise on cdk-nag (or validation-Aspect) errors, but they surface as error-level annotations — and `tests/cdk/test_stage.py::TestNagCompliance` asserts that list is empty for every stack (prod and ephemeral shapes). So `make test-cdk` catches unsuppressed findings locally, without Docker. The CLI `cdk synth '**'` in the CI `cdk-check` job remains the authoritative gate (it also exercises asset bundling); run `make cdk-synth` with Docker started for the full CI-equivalent check before pushing IAM-touching code. +**Local nag gate**: neither `app.synth()` nor `Template.from_stack()` raises on v3 findings (CDK sets `process.exitCode` in jsii's throwaway Node kernel). `tests/cdk/test_stage.py::TestNagCompliance` synthesizes every shipped shape (prod, dev, `appconfig_monitor`, `retain_data`) and parses each assembly's `validation-report.json`; `test_nag_gate_can_fail` is the canary proving the gate is not vacuous. So `make test-cdk` catches unacknowledged findings locally, without Docker. The CI `cdk-check` job pairs `cdk synth '**'` (asset bundling) with `scripts/check_validation_report.py` (the CLI-side nag gate); run `make cdk-synth` with Docker started for the full CI-equivalent check before pushing IAM-touching code. ## Encryption posture diff --git a/Makefile b/Makefile index a90da0f..1a16cf4 100644 --- a/Makefile +++ b/Makefile @@ -193,9 +193,16 @@ coverage-badge: ## Generate the shields-endpoint coverage badge JSON (whole repo cdk-synth: ## Synthesize all CDK stacks and validate cdk-nag rules (CDK CLI via `npm ci` / `make install`) # The '**' glob descends into Stage-nested stacks. Without it, `cdk synth` # stops at the Stage manifest, the five nested stacks never synthesize, - # and cdk-nag rules silently don't fire on them — so a "passing" synth - # can mask findings that surface later in `cdk deploy`. + # and asset bundling silently doesn't run on them. + # + # The explicit report check is the cdk-nag v3 hard gate: CDK signals a + # failed policy validation by setting process.exitCode in the NODE process, + # which for a Python app is jsii's throwaway kernel — so `cdk synth` exits 0 + # even with findings (verified live; see scripts/check_validation_report.py). + # The checker fails on any violation AND on a missing report (packs not + # attached = broken gate, not a pass). $(CDK) synth '**' $(CDK_ENV_ARG) + uv run python scripts/check_validation_report.py cdk.out cdk-notices: ## Show AWS-published CDK notices (CVEs, deprecated CDK versions, upcoming breaking changes) $(CDK) notices diff --git a/README.md b/README.md index 384ce11..a8fd71b 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,12 @@ This project contains source code and supporting files for a serverless applicat - `app.py` - CDK entry point; instantiates the data, WAF, backend, frontend, and audit stacks and calls `app.synth()` - `lambda/` - Code for the application's Lambda function - `infrastructure/data_stack.py` - The stateful data stack — DynamoDB idempotency table + its dedicated CMK, with the `retain_data` retention switch -- `infrastructure/backend_stack.py` - The backend CDK stack — a thin wrapper that composes `BackendApp`, applies cdk-nag Aspects, and wires CfnOutputs +- `infrastructure/backend_stack.py` - The backend CDK stack — a thin wrapper that composes `BackendApp`, applies the compliance Aspects, and wires CfnOutputs - `infrastructure/backend_app.py` - `BackendApp` construct — owns the compute domain resources (Lambda, API Gateway, SSM, AppConfig, monitoring); consumes the idempotency table from the data stack - `infrastructure/waf_stack.py` - The WAF stack (CloudFront-scoped WebACL, always in `us-east-1`) - `infrastructure/frontend_stack.py` - The frontend stack (S3 + CloudFront, access-log bucket, Glue/Athena log analytics) - `infrastructure/audit_stack.py` - The stateful audit stack — CloudTrail object-level S3 data-event trail + its log bucket + a dedicated CMK, `retain_data`-gated (audits the frontend buckets one-way) -- `infrastructure/nag_utils.py` - Shared cdk-nag rule-pack helper (`apply_compliance_aspects`), the `TemplateConventionChecks` validation Aspect, and shared KMS-grant / log-group / suppression helpers +- `infrastructure/nag_utils.py` - Shared cdk-nag v3 helpers (`attach_nag_packs`, `acknowledge_rules`, `apply_compliance_aspects`) plus shared KMS-grant / log-group helpers - `frontend/` - Static assets (`index.html`) deployed to the frontend S3 bucket - `tests/` - Unit and integration tests - `tests/conftest.py` - Shared test fixtures (API Gateway event, Lambda context, mocks) @@ -373,7 +373,7 @@ The project follows the CDK best practice ["model with constructs, deploy with s - [`DataStack`](infrastructure/data_stack.py) (`Stack`) owns the stateful layer — the DynamoDB idempotency table and its dedicated CMK — kept separate from the stateless compute so the two have independent lifecycles (see [Stateful data stack and `retain_data`](#stateful-data-stack-and-retain_data)). - [`BackendApp`](infrastructure/backend_app.py) (`Construct`) owns the compute-side KMS key, SSM parameter, AppConfig app, Lambda function, API Gateway, Application Insights, dashboard, Logs Insights saved queries, and per-resource cdk-nag suppressions. The idempotency table is passed in from the data stack (`idempotency_table=`) rather than created here. -- [`BackendStack`](infrastructure/backend_stack.py) (`Stack`) instantiates `BackendApp(self, "App", idempotency_table=...)`, calls `apply_compliance_aspects(self)`, wires CfnOutputs, attaches stack-level and singleton-scoped suppressions. +- [`BackendStack`](infrastructure/backend_stack.py) (`Stack`) instantiates `BackendApp(self, "App", idempotency_table=...)`, calls `apply_compliance_aspects(self)`, wires CfnOutputs, attaches stack-level and singleton-scoped acknowledgments. - [`AuditStack`](infrastructure/audit_stack.py) (`Stack`) owns the second stateful layer — the CloudTrail object-level S3 data-event trail, its log bucket, and a dedicated CMK. It audits the frontend asset + access-log buckets via a one-way import (`audited_buckets=`; audit → frontend, never the reverse) — see [Audit stack and log retention](#audit-stack-and-log-retention). The data, WAF, and frontend stacks are small enough (single logical unit each) to keep their resources inline — the construct-extraction pattern is demonstrated on the backend as the reference example. @@ -1215,7 +1215,7 @@ Security follows the AWS [CDK security best practices guide](https://docs.aws.am ### CDK security checks -All five stacks use [cdk-nag](https://github.com/cdklabs/cdk-nag) with five rule packs applied at synth time. Any unsuppressed finding fails `cdk synth` — infrastructure misconfigurations are caught before deployment. Checks run automatically on every `cdk synth` and `cdk deploy`; the pack set is attached by [`apply_compliance_aspects`](infrastructure/nag_utils.py), called from each stack's constructor, so adding or removing a pack is a one-line change in one place. +All five stacks use [cdk-nag](https://github.com/cdklabs/cdk-nag) with five rule packs applied at synth time. Any unsuppressed finding fails `cdk synth` — infrastructure misconfigurations are caught before deployment. Checks run automatically on every `cdk synth` and `cdk deploy`; the pack set is attached once at the App root by [`attach_nag_packs`](infrastructure/nag_utils.py) (cdk-nag v3 packs are policy-validation plugins over the synthesized assembly, not per-stack Aspects), so adding or removing a pack is a one-line change in one place. Because CDK reports a failed validation by setting the exit code of the Node process — jsii's throwaway kernel for a Python app — the CLI synth alone is not the gate: `make cdk-synth` and CI pair it with [`scripts/check_validation_report.py`](scripts/check_validation_report.py), which fails on any violation and on a missing report. The gate also runs **inside the test suite**: `tests/cdk/test_stage.py::TestNagCompliance` synthesizes every stack in-process and asserts zero error-level annotations. `Template.from_stack()` never *raises* on Aspect errors (which historically meant a clean `make test-cdk` could still ship a nag-failing commit), but the findings are present as annotations — so the test catches an unsuppressed finding locally, without Docker, before CI does. The CLI `cdk synth '**'` in the `cdk-check` job remains the authoritative gate since it also exercises asset bundling. @@ -1249,11 +1249,11 @@ cdk-nag is the most actively maintained CDK-native compliance gate and the only | Tool | Architecture | Rule language | Suppression UX | |------|--------------|---------------|----------------| -| **cdk-nag** (in use) | CDK Aspects, walks the construct tree at synth time | TypeScript / Python rules shipped in the cdk-nag package | Native CDK API — `NagSuppressions.add_resource_suppressions(...)` with reason strings, per-resource or stack-level, supports `apply_to_children=True` | +| **cdk-nag** (in use, v3) | CDK policy-validation plugins evaluating the synthesized assembly (v3 rewrote the v2 Aspect engine onto CDK's native validation framework) | TypeScript / Python rules shipped in the cdk-nag package | Native CDK API — `Validations.of(construct).acknowledge(...)` with reason strings (this repo's `acknowledge_rules` adapter keeps the `{id, reason, applies_to}` data shape); acknowledgments cover the construct subtree | | **[cdk-validator-cfnguard](https://github.com/cdklabs/cdk-validator-cfnguard)** | `PolicyValidationPluginBeta1` plugin, runs the [cfn-guard](https://github.com/aws-cloudformation/cloudformation-guard) binary against the synthesized CFN JSON | [Guard DSL](https://github.com/aws-cloudformation/aws-guard-rules-registry) (declarative `.guard` files) | CFN template metadata (`Metadata.guard.SuppressedRules`) or CLI skip flags — no CDK-aware ergonomics | | **[cdk-validator-checkov](https://github.com/cdklabs/cdk-validator-checkov)** | Same plugin pattern, wraps [Checkov](https://github.com/bridgecrewio/checkov) | Python rules (Checkov's 1,000+ rule database across many cloud providers) | Comments in synthesized JSON or CLI flags — same ergonomic gap as cfn-guard | -The two plugin wrappers are *architecturally newer* (the `PolicyValidationPluginBeta1` framework post-dates cdk-nag's Aspects pattern) but they're a different category: they bolt CFN-template scanners onto CDK after synthesis. They lose the CDK-aware view (construct tree, parent/child relationships, pre-synth attributes), and they introduce a second suppression model that doesn't compose with the cdk-nag suppressions already in this repo. The compliance content also overlaps heavily — NIST 800-53 R5, HIPAA Security, and PCI DSS 3.2.1 are all available in both ecosystems. +Since v3, cdk-nag itself runs on the same `PolicyValidationPluginBeta1` framework as the two wrappers, so the architectural distinction is gone — what remains different is the category: the wrappers bolt generic CFN-template scanners onto CDK, losing the CDK-aware suppression ergonomics (cdk-nag keeps construct-scoped acknowledgments with reasons; the wrappers suppress via template metadata or CLI flags), and they introduce a second allow-list that doesn't compose with the acknowledgments already in this repo. The compliance content also overlaps heavily — NIST 800-53 R5, HIPAA Security, and PCI DSS 3.2.1 are all available in both ecosystems. **When adding a plugin wrapper would make sense (not the case here):** @@ -1291,13 +1291,13 @@ FedRAMP Moderate and High are AWS's profiles for federal-government workloads. B Most of these are either N/A (no VPC, no root-account-level rules in a template), already implemented (encryption, logging), or covered procedurally rather than at the CFN level (CloudTrail and Backup are typically deployed account-wide, not per-stack). Adding the FedRAMP packs to *this* stack would surface ~zero net-new actionable findings; it would matter for an account intended for FedRAMP authorization, which a public reference architecture isn't. -For a serverless reference architecture already invested in the cdk-nag suppression model — `CDK_LAMBDA_SUPPRESSIONS`, `suppress_cdk_singletons`, per-construct `add_resource_suppressions` calls, the five-pack `apply_compliance_aspects` aspect — layering a second validator would be net-negative: duplicate findings, two allow-lists to maintain, no unique rule coverage gained. cdk-nag stays as the sole compliance gate. +For a serverless reference architecture already invested in the cdk-nag acknowledgment model — `CDK_LAMBDA_SUPPRESSIONS`, `suppress_cdk_singletons`, per-construct `acknowledge_rules` calls, the five packs attached by `attach_nag_packs` — layering a second validator would be net-negative: duplicate findings, two allow-lists to maintain, no unique rule coverage gained. cdk-nag stays as the sole compliance gate. #### Suppressions -Suppressions live in the stack file via `NagSuppressions.add_stack_suppressions` (stack-wide) or `add_resource_suppressions`/`add_resource_suppressions_by_path` (targeted). Each entry has a `reason` explaining why it was suppressed rather than fixed. +Suppressions are cdk-nag v3 **acknowledgments**, applied via `acknowledge_rules(construct, [...])` in [infrastructure/nag_utils.py](infrastructure/nag_utils.py) — the project-wide adapter that keeps the `{id, reason, applies_to}` data shape from the v2 era and maps it onto CDK's native `Validations.of().acknowledge()`. Each entry has a `reason` explaining why it was acknowledged rather than fixed, and acknowledgments cover the construct's whole subtree (v3 walks the ancestor tree when matching a finding). -Stack-level suppressions are reserved for findings that are genuinely stack-wide (e.g. no custom domain, no VPC by design). Everything else is suppressed at the resource level to keep the blast radius small. CDK-managed singleton Lambdas (BucketDeployment provider, S3AutoDeleteObjects, AwsCustomResource) share a common list in `infrastructure/nag_utils.py` (`CDK_LAMBDA_SUPPRESSIONS`), applied via the `suppress_cdk_singletons` helper. It resolves each singleton by stable construct ID through `node.try_find_child` + `add_resource_suppressions(..., apply_to_children=True)` rather than `add_resource_suppressions_by_path` — absolute-path suppression is intentionally avoided because the path prefix changes when the stacks are nested under a `cdk.Stage`. `AwsSolutions-IAM5` suppressions on `ApiFunction` use `applies_to` to scope to specific wildcard actions rather than suppressing all IAM5 findings. +Stack-level acknowledgments are reserved for findings that are genuinely stack-wide (e.g. no custom domain, no VPC by design). Everything else is acknowledged at the resource level to keep the blast radius small. CDK-managed singleton Lambdas (BucketDeployment provider, S3AutoDeleteObjects, AwsCustomResource) share a common list in `infrastructure/nag_utils.py` (`CDK_LAMBDA_SUPPRESSIONS`), applied via the `suppress_cdk_singletons` helper, which resolves each singleton by stable construct ID through `node.try_find_child` — absolute-path targeting is intentionally avoided because the path prefix changes when the stacks are nested under a `cdk.Stage`. Three v3 mechanics matter when adding one: granular rules (IAM4/IAM5) match individual `Rule[Finding]` ids only, so every wildcard carries its exact `applies_to` finding (a bare `AwsSolutions-IAM5` acknowledgment matches nothing — the gate's failure output prints the ids to use); finding ids with more than one `::` (IAM4's `Policy::arn:...` form) are rejected by CDK's acknowledge API and route through `acknowledge_rules`' documented metadata fallback; and the v2-style `cdk_nag` Metadata audit trail in templates is preserved by registering `WriteNagSuppressionsToCloudFormationAspect` per stack in `apply_compliance_aspects` (Aspects don't cross `cdk.Stage` boundaries, so an App-level registration would silently skip every nested stack). Every log group — including singleton Lambdas' — is pre-created in CDK and passed via `log_group=` instead of the legacy `log_retention=` path (which creates an unencrypted group through the `LogRetention` singleton). This closes the `*-CloudWatchLogGroupEncrypted` finding without an Aspect-level suppression. @@ -1678,7 +1678,7 @@ This template ships **one** opinionated, maximal baseline — a static SPA on Cl - **API-only (no frontend):** drop `FrontendStack` (S3 + CloudFront + RUM + Cognito + the Athena/Glue access-log tables), `WafStack` (the CloudFront-scoped WebACL — only CloudFront consumes it), and `AuditStack` (it exists solely to audit the frontend's buckets); keep `DataStack` + `BackendStack`. The API keeps its protection — the **regional** WAF lives inside `BackendApp`, not in `WafStack`, so it stays. In `AppStage` you also remove the now-orphaned cross-refs: the `waf_acl_arn`, the `audited_buckets` import, and the WAF-log-location strings that were fed to the frontend. - **No object-level audit needed?** Removing the Trail is a clean deletion of `AuditStack` (see [Audit stack and log retention](#audit-stack-and-log-retention)) — the most expensive line in the [cost overview](#cost-overview). -- **One compliance framework, not five?** `apply_compliance_aspects` in [`nag_utils.py`](infrastructure/nag_utils.py) runs AwsSolutions + Serverless + NIST + HIPAA + PCI together to *demonstrate* the pattern; keep the one you're actually audited against and drop the rest (which sheds their suppressions too). +- **One compliance framework, not five?** `attach_nag_packs` in [`nag_utils.py`](infrastructure/nag_utils.py) runs AwsSolutions + Serverless + NIST + HIPAA + PCI together to *demonstrate* the pattern; keep the one you're actually audited against and drop the rest (which sheds their suppressions too). - **Cut topology cost:** fold the data/audit stacks back into compute for a small single-service fork (a documented, valid simplification — see [Design decisions](#design-decisions-and-known-limitations)), or share one `WafStack` across long-lived environments rather than one per deployment ([cost overview](#cost-overview)). **2. Then add what production needs.** Once it's stripped to your shape, work the **Production readiness checklist in [`TODO.md`](TODO.md)** — the central add-list. The headline items deliberately left unshipped: **authentication/authorization** on the API (Cognito/JWT authorizer), restricting **CORS** from `*` to your real origin, an **AWS Backup** plan plus `-c retain_data=true` ([retain_data](#stateful-data-stack-and-retain_data)), and a **custom domain + ACM certificate**. Per-item rationale lives in [Scaling beyond a reference architecture](#scaling-beyond-a-reference-architecture). @@ -1852,7 +1852,7 @@ This project is a single-developer sample. AWS publishes broader guidance in [Be | Recommendation | Status | |----------------|--------| -| **cdk-nag with custom Aspects** for org-specific policy enforcement | Five rule packs plus a bespoke `TemplateConventionChecks` Aspect (log-group retention + explicit removal policy), both wired by `apply_compliance_aspects`, see [CDK security checks](#cdk-security-checks) | +| **cdk-nag with custom Aspects** for org-specific policy enforcement | Five rule packs (v3 policy-validation plugins via `attach_nag_packs`) plus a bespoke `TemplateConventionChecks` Aspect (log-group retention + explicit removal policy) wired per stack by `apply_compliance_aspects`, see [CDK security checks](#cdk-security-checks) | | **cdk-monitoring-constructs** for dashboards and alarms | Wired through `MonitoringFacade` covering Lambda, API Gateway, DynamoDB | | **CDK code held to application-code quality standards** | ruff, mypy, pylint, bandit, pip-audit all run via pre-commit and CI | | **Automated CI/CD with policy gates** | GitHub Actions runs `cdk synth` (which runs cdk-nag) on every PR, blocks merge on findings; a `cdk-diff` job posts the CloudFormation diff for review | diff --git a/app.py b/app.py index 3dd2c2e..aeb151f 100644 --- a/app.py +++ b/app.py @@ -63,9 +63,18 @@ import aws_cdk as cdk from infrastructure.app_stage import DEFAULT_ENV_NAME, AppStage, parse_context_flag, stage_id +from infrastructure.nag_utils import attach_nag_packs app = cdk.App() +# cdk-nag v3: the five rule packs are policy-validation plugins evaluated over +# the synthesized assembly during app.synth() — attached ONCE here at the App +# root, not per stack (per-stack Aspects carry only the project's own +# TemplateConventionChecks; see nag_utils.attach_nag_packs). Unacknowledged +# findings fail the synth itself, so `cdk synth` (CLI and in-process alike) +# remains the hard gate. +attach_nag_packs(app) + # Target region for the backend and frontend stacks. Defaults to us-east-1 # when no context value is provided. WAF is always pinned to us-east-1 # inside the Stage regardless of this value. diff --git a/infrastructure/audit_stack.py b/infrastructure/audit_stack.py index 590d837..4d7036b 100644 --- a/infrastructure/audit_stack.py +++ b/infrastructure/audit_stack.py @@ -34,10 +34,10 @@ from aws_cdk import aws_kms as kms from aws_cdk import aws_logs as logs from aws_cdk import aws_s3 as s3 -from cdk_nag import NagSuppressions from constructs import Construct from infrastructure.nag_utils import ( + acknowledge_rules, apply_compliance_aspects, create_auto_delete_objects_log_group, create_sse_s3_log_bucket, @@ -199,14 +199,13 @@ def __init__( include_management_events=False, ) inline_policy_reason = "CDK generates the trail's LogsRole default policy inline — not directly configurable" - NagSuppressions.add_resource_suppressions( + acknowledge_rules( s3_data_events_trail, [ {"id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": inline_policy_reason}, {"id": "HIPAA.Security-IAMNoInlinePolicy", "reason": inline_policy_reason}, {"id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": inline_policy_reason}, ], - apply_to_children=True, ) # The destroy-friendly bucket uses auto_delete_objects, which synthesizes diff --git a/infrastructure/backend_app.py b/infrastructure/backend_app.py index 51830e7..534b86a 100644 --- a/infrastructure/backend_app.py +++ b/infrastructure/backend_app.py @@ -76,10 +76,10 @@ MonitoringFacade, SnsAlarmActionStrategy, ) -from cdk_nag import NagSuppressions from constructs import Construct from infrastructure.nag_utils import ( + acknowledge_rules, build_managed_threat_rules, create_auto_delete_objects_log_group, create_waf_logs_bucket, @@ -781,7 +781,7 @@ def _build_monitoring(self, lambda_log_group: logs.LogGroup, is_production_env: # on every alarm under the facade. Scoped to the monitoring subtree; # the production shape routes every alarm to SNS and needs no # suppression. - NagSuppressions.add_resource_suppressions( + acknowledge_rules( monitoring, [ { @@ -793,7 +793,6 @@ def _build_monitoring(self, lambda_log_group: logs.LogGroup, is_production_env: "reason": "Ephemeral/dev environment — alarms are dashboard signals only; no paging channel by design", }, ], - apply_to_children=True, ) return alarm_topic @@ -1023,15 +1022,17 @@ def _attach_canary_deployment(self, is_production_env: bool) -> None: # Roll back on a failed deployment or if the alarm fires mid-shift. auto_rollback=codedeploy.AutoRollbackConfig(failed_deployment=True, deployment_in_alarm=True), ) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( deployment_group, [ { "id": "AwsSolutions-IAM4", "reason": "CodeDeploy service role uses the AWS managed AWSCodeDeployRoleForLambdaLimited policy — the documented least-privilege role for Lambda traffic-shifting deployments", + "applies_to": [ + "Policy::arn::iam::aws:policy/service-role/AWSCodeDeployRoleForLambdaLimited" + ], }, ], - apply_to_children=True, ) def _attach_appconfig_rollback_monitor(self, app_config_env: appconfig.CfnEnvironment) -> None: @@ -1102,7 +1103,7 @@ def _attach_appconfig_rollback_monitor(self, app_config_env: appconfig.CfnEnviro description="Lets AppConfig read the rollback alarm state during a flag deployment", ) monitor_role.add_to_policy(iam.PolicyStatement(actions=["cloudwatch:DescribeAlarms"], resources=["*"])) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( monitor_role, [ { @@ -1123,7 +1124,6 @@ def _attach_appconfig_rollback_monitor(self, app_config_env: appconfig.CfnEnviro "reason": "CDK-generated inline policy on the AppConfig monitor role", }, ], - apply_to_children=True, ) # Wire the monitor onto the environment created earlier. Set here (rather @@ -1151,7 +1151,7 @@ def _suppress_rollback_alarm_actions(self, alarm: cloudwatch.Alarm, consumer: st f"Alarm is consumed by {consumer}, which polls its state to decide on rollback — " "not a notification channel, so no SNS action by design" ) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( alarm, [ {"id": "NIST.800.53.R5-CloudWatchAlarmAction", "reason": reason}, @@ -1167,7 +1167,7 @@ def _add_resource_suppressions(self, app_insights_dashboard_cleanup: cr.AwsCusto intentional design decisions (no VPC, no DLQ, no concurrency) and work around CDK-level limitations (inline policies, KMS wildcard actions). """ - NagSuppressions.add_resource_suppressions( + acknowledge_rules( self.function, [ # AwsSolutions-L1 / Serverless-LambdaLatestVersion suppressions @@ -1201,7 +1201,9 @@ def _add_resource_suppressions(self, app_insights_dashboard_cleanup: cr.AwsCusto {"id": "NIST.800.53.R5-LambdaInsideVPC", "reason": "No VPC — adds significant operational complexity"}, {"id": "HIPAA.Security-LambdaInsideVPC", "reason": "No VPC — adds significant operational complexity"}, {"id": "PCI.DSS.321-LambdaInsideVPC", "reason": "No VPC — adds significant operational complexity"}, - # Service role uses AWSLambdaBasicExecutionRole managed policy + # Service role uses AWSLambdaBasicExecutionRole managed policy. + # The Policy:: finding id contains multiple '::', which routes + # through acknowledge_rules' metadata fallback (see nag_utils). { "id": "AwsSolutions-IAM4", "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution", @@ -1237,13 +1239,12 @@ def _add_resource_suppressions(self, app_insights_dashboard_cleanup: cr.AwsCusto "reason": "CDK generates the default policy inline on the Lambda service role — not directly configurable", }, ], - apply_to_children=True, # covers service role and default policy ) # AppInsights cleanup custom resource policy: scoped to one dashboard ARN, # so only the inline-policy nag rules need a suppression — IAM5 wildcard # no longer applies since the policy is resource-scoped. - NagSuppressions.add_resource_suppressions( + acknowledge_rules( app_insights_dashboard_cleanup, [ { @@ -1259,7 +1260,6 @@ def _add_resource_suppressions(self, app_insights_dashboard_cleanup: cr.AwsCusto "reason": "AwsCustomResource generates an inline policy — not directly configurable", }, ], - apply_to_children=True, ) # API Gateway CloudWatch role — CDK-managed, uses managed policy. @@ -1269,15 +1269,17 @@ def _add_resource_suppressions(self, app_insights_dashboard_cleanup: cr.AwsCusto # execution logging, which requires the account-level CloudWatch role. api_cw_role = self.api.node.try_find_child("CloudWatchRole") if api_cw_role is not None: - NagSuppressions.add_resource_suppressions( + acknowledge_rules( cast(Construct, api_cw_role), [ { "id": "AwsSolutions-IAM4", "reason": "CDK-managed API Gateway CloudWatch role uses AWS managed policy", + "applies_to": [ + "Policy::arn::iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" + ], } ], - apply_to_children=True, ) def _create_insights_queries(self, lambda_log_group: logs.LogGroup, api_log_group: logs.LogGroup) -> None: diff --git a/infrastructure/backend_stack.py b/infrastructure/backend_stack.py index 9327f0b..543728a 100644 --- a/infrastructure/backend_stack.py +++ b/infrastructure/backend_stack.py @@ -3,12 +3,12 @@ from aws_cdk import CfnOutput, Stack from aws_cdk import aws_dynamodb as dynamodb from aws_cdk import aws_iam as iam -from cdk_nag import NagSuppressions from constructs import Construct from infrastructure.backend_app import BackendApp from infrastructure.nag_utils import ( AWS_CUSTOM_RESOURCE_PROVIDER_ID, + acknowledge_rules, apply_compliance_aspects, attach_async_failure_destination, suppress_cdk_singletons, @@ -154,7 +154,7 @@ def __init__( ) # ── Stack-level cdk-nag suppressions (genuinely stack-wide) ───────────── - NagSuppressions.add_stack_suppressions( + acknowledge_rules( self, [ # ── AWS Solutions ──────────────────────────────────────────────── diff --git a/infrastructure/data_stack.py b/infrastructure/data_stack.py index e462a7a..c06ffd5 100644 --- a/infrastructure/data_stack.py +++ b/infrastructure/data_stack.py @@ -27,10 +27,9 @@ from aws_cdk import CfnOutput, Duration, RemovalPolicy, Stack from aws_cdk import aws_dynamodb as dynamodb from aws_cdk import aws_kms as kms -from cdk_nag import NagSuppressions from constructs import Construct -from infrastructure.nag_utils import apply_compliance_aspects +from infrastructure.nag_utils import acknowledge_rules, apply_compliance_aspects class DataStack(Stack): @@ -116,7 +115,7 @@ def __init__(self, scope: Construct, construct_id: str, *, retain_data: bool = F # scope for this reference (PITR covers the rolling-window recovery a # TTL'd idempotency cache needs); production forks that set # retain_data=True should also add AWS Backup — see TODO.md. - NagSuppressions.add_stack_suppressions( + acknowledge_rules( self, [ { diff --git a/infrastructure/frontend_stack.py b/infrastructure/frontend_stack.py index 1ed8d4d..6d3b07c 100644 --- a/infrastructure/frontend_stack.py +++ b/infrastructure/frontend_stack.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, cast from aws_cdk import ( CfnOutput, @@ -43,12 +43,12 @@ from aws_cdk import ( custom_resources as cr, ) -from cdk_nag import NagSuppressions from constructs import Construct from infrastructure.nag_utils import ( AWS_CUSTOM_RESOURCE_PROVIDER_ID, BUCKET_DEPLOYMENT_PROVIDER_ID, + acknowledge_rules, apply_compliance_aspects, attach_async_failure_destination, create_auto_delete_objects_log_group, @@ -549,14 +549,13 @@ def __init__( "cloudfront:CreateInvalidation on this stack's distribution ARN — managed-policy " "reuse adds nothing" ) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( invalidate_cf_cache, [ {"id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": cf_invalidation_inline_reason}, {"id": "HIPAA.Security-IAMNoInlinePolicy", "reason": cf_invalidation_inline_reason}, {"id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": cf_invalidation_inline_reason}, ], - apply_to_children=True, ) CfnOutput( @@ -594,7 +593,7 @@ def __init__( # Unauthenticated identities are intentional — browsers have no prior # identity and RUM's guest-credentials model is the standard pattern. # The role's only permission is rum:PutRumEvents on this monitor. - NagSuppressions.add_resource_suppressions( + acknowledge_rules( rum_identity_pool, [ { @@ -612,7 +611,7 @@ def __init__( "Single least-privilege inline policy (rum:PutRumEvents on one monitor ARN) " "is tightly bound to this role's sole purpose — anonymous browser telemetry upload" ) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( rum_unauth_role, [ {"id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": inline_policy_reason}, @@ -672,6 +671,8 @@ def __init__( queue_id="BucketDeploymentProviderDlq", ) + self._acknowledge_bucket_deployment_grants(bucket) + # minimizePolicies restructures the BucketDeployment handler's inline # policy into a separate resource under DeployFrontend/CustomResourceHandler. deploy_frontend = self.node.try_find_child("DeployFrontend") @@ -691,11 +692,55 @@ def __init__( ("PCI.DSS.321-S3BucketReplicationEnabled", replication_reason), ("PCI.DSS.321-S3BucketVersioningEnabled", versioning_reason), ] - NagSuppressions.add_stack_suppressions( + acknowledge_rules( self, [{"id": rule, "reason": reason} for rule, reason in stack_suppressions], ) + def _acknowledge_bucket_deployment_grants(self, bucket: s3.Bucket) -> None: + """Acknowledge the BucketDeployment handler's CDK-generated s3 grants. + + cdk-nag v3 matches IAM5 findings individually, so these are enumerated + here rather than in the shared CDK_LAMBDA_SUPPRESSIONS list: the + asset-bucket finding id embeds this stack's region (and the default + bootstrap qualifier hnb659fds), and the destination-bucket finding id + embeds the bucket's logical id — neither is expressible in a static + shared list. The handler needs read on the CDK bootstrap asset bucket + and read/write/delete on the destination bucket; these seven findings + are exactly that policy, and anything new the handler grows will fail + the nag gate rather than being silently absorbed. + """ + bucket_deployment_provider = self.node.try_find_child(BUCKET_DEPLOYMENT_PROVIDER_ID) + if bucket_deployment_provider is None: + return + deployment_reason = ( + "CDK BucketDeployment handler policy — CDK-generated s3 read on the bootstrap asset " + "bucket and read/write/delete on the destination bucket; not configurable by the caller" + ) + bucket_logical_id = self.get_logical_id(cast(s3.CfnBucket, bucket.node.default_child)) + acknowledge_rules( + bucket_deployment_provider, + [ + { + "id": "AwsSolutions-IAM5", + "reason": deployment_reason, + "applies_to": [ + "Action::s3:GetBucket*", + "Action::s3:GetObject*", + "Action::s3:List*", + "Action::s3:DeleteObject*", + "Action::s3:Abort*", + # Both partition renderings — see the RUM cleanup note: + # the CLI synth resolves arn:aws: literals, the + # flag-less test synth renders . + f"Resource::arn:aws:s3:::cdk-hnb659fds-assets--{self.region}/*", + f"Resource::arn::s3:::cdk-hnb659fds-assets--{self.region}/*", + f"Resource::<{bucket_logical_id}.Arn>/*", + ], + }, + ], + ) + def _build_response_headers_policy(self) -> cloudfront.ResponseHeadersPolicy: """Build the CloudFront ResponseHeadersPolicy (managed SECURITY_HEADERS + HSTS + CSP). @@ -892,14 +937,13 @@ def _wire_rum_metrics_extras( "scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" ) for construct in (rum_metrics_destination, rum_extended_metrics): - NagSuppressions.add_resource_suppressions( + acknowledge_rules( construct, [ {"id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": reason}, {"id": "HIPAA.Security-IAMNoInlinePolicy", "reason": reason}, {"id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": reason}, ], - apply_to_children=True, ) return rum_extended_metrics @@ -925,13 +969,18 @@ def _wire_rum_log_group_cleanup( """ monitor_id_prefix = Fn.select(0, Fn.split("-", rum_app_monitor.attr_id)) log_group_name = Fn.join("", [f"/aws/vendedlogs/RUMService_{rum_monitor_name}", monitor_id_prefix]) - log_group_arn = Fn.join( - "", - [ - f"arn:{self.partition}:logs:{self.region}:{self.account}:log-group:/aws/vendedlogs/RUMService_{rum_monitor_name}", - monitor_id_prefix, - ":*", - ], + # The IAM scope uses a name-suffix wildcard (RUMService_{name}*) instead + # of folding the runtime-resolved monitor-id token into the ARN. The + # delete call itself still targets the exact log group (log_group_name + # above); widening only the *grant* from "this monitor id" to "this + # monitor name, any id" costs nothing real — the name embeds the stack + # name — and keeps the ARN a plain literal, which cdk-nag v3 needs: its + # granular IAM5 finding id is the verbatim resource string, and a token + # in the ARN would serialize an Fn::Select JSON blob into the finding id + # this code must then reproduce byte-for-byte to acknowledge. + log_group_arn = ( + f"arn:{self.partition}:logs:{self.region}:{self.account}:" + f"log-group:/aws/vendedlogs/RUMService_{rum_monitor_name}*:*" ) cleanup = cr.AwsCustomResource( self, @@ -957,28 +1006,38 @@ def _wire_rum_log_group_cleanup( "Single least-privilege inline policy attached to the CDK AwsCustomResource handler — " "scoped to logs:DeleteLogGroup on one log-group ARN; managed-policy reuse adds nothing" ) - # AwsSolutions-IAM5 fires because the log-group ARN ends in `:*`, which - # is the standard CloudWatch Logs log-stream wildcard required by every - # log-group resource ARN per the IAM docs — there is no way to grant - # logs:DeleteLogGroup on a log group without the `:*` suffix. The - # resource is otherwise fully scoped to one specific log group (path - # built from this monitor's runtime-resolved ID prefix), so the - # wildcard portion only authorizes log-stream-scope wildcards within - # that one log group, not across log groups. + # AwsSolutions-IAM5 fires on the two wildcards in the grant ARN: the + # standard :* log-stream suffix required by every CloudWatch Logs + # resource ARN per the IAM docs, and the monitor-name suffix wildcard + # explained on log_group_arn above. The acknowledgment pins the exact + # finding id (cdk-nag v3 renders the verbatim resource string, with + # pseudo-parameters as / placeholders). iam5_reason = ( - "Log-group ARN includes the standard :* log-stream wildcard suffix — required for any " - "CloudWatch Logs resource ARN per the IAM service authorization docs. The resource is " - "otherwise scoped to one specific log group built from the monitor's runtime-resolved ID." + "Log-group ARN carries the standard :* log-stream wildcard suffix (required for any " + "CloudWatch Logs resource ARN per the IAM service authorization docs) plus a monitor-name " + "suffix wildcard that keeps the ARN literal for cdk-nag v3's verbatim finding ids — the " + "grant still reaches only this stack's RUM vended log group namespace." ) - NagSuppressions.add_resource_suppressions( + # Both partition renderings: the CLI synth resolves arn:aws: literals + # (cdk.json's @aws-cdk/core:enablePartitionLiterals + target-partitions + # ["aws"]), while the flag-less in-process test synth renders the + # placeholder — and cdk-nag v3's raw IAM5 resource + # finding ids reproduce whichever form the template carries (unlike + # IAM4, which normalizes the partition). Acknowledge both so the CLI + # gate and the test gate stay in lockstep. + rum_log_group_findings = [ + f"Resource::arn:{partition}:logs:{self.region}::" + f"log-group:/aws/vendedlogs/RUMService_{rum_monitor_name}*:*" + for partition in ("aws", "") + ] + acknowledge_rules( cleanup, [ {"id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": reason}, {"id": "HIPAA.Security-IAMNoInlinePolicy", "reason": reason}, {"id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": reason}, - {"id": "AwsSolutions-IAM5", "reason": iam5_reason, "applies_to": ["Resource::*"]}, + {"id": "AwsSolutions-IAM5", "reason": iam5_reason, "applies_to": rum_log_group_findings}, ], - apply_to_children=True, ) def _create_athena_glue_resources(self, access_log_bucket: s3.Bucket, encryption_key: kms.Key) -> None: diff --git a/infrastructure/nag_utils.py b/infrastructure/nag_utils.py index 7ec5cb9..d36e75c 100644 --- a/infrastructure/nag_utils.py +++ b/infrastructure/nag_utils.py @@ -1,30 +1,54 @@ """Shared cdk-nag helpers. -``apply_compliance_aspects`` applies the full available rule-pack set to a -stack so every stack exercises the same compliance gauntlet, plus this +cdk-nag v3 rule packs are ``IPolicyValidationPlugin``s evaluated over the +synthesized assembly, not per-stack ``IAspect``s — so pack attachment and +suppression plumbing both live here in v3 form: + +``attach_nag_packs`` registers the five rule packs as policy-validation +plugins on an App (or test App) root — once per tree, not once per stack. +``apply_compliance_aspects`` remains per-stack but now carries only this project's own ``TemplateConventionChecks`` validation Aspect (log-group retention + explicit removal policy on stateful resources — see -``infrastructure.validation_aspects``). NIST 800-53 R4 is intentionally omitted — -R5 supersedes it and running both would duplicate findings on overlapping -controls. +``infrastructure.validation_aspects``). NIST 800-53 R4 is intentionally +omitted — R5 supersedes it and running both would duplicate findings on +overlapping controls. + +``acknowledge_rules`` is the project-wide adapter from this repo's +suppression data shape (``{id, reason, applies_to?}`` dicts, unchanged from +the v2 era so every call site's *data* stays diff-stable) onto v3's +``Validations.of(construct).acknowledge()``: each ``applies_to`` entry +expands to the granular ``Rule[Finding]`` id v3 requires, and entries +without ``applies_to`` acknowledge the bare rule id. Acknowledgments apply +to the construct's whole subtree (v3 walks the ancestor tree when matching), +which subsumes v2's ``apply_to_children=True``. ``CDK_LAMBDA_SUPPRESSIONS`` is the canonical suppression list for CDK-managed singleton Lambdas (AwsCustomResource provider, BucketDeployment, S3AutoDeleteObjects). Their runtime, memory, tracing, DLQ, VPC, and IAM policies are all managed by CDK and cannot be configured by the caller. Import it and pass it to -``NagSuppressions.add_resource_suppressions`` with ``apply_to_children=True``, -or use the ``suppress_cdk_singletons`` helper. Absolute-path suppression -(``add_resource_suppressions_by_path``) is intentionally avoided throughout this -project: the singletons are resolved via ``node.try_find_child`` so suppressions +``acknowledge_rules``, or use the ``suppress_cdk_singletons`` helper. +Absolute-path suppression is intentionally avoided throughout this project: +the singletons are resolved via ``node.try_find_child`` so suppressions keep working when the stacks are nested under a ``cdk.Stage`` (a path string would break on the added Stage prefix). """ import hashlib -from collections.abc import Iterable +from collections.abc import Iterable, Mapping from typing import cast -from aws_cdk import Annotations, Aspects, CfnOutput, CustomResourceProviderBase, Duration, Fn, RemovalPolicy, Stack +from aws_cdk import ( + Acknowledgment, + Annotations, + Aspects, + CfnOutput, + CustomResourceProviderBase, + Duration, + Fn, + RemovalPolicy, + Stack, + Validations, +) from aws_cdk import aws_iam as iam from aws_cdk import aws_kms as kms from aws_cdk import aws_lambda as _lambda @@ -36,10 +60,10 @@ from cdk_nag import ( AwsSolutionsChecks, HIPAASecurityChecks, - NagSuppressions, NIST80053R5Checks, PCIDSS321Checks, ServerlessChecks, + WriteNagSuppressionsToCloudFormationAspect, ) from constructs import Construct, IConstruct @@ -54,16 +78,92 @@ BUCKET_DEPLOYMENT_PROVIDER_ID = "Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C" +def attach_nag_packs(scope: IConstruct) -> None: + """Register the five cdk-nag v3 rule packs as policy-validation plugins. + + Call once on the App root (``app.py`` does; the nag-gating test fixtures + do the same on their test Apps). cdk-nag v3 packs participate in CDK's + policy validation framework: they evaluate the *synthesized assembly* + during ``app.synth()`` and fail the synthesis on unacknowledged findings — + they are no longer ``IAspect``s added per stack. + + The v2-compatible ``cdk_nag`` Metadata audit trail is NOT enabled here: + the packs' ``write_suppressions_to_cloud_formation`` flag registers its + aspect at this (App) scope, and CDK aspects do not cross ``cdk.Stage`` + boundaries — Stage-nested stacks would silently lose the metadata + (verified empirically against 3.0.1). ``apply_compliance_aspects`` + registers ``WriteNagSuppressionsToCloudFormationAspect`` per stack + instead, which reaches every stack regardless of nesting. + """ + Validations.of(scope).add_plugins( + AwsSolutionsChecks(scope, verbose=True), + ServerlessChecks(scope, verbose=True), + NIST80053R5Checks(scope, verbose=True), + HIPAASecurityChecks(scope, verbose=True), + PCIDSS321Checks(scope, verbose=True), + ) + + def apply_compliance_aspects(stack: Stack) -> None: - """Attach every cdk-nag rule pack plus this project's validation Aspect to ``stack``.""" - Aspects.of(stack).add(AwsSolutionsChecks(verbose=True)) - Aspects.of(stack).add(ServerlessChecks(verbose=True)) - Aspects.of(stack).add(NIST80053R5Checks(verbose=True)) - Aspects.of(stack).add(HIPAASecurityChecks(verbose=True)) - Aspects.of(stack).add(PCIDSS321Checks(verbose=True)) - # Project-specific invariants no rule pack covers: log-group retention and - # explicit removal policies on stateful resources. + """Attach this project's validation Aspect to ``stack``. + + Carries ``TemplateConventionChecks`` plus cdk-nag's + ``WriteNagSuppressionsToCloudFormationAspect`` since cdk-nag v3: the five + rule packs are policy-validation plugins registered once at the App root + via :func:`attach_nag_packs`, not per-stack Aspects. The project-specific + invariants (log-group retention, explicit removal policies on stateful + resources) remain a per-stack Aspect surfacing error annotations, and the + write-suppressions aspect keeps the v2-style ``cdk_nag`` Metadata audit + trail (acknowledged rules + reasons) in every synthesized template — it + must be per stack because aspects do not cross ``cdk.Stage`` boundaries. + """ Aspects.of(stack).add(TemplateConventionChecks()) + Aspects.of(stack).add(WriteNagSuppressionsToCloudFormationAspect()) + + +def acknowledge_rules(construct: IConstruct, suppressions: Iterable[Mapping[str, object]]) -> None: + """Acknowledge cdk-nag findings on ``construct`` (v3 ``Validations`` API). + + The project-wide adapter from this repo's suppression data shape — + ``{"id": rule, "reason": why, "applies_to": [findings...]?}``, unchanged + from the v2 era — onto ``Validations.of().acknowledge()``: + + - each ``applies_to`` entry expands to the granular ``Rule[Finding]`` id + v3 matches individually (e.g. ``AwsSolutions-IAM5[Resource::*]``); + - entries without ``applies_to`` acknowledge the bare rule id, which + matches rules that report a single, non-granular finding. + + Acknowledgments cover the construct's whole subtree — v3 walks the + ancestor tree when checking a finding — so v2's ``apply_to_children`` + distinction no longer exists. + + **Metadata fallback for validator-rejected ids.** CDK's + ``Validations.acknowledge`` rejects any id containing more than one + ``::``, but cdk-nag 3.0.1's own IAM4 findings embed managed-policy ARNs + (``AwsSolutions-IAM4[Policy::arn::iam::aws:policy/…]``) — + the packs emit finding ids their acknowledge API refuses to accept. The + plugin's matching reads the ``aws:cdk:acknowledged-rules`` construct + metadata that ``acknowledge()`` writes, WITHOUT re-validating (verified + empirically against 3.0.1), so ids the front door rejects are written to + that metadata key directly. Drop the fallback once cdk-nag/CDK reconcile + the id grammar upstream. + """ + front_door: list[Acknowledgment] = [] + fallback: dict[str, str] = {} + for suppression in suppressions: + rule_id = cast(str, suppression["id"]) + reason = cast(str, suppression["reason"]) + applies_to = cast("list[str] | None", suppression.get("applies_to")) + finding_ids = [f"{rule_id}[{finding}]" for finding in applies_to] if applies_to else [rule_id] + for finding_id in finding_ids: + if finding_id.count("::") > 1: + fallback[finding_id] = reason + else: + front_door.append(Acknowledgment(id=finding_id, reason=reason)) + if front_door: + Validations.of(construct).acknowledge(*front_door) + if fallback: + construct.node.add_metadata("aws:cdk:acknowledged-rules", fallback) def grant_logs_service_to_key(key: kms.Key, *, region: str, account: str, partition: str) -> None: @@ -281,7 +381,7 @@ def attach_async_failure_destination( dlq_terminal_reason = ( "Terminal DLQ: this queue IS the dead-letter destination — recursing into another DLQ has no recovery value" ) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( dlq, [ {"id": "AwsSolutions-SQS3", "reason": dlq_terminal_reason}, @@ -302,7 +402,7 @@ def attach_async_failure_destination( kms_wildcard_reason = ( "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" ) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( cast(Construct, singleton), [ { @@ -320,7 +420,6 @@ def attach_async_failure_destination( }, {"id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role"}, ], - apply_to_children=True, ) # Surface the DLQ URL so operators can find captured provider failures. Emitted @@ -347,10 +446,9 @@ def suppress_cdk_singletons(scope: IConstruct, singleton_ids: Iterable[str]) -> for singleton_id in singleton_ids: singleton = scope.node.try_find_child(singleton_id) if singleton is not None: - NagSuppressions.add_resource_suppressions( + acknowledge_rules( cast(Construct, singleton), CDK_LAMBDA_SUPPRESSIONS, - apply_to_children=True, ) @@ -402,7 +500,7 @@ def create_auto_delete_objects_log_group(scope: Stack, encryption_key: kms.Key) auto_delete_cr.node.add_dependency(log_group) # The provider is a CDK-managed singleton Lambda; suppress its standard # singleton nag findings here so callers don't each repeat the block. - NagSuppressions.add_resource_suppressions(provider, CDK_LAMBDA_SUPPRESSIONS, apply_to_children=True) + acknowledge_rules(provider, CDK_LAMBDA_SUPPRESSIONS) return provider @@ -481,7 +579,7 @@ def create_sse_s3_log_bucket( removal_policy=removal_policy, auto_delete_objects=auto_delete, ) - NagSuppressions.add_resource_suppressions( + acknowledge_rules( bucket, [{"id": rule, "reason": suppression_reason} for rule in _LOG_SINK_SUPPRESSION_RULES], ) @@ -596,9 +694,20 @@ def create_waf_logs_bucket(scope: Construct, suffix: str) -> s3.Bucket: return bucket -CDK_LAMBDA_SUPPRESSIONS = [ - {"id": "AwsSolutions-IAM4", "reason": "CDK-managed singleton Lambda uses AWS managed execution role"}, - {"id": "AwsSolutions-IAM5", "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy"}, +CDK_LAMBDA_SUPPRESSIONS: list[dict[str, object]] = [ + # cdk-nag v3 matches granular IAM4/IAM5 findings individually, so the + # v2-era blanket entries are enumerated: every CDK-managed singleton runs + # on AWSLambdaBasicExecutionRole (the Policy:: finding id routes through + # acknowledge_rules' metadata fallback — it contains multiple '::'). + # Singleton-specific IAM5 wildcard findings (e.g. the BucketDeployment + # handler's s3 grants) are acknowledged at their call sites, where the + # region- and construct-specific finding ids can be built — a blanket + # AwsSolutions-IAM5 here would match nothing in v3. + { + "id": "AwsSolutions-IAM4", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role", + "applies_to": ["Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"], + }, {"id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable"}, {"id": "Serverless-LambdaTracing", "reason": "CDK-managed singleton Lambda — tracing is not configurable"}, {"id": "Serverless-LambdaDLQ", "reason": "CDK-managed singleton Lambda — DLQ is not configurable"}, diff --git a/infrastructure/waf_stack.py b/infrastructure/waf_stack.py index 466e206..ba894c7 100644 --- a/infrastructure/waf_stack.py +++ b/infrastructure/waf_stack.py @@ -12,10 +12,10 @@ from aws_cdk import ( aws_wafv2 as wafv2, ) -from cdk_nag import NagSuppressions from constructs import Construct from infrastructure.nag_utils import ( + acknowledge_rules, apply_compliance_aspects, build_managed_threat_rules, create_auto_delete_objects_log_group, @@ -162,7 +162,7 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs: Any) -> None: "cross_region_references after stack construction) uses a CDK-generated " "inline policy on its provider role — not directly configurable" ) - NagSuppressions.add_stack_suppressions( + acknowledge_rules( self, [ {"id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": cross_region_writer_reason}, diff --git a/pyproject.toml b/pyproject.toml index 671165c..22d4b1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ cdk = [ "constructs==10.6.0", "aws-cdk-aws-lambda-python-alpha==2.261.0a0", "cdk-monitoring-constructs==10.1.0", - "cdk-nag==2.38.2", + "cdk-nag==3.0.1", ] # Test runner — environment-agnostic; pairs with either `lambda` (unit tests, @@ -151,6 +151,9 @@ known-third-party = ["aws_lambda_powertools", "boto3"] # S603/S607: shells out to the pinned `npx cdk` with a fixed argv (no shell, no # untrusted input). Mirrored by `# nosec` for the separate bandit hook. "scripts/cdk_pr_diff.py" = ["S603", "S607"] +# T201: this is a CLI gate script — its printed report IS the product (the +# violation list operators act on from the build log). +"scripts/check_validation_report.py" = ["T201"] # ============================================================================= # Mypy — static type checker diff --git a/scripts/check_validation_report.py b/scripts/check_validation_report.py new file mode 100644 index 0000000..bd7f8c7 --- /dev/null +++ b/scripts/check_validation_report.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"""Fail when the cdk-nag v3 policy-validation report contains violations. + +cdk-nag v3 packs are CDK policy-validation plugins, and CDK core signals a +failed validation by setting ``process.exitCode = 1`` **in the Node process** +(aws-cdk-lib ``core/lib/private/synthesis.js``). For a Node CDK app that fails +the synth; for a Python app the Node process is jsii's throwaway kernel, so +the exit code is discarded — ``app.synth()`` returns normally and ``cdk synth`` +exits 0 with findings only *printed*. Verified against aws-cdk-lib 2.261.0 + +cdk-nag 3.0.1: a deliberately non-compliant stack synthesized "successfully" +via the CLI while the report listed errors. + +This script is therefore the hard gate for CLI-driven synthesis (``make +cdk-synth`` and the CI ``cdk-check`` job run it right after ``cdk synth``): + +- a **missing report** fails: the packs always attach in ``app.py``, so no + report means validation never ran — the vacuous-gate case, not a pass; +- any **violation** fails, printed with its resources so the finding is + actionable from the build log. + +The in-process equivalent lives in ``tests/cdk/test_stage.py`` (report +parsing + a canary test proving the gate can fail). Drop this script if CDK +ever makes jsii-app synthesis fail natively on validation errors. + +Usage: python scripts/check_validation_report.py [cdk.out] +""" + +import json +import sys +from pathlib import Path + +outdir = Path(sys.argv[1] if len(sys.argv) > 1 else "cdk.out") +report_path = outdir / "validation-report.json" + +if not report_path.exists(): + print( + f"ERROR: {report_path} not found — policy validation never ran. " + "The cdk-nag packs must be attached (attach_nag_packs in app.py); " + "a missing report is a broken gate, not a pass.", + file=sys.stderr, + ) + sys.exit(1) + +report = json.loads(report_path.read_text()) +violations = [ + (violation.get("ruleName", "?"), [r.get("resourceLogicalId", "?") for r in violation.get("violatingResources", [])]) + for plugin_report in report.get("pluginReports", []) + for violation in plugin_report.get("violations", []) +] + +if violations: + print(f"ERROR: {len(violations)} unacknowledged cdk-nag finding(s) in {report_path}:", file=sys.stderr) + for rule, resources in violations: + print(f" {rule} @ {', '.join(resources)}", file=sys.stderr) + sys.exit(1) + +print(f"cdk-nag validation clean ({report_path})") diff --git a/tests/cdk/snapshots/ServerlessAppAudit-us-east-1.json b/tests/cdk/snapshots/ServerlessAppAudit-us-east-1.json index 66c3592..5eeac71 100644 --- a/tests/cdk/snapshots/ServerlessAppAudit-us-east-1.json +++ b/tests/cdk/snapshots/ServerlessAppAudit-us-east-1.json @@ -167,6 +167,64 @@ "AutoDeleteObjectsLogGroupC1303B18", "CloudTrailLogsBucketPolicyF40858DB" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + } + ] + } + }, "Properties": { "BucketName": { "Ref": "CloudTrailLogsBucketF4E95F70" @@ -188,68 +246,55 @@ "rules_to_suppress": [ { "id": "AwsSolutions-S1", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "NIST.800.53.R5-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "HIPAA.Security-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "PCI.DSS.321-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "HIPAA.Security-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "PCI.DSS.321-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "NIST.800.53.R5-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "HIPAA.Security-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "PCI.DSS.321-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "NIST.800.53.R5-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "HIPAA.Security-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" }, { "id": "PCI.DSS.321-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "Q2xvdWRUcmFpbCBsb2cgYnVja2V0IOKAlCBTU0UtUzMgKENsb3VkVHJhaWwgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgZGVzdGluYXRpb24gYnVja2V0czsgdHJhaWwgbG9nIGZpbGVzIGFyZSBwZXItb2JqZWN0IFNTRS1LTVMpLCBzZWxmLWxvZ2dpbmcgd291bGQgY3JlYXRlIGNpcmN1bGFyIGF1ZGl0IHRyYWlscywgbm8gdmVyc2lvbmluZy9yZXBsaWNhdGlvbiBmb3IgYW4gYXBwZW5kLW9ubHksIGludGVncml0eS12YWxpZGF0ZWQgbG9nIHNpbms=" + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" } ] } @@ -293,6 +338,64 @@ "UpdateReplacePolicy": "Delete" }, "CloudTrailLogsBucketPolicyF40858DB": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "CloudTrail log bucket \u2014 SSE-S3 (CloudTrail delivery doesn't support KMS-CMK destination buckets; trail log files are per-object SSE-KMS), self-logging would create circular audit trails, no versioning/replication for an append-only, integrity-validated log sink" + } + ] + } + }, "Properties": { "Bucket": { "Ref": "CloudTrailLogsBucketF4E95F70" @@ -519,32 +622,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -556,18 +648,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -575,18 +664,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -594,8 +680,11 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" } ] } @@ -636,32 +725,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -673,18 +751,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -692,18 +767,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -711,8 +783,11 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" } ] } @@ -749,18 +824,15 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" } ] } @@ -849,18 +921,15 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" } ] } @@ -887,18 +956,15 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgdHJhaWwncyBMb2dzUm9sZSBkZWZhdWx0IHBvbGljeSBpbmxpbmUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "CDK generates the trail's LogsRole default policy inline \u2014 not directly configurable" } ] } diff --git a/tests/cdk/snapshots/ServerlessAppBackend-us-east-1.json b/tests/cdk/snapshots/ServerlessAppBackend-us-east-1.json index 0ad490d..fd48421 100644 --- a/tests/cdk/snapshots/ServerlessAppBackend-us-east-1.json +++ b/tests/cdk/snapshots/ServerlessAppBackend-us-east-1.json @@ -1,54 +1,4 @@ { - "Metadata": { - "cdk_nag": { - "rules_to_suppress": [ - { - "id": "AwsSolutions-APIG2", - "reason": "Request validation not needed for sample app" - }, - { - "id": "AwsSolutions-APIG4", - "reason": "Authorization not needed for sample app" - }, - { - "id": "AwsSolutions-COG4", - "reason": "Cognito authorizer not needed for sample app" - }, - { - "id": "CdkNagValidationFailure", - "is_reason_encoded": true, - "reason": "U2VydmVybGVzcy1BUElHV1N0cnVjdHVyZWRMb2dnaW5nIHZhbGlkYXRpb24gZmFpbHMgZHVlIHRvIGludHJpbnNpYyBmdW5jdGlvbiByZWZlcmVuY2UgaW4gYWNjZXNzIGxvZyBkZXN0aW5hdGlvbiDigJQgc3RydWN0dXJlZCBKU09OIGxvZ2dpbmcgaXMgY29uZmlndXJlZCB2aWEgbG9nZ2luZ19mb3JtYXQ9SlNPTiBvbiB0aGUgTGFtYmRh" - }, - { - "id": "NIST.800.53.R5-APIGWSSLEnabled", - "reason": "Client-side SSL certificates not required for sample app" - }, - { - "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", - "is_reason_encoded": true, - "reason": "QVBJIEdhdGV3YXkgY2FjaGUgY2x1c3RlciBpbnRlbnRpb25hbGx5IGRpc2FibGVkIGZvciBjb3N0IHJlYXNvbnMg4oCUIHRoZSBzbWFsbGVzdCAwLjUgR0IgY2x1c3RlciBpcyB+JDE0L21vbnRoIGZvciBhIHNhbXBsZSBhcHAuIENhY2hpbmcgR0VUIC9ncmVldGluZyB3b3VsZCBhbHNvIHNlcnZlIHN0YWxlIHZhbHVlcyBhY3Jvc3MgU1NNIHBhcmFtZXRlciBhbmQgQXBwQ29uZmlnIGZlYXR1cmUtZmxhZyBjaGFuZ2VzLg==" - }, - { - "id": "HIPAA.Security-APIGWSSLEnabled", - "reason": "Client-side SSL certificates not required for sample app" - }, - { - "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", - "is_reason_encoded": true, - "reason": "QVBJIEdhdGV3YXkgY2FjaGUgY2x1c3RlciBpbnRlbnRpb25hbGx5IGRpc2FibGVkIGZvciBjb3N0IHJlYXNvbnMg4oCUIHNlZSBOSVNULjgwMC41My5SNS1BUElHV0NhY2hlRW5hYmxlZEFuZEVuY3J5cHRlZCByYXRpb25hbGUgYWJvdmUu" - }, - { - "id": "PCI.DSS.321-APIGWSSLEnabled", - "reason": "Client-side SSL certificates not required for sample app" - }, - { - "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", - "is_reason_encoded": true, - "reason": "QVBJIEdhdGV3YXkgY2FjaGUgY2x1c3RlciBpbnRlbnRpb25hbGx5IGRpc2FibGVkIGZvciBjb3N0IHJlYXNvbnMg4oCUIHNlZSBOSVNULjgwMC41My5SNS1BUElHV0NhY2hlRW5hYmxlZEFuZEVuY3J5cHRlZCByYXRpb25hbGUgYWJvdmUu" - } - ] - } - }, "Outputs": { "AlarmTopicName": { "Description": "SNS topic that CloudWatch alarms publish to (attach subscriptions here)", @@ -175,32 +125,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -212,18 +151,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -231,18 +167,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -250,12 +183,59 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" }, { - "id": "AwsSolutions-IAM5", + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -289,20 +269,116 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM5", - "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + "id": "AwsSolutions-L1", + "reason": "CDK-managed singleton Lambda runtime is not configurable" + }, + { + "id": "Serverless-LambdaTracing", + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" + }, + { + "id": "Serverless-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "Serverless-LambdaDefaultMemorySize", + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" + }, + { + "id": "Serverless-LambdaLatestVersion", + "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "NIST.800.53.R5-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "HIPAA.Security-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "HIPAA.Security-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "HIPAA.Security-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "HIPAA.Security-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "PCI.DSS.321-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -329,32 +405,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -366,18 +431,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -385,18 +447,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -404,12 +463,59 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" }, { - "id": "AwsSolutions-IAM5", + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -449,20 +555,116 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM5", - "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + "id": "AwsSolutions-L1", + "reason": "CDK-managed singleton Lambda runtime is not configurable" + }, + { + "id": "Serverless-LambdaTracing", + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" + }, + { + "id": "Serverless-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "Serverless-LambdaDefaultMemorySize", + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" + }, + { + "id": "Serverless-LambdaLatestVersion", + "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "NIST.800.53.R5-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "HIPAA.Security-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "HIPAA.Security-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "HIPAA.Security-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "HIPAA.Security-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "PCI.DSS.321-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -512,6 +714,52 @@ "Type": "AWS::IAM::Policy" }, "AppAlarmTopicAD260C2E": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "KmsMasterKeyId": { "Fn::GetAtt": [ @@ -523,17 +771,63 @@ "Type": "AWS::SNS::Topic" }, "AppAlarmTopicPolicyC01D10E0": { - "Properties": { - "PolicyDocument": { - "Statement": [ + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ { - "Action": "sns:Publish", - "Condition": { - "Bool": { - "aws:SecureTransport": "false" - } - }, - "Effect": "Deny", + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", "Principal": "*", "Resource": { "Ref": "AppAlarmTopicAD260C2E" @@ -589,6 +883,52 @@ }, "AppApiAccessLogsDD2CC3E5": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -603,6 +943,52 @@ }, "AppApiExecutionLogs0AE84813": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -634,67 +1020,99 @@ "rules_to_suppress": [ { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "Serverless-LambdaAsyncFailureDestination", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgbm8gYXN5bmMgZXZlbnQgc291cmNlIGV4aXN0czsgdGhlIEV2ZW50SW52b2tlQ29uZmlnIGV4aXN0cyBvbmx5IHRvIHBpbiByZXRyeV9hdHRlbXB0cz0w" + "reason": "Invoked synchronously via API Gateway \u2014 no async event source exists; the EventInvokeConfig exists only to pin retry_attempts=0" }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { - "id": "AwsSolutions-IAM4", - "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "a21zOkdlbmVyYXRlRGF0YUtleSogYW5kIGttczpSZUVuY3J5cHQqIHJlcXVpcmUgd2lsZGNhcmQgYWN0aW9uIHN1ZmZpeCDigJQgc3RhbmRhcmQgS01TIHVzYWdlIHBhdHRlcm4=" + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "WC1SYXkgc2VnbWVudHMgaGF2ZSBubyByZXNvdXJjZS1sZXZlbCBBUk4g4oCUIHdpbGRjYXJkIGlzIHJlcXVpcmVkIGZvciB0aGUgWC1SYXkgd3JpdGUgc3RhdGVtZW50IG9ubHk=" + "id": "AwsSolutions-IAM5[Resource::*]", + "reason": "X-Ray segments have no resource-level ARN \u2014 wildcard is required for the X-Ray write statement only" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -716,67 +1134,99 @@ "rules_to_suppress": [ { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "Serverless-LambdaAsyncFailureDestination", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgbm8gYXN5bmMgZXZlbnQgc291cmNlIGV4aXN0czsgdGhlIEV2ZW50SW52b2tlQ29uZmlnIGV4aXN0cyBvbmx5IHRvIHBpbiByZXRyeV9hdHRlbXB0cz0w" + "reason": "Invoked synchronously via API Gateway \u2014 no async event source exists; the EventInvokeConfig exists only to pin retry_attempts=0" }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { - "id": "AwsSolutions-IAM4", - "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "a21zOkdlbmVyYXRlRGF0YUtleSogYW5kIGttczpSZUVuY3J5cHQqIHJlcXVpcmUgd2lsZGNhcmQgYWN0aW9uIHN1ZmZpeCDigJQgc3RhbmRhcmQgS01TIHVzYWdlIHBhdHRlcm4=" + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "WC1SYXkgc2VnbWVudHMgaGF2ZSBubyByZXNvdXJjZS1sZXZlbCBBUk4g4oCUIHdpbGRjYXJkIGlzIHJlcXVpcmVkIGZvciB0aGUgWC1SYXkgd3JpdGUgc3RhdGVtZW50IG9ubHk=" + "id": "AwsSolutions-IAM5[Resource::*]", + "reason": "X-Ray segments have no resource-level ARN \u2014 wildcard is required for the X-Ray write statement only" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -845,67 +1295,99 @@ "rules_to_suppress": [ { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "Serverless-LambdaAsyncFailureDestination", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgbm8gYXN5bmMgZXZlbnQgc291cmNlIGV4aXN0czsgdGhlIEV2ZW50SW52b2tlQ29uZmlnIGV4aXN0cyBvbmx5IHRvIHBpbiByZXRyeV9hdHRlbXB0cz0w" + "reason": "Invoked synchronously via API Gateway \u2014 no async event source exists; the EventInvokeConfig exists only to pin retry_attempts=0" }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { - "id": "AwsSolutions-IAM4", - "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "a21zOkdlbmVyYXRlRGF0YUtleSogYW5kIGttczpSZUVuY3J5cHQqIHJlcXVpcmUgd2lsZGNhcmQgYWN0aW9uIHN1ZmZpeCDigJQgc3RhbmRhcmQgS01TIHVzYWdlIHBhdHRlcm4=" + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "WC1SYXkgc2VnbWVudHMgaGF2ZSBubyByZXNvdXJjZS1sZXZlbCBBUk4g4oCUIHdpbGRjYXJkIGlzIHJlcXVpcmVkIGZvciB0aGUgWC1SYXkgd3JpdGUgc3RhdGVtZW50IG9ubHk=" + "id": "AwsSolutions-IAM5[Resource::*]", + "reason": "X-Ray segments have no resource-level ARN \u2014 wildcard is required for the X-Ray write statement only" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -925,67 +1407,99 @@ "rules_to_suppress": [ { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "Serverless-LambdaAsyncFailureDestination", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgbm8gYXN5bmMgZXZlbnQgc291cmNlIGV4aXN0czsgdGhlIEV2ZW50SW52b2tlQ29uZmlnIGV4aXN0cyBvbmx5IHRvIHBpbiByZXRyeV9hdHRlbXB0cz0w" + "reason": "Invoked synchronously via API Gateway \u2014 no async event source exists; the EventInvokeConfig exists only to pin retry_attempts=0" }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { - "id": "AwsSolutions-IAM4", - "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "a21zOkdlbmVyYXRlRGF0YUtleSogYW5kIGttczpSZUVuY3J5cHQqIHJlcXVpcmUgd2lsZGNhcmQgYWN0aW9uIHN1ZmZpeCDigJQgc3RhbmRhcmQgS01TIHVzYWdlIHBhdHRlcm4=" + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "WC1SYXkgc2VnbWVudHMgaGF2ZSBubyByZXNvdXJjZS1sZXZlbCBBUk4g4oCUIHdpbGRjYXJkIGlzIHJlcXVpcmVkIGZvciB0aGUgWC1SYXkgd3JpdGUgc3RhdGVtZW50IG9ubHk=" + "id": "AwsSolutions-IAM5[Resource::*]", + "reason": "X-Ray segments have no resource-level ARN \u2014 wildcard is required for the X-Ray write statement only" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1026,67 +1540,99 @@ "rules_to_suppress": [ { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "Serverless-LambdaAsyncFailureDestination", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgbm8gYXN5bmMgZXZlbnQgc291cmNlIGV4aXN0czsgdGhlIEV2ZW50SW52b2tlQ29uZmlnIGV4aXN0cyBvbmx5IHRvIHBpbiByZXRyeV9hdHRlbXB0cz0w" + "reason": "Invoked synchronously via API Gateway \u2014 no async event source exists; the EventInvokeConfig exists only to pin retry_attempts=0" }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "SW52b2tlZCBzeW5jaHJvbm91c2x5IHZpYSBBUEkgR2F0ZXdheSDigJQgYXN5bmMgRExRIHBhdHRlcm4gZG9lcyBub3QgYXBwbHk=" + "reason": "Invoked synchronously via API Gateway \u2014 async DLQ pattern does not apply" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Tm8gVlBDIOKAlCBhZGRzIHNpZ25pZmljYW50IG9wZXJhdGlvbmFsIGNvbXBsZXhpdHk=" + "reason": "No VPC \u2014 adds significant operational complexity" }, { - "id": "AwsSolutions-IAM4", - "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "a21zOkdlbmVyYXRlRGF0YUtleSogYW5kIGttczpSZUVuY3J5cHQqIHJlcXVpcmUgd2lsZGNhcmQgYWN0aW9uIHN1ZmZpeCDigJQgc3RhbmRhcmQgS01TIHVzYWdlIHBhdHRlcm4=" + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "kms:GenerateDataKey* and kms:ReEncrypt* require wildcard action suffix \u2014 standard KMS usage pattern" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "WC1SYXkgc2VnbWVudHMgaGF2ZSBubyByZXNvdXJjZS1sZXZlbCBBUk4g4oCUIHdpbGRjYXJkIGlzIHJlcXVpcmVkIGZvciB0aGUgWC1SYXkgd3JpdGUgc3RhdGVtZW50IG9ubHk=" + "id": "AwsSolutions-IAM5[Resource::*]", + "reason": "X-Ray segments have no resource-level ARN \u2014 wildcard is required for the X-Ray write statement only" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLIGdlbmVyYXRlcyB0aGUgZGVmYXVsdCBwb2xpY3kgaW5saW5lIG9uIHRoZSBMYW1iZGEgc2VydmljZSByb2xlIOKAlCBub3QgZGlyZWN0bHkgY29uZmlndXJhYmxl" + "reason": "CDK generates the default policy inline on the Lambda service role \u2014 not directly configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "AWSLambdaBasicExecutionRole is the minimal managed policy for Lambda execution" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1228,6 +1774,52 @@ "Type": "AWS::IAM::Policy" }, "AppApiGatewayErrors13599598": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "LogGroupNames": [ { @@ -1240,33 +1832,171 @@ "Type": "AWS::Logs::QueryDefinition" }, "AppApiGatewayLatency044C2C9C": { - "Properties": { - "LogGroupNames": [ - { - "Ref": "AppApiAccessLogsDD2CC3E5" - } - ], - "Name": "ServerlessAppBackend-us-east-1/ApiGateway/SlowestRequests", - "QueryString": "fields @timestamp, responseLatency, status, httpMethod, resourcePath, responseLength, ip, xrayTraceId, requestId\n| sort responseLatency desc\n| limit 50" - }, - "Type": "AWS::Logs::QueryDefinition" - }, - "AppApiGatewayRequestsByIp56EA52A3": { - "Properties": { - "LogGroupNames": [ - { - "Ref": "AppApiAccessLogsDD2CC3E5" - } - ], - "Name": "ServerlessAppBackend-us-east-1/ApiGateway/RequestsByIP", - "QueryString": "fields ip\n| stats count(*) as requestCount by ip\n| sort requestCount desc\n| limit 25" - }, - "Type": "AWS::Logs::QueryDefinition" - }, - "AppApiRegionalWafLoggingB2DC4D0F": { - "DependsOn": [ + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, + "Properties": { + "LogGroupNames": [ + { + "Ref": "AppApiAccessLogsDD2CC3E5" + } + ], + "Name": "ServerlessAppBackend-us-east-1/ApiGateway/SlowestRequests", + "QueryString": "fields @timestamp, responseLatency, status, httpMethod, resourcePath, responseLength, ip, xrayTraceId, requestId\n| sort responseLatency desc\n| limit 50" + }, + "Type": "AWS::Logs::QueryDefinition" + }, + "AppApiGatewayRequestsByIp56EA52A3": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, + "Properties": { + "LogGroupNames": [ + { + "Ref": "AppApiAccessLogsDD2CC3E5" + } + ], + "Name": "ServerlessAppBackend-us-east-1/ApiGateway/RequestsByIP", + "QueryString": "fields ip\n| stats count(*) as requestCount by ip\n| sort requestCount desc\n| limit 25" + }, + "Type": "AWS::Logs::QueryDefinition" + }, + "AppApiRegionalWafLoggingB2DC4D0F": { + "DependsOn": [ "WafLogsBucketPolicyC1654A35" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "LogDestinationConfigs": [ { @@ -1286,6 +2016,52 @@ "Type": "AWS::WAFv2::LoggingConfiguration" }, "AppApiRegionalWebACL03074A4B": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "DefaultAction": { "Allow": {} @@ -1414,6 +2190,52 @@ "Type": "AWS::WAFv2::WebACL" }, "AppApiRegionalWebACLAssociationBE75DEA8": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ResourceArn": { "Fn::Join": [ @@ -1454,18 +2276,55 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgZ2VuZXJhdGVzIGFuIGlubGluZSBwb2xpY3kg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "AwsCustomResource generates an inline policy \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgZ2VuZXJhdGVzIGFuIGlubGluZSBwb2xpY3kg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "AwsCustomResource generates an inline policy \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgZ2VuZXJhdGVzIGFuIGlubGluZSBwb2xpY3kg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "AwsCustomResource generates an inline policy \u2014 not directly configurable" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1492,18 +2351,55 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgZ2VuZXJhdGVzIGFuIGlubGluZSBwb2xpY3kg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "AwsCustomResource generates an inline policy \u2014 not directly configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgZ2VuZXJhdGVzIGFuIGlubGluZSBwb2xpY3kg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "AwsCustomResource generates an inline policy \u2014 not directly configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgZ2VuZXJhdGVzIGFuIGlubGluZSBwb2xpY3kg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" + "reason": "AwsCustomResource generates an inline policy \u2014 not directly configurable" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1547,6 +2443,52 @@ "DependsOn": [ "AppApplicationResourceGroup6F8349BE" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "AutoConfigurationEnabled": true, "ResourceGroupName": "ApplicationInsights-ServerlessAppBackend-us-east-1" @@ -1554,6 +2496,52 @@ "Type": "AWS::ApplicationInsights::Application" }, "AppApplicationResourceGroup6F8349BE": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Name": "ApplicationInsights-ServerlessAppBackend-us-east-1", "ResourceQuery": { @@ -1564,15 +2552,61 @@ }, "AppAwsCustomResourceLogGroup9899DFE4": { "DeletionPolicy": "Delete", - "Properties": { - "KmsKeyId": { - "Fn::GetAtt": [ - "AppEncryptionKey7F644894", - "Arn" - ] - }, - "RetentionInDays": 7 - }, + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, + "Properties": { + "KmsKeyId": { + "Fn::GetAtt": [ + "AppEncryptionKey7F644894", + "Arn" + ] + }, + "RetentionInDays": 7 + }, "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Delete" }, @@ -1581,9 +2615,48 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM4", - "is_reason_encoded": true, - "reason": "Q29kZURlcGxveSBzZXJ2aWNlIHJvbGUgdXNlcyB0aGUgQVdTIG1hbmFnZWQgQVdTQ29kZURlcGxveVJvbGVGb3JMYW1iZGFMaW1pdGVkIHBvbGljeSDigJQgdGhlIGRvY3VtZW50ZWQgbGVhc3QtcHJpdmlsZWdlIHJvbGUgZm9yIExhbWJkYSB0cmFmZmljLXNoaWZ0aW5nIGRlcGxveW1lbnRz" + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSCodeDeployRoleForLambdaLimited]", + "reason": "CodeDeploy service role uses the AWS managed AWSCodeDeployRoleForLambdaLimited policy \u2014 the documented least-privilege role for Lambda traffic-shifting deployments" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1628,9 +2701,48 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM4", - "is_reason_encoded": true, - "reason": "Q29kZURlcGxveSBzZXJ2aWNlIHJvbGUgdXNlcyB0aGUgQVdTIG1hbmFnZWQgQVdTQ29kZURlcGxveVJvbGVGb3JMYW1iZGFMaW1pdGVkIHBvbGljeSDigJQgdGhlIGRvY3VtZW50ZWQgbGVhc3QtcHJpdmlsZWdlIHJvbGUgZm9yIExhbWJkYSB0cmFmZmljLXNoaWZ0aW5nIGRlcGxveW1lbnRz" + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSCodeDeployRoleForLambdaLimited]", + "reason": "CodeDeploy service role uses the AWS managed AWSCodeDeployRoleForLambdaLimited policy \u2014 the documented least-privilege role for Lambda traffic-shifting deployments" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1645,9 +2757,48 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM4", - "is_reason_encoded": true, - "reason": "Q29kZURlcGxveSBzZXJ2aWNlIHJvbGUgdXNlcyB0aGUgQVdTIG1hbmFnZWQgQVdTQ29kZURlcGxveVJvbGVGb3JMYW1iZGFMaW1pdGVkIHBvbGljeSDigJQgdGhlIGRvY3VtZW50ZWQgbGVhc3QtcHJpdmlsZWdlIHJvbGUgZm9yIExhbWJkYSB0cmFmZmljLXNoaWZ0aW5nIGRlcGxveW1lbnRz" + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSCodeDeployRoleForLambdaLimited]", + "reason": "CodeDeploy service role uses the AWS managed AWSCodeDeployRoleForLambdaLimited policy \u2014 the documented least-privilege role for Lambda traffic-shifting deployments" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1688,13 +2839,51 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-CloudWatchAlarmAction", - "is_reason_encoded": true, - "reason": "QWxhcm0gaXMgY29uc3VtZWQgYnkgdGhlIENvZGVEZXBsb3kgY2FuYXJ5IHJvbGxiYWNrLCB3aGljaCBwb2xscyBpdHMgc3RhdGUgdG8gZGVjaWRlIG9uIHJvbGxiYWNrIOKAlCBub3QgYSBub3RpZmljYXRpb24gY2hhbm5lbCwgc28gbm8gU05TIGFjdGlvbiBieSBkZXNpZ24=" + "reason": "Alarm is consumed by the CodeDeploy canary rollback, which polls its state to decide on rollback \u2014 not a notification channel, so no SNS action by design" }, { "id": "HIPAA.Security-CloudWatchAlarmAction", - "is_reason_encoded": true, - "reason": "QWxhcm0gaXMgY29uc3VtZWQgYnkgdGhlIENvZGVEZXBsb3kgY2FuYXJ5IHJvbGxiYWNrLCB3aGljaCBwb2xscyBpdHMgc3RhdGUgdG8gZGVjaWRlIG9uIHJvbGxiYWNrIOKAlCBub3QgYSBub3RpZmljYXRpb24gY2hhbm5lbCwgc28gbm8gU05TIGFjdGlvbiBieSBkZXNpZ24=" + "reason": "Alarm is consumed by the CodeDeploy canary rollback, which polls its state to decide on rollback \u2014 not a notification channel, so no SNS action by design" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -1736,6 +2925,52 @@ }, "AppEncryptionKey7F644894": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Description": "KMS key for ServerlessAppBackend-us-east-1 log groups, Lambda env, AppConfig, and SNS", "EnableKeyRotation": true, @@ -1827,12 +3062,104 @@ "UpdateReplacePolicy": "Delete" }, "AppFeatureFlagsAppD0EAAC11": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Name": "ServerlessAppBackend-us-east-1-features" }, "Type": "AWS::AppConfig::Application" }, "AppFeatureFlagsDeployStrategyF51A0361": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "DeploymentDurationInMinutes": 0, "FinalBakeTimeInMinutes": 0, @@ -1844,6 +3171,52 @@ "Type": "AWS::AppConfig::DeploymentStrategy" }, "AppFeatureFlagsDeployment1CB9CC7C": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ApplicationId": { "Ref": "AppFeatureFlagsAppD0EAAC11" @@ -1870,6 +3243,52 @@ "Type": "AWS::AppConfig::Deployment" }, "AppFeatureFlagsEnvBF21F0D3": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ApplicationId": { "Ref": "AppFeatureFlagsAppD0EAAC11" @@ -1880,6 +3299,52 @@ "Type": "AWS::AppConfig::Environment" }, "AppFeatureFlagsProfile324F0464": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ApplicationId": { "Ref": "AppFeatureFlagsAppD0EAAC11" @@ -1898,6 +3363,52 @@ "Type": "AWS::AppConfig::ConfigurationProfile" }, "AppFeatureFlagsVersion486FFE3A": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ApplicationId": { "Ref": "AppFeatureFlagsAppD0EAAC11" @@ -1908,10 +3419,56 @@ "Content": "{\n \"enhanced_greeting\": {\n \"default\": false\n }\n}\n", "ContentType": "application/json" }, - "Type": "AWS::AppConfig::HostedConfigurationVersion" - }, - "AppFunctionLogGroupB9961371": { - "DeletionPolicy": "Delete", + "Type": "AWS::AppConfig::HostedConfigurationVersion" + }, + "AppFunctionLogGroupB9961371": { + "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -1925,6 +3482,52 @@ "UpdateReplacePolicy": "Delete" }, "AppGreetingParameterD5E6E64F": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Type": "String", "Value": "hello world" @@ -1932,6 +3535,52 @@ "Type": "AWS::SSM::Parameter" }, "AppLambdaColdStarts6D375D5A": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "LogGroupNames": [ { @@ -1944,6 +3593,52 @@ "Type": "AWS::Logs::QueryDefinition" }, "AppLambdaRecentErrors0008ACA4": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "LogGroupNames": [ { @@ -1956,6 +3651,52 @@ "Type": "AWS::Logs::QueryDefinition" }, "AppLambdaSlowInvocations760C2E05": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "LogGroupNames": [ { @@ -1968,6 +3709,52 @@ "Type": "AWS::Logs::QueryDefinition" }, "AppLiveAlias3872472E": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "FunctionName": { "Ref": "AppApiFunctionDE515850" @@ -1993,6 +3780,52 @@ } }, "AppMonitoringDashboardFactoryDashboard0CCA23D1": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "DashboardBody": { "Fn::Join": [ @@ -2299,6 +4132,52 @@ "Type": "AWS::CloudWatch::Dashboard" }, "AppMonitoringServerlessAppBackenduseast1ApiFunctionLatencyP90p90BC0B58EC": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ActionsEnabled": true, "AlarmActions": [ @@ -2340,6 +4219,52 @@ "Type": "AWS::CloudWatch::Alarm" }, "AppMonitoringServerlessAppBackenduseast1RestApiFaultRateinternalerrorAE344767": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ActionsEnabled": true, "AlarmActions": [ @@ -2389,6 +4314,52 @@ "DependsOn": [ "AppRestApiB4885DCD" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "CloudWatchRoleArn": { "Fn::GetAtt": [ @@ -2397,10 +4368,56 @@ ] } }, - "Type": "AWS::ApiGateway::Account", - "UpdateReplacePolicy": "Delete" - }, - "AppRestApiB4885DCD": { + "Type": "AWS::ApiGateway::Account", + "UpdateReplacePolicy": "Delete" + }, + "AppRestApiB4885DCD": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Name": "RestApi" }, @@ -2412,8 +4429,48 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM4", + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs]", "reason": "CDK-managed API Gateway CloudWatch role uses AWS managed policy" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -2449,14 +4506,58 @@ "Type": "AWS::IAM::Role", "UpdateReplacePolicy": "Delete" }, - "AppRestApiDeployment0A1A30D78b8d00a02962fa08b890d9e21755f7ca": { + "AppRestApiDeployment0A1A30D760305a03281fe5b01a1d0856fa0437ea": { "DependsOn": [ "AppRestApigreetingGETE278858C", "AppRestApigreetingOPTIONS18B87E90", "AppRestApigreeting2BA1B41F" ], "Metadata": { - "aws:cdk:do-not-refactor": true + "aws:cdk:do-not-refactor": true, + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } }, "Properties": { "Description": "Automatically created by the RestApi construct", @@ -2471,6 +4572,52 @@ "AppApiExecutionLogs0AE84813", "AppRestApiAccount86BAD4C4" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "AccessLogSetting": { "DestinationArn": { @@ -2482,7 +4629,7 @@ "Format": "{\"requestId\":\"$context.requestId\",\"accountId\":\"$context.accountId\",\"apiId\":\"$context.apiId\",\"stage\":\"$context.stage\",\"resourcePath\":\"$context.resourcePath\",\"httpMethod\":\"$context.httpMethod\",\"protocol\":\"$context.protocol\",\"status\":\"$context.status\",\"responseType\":\"$context.error.responseType\",\"errorMessage\":\"$context.error.message\",\"requestTime\":\"$context.requestTime\",\"responseLatency\":\"$context.responseLatency\",\"ip\":\"$context.identity.sourceIp\",\"caller\":\"$context.identity.caller\",\"user\":\"$context.identity.user\",\"responseLength\":\"$context.responseLength\",\"xrayTraceId\":\"$context.xrayTraceId\"}" }, "DeploymentId": { - "Ref": "AppRestApiDeployment0A1A30D78b8d00a02962fa08b890d9e21755f7ca" + "Ref": "AppRestApiDeployment0A1A30D760305a03281fe5b01a1d0856fa0437ea" }, "MethodSettings": [ { @@ -2503,6 +4650,52 @@ "Type": "AWS::ApiGateway::Stage" }, "AppRestApigreeting2BA1B41F": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ParentId": { "Fn::GetAtt": [ @@ -2518,6 +4711,52 @@ "Type": "AWS::ApiGateway::Resource" }, "AppRestApigreetingGETApiPermissionServerlessAppuseast1stageServerlessAppBackenduseast1AppRestApi4D1FB3E8GETgreeting5743FCBD": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { @@ -2552,6 +4791,52 @@ "Type": "AWS::Lambda::Permission" }, "AppRestApigreetingGETApiPermissionTestServerlessAppuseast1stageServerlessAppBackenduseast1AppRestApi4D1FB3E8GETgreeting4F9EF522": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Action": "lambda:InvokeFunction", "FunctionName": { @@ -2582,6 +4867,52 @@ "Type": "AWS::Lambda::Permission" }, "AppRestApigreetingGETE278858C": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "AuthorizationType": "NONE", "HttpMethod": "GET", @@ -2615,6 +4946,52 @@ "Type": "AWS::ApiGateway::Method" }, "AppRestApigreetingOPTIONS18B87E90": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "ApiKeyRequired": false, "AuthorizationType": "NONE", @@ -2656,6 +5033,52 @@ }, "AutoDeleteObjectsLogGroupC1303B18": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -2698,14 +5121,52 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-SQS3", - "is_reason_encoded": true, - "reason": "VGVybWluYWwgRExROiB0aGlzIHF1ZXVlIElTIHRoZSBkZWFkLWxldHRlciBkZXN0aW5hdGlvbiDigJQgcmVjdXJzaW5nIGludG8gYW5vdGhlciBETFEgaGFzIG5vIHJlY292ZXJ5IHZhbHVl" + "id": "AwsSolutions-SQS3", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "Serverless-SQSRedrivePolicy", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" }, { - "id": "Serverless-SQSRedrivePolicy", - "is_reason_encoded": true, - "reason": "VGVybWluYWwgRExROiB0aGlzIHF1ZXVlIElTIHRoZSBkZWFkLWxldHRlciBkZXN0aW5hdGlvbiDigJQgcmVjdXJzaW5nIGludG8gYW5vdGhlciBETFEgaGFzIG5vIHJlY292ZXJ5IHZhbHVl" + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -2723,6 +5184,60 @@ "UpdateReplacePolicy": "Delete" }, "AwsCustomResourceProviderDlqPolicyEA02C36D": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-SQS3", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "Serverless-SQSRedrivePolicy", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "PolicyDocument": { "Statement": [ @@ -2762,32 +5277,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -2799,18 +5303,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -2818,18 +5319,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -2837,8 +5335,51 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -2879,32 +5420,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -2916,18 +5446,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -2935,18 +5462,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -2954,8 +5478,51 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -2988,68 +5555,95 @@ "rules_to_suppress": [ { "id": "AwsSolutions-S1", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." } ] } @@ -3117,6 +5711,104 @@ "AutoDeleteObjectsLogGroupC1303B18", "WafLogsBucketPolicyC1654A35" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "BucketName": { "Ref": "WafLogsBucket2E62CA90" @@ -3132,6 +5824,104 @@ "UpdateReplacePolicy": "Delete" }, "WafLogsBucketPolicyC1654A35": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "AwsSolutions-APIG2", + "reason": "Request validation not needed for sample app" + }, + { + "id": "AwsSolutions-APIG4", + "reason": "Authorization not needed for sample app" + }, + { + "id": "AwsSolutions-COG4", + "reason": "Cognito authorizer not needed for sample app" + }, + { + "id": "CdkNagValidationFailure", + "reason": "Serverless-APIGWStructuredLogging validation fails due to intrinsic function reference in access log destination \u2014 structured JSON logging is configured via logging_format=JSON on the Lambda" + }, + { + "id": "NIST.800.53.R5-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "NIST.800.53.R5-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 the smallest 0.5 GB cluster is ~$14/month for a sample app. Caching GET /greeting would also serve stale values across SSM parameter and AppConfig feature-flag changes." + }, + { + "id": "HIPAA.Security-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "HIPAA.Security-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + }, + { + "id": "PCI.DSS.321-APIGWSSLEnabled", + "reason": "Client-side SSL certificates not required for sample app" + }, + { + "id": "PCI.DSS.321-APIGWCacheEnabledAndEncrypted", + "reason": "API Gateway cache cluster intentionally disabled for cost reasons \u2014 see NIST.800.53.R5-APIGWCacheEnabledAndEncrypted rationale above." + } + ] + } + }, "Properties": { "Bucket": { "Ref": "WafLogsBucket2E62CA90" diff --git a/tests/cdk/snapshots/ServerlessAppData-us-east-1.json b/tests/cdk/snapshots/ServerlessAppData-us-east-1.json index 9f329bb..bcfa89e 100644 --- a/tests/cdk/snapshots/ServerlessAppData-us-east-1.json +++ b/tests/cdk/snapshots/ServerlessAppData-us-east-1.json @@ -1,20 +1,4 @@ { - "Metadata": { - "cdk_nag": { - "rules_to_suppress": [ - { - "id": "NIST.800.53.R5-DynamoDBInBackupPlan", - "is_reason_encoded": true, - "reason": "QVdTIEJhY2t1cCBwbGFuIG5vdCBjb25maWd1cmVkIGZvciBzYW1wbGUgYXBwIOKAlCBQSVRSIGlzIGVuYWJsZWQgZm9yIHBvaW50LWluLXRpbWUgcmVjb3Zlcnk=" - }, - { - "id": "HIPAA.Security-DynamoDBInBackupPlan", - "is_reason_encoded": true, - "reason": "QVdTIEJhY2t1cCBwbGFuIG5vdCBjb25maWd1cmVkIGZvciBzYW1wbGUgYXBwIOKAlCBQSVRSIGlzIGVuYWJsZWQgZm9yIHBvaW50LWluLXRpbWUgcmVjb3Zlcnk=" - } - ] - } - }, "Outputs": { "ExportsOutputFnGetAttDataEncryptionKey101796EEArn95D63FE8": { "Export": { @@ -63,6 +47,20 @@ "Resources": { "DataEncryptionKey101796EE": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "NIST.800.53.R5-DynamoDBInBackupPlan", + "reason": "AWS Backup plan not configured for sample app \u2014 PITR is enabled for point-in-time recovery" + }, + { + "id": "HIPAA.Security-DynamoDBInBackupPlan", + "reason": "AWS Backup plan not configured for sample app \u2014 PITR is enabled for point-in-time recovery" + } + ] + } + }, "Properties": { "Description": "KMS key for ServerlessAppData-us-east-1 DynamoDB", "EnableKeyRotation": true, @@ -101,6 +99,20 @@ }, "IdempotencyTableV203A5298E": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "NIST.800.53.R5-DynamoDBInBackupPlan", + "reason": "AWS Backup plan not configured for sample app \u2014 PITR is enabled for point-in-time recovery" + }, + { + "id": "HIPAA.Security-DynamoDBInBackupPlan", + "reason": "AWS Backup plan not configured for sample app \u2014 PITR is enabled for point-in-time recovery" + } + ] + } + }, "Properties": { "AttributeDefinitions": [ { diff --git a/tests/cdk/snapshots/ServerlessAppFrontend-us-east-1.json b/tests/cdk/snapshots/ServerlessAppFrontend-us-east-1.json index fb764d8..d6c0209 100644 --- a/tests/cdk/snapshots/ServerlessAppFrontend-us-east-1.json +++ b/tests/cdk/snapshots/ServerlessAppFrontend-us-east-1.json @@ -1,49 +1,4 @@ { - "Metadata": { - "cdk_nag": { - "rules_to_suppress": [ - { - "id": "AwsSolutions-CFR1", - "reason": "Geo restriction not required for sample app" - }, - { - "id": "AwsSolutions-CFR4", - "is_reason_encoded": true, - "reason": "VXNpbmcgZGVmYXVsdCBDbG91ZEZyb250IGNlcnRpZmljYXRlIOKAlCBubyBjdXN0b20gZG9tYWluIGZvciBzYW1wbGUgYXBw" - }, - { - "id": "NIST.800.53.R5-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "UzMgcmVwbGljYXRpb24gbm90IG5lZWRlZCBmb3Igc2FtcGxlIGFwcCDigJQgc3RhdGljIGFzc2V0cyBhcmUgcmVkZXBsb3lhYmxl" - }, - { - "id": "NIST.800.53.R5-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "UzMgdmVyc2lvbmluZyBub3QgbmVlZGVkIGZvciBzYW1wbGUgYXBwIOKAlCBzdGF0aWMgYXNzZXRzIGFyZSByZWRlcGxveWFibGUgdmlhIGNkayBkZXBsb3k=" - }, - { - "id": "HIPAA.Security-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "UzMgcmVwbGljYXRpb24gbm90IG5lZWRlZCBmb3Igc2FtcGxlIGFwcCDigJQgc3RhdGljIGFzc2V0cyBhcmUgcmVkZXBsb3lhYmxl" - }, - { - "id": "HIPAA.Security-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "UzMgdmVyc2lvbmluZyBub3QgbmVlZGVkIGZvciBzYW1wbGUgYXBwIOKAlCBzdGF0aWMgYXNzZXRzIGFyZSByZWRlcGxveWFibGUgdmlhIGNkayBkZXBsb3k=" - }, - { - "id": "PCI.DSS.321-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "UzMgcmVwbGljYXRpb24gbm90IG5lZWRlZCBmb3Igc2FtcGxlIGFwcCDigJQgc3RhdGljIGFzc2V0cyBhcmUgcmVkZXBsb3lhYmxl" - }, - { - "id": "PCI.DSS.321-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "UzMgdmVyc2lvbmluZyBub3QgbmVlZGVkIGZvciBzYW1wbGUgYXBwIOKAlCBzdGF0aWMgYXNzZXRzIGFyZSByZWRlcGxveWFibGUgdmlhIGNkayBkZXBsb3k=" - } - ] - } - }, "Outputs": { "AthenaWorkGroupName": { "Description": "Athena workgroup for querying access logs", @@ -148,32 +103,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -185,18 +129,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -204,18 +145,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -223,12 +161,51 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { - "id": "AwsSolutions-IAM5", + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -262,20 +239,108 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM5", - "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + "id": "AwsSolutions-L1", + "reason": "CDK-managed singleton Lambda runtime is not configurable" + }, + { + "id": "Serverless-LambdaTracing", + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" + }, + { + "id": "Serverless-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "Serverless-LambdaDefaultMemorySize", + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" + }, + { + "id": "Serverless-LambdaLatestVersion", + "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "NIST.800.53.R5-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "HIPAA.Security-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "HIPAA.Security-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "HIPAA.Security-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "HIPAA.Security-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "PCI.DSS.321-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -302,32 +367,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -339,18 +393,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -358,18 +409,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -377,12 +425,51 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" }, { - "id": "AwsSolutions-IAM5", + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -422,20 +509,108 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM5", - "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + "id": "AwsSolutions-L1", + "reason": "CDK-managed singleton Lambda runtime is not configurable" + }, + { + "id": "Serverless-LambdaTracing", + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" + }, + { + "id": "Serverless-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "Serverless-LambdaDefaultMemorySize", + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" + }, + { + "id": "Serverless-LambdaLatestVersion", + "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "NIST.800.53.R5-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "HIPAA.Security-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "HIPAA.Security-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "HIPAA.Security-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "HIPAA.Security-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "PCI.DSS.321-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -485,6 +660,44 @@ "Type": "AWS::IAM::Policy" }, "AccessLogsDatabase": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "CatalogId": { "Ref": "AWS::AccountId" @@ -497,13 +710,51 @@ "Type": "AWS::Glue::Database" }, "AccessLogsWorkGroup": { - "Properties": { - "Name": "ServerlessAppFrontend-us-east-1-access-logs", - "RecursiveDeleteOption": true, - "State": "ENABLED", - "WorkGroupConfiguration": { - "BytesScannedCutoffPerQuery": 1073741824, - "EnforceWorkGroupConfiguration": true, + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, + "Properties": { + "Name": "ServerlessAppFrontend-us-east-1-access-logs", + "RecursiveDeleteOption": true, + "State": "ENABLED", + "WorkGroupConfiguration": { + "BytesScannedCutoffPerQuery": 1073741824, + "EnforceWorkGroupConfiguration": true, "PublishCloudWatchMetricsEnabled": true, "ResultConfiguration": { "EncryptionConfiguration": { @@ -534,6 +785,44 @@ }, "AutoDeleteObjectsLogGroupC1303B18": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -572,6 +861,44 @@ }, "AwsCustomResourceLogGroup0228D074": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -591,13 +918,43 @@ "rules_to_suppress": [ { "id": "AwsSolutions-SQS3", - "is_reason_encoded": true, - "reason": "VGVybWluYWwgRExROiB0aGlzIHF1ZXVlIElTIHRoZSBkZWFkLWxldHRlciBkZXN0aW5hdGlvbiDigJQgcmVjdXJzaW5nIGludG8gYW5vdGhlciBETFEgaGFzIG5vIHJlY292ZXJ5IHZhbHVl" + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" }, { "id": "Serverless-SQSRedrivePolicy", - "is_reason_encoded": true, - "reason": "VGVybWluYWwgRExROiB0aGlzIHF1ZXVlIElTIHRoZSBkZWFkLWxldHRlciBkZXN0aW5hdGlvbiDigJQgcmVjdXJzaW5nIGludG8gYW5vdGhlciBETFEgaGFzIG5vIHJlY292ZXJ5IHZhbHVl" + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -615,6 +972,52 @@ "UpdateReplacePolicy": "Delete" }, "AwsCustomResourceProviderDlqPolicyEA02C36D": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-SQS3", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "Serverless-SQSRedrivePolicy", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "PolicyDocument": { "Statement": [ @@ -649,6 +1052,44 @@ }, "BucketDeploymentLogGroup8599F66E": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -668,13 +1109,43 @@ "rules_to_suppress": [ { "id": "AwsSolutions-SQS3", - "is_reason_encoded": true, - "reason": "VGVybWluYWwgRExROiB0aGlzIHF1ZXVlIElTIHRoZSBkZWFkLWxldHRlciBkZXN0aW5hdGlvbiDigJQgcmVjdXJzaW5nIGludG8gYW5vdGhlciBETFEgaGFzIG5vIHJlY292ZXJ5IHZhbHVl" + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" }, { "id": "Serverless-SQSRedrivePolicy", - "is_reason_encoded": true, - "reason": "VGVybWluYWwgRExROiB0aGlzIHF1ZXVlIElTIHRoZSBkZWFkLWxldHRlciBkZXN0aW5hdGlvbiDigJQgcmVjdXJzaW5nIGludG8gYW5vdGhlciBETFEgaGFzIG5vIHJlY292ZXJ5IHZhbHVl" + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -692,6 +1163,52 @@ "UpdateReplacePolicy": "Delete" }, "BucketDeploymentProviderDlqPolicy6A6BA613": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-SQS3", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "Serverless-SQSRedrivePolicy", + "reason": "Terminal DLQ: this queue IS the dead-letter destination \u2014 recursing into another DLQ has no recovery value" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "PolicyDocument": { "Statement": [ @@ -728,6 +1245,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Total bytes transferred per edge location", @@ -741,6 +1296,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Request counts and percentages by edge result type (Hit/Miss/Error)", @@ -754,6 +1347,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Recent 4xx/5xx error responses with client and edge details", @@ -761,12 +1392,50 @@ "QueryString": "SELECT log_date, log_time, c_ip, cs_method, cs_uri_stem, sc_status,\n x_edge_result_type, x_edge_detailed_result_type\nFROM cloudfront_logs\nWHERE sc_status LIKE '4%' OR sc_status LIKE '5%'\nORDER BY log_date DESC, log_time DESC\nLIMIT 50", "WorkGroup": "ServerlessAppFrontend-us-east-1-access-logs" }, - "Type": "AWS::Athena::NamedQuery" - }, - "CfTopClientIps": { - "DependsOn": [ - "AccessLogsWorkGroup" - ], + "Type": "AWS::Athena::NamedQuery" + }, + "CfTopClientIps": { + "DependsOn": [ + "AccessLogsWorkGroup" + ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Highest-traffic client IPs with error counts", @@ -780,6 +1449,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Most frequently requested URIs with error counts", @@ -793,6 +1500,44 @@ "DependsOn": [ "AccessLogsDatabase" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "CatalogId": { "Ref": "AWS::AccountId" @@ -975,32 +1720,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -1012,18 +1746,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -1031,18 +1762,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -1050,12 +1778,83 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" }, { - "id": "AwsSolutions-IAM5", + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:GetBucket*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:GetObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:List*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:DeleteObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:Abort*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn:aws:s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn::s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -1099,20 +1898,140 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM5", - "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + "id": "AwsSolutions-L1", + "reason": "CDK-managed singleton Lambda runtime is not configurable" + }, + { + "id": "Serverless-LambdaTracing", + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" + }, + { + "id": "Serverless-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "Serverless-LambdaDefaultMemorySize", + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" + }, + { + "id": "Serverless-LambdaLatestVersion", + "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "NIST.800.53.R5-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "NIST.800.53.R5-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "HIPAA.Security-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" }, + { + "id": "HIPAA.Security-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "HIPAA.Security-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "HIPAA.Security-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "PCI.DSS.321-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:GetBucket*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:GetObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:List*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:DeleteObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:Abort*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn:aws:s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn::s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -1140,86 +2059,140 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + "id": "AwsSolutions-L1", + "reason": "CDK-managed singleton Lambda runtime is not configurable" + }, + { + "id": "Serverless-LambdaTracing", + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" + }, + { + "id": "Serverless-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "Serverless-LambdaDefaultMemorySize", + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" + }, + { + "id": "Serverless-LambdaLatestVersion", + "reason": "CDK-managed singleton Lambda runtime is not configurable" + }, + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "NIST.800.53.R5-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "NIST.800.53.R5-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "HIPAA.Security-LambdaDLQ", + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" + }, + { + "id": "HIPAA.Security-LambdaConcurrency", + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" + }, + { + "id": "HIPAA.Security-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK-generated inline policy on singleton service role" + }, + { + "id": "PCI.DSS.321-LambdaInsideVPC", + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" }, { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" }, { - "id": "AwsSolutions-L1", - "reason": "CDK-managed singleton Lambda runtime is not configurable" + "id": "AwsSolutions-IAM5[Action::s3:GetBucket*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "id": "AwsSolutions-IAM5[Action::s3:GetObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "id": "AwsSolutions-IAM5[Action::s3:List*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "id": "AwsSolutions-IAM5[Action::s3:DeleteObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "Serverless-LambdaLatestVersion", - "reason": "CDK-managed singleton Lambda runtime is not configurable" + "id": "AwsSolutions-IAM5[Action::s3:Abort*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "reason": "CDK-generated inline policy on singleton service role" + "id": "AwsSolutions-IAM5[Resource::/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "id": "AwsSolutions-IAM5[Resource::arn:aws:s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "id": "AwsSolutions-IAM5[Resource::arn::s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" }, { - "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" }, { - "id": "HIPAA.Security-IAMNoInlinePolicy", - "reason": "CDK-generated inline policy on singleton service role" + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" }, { - "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" }, { - "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" }, { - "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" }, { - "id": "PCI.DSS.321-IAMNoInlinePolicy", - "reason": "CDK-generated inline policy on singleton service role" + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" }, { - "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" }, { - "id": "AwsSolutions-IAM5", - "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -1258,32 +2231,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -1295,18 +2257,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -1314,18 +2273,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -1333,12 +2289,83 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-IAM5[Action::kms:GenerateDataKey*]", + "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" }, { - "id": "AwsSolutions-IAM5", + "id": "AwsSolutions-IAM5[Action::kms:ReEncrypt*]", "reason": "KMS wildcards required by configure_async_invoke to encrypt messages to the CMK-encrypted DLQ" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:GetBucket*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:GetObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:List*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:DeleteObject*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Action::s3:Abort*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn:aws:s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn::s3:::cdk-hnb659fds-assets--us-east-1/*]", + "reason": "CDK BucketDeployment handler policy \u2014 CDK-generated s3 read on the bootstrap asset bucket and read/write/delete on the destination bucket; not configurable by the caller" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -1488,32 +2515,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -1525,18 +2541,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -1544,18 +2557,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -1563,8 +2573,43 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -1605,32 +2650,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -1642,18 +2676,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -1661,18 +2692,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -1680,8 +2708,43 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -1712,6 +2775,44 @@ "RumExtendedMetricsCustomResourcePolicy33BDB00B", "RumExtendedMetrics5A0A201E" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Content": { "S3Bucket": { @@ -1729,6 +2830,44 @@ "RumExtendedMetricsCustomResourcePolicy33BDB00B", "RumExtendedMetrics5A0A201E" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "DestinationBucketName": { "Ref": "FrontendBucketEFE2E19C" @@ -1815,6 +2954,44 @@ "UpdateReplacePolicy": "Delete" }, "Distribution830FAC52": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "DistributionConfig": { "CustomErrorResponses": [ @@ -1870,15 +3047,53 @@ "OriginAccessIdentity": "" } } - ], - "WebACLId": { - "Fn::ImportValue": "ServerlessAppWaf-us-east-1:ExportsOutputFnGetAttWebACLArn3404FD8D" - } + ], + "WebACLId": { + "Fn::ImportValue": "ServerlessAppWaf-us-east-1:ExportsOutputFnGetAttWebACLArn3404FD8D" + } + } + }, + "Type": "AWS::CloudFront::Distribution" + }, + "DistributionOrigin1S3OriginAccessControlEB606076": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] } }, - "Type": "AWS::CloudFront::Distribution" - }, - "DistributionOrigin1S3OriginAccessControlEB606076": { "Properties": { "OriginAccessControlConfig": { "Name": "ServerlessAppFrontenduseast1Origin1S3OriginAccessControl5EB408FD", @@ -1895,6 +3110,72 @@ "AutoDeleteObjectsLogGroupC1303B18", "FrontendAccessLogBucketPolicyAA93CA90" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + } + ] + } + }, "Properties": { "BucketName": { "Ref": "FrontendAccessLogBucketD05E8E55" @@ -1916,68 +3197,63 @@ "rules_to_suppress": [ { "id": "AwsSolutions-S1", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "NIST.800.53.R5-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "HIPAA.Security-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "PCI.DSS.321-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "HIPAA.Security-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "PCI.DSS.321-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "NIST.800.53.R5-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "HIPAA.Security-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "PCI.DSS.321-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "NIST.800.53.R5-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "HIPAA.Security-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" }, { "id": "PCI.DSS.321-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "QWNjZXNzLWxvZyBidWNrZXQg4oCUIFNTRS1TMyAoUzMvQ2xvdWRGcm9udCBsb2cgZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgdGFyZ2V0IGJ1Y2tldHMpLCBzZWxmLWxvZ2dpbmcgd291bGQgYmUgY2lyY3VsYXIsIG5vIHZlcnNpb25pbmcvcmVwbGljYXRpb24gZm9yIGFwcGVuZC1vbmx5IHRyYW5zaWVudCBsb2dz" + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" } ] } @@ -2029,6 +3305,72 @@ "UpdateReplacePolicy": "Delete" }, "FrontendAccessLogBucketPolicyAA93CA90": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "Access-log bucket \u2014 SSE-S3 (S3/CloudFront log delivery doesn't support KMS-CMK target buckets), self-logging would be circular, no versioning/replication for append-only transient logs" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + } + ] + } + }, "Properties": { "Bucket": { "Ref": "FrontendAccessLogBucketD05E8E55" @@ -2120,6 +3462,44 @@ "AutoDeleteObjectsLogGroupC1303B18", "FrontendBucketPolicy1DFF75D9" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "BucketName": { "Ref": "FrontendBucketEFE2E19C" @@ -2136,6 +3516,44 @@ }, "FrontendBucketEFE2E19C": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "BucketEncryption": { "ServerSideEncryptionConfiguration": [ @@ -2179,6 +3597,44 @@ "UpdateReplacePolicy": "Delete" }, "FrontendBucketPolicy1DFF75D9": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Bucket": { "Ref": "FrontendBucketEFE2E19C" @@ -2310,6 +3766,44 @@ }, "FrontendEncryptionKey272BB0CA": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Description": "KMS key for ServerlessAppFrontend-us-east-1 S3 bucket and log groups", "EnableKeyRotation": true, @@ -2420,18 +3914,47 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgcG9saWN5IGlzIGEgc2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgc3RhdGVtZW50IHNjb3BlZCB0byBjbG91ZGZyb250OkNyZWF0ZUludmFsaWRhdGlvbiBvbiB0aGlzIHN0YWNrJ3MgZGlzdHJpYnV0aW9uIEFSTiDigJQgbWFuYWdlZC1wb2xpY3kgcmV1c2UgYWRkcyBub3RoaW5n" + "reason": "AwsCustomResource policy is a single least-privilege inline statement scoped to cloudfront:CreateInvalidation on this stack's distribution ARN \u2014 managed-policy reuse adds nothing" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgcG9saWN5IGlzIGEgc2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgc3RhdGVtZW50IHNjb3BlZCB0byBjbG91ZGZyb250OkNyZWF0ZUludmFsaWRhdGlvbiBvbiB0aGlzIHN0YWNrJ3MgZGlzdHJpYnV0aW9uIEFSTiDigJQgbWFuYWdlZC1wb2xpY3kgcmV1c2UgYWRkcyBub3RoaW5n" + "reason": "AwsCustomResource policy is a single least-privilege inline statement scoped to cloudfront:CreateInvalidation on this stack's distribution ARN \u2014 managed-policy reuse adds nothing" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgcG9saWN5IGlzIGEgc2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgc3RhdGVtZW50IHNjb3BlZCB0byBjbG91ZGZyb250OkNyZWF0ZUludmFsaWRhdGlvbiBvbiB0aGlzIHN0YWNrJ3MgZGlzdHJpYnV0aW9uIEFSTiDigJQgbWFuYWdlZC1wb2xpY3kgcmV1c2UgYWRkcyBub3RoaW5n" + "reason": "AwsCustomResource policy is a single least-privilege inline statement scoped to cloudfront:CreateInvalidation on this stack's distribution ARN \u2014 managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2505,19 +4028,48 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgcG9saWN5IGlzIGEgc2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgc3RhdGVtZW50IHNjb3BlZCB0byBjbG91ZGZyb250OkNyZWF0ZUludmFsaWRhdGlvbiBvbiB0aGlzIHN0YWNrJ3MgZGlzdHJpYnV0aW9uIEFSTiDigJQgbWFuYWdlZC1wb2xpY3kgcmV1c2UgYWRkcyBub3RoaW5n" + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "AwsCustomResource policy is a single least-privilege inline statement scoped to cloudfront:CreateInvalidation on this stack's distribution ARN \u2014 managed-policy reuse adds nothing" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "AwsCustomResource policy is a single least-privilege inline statement scoped to cloudfront:CreateInvalidation on this stack's distribution ARN \u2014 managed-policy reuse adds nothing" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "AwsCustomResource policy is a single least-privilege inline statement scoped to cloudfront:CreateInvalidation on this stack's distribution ARN \u2014 managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" }, { - "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgcG9saWN5IGlzIGEgc2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgc3RhdGVtZW50IHNjb3BlZCB0byBjbG91ZGZyb250OkNyZWF0ZUludmFsaWRhdGlvbiBvbiB0aGlzIHN0YWNrJ3MgZGlzdHJpYnV0aW9uIEFSTiDigJQgbWFuYWdlZC1wb2xpY3kgcmV1c2UgYWRkcyBub3RoaW5n" + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" }, { - "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "QXdzQ3VzdG9tUmVzb3VyY2UgcG9saWN5IGlzIGEgc2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgc3RhdGVtZW50IHNjb3BlZCB0byBjbG91ZGZyb250OkNyZWF0ZUludmFsaWRhdGlvbiBvbiB0aGlzIHN0YWNrJ3MgZGlzdHJpYnV0aW9uIEFSTiDigJQgbWFuYWdlZC1wb2xpY3kgcmV1c2UgYWRkcyBub3RoaW5n" + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2561,6 +4113,44 @@ "Type": "AWS::IAM::Policy" }, "RumAppMonitor": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "AppMonitorConfiguration": { "AllowCookies": true, @@ -2607,18 +4197,47 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2647,18 +4266,47 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2705,6 +4353,38 @@ { "id": "AwsSolutions-COG7", "reason": "RUM requires unauthenticated guest credentials for anonymous browser telemetry" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2716,6 +4396,44 @@ "Type": "AWS::Cognito::IdentityPool" }, "RumIdentityPoolRoleAttachment": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "IdentityPoolId": { "Ref": "RumIdentityPool" @@ -2740,23 +4458,55 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIGxvZ3M6RGVsZXRlTG9nR3JvdXAgb24gb25lIGxvZy1ncm91cCBBUk47IG1hbmFnZWQtcG9saWN5IHJldXNlIGFkZHMgbm90aGluZw==" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to logs:DeleteLogGroup on one log-group ARN; managed-policy reuse adds nothing" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIGxvZ3M6RGVsZXRlTG9nR3JvdXAgb24gb25lIGxvZy1ncm91cCBBUk47IG1hbmFnZWQtcG9saWN5IHJldXNlIGFkZHMgbm90aGluZw==" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to logs:DeleteLogGroup on one log-group ARN; managed-policy reuse adds nothing" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIGxvZ3M6RGVsZXRlTG9nR3JvdXAgb24gb25lIGxvZy1ncm91cCBBUk47IG1hbmFnZWQtcG9saWN5IHJldXNlIGFkZHMgbm90aGluZw==" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to logs:DeleteLogGroup on one log-group ARN; managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn:aws:logs:us-east-1::log-group:/aws/vendedlogs/RUMService_ServerlessAppFrontend-us-east-1-rum*:*]", + "reason": "Log-group ARN carries the standard :* log-stream wildcard suffix (required for any CloudWatch Logs resource ARN per the IAM service authorization docs) plus a monitor-name suffix wildcard that keeps the ARN literal for cdk-nag v3's verbatim finding ids \u2014 the grant still reaches only this stack's RUM vended log group namespace." + }, + { + "id": "AwsSolutions-IAM5[Resource::arn::logs:us-east-1::log-group:/aws/vendedlogs/RUMService_ServerlessAppFrontend-us-east-1-rum*:*]", + "reason": "Log-group ARN carries the standard :* log-stream wildcard suffix (required for any CloudWatch Logs resource ARN per the IAM service authorization docs) plus a monitor-name suffix wildcard that keeps the ARN literal for cdk-nag v3's verbatim finding ids \u2014 the grant still reaches only this stack's RUM vended log group namespace." + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "TG9nLWdyb3VwIEFSTiBpbmNsdWRlcyB0aGUgc3RhbmRhcmQgOiogbG9nLXN0cmVhbSB3aWxkY2FyZCBzdWZmaXgg4oCUIHJlcXVpcmVkIGZvciBhbnkgQ2xvdWRXYXRjaCBMb2dzIHJlc291cmNlIEFSTiBwZXIgdGhlIElBTSBzZXJ2aWNlIGF1dGhvcml6YXRpb24gZG9jcy4gVGhlIHJlc291cmNlIGlzIG90aGVyd2lzZSBzY29wZWQgdG8gb25lIHNwZWNpZmljIGxvZyBncm91cCBidWlsdCBmcm9tIHRoZSBtb25pdG9yJ3MgcnVudGltZS1yZXNvbHZlZCBJRC4=" + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2779,24 +4529,7 @@ { "Ref": "AWS::AccountId" }, - ":log-group:/aws/vendedlogs/RUMService_ServerlessAppFrontend-us-east-1-rum", - { - "Fn::Select": [ - 0, - { - "Fn::Split": [ - "-", - { - "Fn::GetAtt": [ - "RumAppMonitor", - "Id" - ] - } - ] - } - ] - }, - ":*" + ":log-group:/aws/vendedlogs/RUMService_ServerlessAppFrontend-us-east-1-rum*:*" ] ] } @@ -2824,23 +4557,55 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIGxvZ3M6RGVsZXRlTG9nR3JvdXAgb24gb25lIGxvZy1ncm91cCBBUk47IG1hbmFnZWQtcG9saWN5IHJldXNlIGFkZHMgbm90aGluZw==" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to logs:DeleteLogGroup on one log-group ARN; managed-policy reuse adds nothing" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIGxvZ3M6RGVsZXRlTG9nR3JvdXAgb24gb25lIGxvZy1ncm91cCBBUk47IG1hbmFnZWQtcG9saWN5IHJldXNlIGFkZHMgbm90aGluZw==" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to logs:DeleteLogGroup on one log-group ARN; managed-policy reuse adds nothing" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIGxvZ3M6RGVsZXRlTG9nR3JvdXAgb24gb25lIGxvZy1ncm91cCBBUk47IG1hbmFnZWQtcG9saWN5IHJldXNlIGFkZHMgbm90aGluZw==" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to logs:DeleteLogGroup on one log-group ARN; managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-IAM5[Resource::arn:aws:logs:us-east-1::log-group:/aws/vendedlogs/RUMService_ServerlessAppFrontend-us-east-1-rum*:*]", + "reason": "Log-group ARN carries the standard :* log-stream wildcard suffix (required for any CloudWatch Logs resource ARN per the IAM service authorization docs) plus a monitor-name suffix wildcard that keeps the ARN literal for cdk-nag v3's verbatim finding ids \u2014 the grant still reaches only this stack's RUM vended log group namespace." + }, + { + "id": "AwsSolutions-IAM5[Resource::arn::logs:us-east-1::log-group:/aws/vendedlogs/RUMService_ServerlessAppFrontend-us-east-1-rum*:*]", + "reason": "Log-group ARN carries the standard :* log-stream wildcard suffix (required for any CloudWatch Logs resource ARN per the IAM service authorization docs) plus a monitor-name suffix wildcard that keeps the ARN literal for cdk-nag v3's verbatim finding ids \u2014 the grant still reaches only this stack's RUM vended log group namespace." + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" }, { - "id": "AwsSolutions-IAM5", - "is_reason_encoded": true, - "reason": "TG9nLWdyb3VwIEFSTiBpbmNsdWRlcyB0aGUgc3RhbmRhcmQgOiogbG9nLXN0cmVhbSB3aWxkY2FyZCBzdWZmaXgg4oCUIHJlcXVpcmVkIGZvciBhbnkgQ2xvdWRXYXRjaCBMb2dzIHJlc291cmNlIEFSTiBwZXIgdGhlIElBTSBzZXJ2aWNlIGF1dGhvcml6YXRpb24gZG9jcy4gVGhlIHJlc291cmNlIGlzIG90aGVyd2lzZSBzY29wZWQgdG8gb25lIHNwZWNpZmljIGxvZyBncm91cCBidWlsdCBmcm9tIHRoZSBtb25pdG9yJ3MgcnVudGltZS1yZXNvbHZlZCBJRC4=" + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2893,18 +4658,47 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2932,18 +4726,47 @@ "rules_to_suppress": [ { "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IGF0dGFjaGVkIHRvIHRoZSBDREsgQXdzQ3VzdG9tUmVzb3VyY2UgaGFuZGxlciDigJQgc2NvcGVkIHRvIHNwZWNpZmljIHJ1bToqIGFjdGlvbnMgb24gb25lIG1vbml0b3IgQVJOOyBtYW5hZ2VkLXBvbGljeSByZXVzZSBhZGRzIG5vdGhpbmc=" + "reason": "Single least-privilege inline policy attached to the CDK AwsCustomResource handler \u2014 scoped to specific rum:* actions on one monitor ARN; managed-policy reuse adds nothing" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -2992,19 +4815,48 @@ "cdk_nag": { "rules_to_suppress": [ { - "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IChydW06UHV0UnVtRXZlbnRzIG9uIG9uZSBtb25pdG9yIEFSTikgaXMgdGlnaHRseSBib3VuZCB0byB0aGlzIHJvbGUncyBzb2xlIHB1cnBvc2Ug4oCUIGFub255bW91cyBicm93c2VyIHRlbGVtZXRyeSB1cGxvYWQ=" + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "Single least-privilege inline policy (rum:PutRumEvents on one monitor ARN) is tightly bound to this role's sole purpose \u2014 anonymous browser telemetry upload" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "Single least-privilege inline policy (rum:PutRumEvents on one monitor ARN) is tightly bound to this role's sole purpose \u2014 anonymous browser telemetry upload" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "Single least-privilege inline policy (rum:PutRumEvents on one monitor ARN) is tightly bound to this role's sole purpose \u2014 anonymous browser telemetry upload" + }, + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" }, { - "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IChydW06UHV0UnVtRXZlbnRzIG9uIG9uZSBtb25pdG9yIEFSTikgaXMgdGlnaHRseSBib3VuZCB0byB0aGlzIHJvbGUncyBzb2xlIHB1cnBvc2Ug4oCUIGFub255bW91cyBicm93c2VyIHRlbGVtZXRyeSB1cGxvYWQ=" + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" }, { - "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "U2luZ2xlIGxlYXN0LXByaXZpbGVnZSBpbmxpbmUgcG9saWN5IChydW06UHV0UnVtRXZlbnRzIG9uIG9uZSBtb25pdG9yIEFSTikgaXMgdGlnaHRseSBib3VuZCB0byB0aGlzIHJvbGUncyBzb2xlIHB1cnBvc2Ug4oCUIGFub255bW91cyBicm93c2VyIHRlbGVtZXRyeSB1cGxvYWQ=" + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" } ] } @@ -3070,6 +4922,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Recent 403 AccessDenied responses with requester and operation details", @@ -3083,6 +4973,44 @@ "DependsOn": [ "AccessLogsDatabase" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "CatalogId": { "Ref": "AWS::AccountId" @@ -3231,6 +5159,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Recent failed S3 requests with error details", @@ -3244,6 +5210,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Who read which object (GET.OBJECT operations) with status and bytes", @@ -3257,6 +5261,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Highest-latency S3 requests by total_time (ms)", @@ -3270,6 +5312,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Most common S3 operations with error counts", @@ -3283,6 +5363,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Highest-traffic S3 requesters with error counts", @@ -3293,6 +5411,44 @@ "Type": "AWS::Athena::NamedQuery" }, "SecurityHeadersPolicyE1741D63": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "ResponseHeadersPolicyConfig": { "Comment": "Security headers for the Serverless App frontend (managed SECURITY_HEADERS + HSTS + CSP)", @@ -3344,6 +5500,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "BLOCK actions grouped by client country (last 30 days)", @@ -3357,6 +5551,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Most recent BLOCK actions (last 30 days) with client IP, country, URI, and terminating rule", @@ -3370,6 +5602,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Client IPs with the most BLOCK actions (last 30 days)", @@ -3383,6 +5653,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Which rules terminated the most requests (last 30 days)", @@ -3396,6 +5704,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "BLOCK actions grouped by client country (last 30 days)", @@ -3409,6 +5755,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Most recent BLOCK actions (last 30 days) with client IP, country, URI, and terminating rule", @@ -3422,6 +5806,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Client IPs with the most BLOCK actions (last 30 days)", @@ -3435,6 +5857,44 @@ "DependsOn": [ "AccessLogsWorkGroup" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "Database": "serverlessappfrontend_us_east_1_access_logs", "Description": "Which rules terminated the most requests (last 30 days)", @@ -3448,6 +5908,44 @@ "DependsOn": [ "AccessLogsDatabase" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "CatalogId": { "Ref": "AWS::AccountId" @@ -3613,6 +6111,44 @@ "DependsOn": [ "AccessLogsDatabase" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-CFR1", + "reason": "Geo restriction not required for sample app" + }, + { + "id": "AwsSolutions-CFR4", + "reason": "Using default CloudFront certificate \u2014 no custom domain for sample app" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "S3 replication not needed for sample app \u2014 static assets are redeployable" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "S3 versioning not needed for sample app \u2014 static assets are redeployable via cdk deploy" + } + ] + } + }, "Properties": { "CatalogId": { "Ref": "AWS::AccountId" diff --git a/tests/cdk/snapshots/ServerlessAppWaf-us-east-1.json b/tests/cdk/snapshots/ServerlessAppWaf-us-east-1.json index 9fea658..e4da61b 100644 --- a/tests/cdk/snapshots/ServerlessAppWaf-us-east-1.json +++ b/tests/cdk/snapshots/ServerlessAppWaf-us-east-1.json @@ -1,25 +1,4 @@ { - "Metadata": { - "cdk_nag": { - "rules_to_suppress": [ - { - "id": "NIST.800.53.R5-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLJ3MgQ3Jvc3NSZWdpb25FeHBvcnRXcml0ZXIgY3VzdG9tIHJlc291cmNlIChjcmVhdGVkIGxhemlseSBieSBjcm9zc19yZWdpb25fcmVmZXJlbmNlcyBhZnRlciBzdGFjayBjb25zdHJ1Y3Rpb24pIHVzZXMgYSBDREstZ2VuZXJhdGVkIGlubGluZSBwb2xpY3kgb24gaXRzIHByb3ZpZGVyIHJvbGUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" - }, - { - "id": "HIPAA.Security-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLJ3MgQ3Jvc3NSZWdpb25FeHBvcnRXcml0ZXIgY3VzdG9tIHJlc291cmNlIChjcmVhdGVkIGxhemlseSBieSBjcm9zc19yZWdpb25fcmVmZXJlbmNlcyBhZnRlciBzdGFjayBjb25zdHJ1Y3Rpb24pIHVzZXMgYSBDREstZ2VuZXJhdGVkIGlubGluZSBwb2xpY3kgb24gaXRzIHByb3ZpZGVyIHJvbGUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" - }, - { - "id": "PCI.DSS.321-IAMNoInlinePolicy", - "is_reason_encoded": true, - "reason": "Q0RLJ3MgQ3Jvc3NSZWdpb25FeHBvcnRXcml0ZXIgY3VzdG9tIHJlc291cmNlIChjcmVhdGVkIGxhemlseSBieSBjcm9zc19yZWdpb25fcmVmZXJlbmNlcyBhZnRlciBzdGFjayBjb25zdHJ1Y3Rpb24pIHVzZXMgYSBDREstZ2VuZXJhdGVkIGlubGluZSBwb2xpY3kgb24gaXRzIHByb3ZpZGVyIHJvbGUg4oCUIG5vdCBkaXJlY3RseSBjb25maWd1cmFibGU=" - } - ] - } - }, "Outputs": { "ExportsOutputFnGetAttWebACLArn3404FD8D": { "Export": { @@ -67,6 +46,24 @@ "Resources": { "AutoDeleteObjectsLogGroupC1303B18": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + } + ] + } + }, "Properties": { "KmsKeyId": { "Fn::GetAtt": [ @@ -110,32 +107,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -147,18 +133,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -166,18 +149,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -185,8 +165,11 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" } ] } @@ -227,32 +210,21 @@ "Metadata": { "cdk_nag": { "rules_to_suppress": [ - { - "id": "AwsSolutions-IAM4", - "reason": "CDK-managed singleton Lambda uses AWS managed execution role" - }, - { - "id": "AwsSolutions-IAM5", - "reason": "CDK-managed singleton Lambda uses wildcard in auto-generated policy" - }, { "id": "AwsSolutions-L1", "reason": "CDK-managed singleton Lambda runtime is not configurable" }, { "id": "Serverless-LambdaTracing", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgdHJhY2luZyBpcyBub3QgY29uZmlndXJhYmxl" + "reason": "CDK-managed singleton Lambda \u2014 tracing is not configurable" }, { "id": "Serverless-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "Serverless-LambdaDefaultMemorySize", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgbWVtb3J5IGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 memory is not configurable" }, { "id": "Serverless-LambdaLatestVersion", @@ -264,18 +236,15 @@ }, { "id": "NIST.800.53.R5-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "NIST.800.53.R5-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "NIST.800.53.R5-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "HIPAA.Security-IAMNoInlinePolicy", @@ -283,18 +252,15 @@ }, { "id": "HIPAA.Security-LambdaDLQ", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgRExRIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 DLQ is not configurable" }, { "id": "HIPAA.Security-LambdaConcurrency", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgY29uY3VycmVuY3kgaXMgbm90IGNvbmZpZ3VyYWJsZQ==" + "reason": "CDK-managed singleton Lambda \u2014 concurrency is not configurable" }, { "id": "HIPAA.Security-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" }, { "id": "PCI.DSS.321-IAMNoInlinePolicy", @@ -302,8 +268,11 @@ }, { "id": "PCI.DSS.321-LambdaInsideVPC", - "is_reason_encoded": true, - "reason": "Q0RLLW1hbmFnZWQgc2luZ2xldG9uIExhbWJkYSDigJQgVlBDIGlzIG5vdCBjb25maWd1cmFibGU=" + "reason": "CDK-managed singleton Lambda \u2014 VPC is not configurable" + }, + { + "id": "AwsSolutions-IAM4[Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]", + "reason": "CDK-managed singleton Lambda uses AWS managed execution role" } ] } @@ -333,6 +302,24 @@ "DependsOn": [ "WafLogsBucketPolicyC1654A35" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + } + ] + } + }, "Properties": { "LogDestinationConfigs": [ { @@ -353,6 +340,24 @@ }, "WafEncryptionKeyB025E51A": { "DeletionPolicy": "Delete", + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + } + ] + } + }, "Properties": { "Description": "KMS key for ServerlessAppWaf-us-east-1 provider log group encryption", "EnableKeyRotation": true, @@ -430,68 +435,67 @@ "rules_to_suppress": [ { "id": "AwsSolutions-S1", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3BucketLoggingEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3DefaultEncryptionKMS", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3BucketVersioningEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "NIST.800.53.R5-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "HIPAA.Security-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" }, { "id": "PCI.DSS.321-S3BucketReplicationEnabled", - "is_reason_encoded": true, - "reason": "V0FGIGxvZyBkZXN0aW5hdGlvbiBidWNrZXQg4oCUIFNTRS1TMyAoZGVsaXZlcnkgZG9lc24ndCBzdXBwb3J0IEtNUy1DTUsgYnVja2V0cyksIG5vIHNlbGYtbG9nZ2luZy92ZXJzaW9uaW5nL3JlcGxpY2F0aW9uIGZvciBhbiBhcHBlbmQtb25seSBsb2cgc2luaw==" + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" } ] } @@ -559,6 +563,76 @@ "AutoDeleteObjectsLogGroupC1303B18", "WafLogsBucketPolicyC1654A35" ], + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + } + ] + } + }, "Properties": { "BucketName": { "Ref": "WafLogsBucket2E62CA90" @@ -574,6 +648,76 @@ "UpdateReplacePolicy": "Delete" }, "WafLogsBucketPolicyC1654A35": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "AwsSolutions-S1", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketLoggingEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3DefaultEncryptionKMS", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketVersioningEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "HIPAA.Security-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "PCI.DSS.321-S3BucketReplicationEnabled", + "reason": "WAF log destination bucket \u2014 SSE-S3 (delivery doesn't support KMS-CMK buckets), no self-logging/versioning/replication for an append-only log sink" + }, + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + } + ] + } + }, "Properties": { "Bucket": { "Ref": "WafLogsBucket2E62CA90" @@ -754,6 +898,24 @@ "Type": "AWS::S3::BucketPolicy" }, "WebACL": { + "Metadata": { + "cdk_nag": { + "rules_to_suppress": [ + { + "id": "NIST.800.53.R5-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "HIPAA.Security-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + }, + { + "id": "PCI.DSS.321-IAMNoInlinePolicy", + "reason": "CDK's CrossRegionExportWriter custom resource (created lazily by cross_region_references after stack construction) uses a CDK-generated inline policy on its provider role \u2014 not directly configurable" + } + ] + } + }, "Properties": { "DefaultAction": { "Allow": {} diff --git a/tests/cdk/test_snapshots.py b/tests/cdk/test_snapshots.py index 5fb1e75..8156c2f 100644 --- a/tests/cdk/test_snapshots.py +++ b/tests/cdk/test_snapshots.py @@ -34,6 +34,7 @@ from aws_cdk.assertions import Template from infrastructure.app_stage import AppStage +from infrastructure.nag_utils import attach_nag_packs # Skip Docker bundling so these tests run without Docker (same key the CLI honours). _NO_BUNDLING = {"aws:cdk:bundling-stacks": []} @@ -66,8 +67,13 @@ def _normalize(template: dict) -> str: @pytest.fixture(scope="module") def prod_stage() -> AppStage: - """Synthesize the default (prod) stage for us-east-1.""" + """Synthesize the default (prod) stage for us-east-1. + + Packs attached as in app.py: cdk-nag v3's write-suppressions aspect is + what keeps the cdk_nag Metadata audit trail in the snapshotted templates. + """ app = cdk.App(context=_NO_BUNDLING) + attach_nag_packs(app) return AppStage(app, "ServerlessApp-us-east-1-stage", region="us-east-1") diff --git a/tests/cdk/test_stage.py b/tests/cdk/test_stage.py index 18595d9..4e526d0 100644 --- a/tests/cdk/test_stage.py +++ b/tests/cdk/test_stage.py @@ -8,43 +8,89 @@ 1. **prod keeps the legacy names byte-for-byte** — any drift would orphan the currently deployed stacks (CloudFormation matches stacks by name). 2. **Non-prod names embed the env name and disable alarm paging.** -3. **Every stack synthesizes with zero unsuppressed cdk-nag errors.** This is - a near-equivalent of the CLI ``cdk synth '**'`` gate that runs without - Docker: ``Template.from_stack`` never *raises* on Aspect errors, but the - findings are present as error-level annotations, so asserting the - annotation list is empty catches an unsuppressed finding locally instead - of in the CI cdk-check job. The CLI synth remains the authoritative gate - (it also covers asset bundling); see CLAUDE.md. +3. **Every shipped shape synthesizes with zero unacknowledged cdk-nag + findings.** cdk-nag v3 packs are policy-validation plugins evaluated over + the synthesized assembly; the gate here synthesizes each deployment shape + (fixtures attach the packs the same way ``app.py`` does) and parses the + assembly's ``validation-report.json`` — neither ``app.synth()`` nor the + CLI fails natively for Python apps (see ``_unacknowledged_findings``). + The project's own ``TemplateConventionChecks`` Aspect still surfaces + error-level annotations, asserted separately. The CLI path stays gated by + ``scripts/check_validation_report.py`` in ``make cdk-synth`` and CI. """ import json +from pathlib import Path +from typing import cast import pytest aws_cdk = pytest.importorskip("aws_cdk", reason="aws_cdk not installed — skipping CDK stage tests") import aws_cdk as cdk +from aws_cdk import aws_s3 as s3 from aws_cdk.assertions import Annotations, Match, Template from infrastructure.app_stage import DEFAULT_ENV_NAME, AppStage, parse_context_flag, validate_env_name +from infrastructure.nag_utils import attach_nag_packs +from infrastructure.validation_aspects import TemplateConventionChecks # Skip Docker bundling so these tests run without Docker (same key the CLI honours). _NO_BUNDLING = {"aws:cdk:bundling-stacks": []} +def _nag_app() -> cdk.App: + """A test App with the five rule packs attached, mirroring app.py.""" + app = cdk.App(context=_NO_BUNDLING) + attach_nag_packs(app) + return app + + +def _unacknowledged_findings(root: cdk.App) -> list[str]: + """Synthesize an App and return its unacknowledged cdk-nag v3 findings. + + Policy-validation plugins evaluate the produced assembly during + ``App.synth()``, but the in-process synth does NOT raise on findings + (observed against cdk-nag 3.0.1) — it prints the report and writes + ``validation-report.json`` into the cloud assembly. Reading that file is + therefore the reliable in-process gate. The CLI synth doesn't fail for + Python apps either (CDK sets process.exitCode in jsii's throwaway Node + kernel), which is why make cdk-synth / CI run + scripts/check_validation_report.py over cdk.out after synthesizing. + """ + assembly = root.synth() + report_path = Path(assembly.directory) / "validation-report.json" + if not report_path.exists(): + return [] + report = json.loads(report_path.read_text()) + return [ + f"{violation.get('ruleName')} @ " + + ", ".join(r.get("resourceLogicalId", "?") for r in violation.get("violatingResources", [])) + for plugin_report in report.get("pluginReports", []) + for violation in plugin_report.get("violations", []) + ] + + +def _assert_nag_clean(stage: AppStage) -> None: + findings = _unacknowledged_findings(cast(cdk.App, stage.node.root)) + details = "\n".join(f" {f}" for f in findings) + assert not findings, ( + f"unacknowledged cdk-nag findings — fix the resource or add a scoped " + f"acknowledgment with a reason (see CLAUDE.md):\n{details}" + ) + + @pytest.fixture(scope="module") def prod_stage() -> AppStage: """Synthesize the default (prod) stage for us-east-1.""" - app = cdk.App(context=_NO_BUNDLING) - return AppStage(app, "ServerlessApp-us-east-1-stage", region="us-east-1") + return AppStage(_nag_app(), "ServerlessApp-us-east-1-stage", region="us-east-1") @pytest.fixture(scope="module") def dev_stage() -> AppStage: """Synthesize an ephemeral developer stage.""" - app = cdk.App(context=_NO_BUNDLING) return AppStage( - app, + _nag_app(), "ServerlessApp-alice-feature-x-us-east-1-stage", region="us-east-1", env_name="alice-feature-x", @@ -214,73 +260,76 @@ def test_dev_codedeploy_is_all_at_once(self, dev_stage: AppStage) -> None: class TestNagCompliance: - """Zero error-level annotations per stack, asserted without Docker. + """cdk-nag v3 gate: unacknowledged findings fail ``app.synth()`` itself. - Covers both cdk-nag rule-pack findings and the project's - ``TemplateConventionChecks`` validation Aspect — both surface as - error-level annotations through ``apply_compliance_aspects``. + The five rule packs are policy-validation plugins on each fixture's test + App (attached exactly the way ``app.py`` does), so one full-app synth per + deployment shape is the compliance gate for every stack in that shape. + The project's own ``TemplateConventionChecks`` Aspect still surfaces + error-level annotations and is asserted separately. """ - @pytest.mark.parametrize("stack_attr", ["waf", "data", "backend", "frontend", "audit"]) - def test_no_unsuppressed_nag_errors(self, prod_stage: AppStage, stack_attr: str) -> None: - stack = getattr(prod_stage, stack_attr) - errors = Annotations.from_stack(stack).find_error("*", Match.string_like_regexp(".*")) - details = "\n".join( - f" {e.id}: {(e.entry.data if isinstance(e.entry.data, str) else str(e.entry.data)).splitlines()[0]}" - for e in errors - ) - assert not errors, ( - f"unsuppressed cdk-nag findings on {stack.stack_name} — fix the resource or add a " - f"scoped suppression with a reason (see CLAUDE.md):\n{details}" - ) + def test_prod_stage_has_no_unacknowledged_findings(self, prod_stage: AppStage) -> None: + _assert_nag_clean(prod_stage) - def test_dev_stage_has_no_unsuppressed_nag_errors(self, dev_stage: AppStage) -> None: + def test_dev_stage_has_no_unacknowledged_findings(self, dev_stage: AppStage) -> None: # The non-prod shape (no SNS topic) must be nag-clean too — otherwise # ephemeral stacks would fail the CI synth gate when env is overridden. - errors = Annotations.from_stack(dev_stage.backend).find_error("*", Match.string_like_regexp(".*")) - assert not errors + _assert_nag_clean(dev_stage) - def test_appconfig_monitor_shape_has_no_unsuppressed_nag_errors(self) -> None: + def test_appconfig_monitor_shape_has_no_unacknowledged_findings(self) -> None: # The opt-in monitor shape adds an alarm + a cloudwatch:DescribeAlarms - # role; its suppressions (IAM5 wildcard, inline-policy, no-alarm-action) - # must be complete or a fork enabling appconfig_monitor would fail synth. - app = cdk.App(context=_NO_BUNDLING) - stage = AppStage(app, "ServerlessApp-us-east-1-stage", region="us-east-1", appconfig_monitor=True) - errors = Annotations.from_stack(stage.backend).find_error("*", Match.string_like_regexp(".*")) - assert not errors + # role; its acknowledgments (IAM5 wildcard, inline-policy, + # no-alarm-action) must be complete or a fork enabling + # appconfig_monitor would fail synth. + stage = AppStage(_nag_app(), "ServerlessApp-us-east-1-stage", region="us-east-1", appconfig_monitor=True) + _assert_nag_clean(stage) - def test_retain_data_shape_has_no_unsuppressed_nag_errors(self) -> None: + def test_retain_data_shape_has_no_unacknowledged_findings(self) -> None: # The production-fork shape (retain_data=True: RETAIN + deletion/ # termination protection, retained buckets with no auto-delete # provider) is never synthesized by the CI CLI gate (default context) — # this is its only nag gate. A finding unique to the retained shape # would otherwise surface for the first time on a production fork's # own synth, after they flip the one switch the template tells them to. - app = cdk.App(context=_NO_BUNDLING) - stage = AppStage(app, "ServerlessApp-us-east-1-stage", region="us-east-1", retain_data=True) - for stack in (stage.data, stage.audit): - errors = Annotations.from_stack(stack).find_error("*", Match.string_like_regexp(".*")) - assert not errors, f"unsuppressed cdk-nag findings on the retained shape of {stack.stack_name}" + stage = AppStage(_nag_app(), "ServerlessApp-us-east-1-stage", region="us-east-1", retain_data=True) + _assert_nag_clean(stage) + + def test_nag_gate_can_fail(self) -> None: + # The assertion that the gate is not vacuous: a deliberately + # non-compliant resource must produce findings in the validation + # report. If the packs stop attaching (or the report location/shape + # changes), every clean-gate test above would pass on nothing — this + # canary is what catches that. A bare default Bucket violates several + # pack rules (no access logs, no SSL, no KMS default encryption). + app = _nag_app() + stack = cdk.Stack(app, "NagCanaryStack") + s3.Bucket(stack, "NonCompliantBucket") + findings = _unacknowledged_findings(app) + assert findings, "the nag gate reported nothing for a non-compliant bucket — the gate is vacuous" + assert any("AwsSolutions-S" in f or "S3Bucket" in f for f in findings), findings @pytest.mark.parametrize("stack_attr", ["waf", "data", "backend", "frontend", "audit"]) - def test_compliance_aspects_attached_to_every_stack(self, prod_stage: AppStage, stack_attr: str) -> None: - # The whole nag gate hangs on each stack constructor calling - # apply_compliance_aspects. If a refactor drops that call from one - # stack, every other gate passes VACUOUSLY: zero annotations here, a - # clean CLI synth, and unchanged snapshots (NagSuppressions write - # resource metadata whether or not the packs run). This is the - # assertion that the gate can actually fail — the five rule packs and - # the project's own validation Aspect must be attached to every stack. - aspect_names = {type(aspect).__name__ for aspect in cdk.Aspects.of(getattr(prod_stage, stack_attr)).all} - for expected in ( - "AwsSolutionsChecks", - "ServerlessChecks", - "NIST80053R5Checks", - "HIPAASecurityChecks", - "PCIDSS321Checks", - "TemplateConventionChecks", - ): - assert expected in aspect_names, ( - f"{expected} is not attached to the {stack_attr} stack — apply_compliance_aspects " - "was likely dropped from its constructor, which silently disables the nag gate there" - ) + def test_convention_checks_have_no_error_annotations(self, prod_stage: AppStage, stack_attr: str) -> None: + # TemplateConventionChecks (the project's own Aspect) still reports + # via error-level annotations, independent of the v3 plugin packs. + stack = getattr(prod_stage, stack_attr) + errors = Annotations.from_stack(stack).find_error("*", Match.string_like_regexp(".*")) + details = "\n".join( + f" {e.id}: {(e.entry.data if isinstance(e.entry.data, str) else str(e.entry.data)).splitlines()[0]}" + for e in errors + ) + assert not errors, f"error annotations on {stack.stack_name} (TemplateConventionChecks?):\n{details}" + + @pytest.mark.parametrize("stack_attr", ["waf", "data", "backend", "frontend", "audit"]) + def test_convention_aspect_attached_to_every_stack(self, prod_stage: AppStage, stack_attr: str) -> None: + # The convention gate hangs on each stack constructor calling + # apply_compliance_aspects; dropping the call would pass every other + # gate vacuously. (The five rule packs are covered by + # test_nag_gate_can_fail — they are App-level plugins now, not + # per-stack Aspects.) + aspects = cdk.Aspects.of(getattr(prod_stage, stack_attr)).all + assert any(isinstance(aspect, TemplateConventionChecks) for aspect in aspects), ( + f"TemplateConventionChecks is not attached to the {stack_attr} stack — " + "apply_compliance_aspects was likely dropped from its constructor" + ) diff --git a/uv.lock b/uv.lock index a4ec846..827eab0 100644 --- a/uv.lock +++ b/uv.lock @@ -330,18 +330,17 @@ wheels = [ [[package]] name = "cdk-nag" -version = "2.38.2" +version = "3.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aws-cdk-lib" }, { name = "constructs" }, { name = "jsii" }, { name = "publication" }, - { name = "typeguard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/37/c1/2caccd33f659d95c0a48a13c838ae885aa3a4ee6ed768d40848cc7eb4202/cdk_nag-2.38.2.tar.gz", hash = "sha256:a4d419062ea4d64c2892942214b9184b124eb2bc36d087982007b3455e1ac443", size = 777714, upload-time = "2026-04-27T17:33:08.813Z" } +sdist = { url = "https://files.pythonhosted.org/packages/87/8d/7c98ad8a867925ba1ac7693dd4f74525def8459abb5a4fcef7731496f5be/cdk_nag-3.0.1.tar.gz", hash = "sha256:82db3c3383bbc6ab5e68f55cad688a04b02c9f37cc9e8de566d93b080a2efa87", size = 734645, upload-time = "2026-06-15T07:52:13.636Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/f5/ff45ffcf45d421a668c6a0a994d5e0491167d8ca432108a339e272e012e4/cdk_nag-2.38.2-py3-none-any.whl", hash = "sha256:d37f18ae9450f401bcc55d5d82138beee561486806579849cb9be25ff7565904", size = 771251, upload-time = "2026-04-27T17:33:05.04Z" }, + { url = "https://files.pythonhosted.org/packages/1d/bc/6e4116dad5ae0d2157c39373adcbdd86313822e499da499afefe43b892a5/cdk_nag-3.0.1-py3-none-any.whl", hash = "sha256:b32b77e6ad29501231ed3a99f36f29670e85a587617fc05b554ff2f305243186", size = 732361, upload-time = "2026-06-15T07:52:10.861Z" }, ] [[package]] @@ -1762,7 +1761,7 @@ cdk = [ { name = "aws-cdk-aws-lambda-python-alpha", specifier = "==2.261.0a0" }, { name = "aws-cdk-lib", specifier = "==2.261.0" }, { name = "cdk-monitoring-constructs", specifier = "==10.1.0" }, - { name = "cdk-nag", specifier = "==2.38.2" }, + { name = "cdk-nag", specifier = "==3.0.1" }, { name = "constructs", specifier = "==10.6.0" }, ] docs = [