cdk-local 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,6 +108,8 @@ Use this for fast iteration on Lambda code, API routing checks, and container ta
108
108
 
109
109
  Once your stack is deployed to AWS (via the AWS CDK CLI or any other tool), pass `--from-cfn-stack <StackName>` and cdk-local reads the deployed CloudFormation stack to inject real ARNs, Secrets values, and IAM credentials (resolved from your current AWS profile) into the local execution.
110
110
 
111
+ For Lambda (`invoke`, `start-api`), this also recovers env-var values that CloudFormation resolved at deploy time but `ListStackResources` does not expose — e.g. `SIBLING_ARN: Fn::GetAtt <OtherFunction>.Arn`. cdk-local reads the deployed function's own resolved `Environment.Variables` (via `lambda:GetFunctionConfiguration`) and fills those keys, so a Lambda that calls a sibling Lambda by ARN runs locally without a manual `--env-vars` entry. (These values enter the local container env in plaintext; Lambda env vars are a non-secret property, so this exposes nothing the deployed function doesn't already surface to any caller with `lambda:GetFunctionConfiguration`.)
112
+
111
113
  #### HTTP APIs & Function URLs — `start-api` (the headline use case)
112
114
 
113
115
  A local API talking to real AWS — point a frontend at it for end-to-end debugging, including real Cognito JWT verification.
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { i as createLocalStartApiCommand, r as createLocalRunTaskCommand, t as createLocalStartServiceCommand, u as createLocalInvokeCommand } from "./local-start-service-C-nJKdAj.js";
2
+ import { i as createLocalStartApiCommand, r as createLocalRunTaskCommand, t as createLocalStartServiceCommand, u as createLocalInvokeCommand } from "./local-start-service-BbOWJ2dK.js";
3
3
  import { Command } from "commander";
4
4
 
5
5
  //#region src/cli/index.ts
6
6
  const program = new Command();
7
- program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.8.0");
7
+ program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.10.0");
8
8
  program.addCommand(createLocalInvokeCommand());
9
9
  program.addCommand(createLocalStartApiCommand());
10
10
  program.addCommand(createLocalRunTaskCommand());
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { createLocalInvokeCommand } from './commands/local-invoke.js';\nimport { createLocalStartApiCommand } from './commands/local-start-api.js';\nimport { createLocalRunTaskCommand } from './commands/local-run-task.js';\nimport { createLocalStartServiceCommand } from './commands/local-start-service.js';\n\ndeclare const __CDK_LOCAL_VERSION__: string;\n\nconst program = new Command();\nprogram\n .name('cdkl')\n .description('Run AWS CDK stacks locally with Docker.')\n .version(__CDK_LOCAL_VERSION__);\n\nprogram.addCommand(createLocalInvokeCommand());\nprogram.addCommand(createLocalStartApiCommand());\nprogram.addCommand(createLocalRunTaskCommand());\nprogram.addCommand(createLocalStartServiceCommand());\n\nvoid program.parseAsync(process.argv);\n"],"mappings":";;;;;AAUA,MAAM,UAAU,IAAI,SAAS;AAC7B,QACG,KAAK,OAAO,CACZ,YAAY,0CAA0C,CACtD,gBAA8B;AAEjC,QAAQ,WAAW,0BAA0B,CAAC;AAC9C,QAAQ,WAAW,4BAA4B,CAAC;AAChD,QAAQ,WAAW,2BAA2B,CAAC;AAC/C,QAAQ,WAAW,gCAAgC,CAAC;AAE/C,QAAQ,WAAW,QAAQ,KAAK"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { createLocalInvokeCommand } from './commands/local-invoke.js';\nimport { createLocalStartApiCommand } from './commands/local-start-api.js';\nimport { createLocalRunTaskCommand } from './commands/local-run-task.js';\nimport { createLocalStartServiceCommand } from './commands/local-start-service.js';\n\ndeclare const __CDK_LOCAL_VERSION__: string;\n\nconst program = new Command();\nprogram\n .name('cdkl')\n .description('Run AWS CDK stacks locally with Docker.')\n .version(__CDK_LOCAL_VERSION__);\n\nprogram.addCommand(createLocalInvokeCommand());\nprogram.addCommand(createLocalStartApiCommand());\nprogram.addCommand(createLocalRunTaskCommand());\nprogram.addCommand(createLocalStartServiceCommand());\n\nvoid program.parseAsync(process.argv);\n"],"mappings":";;;;;AAUA,MAAM,UAAU,IAAI,SAAS;AAC7B,QACG,KAAK,OAAO,CACZ,YAAY,0CAA0C,CACtD,iBAA8B;AAEjC,QAAQ,WAAW,0BAA0B,CAAC;AAC9C,QAAQ,WAAW,4BAA4B,CAAC;AAChD,QAAQ,WAAW,2BAA2B,CAAC;AAC/C,QAAQ,WAAW,gCAAgC,CAAC;AAE/C,QAAQ,WAAW,QAAQ,KAAK"}
package/dist/index.d.ts CHANGED
@@ -43,6 +43,7 @@ interface LocalStateProvider {
43
43
  readonly label: string;
44
44
  load(stackName: string, synthRegion: string | undefined): Promise<LocalStateRecord | undefined>;
45
45
  buildCrossStackResolver(consumerRegion: string): Promise<CrossStackResolver | undefined>;
46
+ resolveDeployedFunctionEnv?(functionPhysicalId: string): Promise<Record<string, string> | undefined>;
46
47
  dispose(): void;
47
48
  }
48
49
  //#endregion
@@ -114,10 +115,13 @@ declare class CfnLocalStateProvider implements LocalStateProvider {
114
115
  private readonly cfnStackName;
115
116
  private readonly region;
116
117
  private client;
118
+ private lambdaClient;
117
119
  private readonly clientOptions;
118
120
  private disposed;
119
121
  constructor(opts: CfnLocalStateProviderOptions);
120
122
  private getClient;
123
+ private getLambdaClient;
124
+ resolveDeployedFunctionEnv(functionPhysicalId: string): Promise<Record<string, string> | undefined>;
121
125
  load(_stackName: string, _synthRegion: string | undefined): Promise<LocalStateRecord | undefined>;
122
126
  buildCrossStackResolver(_consumerRegion: string): Promise<CrossStackResolver | undefined>;
123
127
  dispose(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types/state.ts","../src/local/state-resolver.ts","../src/local/local-state-provider.ts","../src/cli/commands/local-state-source.ts","../src/local/embed-config.ts","../src/cli/commands/local-invoke.ts","../src/cli/commands/local-start-api.ts","../src/cli/commands/local-run-task.ts","../src/cli/commands/local-start-service.ts","../src/local/cfn-local-state-provider.ts","../src/local/intrinsic-utils.ts","../src/local/intrinsic-lambda-arn.ts","../src/local/parameter-mapping.ts","../src/local/api-gateway-response.ts","../src/local/docker-inspect.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types/state.ts","../src/local/state-resolver.ts","../src/local/local-state-provider.ts","../src/cli/commands/local-state-source.ts","../src/local/embed-config.ts","../src/cli/commands/local-invoke.ts","../src/cli/commands/local-start-api.ts","../src/cli/commands/local-run-task.ts","../src/cli/commands/local-start-service.ts","../src/local/cfn-local-state-provider.ts","../src/local/intrinsic-utils.ts","../src/local/intrinsic-lambda-arn.ts","../src/local/parameter-mapping.ts","../src/local/api-gateway-response.ts","../src/local/docker-inspect.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as CfnLocalStateProvider, a as resolveSelectionExpression, c as pickRefLogicalId, d as LocalStateSourceError, f as createLocalStateProvider, g as resolveCfnStackName, h as resolveCfnRegion, i as createLocalStartApiCommand, l as resolveLambdaArnIntrinsic, m as rejectExplicitCfnStackWithMultipleStacks, n as getContainerNetworkIp, o as resolveServiceIntegrationParameters, p as isCfnFlagPresent, r as createLocalRunTaskCommand, s as translateLambdaResponse, t as createLocalStartServiceCommand, u as createLocalInvokeCommand } from "./local-start-service-C-nJKdAj.js";
1
+ import { _ as CfnLocalStateProvider, a as resolveSelectionExpression, c as pickRefLogicalId, d as LocalStateSourceError, f as createLocalStateProvider, g as resolveCfnStackName, h as resolveCfnRegion, i as createLocalStartApiCommand, l as resolveLambdaArnIntrinsic, m as rejectExplicitCfnStackWithMultipleStacks, n as getContainerNetworkIp, o as resolveServiceIntegrationParameters, p as isCfnFlagPresent, r as createLocalRunTaskCommand, s as translateLambdaResponse, t as createLocalStartServiceCommand, u as createLocalInvokeCommand } from "./local-start-service-BbOWJ2dK.js";
2
2
 
3
3
  export { CfnLocalStateProvider, LocalStateSourceError, createLocalInvokeCommand, createLocalRunTaskCommand, createLocalStartApiCommand, createLocalStartServiceCommand, createLocalStateProvider, getContainerNetworkIp, isCfnFlagPresent, pickRefLogicalId, rejectExplicitCfnStackWithMultipleStacks, resolveCfnRegion, resolveCfnStackName, resolveLambdaArnIntrinsic, resolveSelectionExpression, resolveServiceIntegrationParameters, translateLambdaResponse };
@@ -8,6 +8,7 @@ import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk
8
8
  import { AssetManifestArtifact } from "@aws-cdk/cloud-assembly-api";
9
9
  import { CdkAppMultiContext, NonInteractiveIoHost, Toolkit } from "@aws-cdk/toolkit-lib";
10
10
  import { CloudFormationClient, DescribeStacksCommand, ListExportsCommand, ListStackResourcesCommand } from "@aws-sdk/client-cloudformation";
11
+ import { GetFunctionConfigurationCommand, LambdaClient } from "@aws-sdk/client-lambda";
11
12
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
12
13
  import { Readable } from "node:stream";
13
14
  import { pipeline } from "node:stream/promises";
@@ -531,13 +532,18 @@ function resolveCdkPathToLogicalIds(input, index) {
531
532
  * map built from `ListStackResources.StackResourceSummaries[]` (one
532
533
  * entry per `(LogicalResourceId, PhysicalResourceId, ResourceType)`
533
534
  * tuple).
534
- * - `Fn::GetAtt: [<LogicalId>, <Attr>]` → **warn-and-drop**. CFn's
535
- * `ListStackResources` does NOT return per-attribute values
536
- * and the v1 policy (issue #606 recommendation (a)) is to surface
537
- * a per-key warn instead of pulling in the full provisioning layer
538
- * to call provider-specific describe APIs (e.g. `GetQueueAttributes`
539
- * for SQS, `GetFunction` for Lambda). Users override the affected
540
- * env var via `--env-vars` if the value is critical.
535
+ * - `Fn::GetAtt: [<LogicalId>, <Attr>]` → not resolvable from
536
+ * `ListStackResources` (which returns physical IDs only, no
537
+ * per-attribute values). For a Lambda function's OWN env vars this
538
+ * gap is closed by {@link CfnLocalStateProvider.resolveDeployedFunctionEnv}:
539
+ * because `--from-cfn-stack` means the consumer function is itself
540
+ * deployed, CloudFormation already resolved every intrinsic (incl.
541
+ * `Fn::GetAtt <Sibling>.Arn`) into the function's
542
+ * `Environment.Variables` at deploy time, so the env-substitution
543
+ * layer reads the concrete value back via
544
+ * `lambda:GetFunctionConfiguration`. Intrinsic values that are NOT a
545
+ * consumer-Lambda env var (e.g. an ECS container env entry) still
546
+ * warn-and-drop and can be overridden via `--env-vars`.
541
547
  * - `Fn::ImportValue: <exportName>` → resolved via `ListExports`
542
548
  * (paginated). Same-region only — CFn exports are region-scoped.
543
549
  * - `Fn::GetStackOutput` → rejected with a clear pointer that the
@@ -574,6 +580,7 @@ var CfnLocalStateProvider = class {
574
580
  cfnStackName;
575
581
  region;
576
582
  client;
583
+ lambdaClient;
577
584
  clientOptions;
578
585
  disposed = false;
579
586
  constructor(opts) {
@@ -590,6 +597,37 @@ var CfnLocalStateProvider = class {
590
597
  });
591
598
  return this.client;
592
599
  }
600
+ getLambdaClient() {
601
+ if (this.disposed) throw new Error("CfnLocalStateProvider used after dispose()");
602
+ if (!this.lambdaClient) this.lambdaClient = new LambdaClient({
603
+ region: this.region,
604
+ ...this.clientOptions.profile !== void 0 && { profile: this.clientOptions.profile }
605
+ });
606
+ return this.lambdaClient;
607
+ }
608
+ /**
609
+ * Read a deployed Lambda function's already-resolved
610
+ * `Environment.Variables` via `lambda:GetFunctionConfiguration`. See
611
+ * {@link LocalStateProvider.resolveDeployedFunctionEnv} for why this
612
+ * closes the `Fn::GetAtt`/`Fn::Sub`/cross-stack gap for a consumer
613
+ * function's own env vars.
614
+ *
615
+ * Best-effort: on any expected miss (function not found, access
616
+ * denied, throttling) logs a warn and returns `undefined` so the
617
+ * caller falls back to warn-and-drop on the affected keys. Never
618
+ * throws out of the substitution pass.
619
+ */
620
+ async resolveDeployedFunctionEnv(functionPhysicalId) {
621
+ if (this.disposed) throw new Error("CfnLocalStateProvider used after dispose()");
622
+ const logger = getLogger();
623
+ const client = this.getLambdaClient();
624
+ try {
625
+ return (await client.send(new GetFunctionConfigurationCommand({ FunctionName: functionPhysicalId }))).Environment?.Variables ?? {};
626
+ } catch (err) {
627
+ logger.warn(`${this.label}: GetFunctionConfiguration(${functionPhysicalId}) failed: ${formatAwsErrorForWarn(err)}. Intrinsic-valued env vars that need the deployed value will warn-and-drop (grant lambda:GetFunctionConfiguration or override via --env-vars).`);
628
+ return;
629
+ }
630
+ }
593
631
  /**
594
632
  * Load the deployed CFn stack's resources + outputs and return them
595
633
  * as a synthetic `LocalStateRecord` (matching the shape the existing
@@ -675,6 +713,10 @@ var CfnLocalStateProvider = class {
675
713
  this.client.destroy();
676
714
  this.client = void 0;
677
715
  }
716
+ if (this.lambdaClient) {
717
+ this.lambdaClient.destroy();
718
+ this.lambdaClient = void 0;
719
+ }
678
720
  }
679
721
  };
680
722
  /**
@@ -2597,6 +2639,52 @@ async function substituteEnvVarsFromStateAsync(templateEnv, contextOrResources)
2597
2639
  audit
2598
2640
  };
2599
2641
  }
2642
+ /**
2643
+ * Last-resort fill for env keys that static substitution could not
2644
+ * resolve, using the consumer resource's already-deployed environment.
2645
+ *
2646
+ * When a Lambda / container is bound to a deployed CloudFormation stack
2647
+ * (`--from-cfn-stack`), the consumer itself is deployed, so CloudFormation
2648
+ * already resolved every intrinsic (`Fn::GetAtt`, `Fn::Sub`,
2649
+ * `Fn::ImportValue`, cross-stack `Ref`) into its concrete deployed env
2650
+ * value. `ListStackResources` does not return per-attribute values, so
2651
+ * the static substituter drops those keys — but the deployed env carries
2652
+ * the answer. The provider fetches the deployed env (e.g.
2653
+ * `lambda:GetFunctionConfiguration`) and this helper splices it in for
2654
+ * the keys that remained unresolved.
2655
+ *
2656
+ * Precedence: only keys in `unresolved` are touched — statically resolved
2657
+ * keys and literal keys are left exactly as the substituter produced
2658
+ * them, and a `--env-vars` override still wins downstream. Keys absent
2659
+ * from `deployedEnv` stay unresolved so the caller's warn-and-drop fires.
2660
+ *
2661
+ * Pure: returns new arrays / a new env map, never mutates the inputs.
2662
+ */
2663
+ function applyDeployedEnvFallback(resolvedEnv, unresolved, deployedEnv) {
2664
+ if (!deployedEnv) return {
2665
+ env: resolvedEnv,
2666
+ filled: [],
2667
+ stillUnresolved: [...unresolved]
2668
+ };
2669
+ const env = { ...resolvedEnv };
2670
+ const filled = [];
2671
+ const stillUnresolved = [];
2672
+ for (const item of unresolved) {
2673
+ const deployedValue = deployedEnv[item.key];
2674
+ if (deployedValue !== void 0) {
2675
+ env[item.key] = deployedValue;
2676
+ filled.push(item.key);
2677
+ } else stillUnresolved.push({
2678
+ key: item.key,
2679
+ reason: item.reason
2680
+ });
2681
+ }
2682
+ return {
2683
+ env,
2684
+ filled,
2685
+ stillUnresolved
2686
+ };
2687
+ }
2600
2688
 
2601
2689
  //#endregion
2602
2690
  //#region src/local/ecs-task-resolver.ts
@@ -5152,10 +5240,28 @@ async function localInvokeCommand(target, options, extraStateProviders) {
5152
5240
  }
5153
5241
  const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);
5154
5242
  templateEnv = env;
5155
- stateAudit = audit;
5156
5243
  const label = stateProvider.label;
5157
5244
  for (const key of audit.resolvedKeys) logger.debug(`${label}: substituted env var ${key}`);
5158
- for (const { key, reason } of audit.unresolved) logger.warn(`${label}: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
5245
+ let unresolved = audit.unresolved;
5246
+ const resolvedKeys = [...audit.resolvedKeys];
5247
+ if (unresolved.length > 0 && stateProvider.resolveDeployedFunctionEnv) {
5248
+ const physicalId = loaded.resources[lambda.logicalId]?.physicalId;
5249
+ if (physicalId) {
5250
+ const deployedEnv = await stateProvider.resolveDeployedFunctionEnv(physicalId);
5251
+ const fb = applyDeployedEnvFallback(templateEnv, unresolved, deployedEnv);
5252
+ templateEnv = fb.env;
5253
+ unresolved = fb.stillUnresolved;
5254
+ for (const key of fb.filled) {
5255
+ resolvedKeys.push(key);
5256
+ logger.debug(`${label}: filled env var ${key} from deployed function config`);
5257
+ }
5258
+ }
5259
+ }
5260
+ stateAudit = {
5261
+ resolvedKeys,
5262
+ unresolved
5263
+ };
5264
+ for (const { key, reason } of unresolved) logger.warn(`${label}: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
5159
5265
  }
5160
5266
  } finally {
5161
5267
  stateProvider.dispose();
@@ -10736,9 +10842,8 @@ function buildCorsConfigFromCloudFrontChain(template) {
10736
10842
  * its own SigV4 credentials (service `lambda`) via the OAC, and the Function
10737
10843
  * URL's auto-generated resource policy trusts `cloudfront.amazonaws.com`.
10738
10844
  * Locally there is no CloudFront in the path, so no client signature can
10739
- * reproduce CloudFront's. Callers use this to relax SigV4 verification
10740
- * (warn-and-pass) for these Function URLs without forcing
10741
- * `--allow-unverified-sigv4`.
10845
+ * reproduce CloudFront's. Callers use this to keep these Function URLs in
10846
+ * warn-and-pass mode even when `--strict-sigv4` is set.
10742
10847
  *
10743
10848
  * Detection: a CloudFront origin whose `DomainName` matches the canonical
10744
10849
  * `Fn::GetAtt[<fnUrlLogicalId>, 'FunctionUrl']` chain (see
@@ -12148,17 +12253,29 @@ function base64UrlDecodeToBuffer(input) {
12148
12253
  * invalid.
12149
12254
  *
12150
12255
  * When the request's `Credential=AKID/...` scope names a different
12151
- * access-key-id than the one the dev has locally, we therefore **deny by
12152
- * default** (fail-closed). A fail-open default would let anyone forge an
12153
- * `Authorization: AWS4-HMAC-SHA256 Credential=AKID-X/...` header and be
12154
- * admitted as principal `AKID-X` against handler code that trusts
12155
- * `event.requestContext.identity.accessKey`. The `--allow-unverified-sigv4`
12156
- * flag (or an OAC-fronted Function URL, where CloudFront re-signs the
12157
- * origin request in production) is the explicit opt-in to **warn-and-pass**
12158
- * instead: allow + a one-line warn + an obviously-fake principalId.
12159
- *
12160
- * Genuinely missing / malformed signatures **are** rejected too those
12161
- * would never reach the deployed API either.
12256
+ * access-key-id than the one the dev has locally (or no local credentials
12257
+ * resolve at all), we therefore **warn-and-pass by default**: allow + a
12258
+ * one-line warn + an obviously-fake principalId
12259
+ * (`unverified-foreign-identity` / `unverified-no-creds`). Local execution
12260
+ * is overwhelmingly used to exercise app logic and ergonomics, not to
12261
+ * reproduce an authorization boundary cdk-local cannot fully emulate
12262
+ * anyway so blocking the most common legitimate case (federated /
12263
+ * Cognito Identity Pool / cross-account signers, which are foreign by
12264
+ * construction) is the wrong default. The fake principalId keeps
12265
+ * identity-based handler authz from silently trusting a forged caller, and
12266
+ * the deployed API Gateway still does the real verification + IAM
12267
+ * evaluation.
12268
+ *
12269
+ * The opt-in **`--strict-sigv4`** flag flips this to **fail-closed**: deny
12270
+ * unverifiable requests. Use it when you want local enforcement to mirror a
12271
+ * verified-identity assumption. OAC-fronted Function URLs always
12272
+ * warn-and-pass regardless of `--strict-sigv4` (CloudFront re-signs the
12273
+ * origin request in production, so no local client signature exists to
12274
+ * verify) and their warn lines reference CloudFront OAC.
12275
+ *
12276
+ * Genuinely missing / malformed signatures (no Authorization header,
12277
+ * unparseable header, wrong algorithm, stale date) **are** rejected in both
12278
+ * modes — those would never reach the deployed API either.
12162
12279
  *
12163
12280
  * # NOT IN SCOPE
12164
12281
  *
@@ -12206,17 +12323,17 @@ function defaultCredentialsLoader() {
12206
12323
  * http-server maps this to 401 (REST v1 `missing-identity`).
12207
12324
  * - **Signature mismatch** under the dev's own credentials → `{allow: false}`.
12208
12325
  * The http-server maps this to 403 (REST v1 `policy-deny`).
12209
- * - **Different `Credential` access-key-id than the dev has**
12210
- * `{allow: false}` by default (we can't reproduce a signing key we
12211
- * don't have). With `allowUnverified` (the `--allow-unverified-sigv4`
12212
- * flag, or `oacFronted` routes) → `{allow: true}` plus a one-line warn
12213
- * (warn-and-pass).
12326
+ * - **Different `Credential` access-key-id than the dev has** (or no
12327
+ * local creds resolve) → unverifiable. `{allow: true}` plus a one-line
12328
+ * warn + fake principalId by default (warn-and-pass). With `strict`
12329
+ * (the `--strict-sigv4` flag) → `{allow: false}` (fail-closed).
12214
12330
  * - **Valid signature with the dev's credentials** → `{allow: true}`.
12215
12331
  * The principal id surfaced to the handler is the parsed
12216
12332
  * `Credential` access-key-id.
12217
- * - **`oacFronted` route** → the caller forces `allowUnverified` on, so
12218
- * foreign / no-creds requests pass through (CloudFront re-signs origin
12219
- * requests in production) and the warn lines reference CloudFront OAC.
12333
+ * - **`oacFronted` route** → foreign / no-creds requests always pass
12334
+ * through regardless of `strict` (CloudFront re-signs origin requests
12335
+ * in production, so no local client signature can be verified) and the
12336
+ * warn lines reference CloudFront OAC.
12220
12337
  */
12221
12338
  async function verifySigV4(req, loadCredentials, opts = {}) {
12222
12339
  const logger = getLogger();
@@ -12276,14 +12393,14 @@ async function verifySigV4(req, loadCredentials, opts = {}) {
12276
12393
  local = await loadCredentials();
12277
12394
  } catch (err) {
12278
12395
  const reason = err instanceof Error ? err.message : String(err);
12279
- if (!opts.allowUnverified) {
12280
- logger.warn(`AWS_IAM authorizer: could not resolve local AWS credentials (${reason}), so the request's SigV4 signature cannot be verified SigV4 is an HMAC (shared-secret) signature, and reproducing it requires credentials cdk-local can read. This is a local-only limitation (the deployed API Gateway verifies the same request against AWS's copy of the secret), not a rejection of an invalid request. Denying by default; configure AWS credentials, or pass --allow-unverified-sigv4 to warn-and-pass in dev.`);
12396
+ if (opts.strict && !opts.oacFronted) {
12397
+ logger.warn(`AWS_IAM authorizer: could not resolve local AWS credentials (${reason}), so the request's SigV4 signature cannot be verified. --strict-sigv4 is set, so cdk-local denies unverifiable IAM requests; remove --strict-sigv4 to warn-and-pass (the default), or configure AWS credentials cdk-local can read.`);
12281
12398
  return {
12282
12399
  allow: false,
12283
12400
  identityHash: void 0
12284
12401
  };
12285
12402
  }
12286
- logger.warn(opts.oacFronted ? `AWS_IAM authorizer: Function URL is fronted by CloudFront OAC (CloudFront re-signs origin requests in production), and local AWS credentials could not be resolved (${reason}). Passing through with unverified principalId 'unverified-no-creds'. Do NOT trust event.requestContext.identity.accessKey in handler code.` : `AWS_IAM authorizer: failed to resolve local AWS credentials (${reason}). --allow-unverified-sigv4 is set; passing through with unverified principalId 'unverified-no-creds'. Do NOT trust event.requestContext.identity.accessKey in handler code.`);
12403
+ logger.warn(opts.oacFronted ? `AWS_IAM authorizer: Function URL is fronted by CloudFront OAC (CloudFront re-signs origin requests in production), and local AWS credentials could not be resolved (${reason}). Passing through with unverified principalId 'unverified-no-creds'. Do NOT trust event.requestContext.identity.accessKey in handler code.` : `AWS_IAM authorizer: could not resolve local AWS credentials (${reason}), so the request's SigV4 signature cannot be verified locally (SigV4 is an HMAC shared-secret signature; the deployed API Gateway verifies it against AWS's copy of the secret). Passing through with unverified principalId 'unverified-no-creds' — cdk-local's default for unverifiable IAM requests; pass --strict-sigv4 to deny instead. Do NOT trust event.requestContext.identity.accessKey in handler code.`);
12287
12404
  return {
12288
12405
  allow: true,
12289
12406
  principalId: "unverified-no-creds",
@@ -12293,9 +12410,9 @@ async function verifySigV4(req, loadCredentials, opts = {}) {
12293
12410
  if (local.accessKeyId.toLowerCase() !== parsed.credentialAccessKeyId.toLowerCase()) {
12294
12411
  const warned = opts.warnedForeignIds;
12295
12412
  const dedupKey = parsed.credentialAccessKeyId.toLowerCase();
12296
- if (!opts.allowUnverified) {
12413
+ if (opts.strict && !opts.oacFronted) {
12297
12414
  if (!warned || !warned.has(dedupKey)) {
12298
- logger.warn(`AWS_IAM authorizer: request signed with access-key-id '${parsed.credentialAccessKeyId}', which differs from the AWS credentials cdk-local resolved locally. SigV4 is an HMAC (shared-secret) signature, so cdk-local can only verify signatures made with its own credentials never a federated / Cognito Identity Pool / cross-account signer's (the deployed API Gateway verifies the same request because AWS holds that secret). This is a local-only limitation, not a rejection of an invalid request. Denying by default; pass --allow-unverified-sigv4 to warn-and-pass in dev, or sign the request with the same credentials cdk-local resolves locally.`);
12415
+ logger.warn(`AWS_IAM authorizer: request signed with access-key-id '${parsed.credentialAccessKeyId}', which differs from the AWS credentials cdk-local resolved locally SigV4 (HMAC / shared-secret) can only be verified with the signer's own credentials, never a federated / Cognito Identity Pool / cross-account signer's. --strict-sigv4 is set, so cdk-local denies it; remove --strict-sigv4 to warn-and-pass (the default), or sign the request with the same credentials cdk-local resolves locally.`);
12299
12416
  warned?.add(dedupKey);
12300
12417
  }
12301
12418
  return {
@@ -12304,7 +12421,7 @@ async function verifySigV4(req, loadCredentials, opts = {}) {
12304
12421
  };
12305
12422
  }
12306
12423
  if (!warned || !warned.has(dedupKey)) {
12307
- logger.warn(opts.oacFronted ? `AWS_IAM authorizer: Function URL is fronted by CloudFront OAC — in production CloudFront re-signs the origin request, so the local client's signature (access-key-id '${parsed.credentialAccessKeyId}') cannot be verified. Passing through with unverified principalId 'unverified-foreign-identity'. Do NOT trust event.requestContext.authorizer.principalId in handler code.` : `AWS_IAM authorizer: request signed with foreign access-key-id '${parsed.credentialAccessKeyId}'. --allow-unverified-sigv4 is set; passing through with unverified principalId 'unverified-foreign-identity'. Do NOT trust event.requestContext.authorizer.principalId in handler code.`);
12424
+ logger.warn(opts.oacFronted ? `AWS_IAM authorizer: Function URL is fronted by CloudFront OAC — in production CloudFront re-signs the origin request, so the local client's signature (access-key-id '${parsed.credentialAccessKeyId}') cannot be verified. Passing through with unverified principalId 'unverified-foreign-identity'. Do NOT trust event.requestContext.authorizer.principalId in handler code.` : `AWS_IAM authorizer: request signed with access-key-id '${parsed.credentialAccessKeyId}', a federated / Cognito Identity Pool / cross-account signer cdk-local cannot verify locally (SigV4 is an HMAC shared-secret signature; the deployed API Gateway verifies it because AWS holds the secret). Passing through with unverified principalId 'unverified-foreign-identity' — cdk-local's default for unverifiable IAM requests; pass --strict-sigv4 to deny instead. Do NOT trust event.requestContext.authorizer.principalId in handler code.`);
12308
12425
  warned?.add(dedupKey);
12309
12426
  }
12310
12427
  return {
@@ -13275,7 +13392,7 @@ async function runAuthorizerPass(authorizer, snapshot, matchCtx, state, opts, re
13275
13392
  body: snapshot.body
13276
13393
  }, opts.sigV4CredentialsLoader, {
13277
13394
  ...opts.sigV4WarnedForeignIds && { warnedForeignIds: opts.sigV4WarnedForeignIds },
13278
- allowUnverified: oacFronted || opts.sigV4AllowUnverified === true,
13395
+ strict: opts.sigV4Strict === true,
13279
13396
  ...oacFronted && { oacFronted: true }
13280
13397
  });
13281
13398
  if (!sigResult.allow) return {
@@ -14420,7 +14537,7 @@ async function localStartApiCommand(target, options, extraStateProviders) {
14420
14537
  jwksWarnedUrls,
14421
14538
  sigV4CredentialsLoader,
14422
14539
  sigV4WarnedForeignIds,
14423
- sigV4AllowUnverified: options.allowUnverifiedSigv4 === true,
14540
+ sigV4Strict: options.strictSigv4 === true,
14424
14541
  ...defaultRegion && { defaultRegion }
14425
14542
  });
14426
14543
  servers.push({
@@ -14466,7 +14583,7 @@ async function localStartApiCommand(target, options, extraStateProviders) {
14466
14583
  jwksCache,
14467
14584
  jwksWarnedUrls,
14468
14585
  sigV4WarnedForeignIds,
14469
- sigV4AllowUnverified: options.allowUnverifiedSigv4 === true,
14586
+ sigV4Strict: options.strictSigv4 === true,
14470
14587
  preDispatch: async (req, res) => {
14471
14588
  if (!registryRef) return false;
14472
14589
  return handleManagementRequest(req, res, registryRef.registry);
@@ -14829,11 +14946,11 @@ function warnIamRoutes(routesWithAuth) {
14829
14946
  }
14830
14947
  if (iamRoutes.length === 0 && oacRoutes.length === 0) return false;
14831
14948
  if (iamRoutes.length > 0) {
14832
- logger.warn(`${iamRoutes.length} route(s) declare AuthorizationType: AWS_IAM — ${getEmbedConfig().cliName} start-api verifies SigV4 signatures against your local AWS credentials, but does NOT emulate IAM policy evaluation (resource / action / condition rules). Signature-verified callers reach the handler under their own identity; downstream authorization is the dev's responsibility.`);
14949
+ logger.warn(`${iamRoutes.length} route(s) declare AuthorizationType: AWS_IAM — ${getEmbedConfig().cliName} start-api verifies the SigV4 signatures it CAN (requests signed with your local AWS credentials), but cannot verify a federated / Cognito Identity Pool / cross-account signer and does NOT emulate IAM policy evaluation (resource / action / condition rules). By default such unverifiable requests warn-and-pass with a placeholder principalId pass --strict-sigv4 to deny them instead. Downstream authorization is the dev's responsibility.`);
14833
14950
  for (const declaredAt of iamRoutes) logger.warn(` - ${declaredAt}`);
14834
14951
  }
14835
14952
  if (oacRoutes.length > 0) {
14836
- logger.warn(`${oacRoutes.length} Function URL route(s) with AuthType: AWS_IAM are fronted by a CloudFront Origin Access Control. In production CloudFront re-signs the origin request, so no local client signature can be verified — ${getEmbedConfig().cliName} start-api passes these through (warn-and-pass) WITHOUT requiring --allow-unverified-sigv4. Do NOT trust the request identity in handler code.`);
14953
+ logger.warn(`${oacRoutes.length} Function URL route(s) with AuthType: AWS_IAM are fronted by a CloudFront Origin Access Control. In production CloudFront re-signs the origin request, so no local client signature can be verified — ${getEmbedConfig().cliName} start-api passes these through (warn-and-pass) and they ignore --strict-sigv4. Do NOT trust the request identity in handler code.`);
14837
14954
  for (const declaredAt of oacRoutes) logger.warn(` - ${declaredAt}`);
14838
14955
  }
14839
14956
  return true;
@@ -14867,9 +14984,24 @@ async function buildContainerSpec(args) {
14867
14984
  if (stateBundle.pseudoParameters) context.pseudoParameters = stateBundle.pseudoParameters;
14868
14985
  const { env, audit } = substituteEnvVarsFromState(templateEnv, context);
14869
14986
  templateEnv = env;
14870
- stateAudit = audit;
14871
14987
  for (const key of audit.resolvedKeys) getLogger().debug(`Lambda ${logicalId}: state source substituted env var ${key}`);
14872
- for (const { key, reason } of audit.unresolved) getLogger().warn(`Lambda ${logicalId}: state source could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
14988
+ let unresolved = audit.unresolved;
14989
+ const resolvedKeys = [...audit.resolvedKeys];
14990
+ const deployedEnv = stateBundle.deployedEnvByLambda?.get(logicalId);
14991
+ if (unresolved.length > 0 && deployedEnv) {
14992
+ const fb = applyDeployedEnvFallback(templateEnv, unresolved, deployedEnv);
14993
+ templateEnv = fb.env;
14994
+ unresolved = fb.stillUnresolved;
14995
+ for (const key of fb.filled) {
14996
+ resolvedKeys.push(key);
14997
+ getLogger().debug(`Lambda ${logicalId}: filled env var ${key} from deployed function config`);
14998
+ }
14999
+ }
15000
+ stateAudit = {
15001
+ resolvedKeys,
15002
+ unresolved
15003
+ };
15004
+ for (const { key, reason } of unresolved) getLogger().warn(`Lambda ${logicalId}: state source could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
14873
15005
  }
14874
15006
  const lambdaCdkPath = readCdkPathOrUndefined(lambda.resource);
14875
15007
  const envResult = resolveEnvVars(logicalId, lambdaCdkPath, templateEnv, overrides);
@@ -15645,6 +15777,19 @@ async function loadStateForRoutedStacks(stacks, routes, routesWithAuth, options,
15645
15777
  const pseudo = await resolvePseudoParametersForStartApi(loaded.region, options);
15646
15778
  if (pseudo) bundle.pseudoParameters = pseudo;
15647
15779
  }
15780
+ if (provider.resolveDeployedFunctionEnv) {
15781
+ const deployedEnvByLambda = /* @__PURE__ */ new Map();
15782
+ for (const logicalId of lambdaIds) {
15783
+ const resource = stack.template.Resources?.[logicalId];
15784
+ if (!resource || resource.Type !== "AWS::Lambda::Function") continue;
15785
+ if (!envHasIntrinsicValue(getTemplateEnv(resource))) continue;
15786
+ const physicalId = loaded.resources[logicalId]?.physicalId;
15787
+ if (!physicalId) continue;
15788
+ const deployedEnv = await provider.resolveDeployedFunctionEnv(physicalId);
15789
+ if (deployedEnv) deployedEnvByLambda.set(logicalId, deployedEnv);
15790
+ }
15791
+ if (deployedEnvByLambda.size > 0) bundle.deployedEnvByLambda = deployedEnvByLambda;
15792
+ }
15648
15793
  out.set(stackName, bundle);
15649
15794
  logger.debug(`${provider.label}: loaded state for ${stackName} (${loaded.region})`);
15650
15795
  } finally {
@@ -15727,7 +15872,7 @@ function resolveMtlsConfig(options) {
15727
15872
  */
15728
15873
  function createLocalStartApiCommand(opts = {}) {
15729
15874
  setEmbedConfig(opts.embedConfig);
15730
- const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and AWS_IAM auth (REST v1 `AuthorizationType: AWS_IAM` and Function URL `AuthType: AWS_IAM` — SigV4 signature verification only; IAM policy evaluation is NOT emulated). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", `Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors \`${getEmbedConfig().cliName} invoke\` / \`${getEmbedConfig().cliName} run-task\` target syntax.`).addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--all-stacks", "Serve every stack's API in a multi-stack app (each API on its own port) instead of erroring out. Mutually exclusive with a positional target, --stack, and an explicit --from-cfn-stack <name>; the bare --from-cfn-stack flag stays compatible (binds each routed stack to its own CFn stack).").default(false)).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--mtls-truststore <path>", `PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj "/CN=${getEmbedConfig().resourceNamePrefix}-ca" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj "/CN=localhost"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj "/CN=client"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...`)).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--allow-unverified-sigv4", "Opt-in: allow AWS_IAM SigV4 requests that cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) to pass through with a placeholder principalId. DEFAULT off fail-closed so unauthenticated bypass is impossible against `event.requestContext.identity.accessKey`-trusting handler code. Use only in dev loops where you understand the risk.").default(false)).action(withErrorHandling(async (target, options) => {
15875
+ const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and AWS_IAM auth (REST v1 `AuthorizationType: AWS_IAM` and Function URL `AuthType: AWS_IAM` — SigV4 signature verification only; IAM policy evaluation is NOT emulated). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", `Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors \`${getEmbedConfig().cliName} invoke\` / \`${getEmbedConfig().cliName} run-task\` target syntax.`).addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--all-stacks", "Serve every stack's API in a multi-stack app (each API on its own port) instead of erroring out. Mutually exclusive with a positional target, --stack, and an explicit --from-cfn-stack <name>; the bare --from-cfn-stack flag stays compatible (binds each routed stack to its own CFn stack).").default(false)).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--mtls-truststore <path>", `PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj "/CN=${getEmbedConfig().resourceNamePrefix}-ca" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj "/CN=localhost"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj "/CN=client"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...`)).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--strict-sigv4", "Opt-in: DENY AWS_IAM SigV4 requests that cannot be cryptographically verified (foreign access-key-id — e.g. a federated / Cognito Identity Pool / cross-account signer — OR no local AWS credentials configured) instead of the default warn-and-pass. DEFAULT off: cdk-local warn-and-passes unverifiable IAM requests with a placeholder principalId so local dev exercises app logic without reproducing an auth boundary it cannot fully emulate. OAC-fronted Function URLs always warn-and-pass regardless.").default(false)).action(withErrorHandling(async (target, options) => {
15731
15876
  await localStartApiCommand(target, options, opts.extraStateProviders);
15732
15877
  }));
15733
15878
  [
@@ -18294,4 +18439,4 @@ function createLocalStartServiceCommand(opts = {}) {
18294
18439
 
18295
18440
  //#endregion
18296
18441
  export { CfnLocalStateProvider as _, resolveSelectionExpression as a, pickRefLogicalId as c, LocalStateSourceError as d, createLocalStateProvider as f, resolveCfnStackName as g, resolveCfnRegion as h, createLocalStartApiCommand as i, resolveLambdaArnIntrinsic as l, rejectExplicitCfnStackWithMultipleStacks as m, getContainerNetworkIp as n, resolveServiceIntegrationParameters as o, isCfnFlagPresent as p, createLocalRunTaskCommand as r, translateLambdaResponse as s, createLocalStartServiceCommand as t, createLocalInvokeCommand as u };
18297
- //# sourceMappingURL=local-start-service-C-nJKdAj.js.map
18442
+ //# sourceMappingURL=local-start-service-BbOWJ2dK.js.map