cdk-local 0.35.0 → 0.35.1

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.
@@ -1,4 +1,4 @@
1
- import { a as runDockerStreaming, c as getEmbedConfig, i as runDockerForeground, n as formatDockerLoginError, o as spawnStreaming, r as getDockerCmd, s as getLogger, u as setEmbedConfig } from "./docker-cmd--8TRSn9z.js";
1
+ import { a as runDockerStreaming, c as getEmbedConfig, i as runDockerForeground, n as formatDockerLoginError, o as spawnStreaming, r as getDockerCmd, s as getLogger, u as setEmbedConfig } from "./docker-cmd-voNPrcRh.js";
2
2
  import { cpSync, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import * as path from "node:path";
@@ -313,9 +313,6 @@ function withErrorHandling(fn) {
313
313
  * Read the `aws:cdk:path` value that CDK encodes into every resource's
314
314
  * `Metadata`. Returns the empty string when not present so callers don't
315
315
  * have to special-case `undefined`.
316
- *
317
- * Hoisted out of `src/cli/commands/import.ts` so `cdkd orphan` can reuse the
318
- * same lookup without duplicating the metadata-walking code.
319
316
  */
320
317
  function readCdkPath(resource) {
321
318
  const meta = resource.Metadata;
@@ -341,10 +338,9 @@ function readCdkPathOrUndefined(resource) {
341
338
  /**
342
339
  * Build a `Map<cdkPath, logicalId>` from a synthesized template.
343
340
  *
344
- * Used by `cdkd orphan <constructPath>` to translate user-supplied
345
- * construct paths (which mirror the upstream `cdk orphan` UX) back to the
346
- * logical IDs that the rest of the pipeline (state, dependency analysis,
347
- * provider lookup) is keyed on.
341
+ * Translates user-supplied construct paths (which mirror the upstream
342
+ * `cdk` construct-path UX) back to the logical IDs that the rest of the
343
+ * pipeline (state, dependency analysis, provider lookup) is keyed on.
348
344
  *
349
345
  * `AWS::CDK::Metadata` resources are excluded — the synthesized
350
346
  * `<Stack>/CDKMetadata/Default` sentinel exists in every stack but is
@@ -356,7 +352,7 @@ function readCdkPathOrUndefined(resource) {
356
352
  *
357
353
  * In practice each path maps to a single logical ID. If the same path
358
354
  * happens to appear twice (which would itself be a bug in the synthesized
359
- * template), the last entry wins — `cdkd orphan` will still surface a
355
+ * template), the last entry wins — callers still surface a
360
356
  * clean "path not found" diff against the indexed map rather than
361
357
  * silently grabbing both.
362
358
  */
@@ -2356,9 +2352,9 @@ function mapStackArtifact(stack) {
2356
2352
  //#endregion
2357
2353
  //#region src/synthesis/synthesizer.ts
2358
2354
  /**
2359
- * Thin wrapper around {@link AssemblyReader} that mimics cdkd's
2360
- * `Synthesizer` API so the ported `cdkl invoke` / `start-api` /
2361
- * `run-task` / `start-service` source compiles without rewrites.
2355
+ * Thin wrapper around {@link AssemblyReader} exposing a `Synthesizer`
2356
+ * API used by the `cdkl invoke` / `start-api` / `run-task` /
2357
+ * `start-service` commands.
2362
2358
  *
2363
2359
  * When `app` resolves to an existing directory it is read as a
2364
2360
  * pre-synthesized cloud assembly (no subprocess synth); otherwise it is
@@ -2527,15 +2523,16 @@ function collectSsmParameterRefs(template) {
2527
2523
  * String parameters resolve too (matching how CloudFormation resolves
2528
2524
  * the `AWS::SSM::Parameter::Value<String>` type at deploy time).
2529
2525
  *
2530
- * Security note: a decrypted SecureString value resolved here is then
2531
- * baked into the container's `Environment` like any other resolved env
2532
- * value, so it follows the standard plaintext-env exposure path it can
2533
- * appear on the `docker run -e KEY=VALUE` argv (visible in host `ps`) and
2534
- * is not redacted by `redactAwsCredentialsInArgs` (which only covers
2535
- * `SENSITIVE_ENV_KEYS`). This mirrors the deployed Lambda/ECS env and is
2536
- * the same inherent exposure documented in `docker-runner`; routing
2537
- * SecureString-resolved values through the value-from-process-env form is
2538
- * tracked as a follow-up.
2526
+ * Security note (issue #99): a decrypted SecureString value resolved here
2527
+ * would otherwise be baked into the container's `Environment` like any
2528
+ * other resolved value and could appear on the `docker run -e KEY=VALUE`
2529
+ * argv (visible in host `ps`). To prevent that, the returned
2530
+ * {@link ResolvedSsmParameters.secureStringLogicalIds} flags every
2531
+ * SecureString parameter; downstream the consuming env keys are routed
2532
+ * through docker's value-from-process-env form (`-e KEY`, value supplied
2533
+ * via the spawned docker process's env) so the secret never lands on the
2534
+ * argv. The value still appears in `docker inspect` Config.Env, which is
2535
+ * inherent to container env and matches deployed behavior.
2539
2536
  *
2540
2537
  * Best-effort: a failed `GetParameters` chunk logs a warn and is skipped
2541
2538
  * (the other chunks still contribute their values); the function never
@@ -2547,7 +2544,10 @@ function collectSsmParameterRefs(template) {
2547
2544
  * Exported for unit testing.
2548
2545
  */
2549
2546
  async function resolveSsmParameters(client, refs, label) {
2550
- const out = {};
2547
+ const out = {
2548
+ values: {},
2549
+ secureStringLogicalIds: []
2550
+ };
2551
2551
  if (refs.length === 0) return out;
2552
2552
  const logger = getLogger();
2553
2553
  const CHUNK = 10;
@@ -2577,7 +2577,11 @@ async function resolveSsmParameters(client, refs, label) {
2577
2577
  if (typeof p.Name !== "string" || typeof p.Value !== "string") continue;
2578
2578
  const requesters = byName.get(p.Name);
2579
2579
  if (!requesters) continue;
2580
- for (const ref of requesters) out[ref.logicalId] = p.Value;
2580
+ const isSecure = p.Type === "SecureString";
2581
+ for (const ref of requesters) {
2582
+ out.values[ref.logicalId] = p.Value;
2583
+ if (isSecure) out.secureStringLogicalIds.push(ref.logicalId);
2584
+ }
2581
2585
  }
2582
2586
  }
2583
2587
  return out;
@@ -2712,7 +2716,10 @@ var CfnLocalStateProvider = class {
2712
2716
  async resolveTemplateSsmParameters(template) {
2713
2717
  if (this.disposed) throw new Error("CfnLocalStateProvider used after dispose()");
2714
2718
  const refs = collectSsmParameterRefs(template);
2715
- if (refs.length === 0) return {};
2719
+ if (refs.length === 0) return {
2720
+ values: {},
2721
+ secureStringLogicalIds: []
2722
+ };
2716
2723
  return resolveSsmParameters(this.getSsmClient(), refs, this.label);
2717
2724
  }
2718
2725
  /**
@@ -3001,8 +3008,8 @@ async function resolveProfileRegion(profile) {
3001
3008
  * Host-extensible state sources (via the `extraStateProviders` option):
3002
3009
  *
3003
3010
  * - Hosts embedding cdk-local can register additional `LocalStateProvider`
3004
- * factories that respond to their own CLI flags (e.g. cdkd's
3005
- * `--from-state` for S3-backed cdkd state). Each entry is keyed by
3011
+ * factories that respond to their own CLI flags (e.g. a host-registered
3012
+ * `--from-state` backed by an S3 state store). Each entry is keyed by
3006
3013
  * the camel-case Commander option name (e.g. `'fromState'`) so the
3007
3014
  * dispatcher reads the corresponding boolean / string off the parsed
3008
3015
  * options bag.
@@ -3126,7 +3133,7 @@ function rejectExplicitCfnStackWithMultipleStacks(options, routedStackCount) {
3126
3133
  * fallback for the CFn client when no explicit region override is set.
3127
3134
  *
3128
3135
  * `extraStateProviders` is the host-supplied registry of additional
3129
- * state sources (e.g. cdkd's `--from-state` / `S3LocalStateProvider`).
3136
+ * state sources (e.g. a host's `--from-state` / S3-backed provider).
3130
3137
  * Each entry's key is the camel-case Commander option name; the
3131
3138
  * dispatcher activates the matching factory when the corresponding
3132
3139
  * options field is truthy.
@@ -3182,7 +3189,7 @@ function stackMatchesPattern(stack, pattern) {
3182
3189
  //#region src/local/intrinsic-image.ts
3183
3190
  /**
3184
3191
  * Derive the AWS pseudo parameters that are trivially knowable from the
3185
- * deploy region alone, without any STS call or cdkd state load.
3192
+ * deploy region alone, without any STS call or state load.
3186
3193
  * `urlSuffix` and `partition` follow the canonical AWS partition rules:
3187
3194
  *
3188
3195
  * - region prefix `cn-*` → partition `aws-cn`, urlSuffix `amazonaws.com.cn`
@@ -3239,7 +3246,7 @@ function derivePseudoParametersFromRegion(region, accountId) {
3239
3246
  * nested `Fn::Select` / `Fn::Split` over an `Fn::GetAtt: [<Repo>, 'Arn']`
3240
3247
  * plus a `Ref` to the same `AWS::ECR::Repository` and a
3241
3248
  * `Ref: AWS::URLSuffix`. For SAME-STACK references the account-id +
3242
- * region only exist in cdkd's S3 state (recorded at deploy time on the
3249
+ * region only exist in the host's S3 state (recorded at deploy time on the
3243
3250
  * Repository's `Arn` attribute), so the resolver inherently requires
3244
3251
  * `--from-state` (Tier 2) for that variant. For IMPORTED repositories
3245
3252
  * the URI components are flat strings + `Ref: AWS::URLSuffix` and
@@ -4258,10 +4265,13 @@ function resolveRef(arg, context) {
4258
4265
  value: resource.physicalId
4259
4266
  };
4260
4267
  const parameterValue = context.parameters?.[arg];
4261
- if (parameterValue !== void 0) return {
4262
- kind: "literal",
4263
- value: parameterValue
4264
- };
4268
+ if (parameterValue !== void 0) {
4269
+ if (context.sensitiveParameters?.has(arg)) context.onSensitiveParameterConsumed?.(arg);
4270
+ return {
4271
+ kind: "literal",
4272
+ value: parameterValue
4273
+ };
4274
+ }
4265
4275
  return {
4266
4276
  kind: "unresolved",
4267
4277
  reason: `Ref '${arg}': no record in the state source (was the resource deployed, or is it a CloudFormation parameter that could not be resolved — e.g. an SSM-backed AWS::SSM::Parameter::Value parameter whose SSM value was not readable?)`
@@ -4737,7 +4747,8 @@ function substituteEnvVarsFromState(templateEnv, contextOrResources) {
4737
4747
  const env = {};
4738
4748
  const audit = {
4739
4749
  resolvedKeys: [],
4740
- unresolved: []
4750
+ unresolved: [],
4751
+ sensitiveKeys: []
4741
4752
  };
4742
4753
  if (!templateEnv) return {
4743
4754
  env,
@@ -4749,10 +4760,11 @@ function substituteEnvVarsFromState(templateEnv, contextOrResources) {
4749
4760
  env[key] = value;
4750
4761
  continue;
4751
4762
  }
4752
- const result = substituteAgainstState(value, context);
4763
+ const { result, consumedSensitive } = resolveWithSensitivity(value, context);
4753
4764
  if (result.kind === "literal") {
4754
4765
  env[key] = result.value;
4755
4766
  audit.resolvedKeys.push(key);
4767
+ if (consumedSensitive) audit.sensitiveKeys.push(key);
4756
4768
  } else audit.unresolved.push({
4757
4769
  key,
4758
4770
  reason: result.reason
@@ -4764,6 +4776,53 @@ function substituteEnvVarsFromState(templateEnv, contextOrResources) {
4764
4776
  };
4765
4777
  }
4766
4778
  /**
4779
+ * Resolve a single intrinsic value while detecting whether its resolution
4780
+ * consumed a sensitive (SecureString) parameter — including refs nested in
4781
+ * `Fn::Join` / `Fn::Sub`, since the combinators recurse through the same
4782
+ * per-key context. Installs a per-call sink on a shallow context copy so
4783
+ * the shared input context is never mutated. Used by both the sync and
4784
+ * async env-var helpers (the async path delegates these intrinsics to the
4785
+ * sync resolver, so the sink fires identically).
4786
+ */
4787
+ function resolveWithSensitivity(value, context) {
4788
+ if (!context.sensitiveParameters || context.sensitiveParameters.size === 0) return {
4789
+ result: substituteAgainstState(value, context),
4790
+ consumedSensitive: false
4791
+ };
4792
+ let consumedSensitive = false;
4793
+ return {
4794
+ result: substituteAgainstState(value, {
4795
+ ...context,
4796
+ onSensitiveParameterConsumed: () => {
4797
+ consumedSensitive = true;
4798
+ }
4799
+ }),
4800
+ consumedSensitive
4801
+ };
4802
+ }
4803
+ /**
4804
+ * Async sibling of {@link resolveWithSensitivity}. Routes through
4805
+ * {@link substituteAgainstStateAsync} (for `Fn::ImportValue` /
4806
+ * `Fn::GetStackOutput`), which delegates SSM-parameter-bearing intrinsics
4807
+ * to the sync resolver — so the sensitivity sink fires the same way.
4808
+ */
4809
+ async function resolveWithSensitivityAsync(value, context) {
4810
+ if (!context.sensitiveParameters || context.sensitiveParameters.size === 0) return {
4811
+ result: await substituteAgainstStateAsync(value, context),
4812
+ consumedSensitive: false
4813
+ };
4814
+ let consumedSensitive = false;
4815
+ return {
4816
+ result: await substituteAgainstStateAsync(value, {
4817
+ ...context,
4818
+ onSensitiveParameterConsumed: () => {
4819
+ consumedSensitive = true;
4820
+ }
4821
+ }),
4822
+ consumedSensitive
4823
+ };
4824
+ }
4825
+ /**
4767
4826
  * Async sibling of {@link substituteEnvVarsFromState}. Routes every
4768
4827
  * intrinsic-valued entry through {@link substituteAgainstStateAsync} so
4769
4828
  * `Fn::ImportValue` / `Fn::GetStackOutput` resolve via the context's
@@ -4781,7 +4840,8 @@ async function substituteEnvVarsFromStateAsync(templateEnv, contextOrResources)
4781
4840
  const env = {};
4782
4841
  const audit = {
4783
4842
  resolvedKeys: [],
4784
- unresolved: []
4843
+ unresolved: [],
4844
+ sensitiveKeys: []
4785
4845
  };
4786
4846
  if (!templateEnv) return {
4787
4847
  env,
@@ -4793,10 +4853,11 @@ async function substituteEnvVarsFromStateAsync(templateEnv, contextOrResources)
4793
4853
  env[key] = value;
4794
4854
  continue;
4795
4855
  }
4796
- const result = await substituteAgainstStateAsync(value, context);
4856
+ const { result, consumedSensitive } = await resolveWithSensitivityAsync(value, context);
4797
4857
  if (result.kind === "literal") {
4798
4858
  env[key] = result.value;
4799
4859
  audit.resolvedKeys.push(key);
4860
+ if (consumedSensitive) audit.sensitiveKeys.push(key);
4800
4861
  } else audit.unresolved.push({
4801
4862
  key,
4802
4863
  reason: result.reason
@@ -5117,7 +5178,7 @@ function parseEcsTarget(target) {
5117
5178
  * available task definition in the matched stack) on any miss.
5118
5179
  *
5119
5180
  * Optional `context` (issue #264): when the caller can supply AWS
5120
- * pseudo-parameter values (Tier 1) and / or cdkd state-recorded resources
5181
+ * pseudo-parameter values (Tier 1) and / or host state-recorded resources
5121
5182
  * (Tier 2), `Fn::Sub`-shaped ECR image URIs that reference
5122
5183
  * `${AWS::AccountId}` / `${AWS::Region}` / a same-stack
5123
5184
  * `AWS::ECR::Repository` are substituted before classification.
@@ -5218,6 +5279,7 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
5218
5279
  const workingDirectory = pickString(c["WorkingDirectory"]);
5219
5280
  const subContext = buildSubstitutionContextFromImageContext(context);
5220
5281
  const environment = {};
5282
+ const sensitiveEnvKeys = [];
5221
5283
  const droppedEnvKeys = [];
5222
5284
  if (Array.isArray(c["Environment"])) for (const entry of c["Environment"]) {
5223
5285
  if (!entry || typeof entry !== "object") continue;
@@ -5230,9 +5292,10 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
5230
5292
  continue;
5231
5293
  }
5232
5294
  if (subContext) {
5233
- const sub = substituteAgainstState(value, subContext);
5295
+ const { result: sub, consumedSensitive } = resolveWithSensitivity(value, subContext);
5234
5296
  if (sub.kind === "literal") {
5235
5297
  environment[key] = String(sub.value);
5298
+ if (consumedSensitive) sensitiveEnvKeys.push(key);
5236
5299
  continue;
5237
5300
  }
5238
5301
  droppedEnvKeys.push({
@@ -5357,6 +5420,7 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, con
5357
5420
  name,
5358
5421
  image,
5359
5422
  environment,
5423
+ sensitiveEnvKeys,
5360
5424
  secrets,
5361
5425
  portMappings,
5362
5426
  mountPoints,
@@ -5386,6 +5450,7 @@ function buildSubstitutionContextFromImageContext(context) {
5386
5450
  const subContext = { resources: context.stateResources };
5387
5451
  if (context.pseudoParameters) subContext.pseudoParameters = { ...context.pseudoParameters };
5388
5452
  if (context.stateParameters) subContext.parameters = { ...context.stateParameters };
5453
+ if (context.stateSensitiveParameters?.length) subContext.sensitiveParameters = new Set(context.stateSensitiveParameters);
5389
5454
  return subContext;
5390
5455
  }
5391
5456
  /**
@@ -5748,10 +5813,11 @@ async function applyCrossStackResolverToTask(task, context) {
5748
5813
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") continue;
5749
5814
  if (key in container.environment) continue;
5750
5815
  if (!isCrossStackIntrinsic(value)) continue;
5751
- const sub = await substituteAgainstStateAsync(value, context);
5816
+ const { result: sub, consumedSensitive } = await resolveWithSensitivityAsync(value, context);
5752
5817
  if (sub.kind === "literal") {
5753
5818
  container.environment[key] = String(sub.value);
5754
5819
  resolvedEnvKeys.add(key);
5820
+ if (consumedSensitive && !container.sensitiveEnvKeys.includes(key)) container.sensitiveEnvKeys.push(key);
5755
5821
  }
5756
5822
  }
5757
5823
  if (Array.isArray(c["Secrets"])) for (const entry of c["Secrets"]) {
@@ -6059,7 +6125,8 @@ async function runDetached(opts) {
6059
6125
  const ro = mount.readOnly ? ":ro" : "";
6060
6126
  args.push("-v", `${mount.hostPath}:${mount.containerPath}${ro}`);
6061
6127
  }
6062
- const passthroughEnv = appendEnvFlags(args, opts.env, SENSITIVE_ENV_KEYS);
6128
+ const sensitiveKeys = opts.sensitiveEnvKeys && opts.sensitiveEnvKeys.size > 0 ? new Set([...SENSITIVE_ENV_KEYS, ...opts.sensitiveEnvKeys]) : SENSITIVE_ENV_KEYS;
6129
+ const passthroughEnv = appendEnvFlags(args, opts.env, sensitiveKeys);
6063
6130
  if (opts.tmpfs) args.push("--tmpfs", `${opts.tmpfs.target}:rw,size=${opts.tmpfs.sizeMb}m`);
6064
6131
  if (opts.workingDir) args.push("--workdir", opts.workingDir);
6065
6132
  let entryPointTail = [];
@@ -6517,8 +6584,8 @@ async function assumeRoleForEcr(roleArn, callerRegion, profile, logger) {
6517
6584
  }
6518
6585
  /**
6519
6586
  * Authenticate the local docker daemon against the target ECR registry.
6520
- * Mirrors `DockerAssetPublisher.ecrLogin` but stays in this module so the
6521
- * local-invoke path doesn't depend on the publisher's larger surface area.
6587
+ * Self-contained in this module so the local-invoke path stays
6588
+ * independent of any full asset-publish path's larger surface area.
6522
6589
  */
6523
6590
  async function ecrLogin(client, accountId, region) {
6524
6591
  getLogger().child("ecr-puller").debug(`ECR login (account=${accountId}, region=${region})`);
@@ -7416,7 +7483,8 @@ async function localInvokeCommand(target, options, extraStateProviders) {
7416
7483
  }
7417
7484
  if (envHasIntrinsicValue$1(templateEnv) && stateProvider.resolveTemplateSsmParameters) {
7418
7485
  const ssmParams = await stateProvider.resolveTemplateSsmParameters(lambda.stack.template);
7419
- if (Object.keys(ssmParams).length > 0) subContext.parameters = ssmParams;
7486
+ if (Object.keys(ssmParams.values).length > 0) subContext.parameters = ssmParams.values;
7487
+ if (ssmParams.secureStringLogicalIds.length > 0) subContext.sensitiveParameters = new Set(ssmParams.secureStringLogicalIds);
7420
7488
  }
7421
7489
  if (envHasCrossStackIntrinsic(templateEnv)) {
7422
7490
  const resolver = await stateProvider.buildCrossStackResolver(loaded.region);
@@ -7443,7 +7511,8 @@ async function localInvokeCommand(target, options, extraStateProviders) {
7443
7511
  }
7444
7512
  stateAudit = {
7445
7513
  resolvedKeys,
7446
- unresolved
7514
+ unresolved,
7515
+ sensitiveKeys: audit.sensitiveKeys
7447
7516
  };
7448
7517
  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.`);
7449
7518
  }
@@ -7523,6 +7592,7 @@ async function localInvokeCommand(target, options, extraStateProviders) {
7523
7592
  mounts: imagePlan.mounts,
7524
7593
  extraMounts: extraMountsWithProfile,
7525
7594
  env: dockerEnv,
7595
+ ...stateAudit && stateAudit.sensitiveKeys.length > 0 && { sensitiveEnvKeys: new Set(stateAudit.sensitiveKeys) },
7526
7596
  cmd: imagePlan.cmd,
7527
7597
  hostPort,
7528
7598
  host: containerHost,
@@ -10489,6 +10559,7 @@ function createContainerPool(specs, options) {
10489
10559
  }],
10490
10560
  extraMounts,
10491
10561
  env: spec.env,
10562
+ ...spec.sensitiveEnvKeys !== void 0 && { sensitiveEnvKeys: spec.sensitiveEnvKeys },
10492
10563
  cmd: [spec.lambda.handler],
10493
10564
  hostPort,
10494
10565
  host: spec.containerHost,
@@ -10506,6 +10577,7 @@ function createContainerPool(specs, options) {
10506
10577
  readOnly: true
10507
10578
  }] },
10508
10579
  env: spec.env,
10580
+ ...spec.sensitiveEnvKeys !== void 0 && { sensitiveEnvKeys: spec.sensitiveEnvKeys },
10509
10581
  cmd: spec.command,
10510
10582
  hostPort,
10511
10583
  host: spec.containerHost,
@@ -14723,9 +14795,9 @@ function groupRoutesByServer(routes) {
14723
14795
  * exact match on the resource's `aws:cdk:path` metadata.
14724
14796
  * 4. **CDK Construct path prefix** — when the input is a strict
14725
14797
  * ancestor of the resource's `aws:cdk:path` (i.e.
14726
- * `cdkPath.startsWith(input + '/')`). Mirrors the prefix rule
14727
- * `cdkd orphan` uses so an L2 wrapper path resolves to its L1
14728
- * child (`MyStack/MyHttpApi` matches `MyStack/MyHttpApi/Resource`).
14798
+ * `cdkPath.startsWith(input + '/')`). Mirrors the upstream CDK
14799
+ * construct-path prefix rule so an L2 wrapper path resolves to its
14800
+ * L1 child (`MyStack/MyHttpApi` matches `MyStack/MyHttpApi/Resource`).
14729
14801
  *
14730
14802
  * Routes discovered before this field set was added (or routes where
14731
14803
  * the synthesized template doesn't carry `aws:cdk:path` metadata —
@@ -15864,6 +15936,7 @@ async function buildContainerSpec(args) {
15864
15936
  const context = { resources: stateBundle.state.resources };
15865
15937
  if (stateBundle.pseudoParameters) context.pseudoParameters = stateBundle.pseudoParameters;
15866
15938
  if (stateBundle.ssmParameters) context.parameters = stateBundle.ssmParameters;
15939
+ if (stateBundle.ssmSecureStringLogicalIds?.length) context.sensitiveParameters = new Set(stateBundle.ssmSecureStringLogicalIds);
15867
15940
  const { env, audit } = substituteEnvVarsFromState(templateEnv, context);
15868
15941
  templateEnv = env;
15869
15942
  for (const key of audit.resolvedKeys) getLogger().debug(`Lambda ${logicalId}: state source substituted env var ${key}`);
@@ -15881,7 +15954,8 @@ async function buildContainerSpec(args) {
15881
15954
  }
15882
15955
  stateAudit = {
15883
15956
  resolvedKeys,
15884
- unresolved
15957
+ unresolved,
15958
+ sensitiveKeys: audit.sensitiveKeys
15885
15959
  };
15886
15960
  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.`);
15887
15961
  }
@@ -15934,11 +16008,13 @@ async function buildContainerSpec(args) {
15934
16008
  target: "/tmp",
15935
16009
  sizeMb: lambda.ephemeralStorageMb
15936
16010
  } : void 0;
16011
+ const sensitiveEnvKeys = stateAudit && stateAudit.sensitiveKeys.length > 0 ? new Set(stateAudit.sensitiveKeys) : void 0;
15937
16012
  if (lambda.kind === "zip") return {
15938
16013
  kind: "zip",
15939
16014
  lambda,
15940
16015
  codeDir,
15941
16016
  env: dockerEnv,
16017
+ ...sensitiveEnvKeys && { sensitiveEnvKeys },
15942
16018
  containerHost,
15943
16019
  ...optDir !== void 0 && { optDir },
15944
16020
  ...debugPort !== void 0 && { debugPort },
@@ -15957,6 +16033,7 @@ async function buildContainerSpec(args) {
15957
16033
  ...lambda.imageConfig.entryPoint !== void 0 && lambda.imageConfig.entryPoint.length > 0 && { entryPoint: lambda.imageConfig.entryPoint },
15958
16034
  ...lambda.imageConfig.workingDirectory !== void 0 && { workingDir: lambda.imageConfig.workingDirectory },
15959
16035
  env: dockerEnv,
16036
+ ...sensitiveEnvKeys && { sensitiveEnvKeys },
15960
16037
  containerHost,
15961
16038
  ...debugPort !== void 0 && { debugPort },
15962
16039
  ...tmpfs !== void 0 && { tmpfs },
@@ -16318,10 +16395,10 @@ function forwardAwsEnv(env) {
16318
16395
  * AWS SDK call fails with `Could not load credentials from any providers`.
16319
16396
  *
16320
16397
  * This helper constructs a transient `STSClient({ profile })` to drive
16321
- * the SDK's default credential provider chain — same code path cdkd's
16322
- * own CFn / CC API clients use when `--profile` is set, so SSO / IAM
16398
+ * the SDK's default credential provider chain — same code path the
16399
+ * host's own AWS SDK clients use when `--profile` is set, so SSO / IAM
16323
16400
  * Identity Center / role-assumption profiles all resolve the same way
16324
- * they already do for cdkd's outbound calls. We then extract the
16401
+ * they already do for the host's outbound calls. We then extract the
16325
16402
  * resolved `AwsCredentialIdentity` via `sts.config.credentials()` and
16326
16403
  * return the underlying `{ accessKeyId, secretAccessKey, sessionToken? }`
16327
16404
  * for env-var injection.
@@ -16675,7 +16752,8 @@ async function loadStateForRoutedStacks(stacks, routes, routesWithAuth, options,
16675
16752
  }
16676
16753
  if (stackHasIntrinsicEnv(stackName) && provider.resolveTemplateSsmParameters) {
16677
16754
  const ssmParameters = await provider.resolveTemplateSsmParameters(stack.template);
16678
- if (Object.keys(ssmParameters).length > 0) bundle.ssmParameters = ssmParameters;
16755
+ if (Object.keys(ssmParameters.values).length > 0) bundle.ssmParameters = ssmParameters.values;
16756
+ if (ssmParameters.secureStringLogicalIds.length > 0) bundle.ssmSecureStringLogicalIds = ssmParameters.secureStringLogicalIds;
16679
16757
  }
16680
16758
  out.set(stackName, bundle);
16681
16759
  logger.debug(`${provider.label}: loaded state for ${stackName} (${loaded.region})`);
@@ -17724,6 +17802,7 @@ function buildDockerRunArgs(opts) {
17724
17802
  }
17725
17803
  const sensitiveEnvKeys = new Set(SENSITIVE_ENV_KEYS);
17726
17804
  for (const s of secrets) sensitiveEnvKeys.add(s.name);
17805
+ for (const k of container.sensitiveEnvKeys) sensitiveEnvKeys.add(k);
17727
17806
  const sensitiveEnv = appendEnvFlags(args, finalEnv, sensitiveEnvKeys);
17728
17807
  if (container.user) args.push("--user", container.user);
17729
17808
  if (container.privileged) args.push("--privileged");
@@ -17840,6 +17919,7 @@ async function localRunTaskCommand(target, options, extraStateProviders) {
17840
17919
  resources: imageContext?.stateResources ?? {},
17841
17920
  ...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
17842
17921
  ...imageContext?.stateParameters && { parameters: imageContext.stateParameters },
17922
+ ...imageContext?.stateSensitiveParameters?.length && { sensitiveParameters: new Set(imageContext.stateSensitiveParameters) },
17843
17923
  consumerRegion,
17844
17924
  crossStackResolver: resolver
17845
17925
  });
@@ -17979,7 +18059,8 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
17979
18059
  if (loaded) ctx.stateResources = loaded.resources;
17980
18060
  if (needs.needsEnvOrSecretSubstitution && stateProvider.resolveTemplateSsmParameters) {
17981
18061
  const ssmParameters = await stateProvider.resolveTemplateSsmParameters(candidate.template);
17982
- if (Object.keys(ssmParameters).length > 0) ctx.stateParameters = ssmParameters;
18062
+ if (Object.keys(ssmParameters.values).length > 0) ctx.stateParameters = ssmParameters.values;
18063
+ if (ssmParameters.secureStringLogicalIds.length > 0) ctx.stateSensitiveParameters = ssmParameters.secureStringLogicalIds;
17983
18064
  }
17984
18065
  } else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute the deployed repository URI. Otherwise the resolver will surface its existing error.");
17985
18066
  else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute them against deployed state. Without a state source these entries are dropped (per-key warnings will follow).");
@@ -18028,7 +18109,7 @@ function readEnvOverridesFile$1(filePath) {
18028
18109
  return parsed;
18029
18110
  }
18030
18111
  /**
18031
- * cdkd#658: pick the credentials forwarded to the AWS-published
18112
+ * Pick the credentials forwarded to the AWS-published
18032
18113
  * `amazon-ecs-local-container-endpoints` sidecar. Precedence:
18033
18114
  * 1. `--assume-task-role <arn>` (or bare `--assume-task-role` against
18034
18115
  * a resolvable `TaskRoleArn`) → STS-assumed temp creds. Highest
@@ -18044,7 +18125,7 @@ function readEnvOverridesFile$1(filePath) {
18044
18125
  *
18045
18126
  * Extracted as an exported helper so a unit test can exercise every
18046
18127
  * branch without having to mock the full Synth + Docker + AWS pipeline
18047
- * (the strategy cdkd#655 used for the Lambda container path).
18128
+ * (the strategy used for the Lambda container path).
18048
18129
  */
18049
18130
  async function resolveSidecarCredentials(options, assumedCredentials) {
18050
18131
  if (assumedCredentials) return assumedCredentials;
@@ -18797,7 +18878,7 @@ function pickEssentialContainerId(instance, service) {
18797
18878
  const defaultWaitForExitImpl = async (containerId) => {
18798
18879
  const { execFile } = await import("node:child_process");
18799
18880
  const { promisify } = await import("node:util");
18800
- const { getDockerCmd } = await import("./docker-cmd--8TRSn9z.js").then((n) => n.t);
18881
+ const { getDockerCmd } = await import("./docker-cmd-voNPrcRh.js").then((n) => n.t);
18801
18882
  const { stdout } = await promisify(execFile)(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
18802
18883
  const code = parseInt(stdout.trim(), 10);
18803
18884
  return Number.isFinite(code) ? code : -1;
@@ -18817,7 +18898,7 @@ const EXIT_LOG_TAIL_LINES = 50;
18817
18898
  const defaultReadContainerLogsImpl = async (containerId) => {
18818
18899
  const { execFile } = await import("node:child_process");
18819
18900
  const { promisify } = await import("node:util");
18820
- const { getDockerCmd } = await import("./docker-cmd--8TRSn9z.js").then((n) => n.t);
18901
+ const { getDockerCmd } = await import("./docker-cmd-voNPrcRh.js").then((n) => n.t);
18821
18902
  const { stdout, stderr } = await promisify(execFile)(getDockerCmd(), [
18822
18903
  "logs",
18823
18904
  "--tail",
@@ -19320,6 +19401,7 @@ async function runOneTarget(target, runState, stacks, options, discovery, skipPu
19320
19401
  resources: imageContext?.stateResources ?? {},
19321
19402
  ...imageContext?.pseudoParameters && { pseudoParameters: imageContext.pseudoParameters },
19322
19403
  ...imageContext?.stateParameters && { parameters: imageContext.stateParameters },
19404
+ ...imageContext?.stateSensitiveParameters?.length && { sensitiveParameters: new Set(imageContext.stateSensitiveParameters) },
19323
19405
  consumerRegion,
19324
19406
  crossStackResolver: resolver
19325
19407
  };
@@ -19436,7 +19518,8 @@ async function buildEcsImageResolutionContext(target, stacks, options, stateProv
19436
19518
  if (loaded) ctx.stateResources = loaded.resources;
19437
19519
  if (needs.needsEnvOrSecretSubstitution && stateProvider.resolveTemplateSsmParameters) {
19438
19520
  const ssmParameters = await stateProvider.resolveTemplateSsmParameters(candidate.template);
19439
- if (Object.keys(ssmParameters).length > 0) ctx.stateParameters = ssmParameters;
19521
+ if (Object.keys(ssmParameters.values).length > 0) ctx.stateParameters = ssmParameters.values;
19522
+ if (ssmParameters.secureStringLogicalIds.length > 0) ctx.stateSensitiveParameters = ssmParameters.secureStringLogicalIds;
19440
19523
  }
19441
19524
  } else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute the deployed repository URI.");
19442
19525
  else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics. Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute them against the deployed state.");
@@ -19500,7 +19583,7 @@ function parseRestartPolicy(raw) {
19500
19583
  throw new LocalStartServiceError(`--restart-policy must be one of 'on-failure', 'always', or 'none' (got '${raw}').`);
19501
19584
  }
19502
19585
  /**
19503
- * cdkd#658: pick the credentials forwarded to the AWS-published
19586
+ * Pick the credentials forwarded to the AWS-published
19504
19587
  * `amazon-ecs-local-container-endpoints` sidecar. `cdkl start-service`'s
19505
19588
  * sidecar is SHARED across every replica boot in one CLI invocation, so
19506
19589
  * this resolves ONCE at startup. Precedence:
@@ -19521,7 +19604,7 @@ function parseRestartPolicy(raw) {
19521
19604
  *
19522
19605
  * Extracted as an exported helper so a unit test can exercise both
19523
19606
  * branches without having to mock the full Synth + Docker + AWS
19524
- * pipeline (the strategy cdkd#655 used for the Lambda container path).
19607
+ * pipeline (the strategy used for the Lambda container path).
19525
19608
  */
19526
19609
  async function resolveSharedSidecarCredentials(options) {
19527
19610
  if (options.profile) return resolveProfileCredentials(options.profile);
@@ -19610,4 +19693,4 @@ function createLocalListCommand(opts = {}) {
19610
19693
 
19611
19694
  //#endregion
19612
19695
  export { ConnectionRegistry as $, computeRequestIdentityHash as A, collectSsmParameterRefs as At, matchRoute as B, resolveLambdaArnIntrinsic as Bt, defaultCredentialsLoader as C, createLocalStateProvider as Ct, verifyCognitoJwt as D, resolveCfnRegion as Dt, createJwksCache as E, resolveCfnFallbackRegion as Et, applyCorsResponseHeaders as F, discoverWebSocketApis as Ft, evaluateResponseParameters as G, applyAuthorizerOverlay as H, buildCorsConfigByApiId as I, discoverWebSocketApisOrThrow as It, tryParseStatus as J, pickResponseTemplate as K, buildCorsConfigFromCloudFrontChain as L, parseSelectionExpressionPath as Lt, invokeRequestAuthorizer as M, resolveWatchConfig as Mt, invokeTokenAuthorizer as N, countTargets as Nt, verifyJwtAuthorizer as O, resolveCfnStackName as Ot, attachAuthorizers as P, listTargets as Pt, bufferToBody as Q, isFunctionUrlOacFronted as R, discoverRoutes as Rt, resolveServiceIntegrationParameters as S, LocalStateSourceError as St, buildJwksUrlFromIssuer as T, rejectExplicitCfnStackWithMultipleStacks as Tt, buildHttpApiV2Event as U, translateLambdaResponse as V, LocalInvokeBuildError as Vt, buildRestV1Event as W, HOST_GATEWAY_MIN_VERSION as X, VtlEvaluationError as Y, probeHostGatewaySupport as Z, filterRoutesByApiIdentifiers as _, resolveEnvVars as _t, CloudMapRegistry as a, buildMessageEvent as at, startApiServer as b, substituteImagePlaceholders as bt, createLocalStartApiCommand as c, buildContainerImage as ct, createAuthorizerCache as d, resolveRuntimeImage as dt, buildMgmtEndpointEnvUrl as et, createFileWatcher as f, EcsTaskResolutionError as ft, filterRoutesByApiIdentifier as g, substituteEnvVarsFromStateAsync as gt, availableApiIdentifiers as h, substituteEnvVarsFromState as ht, buildCloudMapIndex as i, buildDisconnectEvent as it, evaluateCachedLambdaPolicy as j, resolveSsmParameters as jt, buildMethodArn as k, CfnLocalStateProvider as kt, createWatchPredicates as l, resolveRuntimeCodeMountPath as lt, buildStageMap as m, substituteAgainstStateAsync as mt, formatTargetListing as n, parseConnectionsPath as nt, getContainerNetworkIp as o, createLocalInvokeCommand as ot, attachStageContext as p, substituteAgainstState as pt, selectIntegrationResponse as q, createLocalStartServiceCommand as r, buildConnectEvent as rt, createLocalRunTaskCommand as s, architectureToPlatform as st, createLocalListCommand as t, handleConnectionsRequest as tt, resolveApiTargetSubset as u, resolveRuntimeFileExtension as ut, groupRoutesByServer as v, materializeLayerFromArn as vt, buildCognitoJwksUrl as w, isCfnFlagPresent as wt, resolveSelectionExpression as x, tryResolveImageFnJoin as xt, readMtlsMaterialsFromDisk as y, derivePseudoParametersFromRegion as yt, matchPreflight as z, pickRefLogicalId as zt };
19613
- //# sourceMappingURL=local-list-BvDCmBir.js.map
19696
+ //# sourceMappingURL=local-list-8fAEdzBb.js.map