@go-to-k/cdkd 0.284.31 → 0.284.32

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.
@@ -771,7 +771,7 @@ function formatDuration(ms) {
771
771
  * "the referenced thing does not exist" miss (issue
772
772
  * [#1740](https://github.com/go-to-k/cdkd/issues/1740)).
773
773
  *
774
- * The distinction exists for exactly one consumer: `Fn::Sub`'s variable
774
+ * The distinction exists for one consumer of THIS class: `Fn::Sub`'s variable
775
775
  * resolution, which speculatively tries `Ref` and then `Fn::GetAtt` and keeps
776
776
  * the raw `${...}` placeholder when neither resolves. That warn-and-keep is the
777
777
  * long-standing, deliberate behavior for a genuinely unknown variable — but a
@@ -782,11 +782,15 @@ function formatDuration(ms) {
782
782
  *
783
783
  * Throwing this class rather than a bare `Error` is what lets that catch
784
784
  * re-raise a refusal (carrying its own message and remedy) while leaving the
785
- * not-found path on warn-and-keep. Nothing else branches on it.
785
+ * not-found path on warn-and-keep. Nothing else branches on THIS class;
786
+ * `cdkd scrub` branches on the {@link CrossAccountSecretRefusalError} SUBCLASS,
787
+ * for the reason group 3 below gives.
786
788
  *
787
- * **Throw sites split into two groups, and the enumeration is worth keeping
789
+ * **Throw sites split into three groups, and the enumeration is worth keeping
788
790
  * accurate** — an out-of-date one reads as "these are all of them", which is
789
- * how the #1730 site below went unlisted for three releases. All live in
791
+ * how the #1730 site below went unlisted for three releases, and how the
792
+ * cross-account `Fn::GetStackOutput` site in group 3 went unlisted from the
793
+ * day it shipped. All live in
790
794
  * `src/deployment/intrinsic-function-resolver.ts`:
791
795
  *
792
796
  * 1. Reachable from `Fn::Sub`'s `${LogicalId.Attribute}` form, i.e. the ones
@@ -800,29 +804,77 @@ function formatDuration(ms) {
800
804
  * inspects it: `resolveSplit`'s two refusals of a non-string value (#1874).
801
805
  * `Fn::Sub` cannot syntactically contain an `Fn::Split`, so those change no
802
806
  * behavior by being this class.
807
+ * 3. PERMANENT — the one site where no user action and no re-run can make the
808
+ * read succeed: `resolveGetStackOutput`'s cross-account refusal to resolve a
809
+ * producer account's redacted dynamic reference with the consumer's
810
+ * credentials. It throws the SUBCLASS
811
+ * {@link CrossAccountSecretRefusalError} rather than this class, because
812
+ * every site in groups 1 and 2 is USER-FIXABLE (correct the stale
813
+ * placeholder ARN, deploy the producer so STS resolves, enrich the
814
+ * `Fn::GetAtt`, drop `--strict-getatt`, fix the malformed `Fn::Split`) and a
815
+ * consumer that treats "permanent" as a property of the CLASS silently
816
+ * downgrades all five. `cdkd scrub`'s cross-stack pre-pass is that consumer:
817
+ * it records a permanent refusal as a FINDING and scrubs the rest of the
818
+ * stack, but must REFUSE the stack for a fixable one, since a re-run after
819
+ * the fix would scrub it (issue
820
+ * [#2133](https://github.com/go-to-k/cdkd/issues/2133) review). Match on the
821
+ * subclass, never on this class.
803
822
  *
804
823
  * The class is deliberately NOT `markNonRetryable` at construction, unlike
805
824
  * {@link ResourceUpdateNotSupportedError}: EXACTLY ONE of its throw sites is
806
825
  * genuinely time-dependent — the fabricated-account guard, where
807
826
  * `getAccountInfo` caches a fabricated answer for only 10s precisely so a
808
827
  * later attempt can heal — so a constructor-level marker would wrongly make
809
- * that one terminal. Every OTHER site marks at its own `throw`: all five
828
+ * that one terminal. Every OTHER site marks at its own `throw`: all six
810
829
  * decide from inputs a retry cannot change (a persisted state record, an
811
- * attribute-name suffix, a CLI flag, an already-resolved value's type), and
812
- * all five interpolate template-controlled text into their message, which the
830
+ * attribute-name suffix, a CLI flag, an already-resolved value's type, a
831
+ * template's literal `RoleArn`), and all six interpolate
832
+ * template-controlled text into their message, which the
813
833
  * SUBSTRING-matching retry classifiers can read as transient (issue #1838 —
814
834
  * a logical id like `MyDependencyViolationHandler` is enough). So the split is
815
835
  * "which SITE can heal", not "which class"; do not read the unmarked class as
816
836
  * a statement that these refusals are retryable.
817
837
  */
818
838
  var IntrinsicResolutionRefusalError = class IntrinsicResolutionRefusalError extends CdkdError {
819
- constructor(message, cause) {
820
- super(message, "INTRINSIC_RESOLUTION_REFUSAL", cause);
839
+ constructor(message, cause, code = "INTRINSIC_RESOLUTION_REFUSAL") {
840
+ super(message, code, cause);
821
841
  this.name = "IntrinsicResolutionRefusalError";
822
842
  Object.setPrototypeOf(this, IntrinsicResolutionRefusalError.prototype);
823
843
  }
824
844
  };
825
845
  /**
846
+ * The ONE refusal in group 3 of {@link IntrinsicResolutionRefusalError}: a
847
+ * cross-account `Fn::GetStackOutput` whose stored value is a redacted dynamic
848
+ * reference (issue [#2133](https://github.com/go-to-k/cdkd/issues/2133)
849
+ * review).
850
+ *
851
+ * A SUBCLASS rather than a flag, so the two properties stay independent:
852
+ * `instanceof IntrinsicResolutionRefusalError` is still true, which is what
853
+ * `resolveSub`'s catch re-raises on (making this refusal propagate out of an
854
+ * `Fn::Sub` instead of being laundered into a literal `${...}`), while
855
+ * consumers that need "no re-run can change this" match on THIS class and
856
+ * therefore cannot capture the five user-fixable siblings.
857
+ *
858
+ * `code` is distinct for the same reason a message is not: a consumer keying
859
+ * on `INTRINSIC_RESOLUTION_REFUSAL` would capture every sibling, and one
860
+ * keying on message text breaks the moment the wording improves.
861
+ *
862
+ * `cdkd scrub` is the consumer today. Its cross-stack pre-pass records a
863
+ * refusal of THIS class as an unverifiable FINDING — the rest of the stack is
864
+ * still scrubbed, and the run exits non-zero — because refusing the whole
865
+ * stack would strand every other secret in it forever. A sibling refusal
866
+ * (a stale placeholder ARN, an unenriched `Fn::GetAtt`, a malformed
867
+ * `Fn::Split`, all reachable through an `Fn::Sub`-built export name) must
868
+ * REFUSE instead, since the user can fix the cause and re-run.
869
+ */
870
+ var CrossAccountSecretRefusalError = class CrossAccountSecretRefusalError extends IntrinsicResolutionRefusalError {
871
+ constructor(message, cause) {
872
+ super(message, cause, "INTRINSIC_RESOLUTION_REFUSAL_CROSS_ACCOUNT_SECRET");
873
+ this.name = "CrossAccountSecretRefusalError";
874
+ Object.setPrototypeOf(this, CrossAccountSecretRefusalError.prototype);
875
+ }
876
+ };
877
+ /**
826
878
  * Dependency resolution errors
827
879
  */
828
880
  var DependencyError = class DependencyError extends CdkdError {
@@ -13243,6 +13295,12 @@ function buildUnknownIntrinsicError(key) {
13243
13295
  * `Record<string, unknown>` and deliberately NOT coerced to string — a
13244
13296
  * list-valued `Fn::GetAtt` persists a JSON array — so a secret-bearing output
13245
13297
  * is not always a bare string.
13298
+ *
13299
+ * EXPORTED for `cdkd scrub` (issue
13300
+ * [#2133](https://github.com/go-to-k/cdkd/issues/2133)), which asks the inverse
13301
+ * question of the same value: a cross-stack read that comes back carrying NO
13302
+ * dynamic reference is one scrub could not turn into a needle, because a needle
13303
+ * is only ever recorded by resolving a `{{resolve:...}}` expression.
13246
13304
  */
13247
13305
  function carriesDynamicReference(value) {
13248
13306
  if (typeof value === "string") return value.includes("{{resolve:");
@@ -13371,6 +13429,14 @@ const FABRICATED_ACCOUNT_INFO_TTL_MS = 1e4;
13371
13429
  */
13372
13430
  const MAX_DYNAMIC_REFERENCE_THROTTLE_RETRIES = 4;
13373
13431
  /**
13432
+ * How many producer output KEYS an `Fn::GetStackOutput` not-found error may
13433
+ * enumerate (issue #2133 review). See {@link
13434
+ * IntrinsicFunctionResolver.describeAvailableOutputs} for why the list is
13435
+ * bounded at all; the value is "enough to fix a typo, few enough that one error
13436
+ * cannot dump a producer's whole key space".
13437
+ */
13438
+ const MAX_LISTED_AVAILABLE_OUTPUTS = 10;
13439
+ /**
13374
13440
  * Test seam: overriding `sleep` lets unit tests drive the backoff schedule
13375
13441
  * without real waits (mirrors `describeTypeRetryDelays`).
13376
13442
  */
@@ -14975,7 +15041,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
14975
15041
  const resolved1 = await this.resolveValue(value1, context);
14976
15042
  const resolved2 = await this.resolveValue(value2, context);
14977
15043
  const result = JSON.stringify(resolved1) === JSON.stringify(resolved2);
14978
- this.logger.debug(`Resolved Fn::Equals: ${JSON.stringify(resolved1)} === ${JSON.stringify(resolved2)} -> ${result}`);
15044
+ this.logger.debug(`Resolved Fn::Equals: ${this.maskSecretsForLog(JSON.stringify(resolved1), context)} === ${this.maskSecretsForLog(JSON.stringify(resolved2), context)} -> ${result}`);
14979
15045
  return result;
14980
15046
  }
14981
15047
  /**
@@ -15133,7 +15199,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15133
15199
  }
15134
15200
  return v;
15135
15201
  };
15136
- this.logger.debug(`Re-resolving dynamic reference(s) in ${origin}`);
15202
+ this.logger.debug(`Re-resolving dynamic reference(s) in ${this.maskSecretsForLog(origin, context)}`);
15137
15203
  return await walk(value);
15138
15204
  }
15139
15205
  /**
@@ -15220,23 +15286,24 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15220
15286
  const exportName = await this.resolveValue(importValueArg, context);
15221
15287
  if (typeof exportName !== "string") throw new Error(`Fn::ImportValue: export name must resolve to a string, got ${typeof exportName}`);
15222
15288
  if (!context.stateBackend) throw new Error("Fn::ImportValue: state backend is required for cross-stack references");
15223
- this.logger.debug(`Resolving Fn::ImportValue: ${exportName}`);
15289
+ const loggedExportName = this.maskSecretsForLog(exportName, context);
15290
+ this.logger.debug(`Resolving Fn::ImportValue: ${loggedExportName}`);
15224
15291
  if (context.exportIndex) {
15225
15292
  let entry;
15226
15293
  try {
15227
15294
  entry = await context.exportIndex.lookup(exportName);
15228
15295
  } catch (err) {
15229
- this.logger.warn(`Exports index lookup failed for '${exportName}': ${err instanceof Error ? err.message : String(err)}; falling back to state.json scan`);
15296
+ this.logger.warn(`Exports index lookup failed for '${loggedExportName}': ${err instanceof Error ? err.message : String(err)}; falling back to state.json scan`);
15230
15297
  entry = void 0;
15231
15298
  }
15232
15299
  if (entry && (!context.stackName || entry.producerStack !== context.stackName)) {
15233
15300
  this.recordImport(context, exportName, entry.producerStack, entry.producerRegion);
15234
- this.logger.info(`Resolved Fn::ImportValue: ${exportName} = ${JSON.stringify(entry.value)} (from index: ${entry.producerStack} / ${entry.producerRegion})`);
15301
+ this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from index: ${entry.producerStack} / ${entry.producerRegion}; ${carriesDynamicReference(entry.value) ? "redacted dynamic reference" : "literal value"})`);
15235
15302
  return await this.reresolveCrossStackValue(entry.value, entry.producerRegion, context, `Fn::ImportValue '${exportName}' (producer ${entry.producerStack} / ${entry.producerRegion})`);
15236
15303
  }
15237
15304
  }
15238
15305
  const allStacks = await context.stateBackend.listStacks();
15239
- this.logger.debug(`Found ${allStacks.length} state record(s) to search for export: ${exportName}`);
15306
+ this.logger.debug(`Found ${allStacks.length} state record(s) to search for export: ${loggedExportName}`);
15240
15307
  let found;
15241
15308
  for (const ref of allStacks) {
15242
15309
  const { stackName: refStack, region: refRegion } = ref;
@@ -15258,7 +15325,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15258
15325
  const { state } = stateData;
15259
15326
  if (state.outputs && exportName in state.outputs) {
15260
15327
  const value = state.outputs[exportName];
15261
- this.logger.info(`Resolved Fn::ImportValue: ${exportName} = ${JSON.stringify(value)} (from stack: ${refStack} / ${lookupRegion})`);
15328
+ this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from stack: ${refStack} / ${lookupRegion}; ${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
15262
15329
  if (context.exportIndex) context.exportIndex.patchEntry(exportName, {
15263
15330
  value,
15264
15331
  producerStack: refStack,
@@ -15281,9 +15348,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15281
15348
  }
15282
15349
  if (found) return await this.reresolveCrossStackValue(found.value, found.lookupRegion, context, `Fn::ImportValue '${exportName}' (producer ${found.refStack} / ${found.lookupRegion})`);
15283
15350
  if (this.cfnFallback) {
15284
- const cfnExport = await this.lookupCfnExport(exportName);
15351
+ const cfnExport = await this.lookupCfnExport(exportName, context);
15285
15352
  if (cfnExport) {
15286
- this.logger.info(`Resolved Fn::ImportValue: ${exportName} = ${JSON.stringify(cfnExport.value)} (from CloudFormation exports${cfnExport.exportingStackId ? `; exporting stack: ${cfnExport.exportingStackId}` : ""}; weak reference — producer is not cdkd-managed)`);
15353
+ this.logger.info(`Resolved Fn::ImportValue: ${loggedExportName} (from CloudFormation exports${cfnExport.exportingStackId ? `; exporting stack: ${cfnExport.exportingStackId}` : ""}; weak reference — producer is not cdkd-managed)`);
15287
15354
  return cfnExport.value;
15288
15355
  }
15289
15356
  }
@@ -15301,7 +15368,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15301
15368
  * deliberate: without this fallback the deploy would have failed with
15302
15369
  * the same not-found error anyway.
15303
15370
  */
15304
- async lookupCfnExport(exportName) {
15371
+ async lookupCfnExport(exportName, context) {
15305
15372
  let listing = this.cfnExportsPromise;
15306
15373
  if (!listing) {
15307
15374
  listing = this.fetchAllCfnExports();
@@ -15318,10 +15385,40 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15318
15385
  };
15319
15386
  return;
15320
15387
  } catch (error) {
15321
- this.logger.warn(`Fn::ImportValue: CloudFormation ListExports fallback failed for export '${exportName}' (region ${this.resolverRegion}): ${error instanceof Error ? error.message : String(error)}. Grant cloudformation:ListExports to resolve exports from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
15388
+ this.logger.warn(`Fn::ImportValue: CloudFormation ListExports fallback failed for export '${this.maskSecretsForLog(exportName, context)}' (region ${this.resolverRegion}): ${error instanceof Error ? error.message : String(error)}. Grant cloudformation:ListExports to resolve exports from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
15322
15389
  return;
15323
15390
  }
15324
15391
  }
15392
+ /**
15393
+ * Render the `Available outputs: ...` tail of an `Fn::GetStackOutput`
15394
+ * not-found error (issue #2133 review).
15395
+ *
15396
+ * These are the PRODUCER's `state.outputs` / CloudFormation output KEYS, and
15397
+ * they land in a top-level ERROR — the one thing on this path that reaches a
15398
+ * CI log at default verbosity. A key can itself hold plaintext: that is the
15399
+ * `secretBearingStateKeyWarning` class (issue #1919), which `cdkd scrub`
15400
+ * counts and deliberately never prints, so the enumeration must not be the
15401
+ * one place that does.
15402
+ *
15403
+ * MASKED and CAPPED rather than dropped. Masking is the treatment every other
15404
+ * identifier on this path already gets, and the cap bounds what one error can
15405
+ * disclose (a producer with hundreds of outputs would otherwise dump all of
15406
+ * them). Dropping the names entirely was considered and rejected: a typo'd
15407
+ * `OutputName` is the overwhelmingly common cause, and the list is what makes
15408
+ * the error actionable.
15409
+ *
15410
+ * Residual, stated rather than hidden: the needles belong to the CONSUMER's
15411
+ * resolution, so a plaintext sitting in a PRODUCER key that this consumer
15412
+ * never resolved is not maskable from here. The cap is what bounds that case;
15413
+ * `cdkd scrub` reporting the producer's own `secretBearingKeys` is the remedy.
15414
+ */
15415
+ describeAvailableOutputs(keys, context) {
15416
+ if (keys.length === 0) return "(none)";
15417
+ const shown = keys.slice(0, MAX_LISTED_AVAILABLE_OUTPUTS);
15418
+ const rendered = shown.map((k) => this.maskSecretsForLog(k, context)).join(", ");
15419
+ const hidden = keys.length - shown.length;
15420
+ return hidden > 0 ? `${rendered} (+${hidden} more)` : rendered;
15421
+ }
15325
15422
  /** Full paginated ListExports walk backing {@link lookupCfnExport}'s memo. */
15326
15423
  async fetchAllCfnExports() {
15327
15424
  const client = this.getCfnClient(this.resolverRegion);
@@ -15344,7 +15441,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15344
15441
  * logged for non-not-found failures). Same graceful-degradation
15345
15442
  * contract as {@link lookupCfnExport}.
15346
15443
  */
15347
- async lookupCfnStackOutputs(stackName, region) {
15444
+ async lookupCfnStackOutputs(stackName, region, context) {
15348
15445
  const cacheKey = `${region}\0${stackName}`;
15349
15446
  let fetch = this.cfnStackOutputsCache.get(cacheKey);
15350
15447
  if (!fetch) {
@@ -15358,7 +15455,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15358
15455
  return await fetch;
15359
15456
  } catch (error) {
15360
15457
  const message = error instanceof Error ? error.message : String(error);
15361
- this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${stackName}' (${region}): ${message}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
15458
+ this.logger.warn(`Fn::GetStackOutput: CloudFormation DescribeStacks fallback failed for stack '${this.maskSecretsForLog(stackName, context)}' (${region}): ${message}. Grant cloudformation:DescribeStacks to resolve outputs from CloudFormation-managed stacks, or pass --no-cfn-fallback to disable the fallback.`);
15362
15459
  return;
15363
15460
  }
15364
15461
  }
@@ -15473,18 +15570,20 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15473
15570
  roleArn = raw;
15474
15571
  }
15475
15572
  if (!roleArn && context.stackName && context.stackName === stackName && region === this.resolverRegion) throw new Error(`Fn::GetStackOutput: cannot reference own stack '${stackName}' in the same region '${region}'`);
15476
- this.logger.debug(`Resolving Fn::GetStackOutput: StackName=${stackName}, Region=${region}, OutputName=${outputName}${roleArn ? `, RoleArn=${roleArn}` : ""}`);
15573
+ const loggedStackName = this.maskSecretsForLog(stackName, context);
15574
+ const loggedOutputName = this.maskSecretsForLog(outputName, context);
15575
+ this.logger.debug(`Resolving Fn::GetStackOutput: StackName=${loggedStackName}, Region=${region}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${roleArn}` : ""}`);
15477
15576
  const stateData = roleArn ? await this.getCrossAccountStackState(roleArn, stackName, region, context) : await this.getSameAccountStackState(stackName, region, context);
15478
15577
  if (!stateData) {
15479
15578
  if (!roleArn && this.cfnFallback) {
15480
- const cfnOutputs = await this.lookupCfnStackOutputs(stackName, region);
15579
+ const cfnOutputs = await this.lookupCfnStackOutputs(stackName, region, context);
15481
15580
  if (cfnOutputs) {
15482
15581
  if (!(outputName in cfnOutputs)) {
15483
- const available = Object.keys(cfnOutputs).join(", ") || "(none)";
15582
+ const available = this.describeAvailableOutputs(Object.keys(cfnOutputs), context);
15484
15583
  throw new Error(`Fn::GetStackOutput: output '${outputName}' not found in CloudFormation stack '${stackName}' (${region}). Available outputs: ${available}`);
15485
15584
  }
15486
15585
  const value = cfnOutputs[outputName];
15487
- this.logger.info(`Resolved Fn::GetStackOutput: StackName=${stackName}, Region=${region}, OutputName=${outputName} -> ${JSON.stringify(value)} (from CloudFormation stack outputs; weak reference — producer is not cdkd-managed)`);
15586
+ this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${region}, OutputName=${loggedOutputName} (from CloudFormation stack outputs; weak reference — producer is not cdkd-managed)`);
15488
15587
  return value;
15489
15588
  }
15490
15589
  }
@@ -15492,13 +15591,13 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
15492
15591
  }
15493
15592
  const outputs = stateData.state.outputs ?? {};
15494
15593
  if (!(outputName in outputs)) {
15495
- const available = Object.keys(outputs).join(", ") || "(none)";
15594
+ const available = this.describeAvailableOutputs(Object.keys(outputs), context);
15496
15595
  throw new Error(`Fn::GetStackOutput: output '${outputName}' not found in stack '${stackName}' (${region}). Available outputs: ${available}`);
15497
15596
  }
15498
15597
  const value = outputs[outputName];
15499
- this.logger.info(`Resolved Fn::GetStackOutput: StackName=${stackName}, Region=${region}, OutputName=${outputName}${roleArn ? `, RoleArn=${roleArn}` : ""} -> ${JSON.stringify(value)}`);
15598
+ this.logger.info(`Resolved Fn::GetStackOutput: StackName=${loggedStackName}, Region=${region}, OutputName=${loggedOutputName}${roleArn ? `, RoleArn=${roleArn}` : ""} (${carriesDynamicReference(value) ? "redacted dynamic reference" : "literal value"})`);
15500
15599
  if (!roleArn) this.recordOutputRead(context, stackName, region, outputName);
15501
- if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Fn::GetStackOutput: output '${outputName}' of stack '${stackName}' (${region}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${roleArn}). cdkd will not resolve a producer account's secret with the consumer's credentials — a same-named secret in the consumer account would answer instead. Export a non-secret value (e.g. the secret's ARN) and resolve it in the consumer stack, or reference the producer stack from within its own account.`));
15600
+ if (roleArn && !context.skipDynamicReferences && carriesDynamicReference(value)) throw markNonRetryable(new CrossAccountSecretRefusalError(`Fn::GetStackOutput: output '${outputName}' of stack '${stackName}' (${region}) is a redacted dynamic reference, and this is a CROSS-ACCOUNT reference (RoleArn ${roleArn}). cdkd will not resolve a producer account's secret with the consumer's credentials — a same-named secret in the consumer account would answer instead. Export a non-secret value (e.g. the secret's ARN) and resolve it in the consumer stack, or reference the producer stack from within its own account.`));
15502
15601
  return await this.reresolveCrossStackValue(value, region, context, `Fn::GetStackOutput '${outputName}' (producer ${stackName} / ${region})`);
15503
15602
  }
15504
15603
  /**
@@ -16888,7 +16987,7 @@ var CloudControlProvider = class {
16888
16987
  if (context?.finalSnapshotIdentifier !== void 0) throw new ProvisioningError(`${logicalId} (${resourceType}) requires a final snapshot (DeletionPolicy: Snapshot), but the Cloud Control API delete route has no final-snapshot parameter. Re-run with --skip-final-snapshot after snapshotting manually, or retain the resource.`, resourceType, logicalId, physicalId);
16889
16988
  if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
16890
16989
  this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
16891
- const { ASGProvider } = await import("./asg-provider-DJvcY8lj.js").then((n) => n.n);
16990
+ const { ASGProvider } = await import("./asg-provider-BVzQ7mQX.js").then((n) => n.n);
16892
16991
  return await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
16893
16992
  }
16894
16993
  const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
@@ -25801,7 +25900,7 @@ const FLUSH_INTERVAL_MS = 2e3;
25801
25900
  const FLUSH_EVENT_THRESHOLD = 50;
25802
25901
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
25803
25902
  function getCdkdVersion() {
25804
- return "0.284.31";
25903
+ return "0.284.32";
25805
25904
  }
25806
25905
  /**
25807
25906
  * Generate a time-sortable unique run id, e.g.
@@ -28421,5 +28520,5 @@ var DeployEngine = class {
28421
28520
  };
28422
28521
 
28423
28522
  //#endregion
28424
- export { endCommandInterruptScope as $, DependencyError as $n, createAssetRedirectResolver as $t, MULTI_REGION_RECREATE_BLOCKED_TYPES as A, resolveUseCdkBootstrapAssets as An, maskSecretsInText as At, collectDeclaredOutputNames as B, canonicalizeRegion as Bn, INTRINSIC_KEYS as Bt, ccRoutedFinalSnapshotError as C, getLegacyStateBucketName as Cn, __exportAll as Cr, STATE_SOURCED_READBACK_RULES as Ct, unsupportedFinalSnapshotError as D, resolveSkipPrefix as Dn, errorCauseChain as Dt, refusesFinalSnapshot as E, resolveCaptureObservedState as En, dynamicReferenceTokens as Et, cyan as F, MIGRATE_TMP_PREFIX as Fn, s3BucketDualStackDomainName as Ft, stateKeySecretExposure as G, resolveBucketRegion as Gn, LockManager as Gt, exportAliasCollisionScrubWarning as H, AssemblyReader as Hn, withRetry as Ht, gray as I, findLargeInlineResources as In, s3BucketRegionalDomainName as It, clearOnUpdateRemoval as J, resetAwsClients as Jn, shouldRetainResource as Jt, IAMRoleProvider as K, AwsClients as Kn, S3StateBackend as Kt, green as L, uploadCfnTemplate as Ln, s3BucketWebsiteUrl as Lt, renderStatefulReason as M, warnDeprecatedNoPrefixCliFlag as Mn, scrubResourceRecord as Mt, formatResourceLine as N, CFN_TEMPLATE_BODY_LIMIT as Nn, s3BucketArn as Nt, makeCanonicalizePropertiesFn as O, resolveStateBucketWithDefault as On, isSingleDynamicReferenceToken as Ot, bold as P, CFN_TEMPLATE_URL_LIMIT as Pn, s3BucketDomainName as Pt, beginCommandInterruptScope as Q, ConfigError as Qn, buildAssetRedirectMap as Qt, red as R, expectedOwnerParam as Rn, applyRoleArnIfSet as Rt, buildFinalSnapshotIdentifier as S, getDefaultStateBucketName as Sn, markNonRetryable as Sr, STATE_SOURCED_CROSS_GENERATION_RULES as St, isFinalSnapshotError as T, resolveAutoAssetStorage as Tn, createSecretMasker as Tt, isExportAliasCollision as U, processStackMessages as Un, DagBuilder as Ut, collectPublishedOutputNames as V, derivePartitionAndUrlSuffix as Vn, describeTypeWithThrottleRetry as Vt, secretBearingStateKeyWarning as W, clearBucketRegionCache as Wn, TemplateParser as Wt, findActionableSilentDrops as X, AssetError as Xn, stringifyValue as Xt, ProviderRegistry as Y, setAwsClients as Yn, AssetPublisher as Yt, findSilentDropProperties as Z, CdkdError as Zn, WorkGraph as Zt, maskingRetryLogger as _, runDockerStreaming as _n, normalizeAwsError as _r, readConfigString as _t, DeploymentEventsStore as a, BOOTSTRAP_MARKER_PREFIX as an, MissingCdkCliError as ar, isTerminationProtectionPropagationError as at, ATOMIC_FINAL_SNAPSHOT_TYPES as b, Synthesizer as bn, isRetryableTransientError as br, requireConfigObject as bt, planRollback as c, parseBootstrapMarker as cn, ProvisioningError as cr, getAccountInfo as ct, replayRollback as d, validateContainerRepoName as dn, StackHasActiveImportsError as dr, normalizeAwsTagsToCfn as dt, loadPublishableAssetManifest as en, DeployCancelledError as er, isInterruptedWaitError as et, updatePartialMessage as f, buildDenyExternalAccessPolicy as fn, StackTerminationProtectionError as fr, resolveExplicitPhysicalId as ft, withResourceDeadline as g, runDockerForeground as gn, isCdkdError as gr, configStringRefusal as gt, deleteSkipReason as h, getDockerCmd as hn, formatError as hr, configBooleanRefusal as ht, DeploymentEventsReader as i, AssetModeResolver as in, LockError as ir, disableInstanceApiTermination as it, isStatefulRecreateTargetSync as j, stateBucketExistenceConfirmed as jn, redactSecretsForState as jt, extractDeploymentEventError as k, resolveStateBucketWithDefaultAndSource as kn, maskSecretsInError as kt, producerRegionsFromState as l, readBootstrapMarkerBody as ln, ResourceTimeoutError as lr, refStateLookupFromResource as lt, UNSPECIFIED_SKIP_REASON as m, formatDockerLoginError as mn, SynthesisError as mr, coerceCfnBoolean as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, escapeRegExp$1 as nn, LocalMigrateError as nr, CloudControlProvider as nt, classifyReplaySecretRegion as o, ensureAssetStorage as on, NestedStackChildDirectDestroyError as or, IntrinsicFunctionResolver as ot, updatePartialReason as p, buildDockerImage as pn, StateError as pr, assertRegionMatch as pt, collectInlinePolicyNamesManagedBySiblings as q, getAwsClients as qn, rebuildClientForBucketRegion as qt, DeployEngine as r, stripControlChars as rn, LocalStartServiceError as rr, slowCcOperationTimeoutMs as rt, planFailedOps as s, getBootstrapMarkerKey as sn, PartialFailureError as sr, cfnRefValueFromPhysicalId as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, rewriteTemplateAssetReferences as tn, LocalInvokeBuildError as tr, startInterruptWatch as tt, replayFailedOperations as u, validateAssetBucketName as un, ResourceUpdateNotSupportedError as ur, WAFv2WebACLProvider as ut, IMPLICIT_DELETE_DEPENDENCIES as v, AssetManifestLoader as vn, withErrorHandling as vr, replayWarn as vt, createPreDeleteFinalSnapshot as w, resolveApp as wn, TEMPLATE_SOURCED_RULES as wt, PRE_DELETE_SNAPSHOT_TYPES as x, synthesisStatusMessage as xn, isThrottlingError as xr, requireConfigString as xt, computeImplicitDeleteEdges as y, getDockerImageBySourceHash as yn, isMarkedNonRetryable as yr, requireConfigArray as yt, yellow as z, PARTITION_TABLE as zn, DiffCalculator as zt };
28425
- //# sourceMappingURL=deploy-engine-DYdEWX-s.js.map
28523
+ export { endCommandInterruptScope as $, ConfigError as $n, buildAssetRedirectMap as $t, MULTI_REGION_RECREATE_BLOCKED_TYPES as A, resolveStateBucketWithDefaultAndSource as An, maskSecretsInError as At, collectDeclaredOutputNames as B, PARTITION_TABLE as Bn, DiffCalculator as Bt, ccRoutedFinalSnapshotError as C, getDefaultStateBucketName as Cn, isThrottlingError as Cr, STATE_SOURCED_CROSS_GENERATION_RULES as Ct, unsupportedFinalSnapshotError as D, resolveCaptureObservedState as Dn, dynamicReferenceTokens as Dt, refusesFinalSnapshot as E, resolveAutoAssetStorage as En, createSecretMasker as Et, cyan as F, CFN_TEMPLATE_URL_LIMIT as Fn, s3BucketDomainName as Ft, stateKeySecretExposure as G, clearBucketRegionCache as Gn, TemplateParser as Gt, exportAliasCollisionScrubWarning as H, derivePartitionAndUrlSuffix as Hn, describeTypeWithThrottleRetry as Ht, gray as I, MIGRATE_TMP_PREFIX as In, s3BucketDualStackDomainName as It, clearOnUpdateRemoval as J, getAwsClients as Jn, rebuildClientForBucketRegion as Jt, IAMRoleProvider as K, resolveBucketRegion as Kn, LockManager as Kt, green as L, findLargeInlineResources as Ln, s3BucketRegionalDomainName as Lt, renderStatefulReason as M, stateBucketExistenceConfirmed as Mn, redactSecretsForState as Mt, formatResourceLine as N, warnDeprecatedNoPrefixCliFlag as Nn, scrubResourceRecord as Nt, makeCanonicalizePropertiesFn as O, resolveSkipPrefix as On, errorCauseChain as Ot, bold as P, CFN_TEMPLATE_BODY_LIMIT as Pn, s3BucketArn as Pt, beginCommandInterruptScope as Q, CdkdError as Qn, WorkGraph as Qt, red as R, uploadCfnTemplate as Rn, s3BucketWebsiteUrl as Rt, buildFinalSnapshotIdentifier as S, synthesisStatusMessage as Sn, isRetryableTransientError as Sr, requireConfigString as St, isFinalSnapshotError as T, resolveApp as Tn, __exportAll as Tr, TEMPLATE_SOURCED_RULES as Tt, isExportAliasCollision as U, AssemblyReader as Un, withRetry as Ut, collectPublishedOutputNames as V, canonicalizeRegion as Vn, INTRINSIC_KEYS as Vt, secretBearingStateKeyWarning as W, processStackMessages as Wn, DagBuilder as Wt, findActionableSilentDrops as X, setAwsClients as Xn, AssetPublisher as Xt, ProviderRegistry as Y, resetAwsClients as Yn, shouldRetainResource as Yt, findSilentDropProperties as Z, AssetError as Zn, stringifyValue as Zt, maskingRetryLogger as _, runDockerForeground as _n, formatError as _r, configStringRefusal as _t, DeploymentEventsStore as a, AssetModeResolver as an, LocalStartServiceError as ar, isTerminationProtectionPropagationError as at, ATOMIC_FINAL_SNAPSHOT_TYPES as b, getDockerImageBySourceHash as bn, withErrorHandling as br, requireConfigArray as bt, planRollback as c, getBootstrapMarkerKey as cn, NestedStackChildDirectDestroyError as cr, cfnRefValueFromPhysicalId as ct, replayRollback as d, validateAssetBucketName as dn, ResourceTimeoutError as dr, WAFv2WebACLProvider as dt, createAssetRedirectResolver as en, CrossAccountSecretRefusalError as er, isInterruptedWaitError as et, updatePartialMessage as f, validateContainerRepoName as fn, ResourceUpdateNotSupportedError as fr, normalizeAwsTagsToCfn as ft, withResourceDeadline as g, getDockerCmd as gn, SynthesisError as gr, configBooleanRefusal as gt, deleteSkipReason as h, formatDockerLoginError as hn, StateError as hr, coerceCfnBoolean as ht, DeploymentEventsReader as i, stripControlChars as in, LocalMigrateError as ir, disableInstanceApiTermination as it, isStatefulRecreateTargetSync as j, resolveUseCdkBootstrapAssets as jn, maskSecretsInText as jt, extractDeploymentEventError as k, resolveStateBucketWithDefault as kn, isSingleDynamicReferenceToken as kt, producerRegionsFromState as l, parseBootstrapMarker as ln, PartialFailureError as lr, getAccountInfo as lt, UNSPECIFIED_SKIP_REASON as m, buildDockerImage as mn, StackTerminationProtectionError as mr, assertRegionMatch as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, rewriteTemplateAssetReferences as nn, DeployCancelledError as nr, CloudControlProvider as nt, classifyReplaySecretRegion as o, BOOTSTRAP_MARKER_PREFIX as on, LockError as or, IntrinsicFunctionResolver as ot, updatePartialReason as p, buildDenyExternalAccessPolicy as pn, StackHasActiveImportsError as pr, resolveExplicitPhysicalId as pt, collectInlinePolicyNamesManagedBySiblings as q, AwsClients as qn, S3StateBackend as qt, DeployEngine as r, escapeRegExp$1 as rn, LocalInvokeBuildError as rr, slowCcOperationTimeoutMs as rt, planFailedOps as s, ensureAssetStorage as sn, MissingCdkCliError as sr, carriesDynamicReference as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, loadPublishableAssetManifest as tn, DependencyError as tr, startInterruptWatch as tt, replayFailedOperations as u, readBootstrapMarkerBody as un, ProvisioningError as ur, refStateLookupFromResource as ut, IMPLICIT_DELETE_DEPENDENCIES as v, runDockerStreaming as vn, isCdkdError as vr, readConfigString as vt, createPreDeleteFinalSnapshot as w, getLegacyStateBucketName as wn, markNonRetryable as wr, STATE_SOURCED_READBACK_RULES as wt, PRE_DELETE_SNAPSHOT_TYPES as x, Synthesizer as xn, isMarkedNonRetryable as xr, requireConfigObject as xt, computeImplicitDeleteEdges as y, AssetManifestLoader as yn, normalizeAwsError as yr, replayWarn as yt, yellow as z, expectedOwnerParam as zn, applyRoleArnIfSet as zt };
28524
+ //# sourceMappingURL=deploy-engine-DsUQ4GxO.js.map