@go-to-k/cdkd 0.284.40 → 0.284.42

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as startInterruptWatch, $n as CdkdError, $t as WorkGraph, A as renderStatefulReason, An as resolveStateBucketWithDefault, At as redactSecretsForState, B as exportAliasCollisionScrubWarning, Bn as expectedOwnerParam, Bt as DiffCalculator, C as isFinalSnapshotError, Cn as synthesisStatusMessage, Cr as isMarkedNonRetryable, Ct as TEMPLATE_SOURCED_RULES, D as extractDeploymentEventError, Dn as resolveAutoAssetStorage, Dt as isSingleDynamicReferenceToken, E as makeCanonicalizePropertiesFn, En as resolveApp, Er as markNonRetryable, Et as errorCauseChain, F as green, Fn as CFN_TEMPLATE_BODY_LIMIT, Ft as s3BucketDomainName, G as collectInlinePolicyNamesManagedBySiblings, Gn as processStackMessages, Gt as TemplateParser, H as secretBearingStateKeyWarning, Hn as canonicalizeRegion, Ht as describeTypeWithThrottleRetry, I as red, In as CFN_TEMPLATE_URL_LIMIT, It as s3BucketDualStackDomainName, J as findActionableSilentDrops, Jn as AwsClients, Jt as S3StateBackend, K as clearOnUpdateRemoval, Kt as LockManager, L as yellow, Ln as MIGRATE_TMP_PREFIX, Lt as s3BucketRegionalDomainName, M as bold, Mn as resolveUseCdkBootstrapAssets, Mt as classifyReplaySecretRegion, N as cyan, Nn as stateBucketExistenceConfirmed, Nt as producerRegionsFromState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as resolveCaptureObservedState, Ot as maskSecretsInError, P as gray, Pn as warnDeprecatedNoPrefixCliFlag, Pt as s3BucketArn, Q as isInterruptedWaitError, Qt as stringifyValue, R as collectDeclaredOutputNames, Rn as findLargeInlineResources, Rt as s3BucketWebsiteUrl, S as createPreDeleteFinalSnapshot, Sn as Synthesizer, Sr as withErrorHandling, St as STATE_SOURCED_READBACK_RULES, T as unsupportedFinalSnapshotError, Tn as getLegacyStateBucketName, Tr as isThrottlingError, Tt as dynamicReferenceTokens, U as stateKeySecretExposure, Un as derivePartitionAndUrlSuffix, Ut as withRetry, V as isExportAliasCollision, Vn as PARTITION_TABLE, Vt as INTRINSIC_KEYS, W as IAMRoleProvider, Wn as AssemblyReader, Wt as DagBuilder, X as beginCommandInterruptScope, Xn as resetAwsClients, Xt as shouldRetainResource, Y as findSilentDropProperties, Yn as getAwsClients, Yt as rebuildClientForBucketRegion, Z as endCommandInterruptScope, Zn as setAwsClients, Zt as AssetPublisher, _ as computeImplicitDeleteEdges, _n as getDockerCmd, _t as replayWarn, a as DeploymentEventsStore, an as stripControlChars, ar as LocalInvokeBuildError$1, at as carriesDynamicReference, b as buildFinalSnapshotIdentifier, bn as AssetManifestLoader, bt as requireConfigString, c as replayFailedOperations, cn as ensureAssetStorage, ct as refStateLookupFromResource, d as updatePartialReason, dn as readBootstrapMarkerBody, dr as PartialFailureError, dt as resolveExplicitPhysicalId, en as buildAssetRedirectMap, et as CloudControlProvider, f as UNSPECIFIED_SKIP_REASON, fn as validateAssetBucketName, fr as ProvisioningError, ft as assertRegionMatch, g as IMPLICIT_DELETE_DEPENDENCIES, gn as formatDockerLoginError, gr as StackTerminationProtectionError, gt as readConfigString, h as maskingRetryLogger, hn as buildDockerImage, hr as StackHasActiveImportsError, ht as configStringRefusal, i as DeploymentEventsReader, in as escapeRegExp, ir as DynamicReferenceRegionAmbiguousError, it as IntrinsicFunctionResolver, j as formatResourceLine, jn as resolveStateBucketWithDefaultAndSource, jt as scrubResourceRecord, k as isStatefulRecreateTargetSync, kn as resolveSkipPrefix, kt as maskSecretsInText, l as replayRollback, ln as getBootstrapMarkerKey, lr as MissingCdkCliError, lt as WAFv2WebACLProvider, m as withResourceDeadline, mn as buildDenyExternalAccessPolicy, mr as ResourceUpdateNotSupportedError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as loadPublishableAssetManifest, nt as disableInstanceApiTermination, o as planFailedOps, on as AssetModeResolver, or as LocalMigrateError, ot as cfnRefValueFromPhysicalId, p as deleteSkipReason, pn as validateContainerRepoName, pr as ResourceTimeoutError, pt as coerceCfnBoolean, q as ProviderRegistry, qn as resolveBucketRegion, qt as displaySafe, r as DeployEngine, rn as rewriteTemplateAssetReferences, rr as DeployCancelledError, rt as isTerminationProtectionPropagationError, s as planRollback, sn as BOOTSTRAP_MARKER_PREFIX, sr as LocalStartServiceError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as createAssetRedirectResolver, tr as CrossAccountSecretRefusalError, tt as slowCcOperationTimeoutMs, u as updatePartialMessage, un as parseBootstrapMarker, ur as NestedStackChildDirectDestroyError, ut as normalizeAwsTagsToCfn, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as runDockerForeground, vt as requireConfigArray, w as refusesFinalSnapshot, wn as getDefaultStateBucketName, wr as isRetryableTransientError, wt as createSecretMasker, x as ccRoutedFinalSnapshotError, xn as getDockerImageBySourceHash, xr as normalizeAwsError, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as PRE_DELETE_SNAPSHOT_TYPES, yn as runDockerStreaming, yt as requireConfigObject, z as collectPublishedOutputNames, zn as uploadCfnTemplate, zt as applyRoleArnIfSet } from "./deploy-engine-B3MQFW1K.js";
2
+ import { $ as startInterruptWatch, $n as CdkdError, $t as WorkGraph, A as renderStatefulReason, An as resolveStateBucketWithDefault, At as redactSecretsForState, B as exportAliasCollisionScrubWarning, Bn as expectedOwnerParam, Bt as DiffCalculator, C as isFinalSnapshotError, Cn as synthesisStatusMessage, Cr as isMarkedNonRetryable, Ct as TEMPLATE_SOURCED_RULES, D as extractDeploymentEventError, Dn as resolveAutoAssetStorage, Dt as isSingleDynamicReferenceToken, E as makeCanonicalizePropertiesFn, En as resolveApp, Er as markNonRetryable, Et as errorCauseChain, F as green, Fn as CFN_TEMPLATE_BODY_LIMIT, Ft as s3BucketDomainName, G as collectInlinePolicyNamesManagedBySiblings, Gn as processStackMessages, Gt as TemplateParser, H as secretBearingStateKeyWarning, Hn as canonicalizeRegion, Ht as describeTypeWithThrottleRetry, I as red, In as CFN_TEMPLATE_URL_LIMIT, It as s3BucketDualStackDomainName, J as findActionableSilentDrops, Jn as AwsClients, Jt as S3StateBackend, K as clearOnUpdateRemoval, Kt as LockManager, L as yellow, Ln as MIGRATE_TMP_PREFIX, Lt as s3BucketRegionalDomainName, M as bold, Mn as resolveUseCdkBootstrapAssets, Mt as classifyReplaySecretRegion, N as cyan, Nn as stateBucketExistenceConfirmed, Nt as producerRegionsFromState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as resolveCaptureObservedState, Ot as maskSecretsInError, P as gray, Pn as warnDeprecatedNoPrefixCliFlag, Pt as s3BucketArn, Q as isInterruptedWaitError, Qt as stringifyValue, R as collectDeclaredOutputNames, Rn as findLargeInlineResources, Rt as s3BucketWebsiteUrl, S as createPreDeleteFinalSnapshot, Sn as Synthesizer, Sr as withErrorHandling, St as STATE_SOURCED_READBACK_RULES, T as unsupportedFinalSnapshotError, Tn as getLegacyStateBucketName, Tr as isThrottlingError, Tt as dynamicReferenceTokens, U as stateKeySecretExposure, Un as derivePartitionAndUrlSuffix, Ut as withRetry, V as isExportAliasCollision, Vn as PARTITION_TABLE, Vt as INTRINSIC_KEYS, W as IAMRoleProvider, Wn as AssemblyReader, Wt as DagBuilder, X as beginCommandInterruptScope, Xn as resetAwsClients, Xt as shouldRetainResource, Y as findSilentDropProperties, Yn as getAwsClients, Yt as rebuildClientForBucketRegion, Z as endCommandInterruptScope, Zn as setAwsClients, Zt as AssetPublisher, _ as computeImplicitDeleteEdges, _n as getDockerCmd, _t as replayWarn, a as DeploymentEventsStore, an as stripControlChars, ar as LocalInvokeBuildError$1, at as carriesDynamicReference, b as buildFinalSnapshotIdentifier, bn as AssetManifestLoader, bt as requireConfigString, c as replayFailedOperations, cn as ensureAssetStorage, ct as refStateLookupFromResource, d as updatePartialReason, dn as readBootstrapMarkerBody, dr as PartialFailureError, dt as resolveExplicitPhysicalId, en as buildAssetRedirectMap, et as CloudControlProvider, f as UNSPECIFIED_SKIP_REASON, fn as validateAssetBucketName, fr as ProvisioningError, ft as assertRegionMatch, g as IMPLICIT_DELETE_DEPENDENCIES, gn as formatDockerLoginError, gr as StackTerminationProtectionError, gt as readConfigString, h as maskingRetryLogger, hn as buildDockerImage, hr as StackHasActiveImportsError, ht as configStringRefusal, i as DeploymentEventsReader, in as escapeRegExp, ir as DynamicReferenceRegionAmbiguousError, it as IntrinsicFunctionResolver, j as formatResourceLine, jn as resolveStateBucketWithDefaultAndSource, jt as scrubResourceRecord, k as isStatefulRecreateTargetSync, kn as resolveSkipPrefix, kt as maskSecretsInText, l as replayRollback, ln as getBootstrapMarkerKey, lr as MissingCdkCliError, lt as WAFv2WebACLProvider, m as withResourceDeadline, mn as buildDenyExternalAccessPolicy, mr as ResourceUpdateNotSupportedError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as loadPublishableAssetManifest, nt as disableInstanceApiTermination, o as planFailedOps, on as AssetModeResolver, or as LocalMigrateError, ot as cfnRefValueFromPhysicalId, p as deleteSkipReason, pn as validateContainerRepoName, pr as ResourceTimeoutError, pt as coerceCfnBoolean, q as ProviderRegistry, qn as resolveBucketRegion, qt as displaySafe, r as DeployEngine, rn as rewriteTemplateAssetReferences, rr as DeployCancelledError, rt as isTerminationProtectionPropagationError, s as planRollback, sn as BOOTSTRAP_MARKER_PREFIX, sr as LocalStartServiceError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as createAssetRedirectResolver, tr as CrossAccountSecretRefusalError, tt as slowCcOperationTimeoutMs, u as updatePartialMessage, un as parseBootstrapMarker, ur as NestedStackChildDirectDestroyError, ut as normalizeAwsTagsToCfn, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as runDockerForeground, vt as requireConfigArray, w as refusesFinalSnapshot, wn as getDefaultStateBucketName, wr as isRetryableTransientError, wt as createSecretMasker, x as ccRoutedFinalSnapshotError, xn as getDockerImageBySourceHash, xr as normalizeAwsError, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as PRE_DELETE_SNAPSHOT_TYPES, yn as runDockerStreaming, yt as requireConfigObject, z as collectPublishedOutputNames, zn as uploadCfnTemplate, zt as applyRoleArnIfSet } from "./deploy-engine-8ygMMbQ8.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-BeKDzUe9.js";
4
+ import { t as ASGProvider } from "./asg-provider-Dn9oXi9c.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -10324,6 +10324,121 @@ function isSqsQueueUrl(value) {
10324
10324
  return value.startsWith("https://sqs.") && value.includes("/");
10325
10325
  }
10326
10326
 
10327
+ //#endregion
10328
+ //#region src/provisioning/masked-retry-logger.ts
10329
+ /**
10330
+ * The masker a caller supplied, or the identity function when it supplied
10331
+ * none.
10332
+ *
10333
+ * ABSENT MEANS UNMASKED, and that is the back-compatible default the contract
10334
+ * mandates — `create()` / `update()` are also reached from the import path,
10335
+ * from `cdkd drift --revert`, and from tests, and a provider must not care
10336
+ * which caller it got. Centralised here so no call site re-spells the `??`
10337
+ * and accidentally makes the capability required.
10338
+ */
10339
+ function maskerOrIdentity(maskSecrets) {
10340
+ return maskSecrets ?? ((text) => text);
10341
+ }
10342
+ /**
10343
+ * A {@link RetryLogger} whose every line is routed through `maskSecrets`.
10344
+ *
10345
+ * `warn` is ALWAYS provided, never omitted. It is optional on `RetryLogger`,
10346
+ * and dropping it would silence the give-up summary on the calling path only —
10347
+ * trading a disclosure for the reporting hole issue #2018 closed. It goes
10348
+ * through the SAME mask as `debug` precisely because it is the line that
10349
+ * survives a run without `--verbose`; forwarding it unmasked would defeat the
10350
+ * fence at a HIGHER log level than the one the fence was written for.
10351
+ *
10352
+ * `logger` is typed structurally rather than as the concrete `Logger` so this
10353
+ * module stays a leaf and a test can pass two spies. That matches the
10354
+ * precedent already in the tree (`buildMfaConfigRequest` in
10355
+ * `cognito-provider.ts` takes an injected `logger?: { warn }`).
10356
+ */
10357
+ function createMaskedRetryLogger(logger, maskSecrets) {
10358
+ const mask = maskerOrIdentity(maskSecrets);
10359
+ return {
10360
+ debug: (message) => logger.debug(mask(message)),
10361
+ warn: (message) => logger.warn(mask(message))
10362
+ };
10363
+ }
10364
+ /**
10365
+ * Depth cap for {@link maskDeep}.
10366
+ *
10367
+ * What it actually bounds is unbounded WORK on a pathologically deep bag — see
10368
+ * the rationale at the cap itself, which is the authority. It is NOT primarily
10369
+ * a cycle guard: a template-derived bag cannot be cyclic, so that justification
10370
+ * over-claims (issue #2176 security review). It does still terminate one, which
10371
+ * is why the walk is safe to share with a caller that might hand it something
10372
+ * other than a template bag.
10373
+ *
10374
+ * Deliberately generous. Every shape these warnings exist to describe is a
10375
+ * scalar, a list of scalars, or a small record, so the walk does not reach
10376
+ * depth 2 in practice.
10377
+ */
10378
+ const MASK_WALK_MAX_DEPTH = 8;
10379
+ /**
10380
+ * What {@link maskDeep} substitutes for a subtree it declines to descend into.
10381
+ *
10382
+ * MUST equal `SECRET_MASK` in `src/deployment/secret-redaction.ts`. It is
10383
+ * spelled here rather than imported so this module keeps its single-import leaf
10384
+ * property (see the module note above); the two are fenced against drift by a
10385
+ * test that imports both.
10386
+ */
10387
+ const MASK_WALK_DEPTH_CAP_MARKER = "***";
10388
+ /**
10389
+ * Mask every string LEAF and KEY of an arbitrary value, returning a structure
10390
+ * safe to `JSON.stringify` into a log line or an error message (issue
10391
+ * [#2176](https://github.com/go-to-k/cdkd/issues/2176)).
10392
+ *
10393
+ * THE ORDERING IS THE WHOLE POINT, and it is why masking the finished message
10394
+ * is not a substitute. `maskSecretsInText` matches by literal occurrence, so:
10395
+ *
10396
+ * 1. ESCAPING. `JSON.stringify` escapes `"`, `\` and newlines, so a secret
10397
+ * containing any of them no longer OCCURS in the stringified text and a
10398
+ * mask applied afterwards cannot find it. That is not an exotic case — it
10399
+ * is every Secrets Manager JSON document, the commonest real secret shape.
10400
+ * Measured on the pre-fix tree for #2176: a plaintext of
10401
+ * `{"user":"admin","pw":"hunter2"}` interpolated as
10402
+ * `${JSON.stringify(value)}` came through a message-level mask COMPLETELY
10403
+ * unchanged, while masking the leaves first rendered it `"***"`.
10404
+ * 2. LENGTH. A finished message is always longer than the value inside it, so
10405
+ * it can only ever reach `maskSecretsInText`'s SUBSTRING arm, which ignores
10406
+ * needles below `MIN_NEEDLE_LENGTH` (4). Handing the masker each RAW leaf
10407
+ * reaches the WHOLE-VALUE arm, which has no floor. Measured the same way: a
10408
+ * 3-character secret survived `Value 'abc' at 'pin' failed ...` intact.
10409
+ *
10410
+ * Neither pass subsumes the other, so providers do BOTH — this walk on the
10411
+ * value, and the assembled message through the masker as well. The masker is
10412
+ * idempotent, so the overlap is free.
10413
+ *
10414
+ * Keys are masked as well as values: `JSON.stringify` renders them into the
10415
+ * same line, so a resolved secret used as a map key would otherwise escape.
10416
+ *
10417
+ * WHY THIS LIVES HERE rather than as a private walk per provider. SIX
10418
+ * hand-rolled copies had already accumulated — `elbv2-provider.ts`,
10419
+ * `cognito-provider.ts`, `sns-topic-provider.ts`, `dynamodb-table-provider.ts`,
10420
+ * `dynamodb-globaltable-provider.ts` and `apigatewayv2-provider.ts` — and
10421
+ * `elbv2-provider.ts` carried the standing instruction that "a THIRD site is
10422
+ * the point at which this should move into `../masked-retry-logger.ts` ... and
10423
+ * all three converge on it". That trigger had fired and been missed twice over,
10424
+ * and the copies had already diverged exactly as predicted: FOUR of the six
10425
+ * carried NO depth cap at all.
10426
+ *
10427
+ * Worth recording how the last two were nearly missed AGAIN, because it is the
10428
+ * same failure one level up: the first sweep for this issue grepped for
10429
+ * `maskDeep` / `MASK_WALK_MAX_DEPTH` — the SPELLINGS the known copies used —
10430
+ * and the two survivors spell it `maskLeaf` / `maskLeafValue` with no named
10431
+ * constant. Grep for the SHAPE, not the name. These copies encode a security
10432
+ * contract, and a hardening applied to one silently leaves the others behind.
10433
+ */
10434
+ function maskDeep(value, mask, depth = 0) {
10435
+ if (typeof value === "string") return mask(value);
10436
+ if (depth >= 8) return "***";
10437
+ if (Array.isArray(value)) return value.map((entry) => maskDeep(entry, mask, depth + 1));
10438
+ if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [mask(k), maskDeep(v, mask, depth + 1)]));
10439
+ return value;
10440
+ }
10441
+
10327
10442
  //#endregion
10328
10443
  //#region src/provisioning/providers/sns-topic-provider.ts
10329
10444
  /**
@@ -10979,12 +11094,7 @@ function buildDeliveryStatusAttributeMap(logging, logicalId, onMalformed, maskSe
10979
11094
  const warn = (message) => {
10980
11095
  getLogger().child("SNSTopicProvider").warn(maskSecrets(message));
10981
11096
  };
10982
- const maskLeaf = (value) => {
10983
- if (typeof value === "string") return maskSecrets(value);
10984
- if (Array.isArray(value)) return value.map(maskLeaf);
10985
- if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [maskSecrets(k), maskLeaf(v)]));
10986
- return value;
10987
- };
11097
+ const maskLeaf = (value) => maskDeep(value, maskSecrets);
10988
11098
  const seenProtocols = /* @__PURE__ */ new Set();
10989
11099
  if (!Array.isArray(logging)) {
10990
11100
  if (onMalformed === "throw") throw new Error(`SNS topic ${logicalId}: DeliveryStatusLogging must be an array of {Protocol, ...} objects, got ${JSON.stringify(maskLeaf(logging))}`);
@@ -13955,10 +14065,11 @@ function idShape(segments) {
13955
14065
  * mis-names one value frequently mis-names its sibling too, and reporting them
13956
14066
  * one deploy at a time is the shape of remedy nobody finishes.
13957
14067
  */
13958
- function compositeIdSeparatorRefusal(resourceType, logicalId, segments) {
14068
+ function compositeIdSeparatorRefusal(resourceType, logicalId, segments, maskSecrets) {
13959
14069
  const offending = segments.filter((segment) => String(segment.value).includes("|"));
13960
14070
  if (offending.length === 0) return void 0;
13961
- return `${resourceType} ${logicalId}: ${offending.map((segment) => `${segment.name} '${String(segment.value)}'`).join(" and ")} contains '${"|"}', which cdkd uses as the separator in this type's physical id (${idShape(segments)}). Recording it would produce an id that decodes back to a DIFFERENT resource, so a later cdkd destroy / drift / update would target the wrong one — or silently skip it while the real resource stays alive. AWS accepts the name and CloudFormation manages such a resource fine; this is a cdkd limitation tracked in ${COMPOSITE_ID_ISSUE_URL}. Rename the resource so no segment contains '${"|"}', or manage it outside cdkd.`;
14071
+ const mask = maskerOrIdentity(maskSecrets);
14072
+ return `${resourceType} ${logicalId}: ${offending.map((segment) => `${segment.name} '${mask(String(segment.value))}'`).join(" and ")} contains '${"|"}', which cdkd uses as the separator in this type's physical id (${idShape(segments)}). Recording it would produce an id that decodes back to a DIFFERENT resource, so a later cdkd destroy / drift / update would target the wrong one — or silently skip it while the real resource stays alive. AWS accepts the name and CloudFormation manages such a resource fine; this is a cdkd limitation tracked in ${COMPOSITE_ID_ISSUE_URL}. Rename the resource so no segment contains '${"|"}', or manage it outside cdkd.`;
13962
14073
  }
13963
14074
  /**
13964
14075
  * Join the segments into a composite physicalId, REFUSING one that would be
@@ -13970,7 +14081,7 @@ function compositeIdSeparatorRefusal(resourceType, logicalId, segments) {
13970
14081
  * behavior.
13971
14082
  */
13972
14083
  function packCompositeId(resourceType, logicalId, segments, options) {
13973
- const refusal = compositeIdSeparatorRefusal(resourceType, logicalId, segments);
14084
+ const refusal = compositeIdSeparatorRefusal(resourceType, logicalId, segments, options?.maskSecrets);
13974
14085
  if (refusal !== void 0) if (options?.onRefusal) options.onRefusal(`${refusal} Continuing with the ambiguous id because the value comes from a cdkd state record, which no template edit can repair; the same value is REFUSED on a template-path create.`);
13975
14086
  else throw new ProvisioningError(refusal, resourceType, logicalId);
13976
14087
  return segments.map((segment) => String(segment.value)).join("|");
@@ -14163,7 +14274,10 @@ var LambdaEventInvokeConfigProvider = class {
14163
14274
  coerceNumber: true,
14164
14275
  ...replayWarn(this.logger, context)
14165
14276
  });
14166
- const physicalId = this.buildPhysicalId(logicalId, functionName, qualifier, context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
14277
+ const physicalId = this.buildPhysicalId(logicalId, functionName, qualifier, {
14278
+ maskSecrets: context?.maskSecrets,
14279
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
14280
+ });
14167
14281
  try {
14168
14282
  await this.lambdaClient.send(new PutFunctionEventInvokeConfigCommand(this.buildPutInput(properties)));
14169
14283
  this.logger.debug(`Successfully created Lambda EventInvokeConfig ${logicalId}: ${physicalId}`);
@@ -16333,10 +16447,7 @@ const deleteTableRetryDelays$1 = {};
16333
16447
  * leaf is stringified — and escaped — identically.
16334
16448
  */
16335
16449
  function maskLeafValue$1(value, maskSecrets) {
16336
- if (typeof value === "string") return maskSecrets(value);
16337
- if (Array.isArray(value)) return value.map((v) => maskLeafValue$1(v, maskSecrets));
16338
- if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [maskSecrets(k), maskLeafValue$1(v, maskSecrets)]));
16339
- return value;
16450
+ return maskDeep(value, maskSecrets);
16340
16451
  }
16341
16452
  var DynamoDBTableProvider = class {
16342
16453
  dynamoDBClient;
@@ -17245,7 +17356,7 @@ var DynamoDBTableProvider = class {
17245
17356
  const warn = (message) => this.logger.warn(maskSecrets(message));
17246
17357
  const coerced = coerceWarmThroughput(value);
17247
17358
  if (coerced === void 0) return void 0;
17248
- if (coerced.dropped.length > 0) warn(`${scope}: WarmThroughput member(s) ${coerced.dropped.join(", ")} in ${JSON.stringify(maskLeafValue$1(value, maskSecrets))} are not a number DynamoDB accepts, so they were dropped from the request, which leaves ${JSON.stringify(coerced.spec)}. Check for an unresolved intrinsic or a non-numeric value.`);
17359
+ if (coerced.dropped.length > 0) warn(`${scope}: WarmThroughput member(s) ${coerced.dropped.join(", ")} in ${JSON.stringify(maskLeafValue$1(value, maskSecrets))} are not a number DynamoDB accepts, so they were dropped from the request, which leaves ${JSON.stringify(maskLeafValue$1(coerced.spec, maskSecrets))}. Check for an unresolved intrinsic or a non-numeric value.`);
17249
17360
  return coerced.spec;
17250
17361
  }
17251
17362
  /**
@@ -17895,18 +18006,17 @@ const DRIFT_STRIPPED_INDEX_MEMBERS = [{
17895
18006
  * by value rather than by position, so the two layers compose.
17896
18007
  *
17897
18008
  * Walks rather than testing the top level, because a secret nested in an object
17898
- * or array leaf is stringified — and escaped — identically. The sibling
17899
- * `dynamodb-table-provider.ts` carries the same helper; it is duplicated rather
17900
- * than shared because these two files deliberately hold no common module today
17901
- * beyond the narrow rule modules (`dynamodb-warm-throughput.ts`,
17902
- * `dynamodb-index-busy-delete.ts`), each of which exists for a MEASURED
17903
- * divergence rather than for convenience.
18009
+ * or array leaf is stringified — and escaped — identically.
18010
+ *
18011
+ * Issue #2176 hoisted the walk into `../masked-retry-logger.js`. The previous
18012
+ * note here said the helper was "duplicated rather than shared because these two
18013
+ * files deliberately hold no common module today" — that rationale expired the
18014
+ * moment a shared LEAF module existed for exactly this, and it had already cost
18015
+ * something: this copy carried NO depth cap, so it was one of SIX copies that
18016
+ * had drifted apart on the one property that matters.
17904
18017
  */
17905
18018
  function maskLeafValue(value, maskSecrets) {
17906
- if (typeof value === "string") return maskSecrets(value);
17907
- if (Array.isArray(value)) return value.map((v) => maskLeafValue(v, maskSecrets));
17908
- if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [maskSecrets(k), maskLeafValue(v, maskSecrets)]));
17909
- return value;
18019
+ return maskDeep(value, maskSecrets);
17910
18020
  }
17911
18021
  var DynamoDBGlobalTableProvider = class {
17912
18022
  dynamoDBClient;
@@ -22786,11 +22896,11 @@ var SSMParameterProvider = class {
22786
22896
  * whole body is wrapped: an unexpected failure degrades to "no Arn recorded"
22787
22897
  * exactly as the fabricated-account arm does.
22788
22898
  */
22789
- async buildParameterArn(name) {
22899
+ async buildParameterArn(name, mask) {
22790
22900
  try {
22791
- return await this.buildParameterArnUnguarded(name);
22901
+ return await this.buildParameterArnUnguarded(name, mask);
22792
22902
  } catch (error) {
22793
- this.logger.warn(`Could not build the Arn attribute for SSM parameter ${name}: ${error instanceof Error ? error.message : String(error)}. The parameter itself is unaffected; the Arn is NOT recorded, so an Fn::GetAtt on it will fail until a later deploy records it.`);
22903
+ this.logger.warn(mask(`Could not build the Arn attribute for SSM parameter ${name}: ${error instanceof Error ? error.message : String(error)}. The parameter itself is unaffected; the Arn is NOT recorded, so an Fn::GetAtt on it will fail until a later deploy records it.`));
22794
22904
  return;
22795
22905
  }
22796
22906
  }
@@ -22810,10 +22920,10 @@ var SSMParameterProvider = class {
22810
22920
  * import RECORDED it, so the fix belongs at the import boundary — where
22811
22921
  * `import()` now refuses it — and not here. Do not re-add it.
22812
22922
  */
22813
- async buildParameterArnUnguarded(name) {
22923
+ async buildParameterArnUnguarded(name, mask) {
22814
22924
  const accountInfo = await getAccountInfo(await this.ssmClient.config.region());
22815
22925
  if (accountInfo.fabricated) {
22816
- this.logger.warn(`Cannot determine the AWS account (STS is unreachable, and the resolved account id is a placeholder), so the Arn attribute for SSM parameter ${name} would be fabricated and is NOT recorded. An Fn::GetAtt on it will fail until a later deploy resolves the account.`);
22926
+ this.logger.warn(mask(`Cannot determine the AWS account (STS is unreachable, and the resolved account id is a placeholder), so the Arn attribute for SSM parameter ${name} would be fabricated and is NOT recorded. An Fn::GetAtt on it will fail until a later deploy resolves the account.`));
22817
22927
  return;
22818
22928
  }
22819
22929
  const { partition } = derivePartitionAndUrlSuffix(accountInfo.region);
@@ -22823,8 +22933,11 @@ var SSMParameterProvider = class {
22823
22933
  /**
22824
22934
  * Create an SSM parameter
22825
22935
  */
22826
- async create(logicalId, resourceType, properties) {
22827
- this.logger.debug(`Creating SSM parameter ${logicalId}`);
22936
+ async create(logicalId, resourceType, properties, context) {
22937
+ const mask = maskerOrIdentity(context?.maskSecrets);
22938
+ const warn = (message) => this.logger.warn(mask(message));
22939
+ const debug = (message) => this.logger.debug(mask(message));
22940
+ debug(`Creating SSM parameter ${logicalId}`);
22828
22941
  const name = properties["Name"] || `/${generateResourceName(logicalId, {
22829
22942
  maxLength: 1023,
22830
22943
  allowedPattern: /[^a-zA-Z0-9-/_]/g
@@ -22855,14 +22968,14 @@ var SSMParameterProvider = class {
22855
22968
  } catch (innerError) {
22856
22969
  try {
22857
22970
  await this.ssmClient.send(new DeleteParameterCommand({ Name: name }));
22858
- this.logger.debug(`Cleaned up partially-created SSM parameter ${logicalId} (${name}) after wiring failure`);
22971
+ debug(`Cleaned up partially-created SSM parameter ${logicalId} (${name}) after wiring failure`);
22859
22972
  } catch (cleanupError) {
22860
- this.logger.warn(`Failed to clean up partially-created SSM parameter ${logicalId} (${name}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: aws ssm delete-parameter --name '${name}'`);
22973
+ warn(`Failed to clean up partially-created SSM parameter ${logicalId} (${name}): ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}. Manual deletion may be required before the next deploy: aws ssm delete-parameter --name '${name}'`);
22861
22974
  }
22862
22975
  throw innerError;
22863
22976
  }
22864
- this.logger.debug(`Successfully created SSM parameter ${logicalId}: ${name}`);
22865
- const arn = await this.buildParameterArn(name);
22977
+ debug(`Successfully created SSM parameter ${logicalId}: ${name}`);
22978
+ const arn = await this.buildParameterArn(name, mask);
22866
22979
  return {
22867
22980
  physicalId: name,
22868
22981
  attributes: {
@@ -22873,14 +22986,16 @@ var SSMParameterProvider = class {
22873
22986
  };
22874
22987
  } catch (error) {
22875
22988
  const cause = error instanceof Error ? error : void 0;
22876
- throw new ProvisioningError(`Failed to create SSM parameter ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
22989
+ throw new ProvisioningError(`Failed to create SSM parameter ${logicalId}: ${mask(error instanceof Error ? error.message : String(error))}`, resourceType, logicalId, void 0, cause);
22877
22990
  }
22878
22991
  }
22879
22992
  /**
22880
22993
  * Update an SSM parameter
22881
22994
  */
22882
- async update(logicalId, physicalId, resourceType, properties, previousProperties) {
22883
- this.logger.debug(`Updating SSM parameter ${logicalId}: ${physicalId}`);
22995
+ async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
22996
+ const mask = maskerOrIdentity(context?.maskSecrets);
22997
+ const debug = (message) => this.logger.debug(mask(message));
22998
+ debug(`Updating SSM parameter ${logicalId}: ${physicalId}`);
22884
22999
  const type = properties["Type"] || "String";
22885
23000
  const value = properties["Value"];
22886
23001
  if (!value) throw new ProvisioningError(`Value is required for SSM parameter ${logicalId}`, resourceType, logicalId, physicalId);
@@ -22912,10 +23027,10 @@ var SSMParameterProvider = class {
22912
23027
  ResourceId: physicalId,
22913
23028
  Tags: newTags
22914
23029
  }));
22915
- this.logger.debug(`Updated tags for SSM parameter ${physicalId}`);
23030
+ debug(`Updated tags for SSM parameter ${physicalId}`);
22916
23031
  }
22917
- this.logger.debug(`Successfully updated SSM parameter ${logicalId}`);
22918
- const arn = await this.buildParameterArn(physicalId);
23032
+ debug(`Successfully updated SSM parameter ${logicalId}`);
23033
+ const arn = await this.buildParameterArn(physicalId, mask);
22919
23034
  return {
22920
23035
  physicalId,
22921
23036
  wasReplaced: false,
@@ -22927,7 +23042,7 @@ var SSMParameterProvider = class {
22927
23042
  };
22928
23043
  } catch (error) {
22929
23044
  const cause = error instanceof Error ? error : void 0;
22930
- throw new ProvisioningError(`Failed to update SSM parameter ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
23045
+ throw new ProvisioningError(`Failed to update SSM parameter ${logicalId}: ${mask(error instanceof Error ? error.message : String(error))}`, resourceType, logicalId, physicalId, cause);
22931
23046
  }
22932
23047
  }
22933
23048
  /**
@@ -24402,10 +24517,10 @@ var EC2Provider = class {
24402
24517
  case "AWS::EC2::VPCGatewayAttachment": return this.createVpcGatewayAttachment(logicalId, resourceType, properties, context);
24403
24518
  case "AWS::EC2::NatGateway": return this.createNatGateway(logicalId, resourceType, properties);
24404
24519
  case "AWS::EC2::RouteTable": return this.createRouteTable(logicalId, resourceType, properties);
24405
- case "AWS::EC2::Route": return this.createRoute(logicalId, resourceType, properties, context?.replayingState === true ? (message) => this.logger.warn(message) : void 0);
24520
+ case "AWS::EC2::Route": return this.createRoute(logicalId, resourceType, properties, context?.replayingState === true ? (message) => this.logger.warn(message) : void 0, context?.maskSecrets);
24406
24521
  case "AWS::EC2::SubnetRouteTableAssociation": return this.createSubnetRouteTableAssociation(logicalId, resourceType, properties);
24407
24522
  case "AWS::EC2::SecurityGroup": return this.createSecurityGroup(logicalId, resourceType, properties);
24408
- case "AWS::EC2::SecurityGroupIngress": return this.createSecurityGroupIngress(logicalId, resourceType, properties, context?.replayingState === true ? (message) => this.logger.warn(message) : void 0);
24523
+ case "AWS::EC2::SecurityGroupIngress": return this.createSecurityGroupIngress(logicalId, resourceType, properties, context?.replayingState === true ? (message) => this.logger.warn(message) : void 0, context?.maskSecrets);
24409
24524
  case "AWS::EC2::Instance": return this.createInstance(logicalId, resourceType, properties, context);
24410
24525
  case "AWS::EC2::NetworkAcl": return this.createNetworkAcl(logicalId, resourceType, properties);
24411
24526
  case "AWS::EC2::NetworkAclEntry": return this.createNetworkAclEntry(logicalId, resourceType, properties, context);
@@ -24413,16 +24528,16 @@ var EC2Provider = class {
24413
24528
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
24414
24529
  }
24415
24530
  }
24416
- async update(logicalId, physicalId, resourceType, properties, previousProperties) {
24531
+ async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
24417
24532
  try {
24418
- return await this.applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties);
24533
+ return await this.applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, context);
24419
24534
  } catch (error) {
24420
24535
  if (error instanceof CdkdError) throw error;
24421
24536
  const cause = error instanceof Error ? error : void 0;
24422
24537
  throw new ProvisioningError(`Failed to update EC2 resource ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
24423
24538
  }
24424
24539
  }
24425
- async applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties) {
24540
+ async applyUpdate(logicalId, physicalId, resourceType, properties, previousProperties, context) {
24426
24541
  switch (resourceType) {
24427
24542
  case "AWS::EC2::VPC": return this.updateVpc(logicalId, physicalId, resourceType, properties, previousProperties);
24428
24543
  case "AWS::EC2::Subnet": return this.updateSubnet(logicalId, physicalId, resourceType, properties, previousProperties);
@@ -24431,10 +24546,10 @@ var EC2Provider = class {
24431
24546
  case "AWS::EC2::VPCGatewayAttachment": return this.updateVpcGatewayAttachment(logicalId, physicalId);
24432
24547
  case "AWS::EC2::NatGateway": return this.updateNatGateway(logicalId, physicalId);
24433
24548
  case "AWS::EC2::RouteTable": return this.updateRouteTable(logicalId, physicalId);
24434
- case "AWS::EC2::Route": return this.updateRoute(logicalId, physicalId, resourceType, properties, previousProperties);
24549
+ case "AWS::EC2::Route": return this.updateRoute(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
24435
24550
  case "AWS::EC2::SubnetRouteTableAssociation": return this.updateSubnetRouteTableAssociation(logicalId, physicalId);
24436
24551
  case "AWS::EC2::SecurityGroup": return this.updateSecurityGroup(logicalId, physicalId, resourceType, properties, previousProperties);
24437
- case "AWS::EC2::SecurityGroupIngress": return this.updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties);
24552
+ case "AWS::EC2::SecurityGroupIngress": return this.updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties, context?.maskSecrets);
24438
24553
  case "AWS::EC2::Instance": return this.updateInstance(logicalId, physicalId, resourceType, properties, previousProperties);
24439
24554
  case "AWS::EC2::NetworkAcl":
24440
24555
  case "AWS::EC2::NetworkAclEntry":
@@ -24898,7 +25013,10 @@ var EC2Provider = class {
24898
25013
  throw new ProvisioningError(`Failed to create EIP ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
24899
25014
  }
24900
25015
  return {
24901
- physicalId: this.eipPhysicalId(logicalId, publicIp, allocationId, context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0),
25016
+ physicalId: this.eipPhysicalId(logicalId, publicIp, allocationId, {
25017
+ maskSecrets: context?.maskSecrets,
25018
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
25019
+ }),
24902
25020
  attributes: {
24903
25021
  AllocationId: allocationId,
24904
25022
  PublicIp: publicIp
@@ -24980,7 +25098,10 @@ var EC2Provider = class {
24980
25098
  }, {
24981
25099
  name: "vpcId",
24982
25100
  value: vpcId
24983
- }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
25101
+ }], {
25102
+ maskSecrets: context?.maskSecrets,
25103
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
25104
+ });
24984
25105
  try {
24985
25106
  await this.ec2Client.send(new AttachInternetGatewayCommand({
24986
25107
  VpcId: vpcId,
@@ -25133,7 +25254,7 @@ var EC2Provider = class {
25133
25254
  throw new ProvisioningError(`Failed to delete RouteTable ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
25134
25255
  }
25135
25256
  }
25136
- async createRoute(logicalId, resourceType, properties, onMultipleDestinations) {
25257
+ async createRoute(logicalId, resourceType, properties, onMultipleDestinations, maskSecrets) {
25137
25258
  this.logger.debug(`Creating Route ${logicalId}`);
25138
25259
  const routeTableId = properties["RouteTableId"];
25139
25260
  const destinationCidrBlock = properties["DestinationCidrBlock"];
@@ -25157,7 +25278,10 @@ var EC2Provider = class {
25157
25278
  }, {
25158
25279
  name: "destination",
25159
25280
  value: destination
25160
- }], onMultipleDestinations ? { onRefusal: onMultipleDestinations } : void 0);
25281
+ }], {
25282
+ maskSecrets,
25283
+ ...onMultipleDestinations && { onRefusal: onMultipleDestinations }
25284
+ });
25161
25285
  try {
25162
25286
  await this.ec2Client.send(new CreateRouteCommand({
25163
25287
  RouteTableId: routeTableId,
@@ -25185,7 +25309,7 @@ var EC2Provider = class {
25185
25309
  throw new ProvisioningError(`Failed to create Route ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
25186
25310
  }
25187
25311
  }
25188
- async updateRoute(logicalId, physicalId, resourceType, properties, previousProperties) {
25312
+ async updateRoute(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
25189
25313
  this.logger.debug(`Updating Route ${logicalId}: ${physicalId}`);
25190
25314
  if (JSON.stringify(properties) === JSON.stringify(previousProperties)) return {
25191
25315
  physicalId,
@@ -25193,7 +25317,7 @@ var EC2Provider = class {
25193
25317
  };
25194
25318
  try {
25195
25319
  await this.deleteRoute(logicalId, physicalId, resourceType);
25196
- const createResult = await this.createRoute(logicalId, resourceType, properties, (message) => this.logger.warn(message));
25320
+ const createResult = await this.createRoute(logicalId, resourceType, properties, (message) => this.logger.warn(message), maskSecrets);
25197
25321
  return {
25198
25322
  physicalId: createResult.physicalId,
25199
25323
  wasReplaced: true,
@@ -25476,7 +25600,7 @@ var EC2Provider = class {
25476
25600
  * and on a rollback replay the value comes from a STATE record, which no
25477
25601
  * template edit can fix. Absent (the plain CREATE dispatch) it throws.
25478
25602
  */
25479
- async createSecurityGroupIngress(logicalId, resourceType, properties, onUnusableProtocol) {
25603
+ async createSecurityGroupIngress(logicalId, resourceType, properties, onUnusableProtocol, maskSecrets) {
25480
25604
  this.logger.debug(`Creating SecurityGroupIngress ${logicalId}`);
25481
25605
  const groupId = properties["GroupId"];
25482
25606
  if (!groupId) throw new ProvisioningError(`GroupId is required for SecurityGroupIngress ${logicalId}`, resourceType, logicalId);
@@ -25501,7 +25625,10 @@ var EC2Provider = class {
25501
25625
  name: "toPort",
25502
25626
  value: toPort ?? "-1"
25503
25627
  }
25504
- ], onUnusableProtocol ? { onRefusal: onUnusableProtocol } : void 0);
25628
+ ], {
25629
+ maskSecrets,
25630
+ ...onUnusableProtocol && { onRefusal: onUnusableProtocol }
25631
+ });
25505
25632
  try {
25506
25633
  const ruleId = singleSecurityGroupRuleId((await this.ec2Client.send(new AuthorizeSecurityGroupIngressCommand({
25507
25634
  GroupId: groupId,
@@ -25597,7 +25724,7 @@ var EC2Provider = class {
25597
25724
  return;
25598
25725
  }
25599
25726
  }
25600
- async updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties) {
25727
+ async updateSecurityGroupIngress(logicalId, physicalId, resourceType, properties, previousProperties, maskSecrets) {
25601
25728
  this.logger.debug(`Updating SecurityGroupIngress ${logicalId}: ${physicalId}`);
25602
25729
  if (JSON.stringify(properties) === JSON.stringify(previousProperties)) return {
25603
25730
  physicalId,
@@ -25605,7 +25732,7 @@ var EC2Provider = class {
25605
25732
  };
25606
25733
  try {
25607
25734
  await this.deleteSecurityGroupIngress(logicalId, physicalId, resourceType, previousProperties);
25608
- const createResult = await this.createSecurityGroupIngress(logicalId, resourceType, properties, (message) => this.logger.warn(message));
25735
+ const createResult = await this.createSecurityGroupIngress(logicalId, resourceType, properties, (message) => this.logger.warn(message), maskSecrets);
25609
25736
  return {
25610
25737
  physicalId: createResult.physicalId,
25611
25738
  wasReplaced: true,
@@ -26289,7 +26416,10 @@ var EC2Provider = class {
26289
26416
  name: "egress",
26290
26417
  value: egress
26291
26418
  }
26292
- ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
26419
+ ], {
26420
+ maskSecrets: context?.maskSecrets,
26421
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
26422
+ });
26293
26423
  try {
26294
26424
  const cidrBlock = properties["CidrBlock"];
26295
26425
  const ipv6CidrBlock = properties["Ipv6CidrBlock"];
@@ -28338,7 +28468,10 @@ var ApiGatewayProvider = class ApiGatewayProvider {
28338
28468
  name: "httpMethod",
28339
28469
  value: httpMethod
28340
28470
  }
28341
- ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
28471
+ ], {
28472
+ maskSecrets: context?.maskSecrets,
28473
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
28474
+ });
28342
28475
  try {
28343
28476
  await this.apiGatewayClient.send(new PutMethodCommand({
28344
28477
  restApiId,
@@ -30382,12 +30515,7 @@ var ApiGatewayV2Provider = class {
30382
30515
  */
30383
30516
  toSdkResponseParameters(value, maskSecrets = (text) => text) {
30384
30517
  const warn = (message) => this.logger.warn(maskSecrets(message));
30385
- const maskLeaf = (value) => {
30386
- if (typeof value === "string") return maskSecrets(value);
30387
- if (Array.isArray(value)) return value.map(maskLeaf);
30388
- if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [maskSecrets(k), maskLeaf(v)]));
30389
- return value;
30390
- };
30518
+ const maskLeaf = (value) => maskDeep(value, maskSecrets);
30391
30519
  if (value == null) return void 0;
30392
30520
  if (typeof value !== "object" || Array.isArray(value)) return value;
30393
30521
  const out = {};
@@ -34956,44 +35084,6 @@ var ECSProvider = class {
34956
35084
  }
34957
35085
  };
34958
35086
 
34959
- //#endregion
34960
- //#region src/provisioning/masked-retry-logger.ts
34961
- /**
34962
- * The masker a caller supplied, or the identity function when it supplied
34963
- * none.
34964
- *
34965
- * ABSENT MEANS UNMASKED, and that is the back-compatible default the contract
34966
- * mandates — `create()` / `update()` are also reached from the import path,
34967
- * from `cdkd drift --revert`, and from tests, and a provider must not care
34968
- * which caller it got. Centralised here so no call site re-spells the `??`
34969
- * and accidentally makes the capability required.
34970
- */
34971
- function maskerOrIdentity(maskSecrets) {
34972
- return maskSecrets ?? ((text) => text);
34973
- }
34974
- /**
34975
- * A {@link RetryLogger} whose every line is routed through `maskSecrets`.
34976
- *
34977
- * `warn` is ALWAYS provided, never omitted. It is optional on `RetryLogger`,
34978
- * and dropping it would silence the give-up summary on the calling path only —
34979
- * trading a disclosure for the reporting hole issue #2018 closed. It goes
34980
- * through the SAME mask as `debug` precisely because it is the line that
34981
- * survives a run without `--verbose`; forwarding it unmasked would defeat the
34982
- * fence at a HIGHER log level than the one the fence was written for.
34983
- *
34984
- * `logger` is typed structurally rather than as the concrete `Logger` so this
34985
- * module stays a leaf and a test can pass two spies. That matches the
34986
- * precedent already in the tree (`buildMfaConfigRequest` in
34987
- * `cognito-provider.ts` takes an injected `logger?: { warn }`).
34988
- */
34989
- function createMaskedRetryLogger(logger, maskSecrets) {
34990
- const mask = maskerOrIdentity(maskSecrets);
34991
- return {
34992
- debug: (message) => logger.debug(mask(message)),
34993
- warn: (message) => logger.warn(mask(message))
34994
- };
34995
- }
34996
-
34997
35087
  //#endregion
34998
35088
  //#region src/provisioning/providers/elbv2-provider.ts
34999
35089
  /**
@@ -35003,41 +35093,6 @@ function createMaskedRetryLogger(logger, maskSecrets) {
35003
35093
  */
35004
35094
  const capacityReservationDelays = { sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)) };
35005
35095
  /**
35006
- * Depth cap for {@link maskDeep}. Bounds a self-referential bag rather than
35007
- * hanging; `JSON.stringify` would throw on one anyway, and a throw is the
35008
- * better failure of the two.
35009
- */
35010
- const MASK_WALK_MAX_DEPTH$1 = 8;
35011
- /**
35012
- * Mask every string LEAF and KEY of an arbitrary value, returning a structure
35013
- * safe to `JSON.stringify` into a log line (issue #2050).
35014
- *
35015
- * The ordering is the whole point. `maskSecretsInText` matches by literal
35016
- * occurrence, and `JSON.stringify` escapes `"`, `\` and newlines — so a Secrets
35017
- * Manager JSON document (the commonest real secret shape) does not occur in the
35018
- * stringified text and survives a mask applied afterwards. Masking the leaves
35019
- * first also hands the masker each RAW value, which reaches
35020
- * `maskSecretsInText`'s whole-value arm at any length instead of only the
35021
- * substring arm's 4-character floor. See the "three gaps" note in
35022
- * `src/deployment/secret-redaction.ts`.
35023
- *
35024
- * Keys are masked as well as values: `JSON.stringify` renders them into the
35025
- * same line, so a resolved secret used as a map key would otherwise escape.
35026
- *
35027
- * `cognito-provider.ts` carries an equivalent private walk for its own
35028
- * stringified warnings. Two copies is the current cost of keeping this a leaf;
35029
- * a THIRD site is the point at which this should move into
35030
- * `../masked-retry-logger.ts` (already the shared provider-side masking module)
35031
- * and all three converge on it.
35032
- */
35033
- function maskDeep(value, mask, depth = 0) {
35034
- if (typeof value === "string") return mask(value);
35035
- if (depth >= MASK_WALK_MAX_DEPTH$1) return value;
35036
- if (Array.isArray(value)) return value.map((entry) => maskDeep(entry, mask, depth + 1));
35037
- if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [mask(k), maskDeep(v, mask, depth + 1)]));
35038
- return value;
35039
- }
35040
- /**
35041
35096
  * Documented AWS defaults for TargetGroup attributes, used to reset a
35042
35097
  * removed `TargetGroupAttributes` entry. Unlike ModifyLoadBalancerAttributes
35043
35098
  * / ModifyListenerAttributes, ModifyTargetGroupAttributes REJECTS an empty
@@ -39581,10 +39636,10 @@ var Route53Provider = class {
39581
39636
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId);
39582
39637
  }
39583
39638
  }
39584
- async update(logicalId, physicalId, resourceType, properties, previousProperties) {
39639
+ async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
39585
39640
  switch (resourceType) {
39586
39641
  case "AWS::Route53::HostedZone": return this.updateHostedZone(logicalId, physicalId, resourceType, properties, previousProperties);
39587
- case "AWS::Route53::RecordSet": return this.updateRecordSet(logicalId, physicalId, resourceType, properties);
39642
+ case "AWS::Route53::RecordSet": return this.updateRecordSet(logicalId, physicalId, resourceType, properties, context);
39588
39643
  default: throw new ProvisioningError(`Unsupported resource type: ${resourceType}`, resourceType, logicalId, physicalId);
39589
39644
  }
39590
39645
  }
@@ -39902,7 +39957,10 @@ var Route53Provider = class {
39902
39957
  name: "recordType",
39903
39958
  value: recordType
39904
39959
  }
39905
- ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
39960
+ ], {
39961
+ maskSecrets: context?.maskSecrets,
39962
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
39963
+ });
39906
39964
  try {
39907
39965
  const resourceRecordSet = this.buildResourceRecordSet(properties);
39908
39966
  const comment = properties["Comment"];
@@ -39927,7 +39985,7 @@ var Route53Provider = class {
39927
39985
  throw new ProvisioningError(`Failed to create record set ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, cause);
39928
39986
  }
39929
39987
  }
39930
- async updateRecordSet(logicalId, physicalId, resourceType, properties) {
39988
+ async updateRecordSet(logicalId, physicalId, resourceType, properties, context) {
39931
39989
  this.logger.debug(`Updating Route 53 record set ${logicalId}: ${physicalId}`);
39932
39990
  const hostedZoneId = await this.resolveHostedZoneId(properties, logicalId, resourceType, physicalId);
39933
39991
  const recordName = properties["Name"];
@@ -39945,7 +40003,10 @@ var Route53Provider = class {
39945
40003
  name: "recordType",
39946
40004
  value: recordType
39947
40005
  }
39948
- ], { onRefusal: (message) => this.logger.warn(message) });
40006
+ ], {
40007
+ onRefusal: (message) => this.logger.warn(message),
40008
+ maskSecrets: context?.maskSecrets
40009
+ });
39949
40010
  try {
39950
40011
  const resourceRecordSet = this.buildResourceRecordSet(properties);
39951
40012
  const comment = properties["Comment"];
@@ -40978,15 +41039,6 @@ function readDeclaredMfaConfiguration(raw, options) {
40978
41039
  return value.trim() === "" ? "" : value;
40979
41040
  }
40980
41041
  /**
40981
- * Depth cap for the pre-stringify secret walk in {@link buildMfaConfigRequest}.
40982
- *
40983
- * Only a guard against a self-referential bag, not a real bound: a resolved
40984
- * `EnabledMfas` is a list of factor-name scalars, so the walk never reaches
40985
- * depth 2 on any shape this warning exists to describe. Deliberately generous
40986
- * so no plausible mis-shape is left unmasked by it.
40987
- */
40988
- const MASK_WALK_MAX_DEPTH = 8;
40989
- /**
40990
41042
  * Read `EnabledMfas` as the pair (recognized list, "the template asked for a
40991
41043
  * factor at all").
40992
41044
  *
@@ -41120,15 +41172,9 @@ function buildMfaConfigRequest(physicalId, properties, logger, declaredMfaConfig
41120
41172
  request.MfaConfiguration = declaredMfaConfiguration || (enablesMfaFactor ? "OPTIONAL" : "OFF");
41121
41173
  if (enabledMfasBlank) warn(`UserPool ${physicalId}: EnabledMfas is an empty string, which declares no MFA factor and is treated as absent — a mangled Fn::Join or an empty String parameter collapses to this shape. Nothing is enabled from it and MfaConfiguration is ${request.MfaConfiguration}; declare the factor names as a list (e.g. ["${MFA_FACTOR_SOFTWARE_TOKEN}"]) if a factor was intended.`);
41122
41174
  const anyFactorBlock = hasAnyMfaFactorBlock(request);
41123
- const maskDeep = (v, depth = 0) => {
41124
- if (typeof v === "string") return maskSecrets(v);
41125
- if (depth >= MASK_WALK_MAX_DEPTH) return v;
41126
- if (Array.isArray(v)) return v.map((e) => maskDeep(e, depth + 1));
41127
- if (v !== null && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [maskSecrets(k), maskDeep(x, depth + 1)]));
41128
- return v;
41129
- };
41130
- const dropped = unrecognizedFactors.map((f) => JSON.stringify(maskDeep(f)) ?? String(f));
41131
- if (enabledMfasMalformed) dropped.push(`${JSON.stringify(maskDeep(properties["EnabledMfas"]))} (not a list)`);
41175
+ const maskLeaves = (v) => maskDeep(v, maskSecrets);
41176
+ const dropped = unrecognizedFactors.map((f) => JSON.stringify(maskLeaves(f)) ?? String(f));
41177
+ if (enabledMfasMalformed) dropped.push(`${JSON.stringify(maskLeaves(properties["EnabledMfas"]))} (not a list)`);
41132
41178
  if (dropped.length > 0) {
41133
41179
  const consequence = request.MfaConfiguration === "OFF" ? `MfaConfiguration is OFF, so this pool deploys with MFA DISABLED` : anyFactorBlock ? `MfaConfiguration is ${request.MfaConfiguration} and another factor block IS sent, so these entries are silently ignored rather than failing the call on their own` : `MfaConfiguration is ${request.MfaConfiguration} with no factor enabled, so AWS rejects this call rather than deploying the pool with MFA disabled`;
41134
41180
  warn(`UserPool ${physicalId}: EnabledMfas entries ${dropped.join(", ")} do not map to an MFA factor block (known: ${MFA_FACTOR_SMS}, ${MFA_FACTOR_SOFTWARE_TOKEN}, ${MFA_FACTOR_EMAIL_OTP}); no factor is enabled from them. ${consequence}.`);
@@ -44886,7 +44932,10 @@ var AppSyncProvider = class {
44886
44932
  }, {
44887
44933
  name: "name",
44888
44934
  value: name
44889
- }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
44935
+ }], {
44936
+ maskSecrets: context?.maskSecrets,
44937
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
44938
+ });
44890
44939
  let reportedArn;
44891
44940
  try {
44892
44941
  const input = {
@@ -44952,7 +45001,10 @@ var AppSyncProvider = class {
44952
45001
  name: "fieldName",
44953
45002
  value: fieldName
44954
45003
  }
44955
- ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
45004
+ ], {
45005
+ maskSecrets: context?.maskSecrets,
45006
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
45007
+ });
44956
45008
  let reportedArn;
44957
45009
  try {
44958
45010
  const input = {
@@ -45019,7 +45071,10 @@ var AppSyncProvider = class {
45019
45071
  }, {
45020
45072
  name: "apiKeyId",
45021
45073
  value: apiKeyId
45022
- }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
45074
+ }], {
45075
+ maskSecrets: context?.maskSecrets,
45076
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
45077
+ });
45023
45078
  const apiKeyArn = await this.buildAppSyncArnOrWarn(`apis/${apiId}/apikey/${apiKeyId}`, logicalId, physicalId);
45024
45079
  return {
45025
45080
  physicalId,
@@ -45705,7 +45760,10 @@ var GlueProvider = class {
45705
45760
  }, {
45706
45761
  name: "tableName",
45707
45762
  value: tableName
45708
- }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
45763
+ }], {
45764
+ maskSecrets: context?.maskSecrets,
45765
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
45766
+ });
45709
45767
  try {
45710
45768
  await this.getClient().send(new CreateTableCommand$1({
45711
45769
  CatalogId: catalogId,
@@ -57185,7 +57243,10 @@ var S3TablesProvider = class {
57185
57243
  }, {
57186
57244
  name: "namespace",
57187
57245
  value: namespaceName
57188
- }], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
57246
+ }], {
57247
+ maskSecrets: context?.maskSecrets,
57248
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
57249
+ });
57189
57250
  try {
57190
57251
  await this.getClient().send(new CreateNamespaceCommand({
57191
57252
  tableBucketARN,
@@ -57245,7 +57306,10 @@ var S3TablesProvider = class {
57245
57306
  name: "name",
57246
57307
  value: name
57247
57308
  }
57248
- ], context?.replayingState === true ? { onRefusal: (message) => this.logger.warn(message) } : void 0);
57309
+ ], {
57310
+ maskSecrets: context?.maskSecrets,
57311
+ ...context?.replayingState === true && { onRefusal: (message) => this.logger.warn(message) }
57312
+ });
57249
57313
  try {
57250
57314
  const response = await this.getClient().send(new CreateTableCommand$2({
57251
57315
  tableBucketARN,
@@ -58883,7 +58947,11 @@ async function runDestroyForStack(stackName, state, ctx) {
58883
58947
  }
58884
58948
  await saveChain;
58885
58949
  logger.debug("Releasing lock...");
58886
- await ctx.lockManager.releaseLock(stackName, regionForState);
58950
+ try {
58951
+ await ctx.lockManager.releaseLock(stackName, regionForState);
58952
+ } catch (releaseErr) {
58953
+ logger.warn(`Failed to release lock for stack '${stackName}': ${releaseErr instanceof Error ? releaseErr.message : String(releaseErr)}`);
58954
+ }
58887
58955
  } finally {
58888
58956
  process.removeListener("SIGINT", sigintHandler);
58889
58957
  restoreBaseRegionAndClients();
@@ -86555,7 +86623,7 @@ function createMigrateCommand() {
86555
86623
  */
86556
86624
  function buildProgram() {
86557
86625
  const program = new Command();
86558
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.40");
86626
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.42");
86559
86627
  program.hook("preAction", (_thisCommand, actionCommand) => {
86560
86628
  const { profile } = actionCommand.optsWithGlobals();
86561
86629
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;