@go-to-k/cdkd 0.290.8 → 0.290.9
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/{asg-provider-B4m3magB.js → asg-provider-BTsQjH_E.js} +2 -2
- package/dist/{asg-provider-B4m3magB.js.map → asg-provider-BTsQjH_E.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-Bq0TJ9iN.js → deploy-engine-B3yP0pHa.js} +3 -3
- package/dist/{deploy-engine-Bq0TJ9iN.js.map → deploy-engine-B3yP0pHa.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-C7UAuWDp.js → program-BslZXdJR.js} +283 -20
- package/dist/{program-C7UAuWDp.js.map → program-BslZXdJR.js.map} +1 -1
- package/dist/{version-CFdjI1Tq.js → version-wXpog0Ci.js} +2 -2
- package/dist/{version-CFdjI1Tq.js.map → version-wXpog0Ci.js.map} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $r as setAwsClients, Cr as Synthesizer, Dn as LockManager, Jr as clearBucketRegionCache, Kr as AssemblyReader, On as S3StateBackend, Qr as resetAwsClients, Si as normalizeAwsError, Un as AssetPublisher, Xr as AwsClients, Yr as resolveBucketRegion, Zr as getAwsClients, bi as formatError, cn as DagBuilder, ei as AssetError, gt as CloudControlProvider, ii as DependencyError, ln as TemplateParser, ni as ConfigError, nn as DiffCalculator, ot as ProviderRegistry, pi as ProvisioningError, r as DeployEngine, rt as IAMRoleProvider, ti as CdkdError, ui as LockError, vi as StateError, xi as isCdkdError, yi as SynthesisError } from "./deploy-engine-
|
|
1
|
+
import { $r as setAwsClients, Cr as Synthesizer, Dn as LockManager, Jr as clearBucketRegionCache, Kr as AssemblyReader, On as S3StateBackend, Qr as resetAwsClients, Si as normalizeAwsError, Un as AssetPublisher, Xr as AwsClients, Yr as resolveBucketRegion, Zr as getAwsClients, bi as formatError, cn as DagBuilder, ei as AssetError, gt as CloudControlProvider, ii as DependencyError, ln as TemplateParser, ni as ConfigError, nn as DiffCalculator, ot as ProviderRegistry, pi as ProvisioningError, r as DeployEngine, rt as IAMRoleProvider, ti as CdkdError, ui as LockError, vi as StateError, xi as isCdkdError, yi as SynthesisError } from "./deploy-engine-B3yP0pHa.js";
|
|
2
2
|
import { a as setLogger, n as getLogger, t as ConsoleLogger } from "./logger-H8n5Qjhq.js";
|
|
3
3
|
|
|
4
4
|
export { AssemblyReader, AssetError, AssetPublisher, AwsClients, CdkdError, CloudControlProvider, ConfigError, ConsoleLogger, DagBuilder, DependencyError, DeployEngine, DiffCalculator, IAMRoleProvider, LockError, LockManager, ProviderRegistry, ProvisioningError, S3StateBackend, StateError, SynthesisError, Synthesizer, TemplateParser, clearBucketRegionCache, formatError, getAwsClients, getLogger, isCdkdError, normalizeAwsError, resetAwsClients, resolveBucketRegion, setAwsClients, setLogger };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as secretBearing, $n as ensureAssetStorage, $r as setAwsClients, $t as s3BucketRegionalDomainName, A as extractDeploymentEventError, Ai as retryClassificationText, An as buildForceUnlockCommand, Ar as resolveSkipPrefix, At as parameterTypeMayLoseSecretIdentity, B as toCfnInteger, Bn as orphansAfterRollback, Br as uploadCfnTemplate, Bt as configIntegerRefusal, C as buildFinalSnapshotIdentifier, Ci as withErrorHandling, Cn as recordMaskOnlyValue, Cr as Synthesizer, Ct as IntrinsicFunctionResolver, D as refusesFinalSnapshot, Di as isTransientServerError, Dn as LockManager, Dr as resolveApp, Dt as getAccountInfo, E as isFinalSnapshotError, Ei as isThrottlingError, En as scrubResourceRecord, Er as getLegacyStateBucketName, Et as coerceParameterTypedValue, F as isStatefulRecreateTargetSync, Fn as CUSTOM_RESOURCE_RESPONSE_PREFIX, Fr as warnDeprecatedNoPrefixCliFlag, Ft as resolveExplicitPhysicalId, G as gray, Gn as WorkGraph, Gr as derivePartitionAndUrlSuffix, Gt as requireConfigObject, H as formatResourceLine, Hn as shouldRetainResource, Hr as expectedOwnerParam, Ht as readConfigString, I as renderStatefulReason, In as DEFAULT_STATE_PREFIX, Ir as CFN_TEMPLATE_BODY_LIMIT, It as assertRegionMatch, J as yellow, Jn as loadPublishableAssetManifest, Jt as producerRegionsFromState, K as green, Kn as buildAssetRedirectMap, Kr as AssemblyReader, Kt as requireConfigString, L as WARM_THROUGHPUT_MEMBERS, Ln as exportNamesCarriedFrom, Lr as CFN_TEMPLATE_URL_LIMIT, Lt as coerceCfnBoolean, M as planOrphanAdoption, Mn as forceQuitRecoveryClause, Mr as resolveStateBucketWithDefaultAndSource, Mt as isSettledInstanceState, N as MULTI_REGION_RECREATE_BLOCKED_TYPES, Nn as formatLockExpiry, Nr as resolveUseCdkBootstrapAssets, Nt as WAFv2WebACLProvider, O as unsupportedFinalSnapshotError, Oi as markNonRetryable, On as S3StateBackend, Or as resolveAutoAssetStorage, Ot as isImpossibleEmptyStoredAttribute, P as isStatefulRecreateTargetForReplace, Pn as shellQuote, Pr as stateBucketExistenceConfirmed, Pt as normalizeAwsTagsToCfn, Q as isExportAliasCollision, Qn as assertAssetBucketRegion, Qr as resetAwsClients, Qt as s3BucketDualStackDomainName, R as coerceWarmThroughput$1, Rn as importableOutputKeys, Rr as MIGRATE_TMP_PREFIX, Rt as coerceCfnInteger, S as PRE_DELETE_SNAPSHOT_TYPES, Si as normalizeAwsError, Sn as maskSecretsInText, Sr as getDockerImageBySourceHash, St as isTerminationProtectionPropagationError, T as createPreDeleteFinalSnapshot, Ti as isRetryableTransientError, Tn as redactSecretsForState, Tr as getDefaultStateBucketName, Tt as cfnRefValueFromPhysicalId, U as bold, Un as AssetPublisher, Ur as PARTITION_TABLE, Ut as replayWarn, V as toFiniteNumber, Vn as orphansCarriedFrom, Vr as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Vt as configStringRefusal, W as cyan, Wn as stringifyValue, Wr as canonicalizeRegion, Wt as requireConfigArray, X as collectPublishedOutputNames, Xn as AssetModeResolver, Xr as AwsClients, Xt as s3BucketArn, Y as collectDeclaredOutputNames, Yn as rewriteTemplateAssetReferences, Yr as resolveBucketRegion, Yt as withSharedDrainBudget, Z as exportAliasCollisionScrubWarning, Zn as BOOTSTRAP_MARKER_PREFIX, Zr as getAwsClients, Zt as s3BucketDomainName, _ as isSecretBearingReferenceString, _i as StackTerminationProtectionError, _n as errorCauseChain, _r as runDockerForeground, _t as slowCcOperationTimeoutMs, a as DeploymentEventsStore, ai as DeployCancelledError, an as findSilentDropProperties, ar as validateContainerRepoName, at as clearOnUpdateRemoval, b as bindingSkippedOutputs, bn as markSameGenerationBag, br as stripControlChars, bt as deleteSkipReason, c as replayFailedOperations, ci as LocalInvokeBuildError$1, cn as DagBuilder, cr as buildDockerImage, ct as createMaskedRetryLogger, d as updatePartialReason, di as NestedStackChildDirectDestroyError, dn as STATE_SOURCED_CROSS_GENERATION_RULES, dr as describeDockerFailure, dt as beginCommandInterruptScope, en as s3BucketWebsiteUrl, er as getBootstrapMarkerKey, et as secretBearingStateKeyWarning, f as withResourceDeadline, fi as PartialFailureError, fn as STATE_SOURCED_READBACK_RULES, fr as dockerSpawnEnvWithSensitive, ft as endCommandInterruptScope, g as bagHoldsSecretExpression, gi as StackHasActiveImportsError, gn as dynamicReferenceTokens, gr as redactDockerArgvValues, gt as CloudControlProvider, h as computeImplicitDeleteEdges, hi as ResourceUpdateNotSupportedError, hn as createSecretMasker, hr as partitionSensitiveEnv, ht as startInterruptWatch, i as DeploymentEventsReader, in as findActionableSilentDrops, ir as validateAssetBucketName, it as collectInlinePolicyNamesManagedBySiblings, j as makeSiblingClaimReader, jn as buildLockContentionMessage, jr as resolveStateBucketWithDefault, jt as refStateLookupFromResource, k as makeCanonicalizePropertiesFn, ki as markRedactedCause, kn as rebuildClientForBucketRegion, kr as resolveCaptureObservedState, kt as isUnboundTemplateParameter, l as replayRollback, li as LocalStartServiceError, ln as TemplateParser, lr as describeDockerCapturedOutput, lt as maskDeep, m as IMPLICIT_DELETE_DEPENDENCIES, mi as ResourceTimeoutError, mn as carriesSecretMask, mr as getDockerCmd, mt as isInterruptedWaitError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as DiffCalculator, nr as parseBootstrapMarker, nt as getCurrentResourceSecrets, o as planFailedOps, oi as DynamicReferenceRegionAmbiguousError, on as describeTypeWithThrottleRetry, or as buildDenyExternalAccessPolicy, ot as ProviderRegistry, p as maskingRetryLogger, pi as ProvisioningError, pn as TEMPLATE_SOURCED_RULES, pr as formatDockerLoginError, pt as interruptWatchListenerCount, q as red, qn as createAssetRedirectResolver, qr as processStackMessages, qt as classifyReplaySecretRegion, r as DeployEngine, ri as CrossAccountSecretRefusalError, rn as INTRINSIC_KEYS, rr as readBootstrapMarkerBody, rt as IAMRoleProvider, s as planRollback, si as IntrinsicResolutionRefusalError, sn as withRetry, sr as describeAwsFailure, st as wouldReturnToSdkProvider, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as CdkdError, tn as applyRoleArnIfSet, tr as isCrossRegionRedirect, tt as secretSafeKeyDisplay, u as updatePartialMessage, un as STATE_SOURCED_BASELINE_RULES, ur as describeDockerExecFailure, ut as maskerOrIdentity, v as keptWholeReasonText, vn as identityKeyFor, vr as runDockerStreaming, vt as UNSPECIFIED_SKIP_REASON, w as ccRoutedFinalSnapshotError, wi as isMarkedNonRetryable, wn as recoverMaskedOutput, wr as synthesisStatusMessage, wt as carriesDynamicReference, x as ATOMIC_FINAL_SNAPSHOT_TYPES, xn as maskSecretsInError, xr as AssetManifestLoader, xt as disableInstanceApiTermination, y as mergeNoChangeOutputs, yn as isSingleDynamicReferenceToken, yr as escapeRegExp, yt as deleteIndeterminateGuards, z as isWarmThroughputDecrease, zn as importableOutputs, zr as findLargeInlineResources, zt as configBooleanRefusal } from "./deploy-engine-
|
|
1
|
+
import { $ as secretBearing, $n as ensureAssetStorage, $r as setAwsClients, $t as s3BucketRegionalDomainName, A as extractDeploymentEventError, Ai as retryClassificationText, An as buildForceUnlockCommand, Ar as resolveSkipPrefix, At as parameterTypeMayLoseSecretIdentity, B as toCfnInteger, Bn as orphansAfterRollback, Br as uploadCfnTemplate, Bt as configIntegerRefusal, C as buildFinalSnapshotIdentifier, Ci as withErrorHandling, Cn as recordMaskOnlyValue, Cr as Synthesizer, Ct as IntrinsicFunctionResolver, D as refusesFinalSnapshot, Di as isTransientServerError, Dn as LockManager, Dr as resolveApp, Dt as getAccountInfo, E as isFinalSnapshotError, Ei as isThrottlingError, En as scrubResourceRecord, Er as getLegacyStateBucketName, Et as coerceParameterTypedValue, F as isStatefulRecreateTargetSync, Fn as CUSTOM_RESOURCE_RESPONSE_PREFIX, Fr as warnDeprecatedNoPrefixCliFlag, Ft as resolveExplicitPhysicalId, G as gray, Gn as WorkGraph, Gr as derivePartitionAndUrlSuffix, Gt as requireConfigObject, H as formatResourceLine, Hn as shouldRetainResource, Hr as expectedOwnerParam, Ht as readConfigString, I as renderStatefulReason, In as DEFAULT_STATE_PREFIX, Ir as CFN_TEMPLATE_BODY_LIMIT, It as assertRegionMatch, J as yellow, Jn as loadPublishableAssetManifest, Jt as producerRegionsFromState, K as green, Kn as buildAssetRedirectMap, Kr as AssemblyReader, Kt as requireConfigString, L as WARM_THROUGHPUT_MEMBERS, Ln as exportNamesCarriedFrom, Lr as CFN_TEMPLATE_URL_LIMIT, Lt as coerceCfnBoolean, M as planOrphanAdoption, Mn as forceQuitRecoveryClause, Mr as resolveStateBucketWithDefaultAndSource, Mt as isSettledInstanceState, N as MULTI_REGION_RECREATE_BLOCKED_TYPES, Nn as formatLockExpiry, Nr as resolveUseCdkBootstrapAssets, Nt as WAFv2WebACLProvider, O as unsupportedFinalSnapshotError, Oi as markNonRetryable, On as S3StateBackend, Or as resolveAutoAssetStorage, Ot as isImpossibleEmptyStoredAttribute, P as isStatefulRecreateTargetForReplace, Pn as shellQuote, Pr as stateBucketExistenceConfirmed, Pt as normalizeAwsTagsToCfn, Q as isExportAliasCollision, Qn as assertAssetBucketRegion, Qr as resetAwsClients, Qt as s3BucketDualStackDomainName, R as coerceWarmThroughput$1, Rn as importableOutputKeys, Rr as MIGRATE_TMP_PREFIX, Rt as coerceCfnInteger, S as PRE_DELETE_SNAPSHOT_TYPES, Si as normalizeAwsError, Sn as maskSecretsInText, Sr as getDockerImageBySourceHash, St as isTerminationProtectionPropagationError, T as createPreDeleteFinalSnapshot, Ti as isRetryableTransientError, Tn as redactSecretsForState, Tr as getDefaultStateBucketName, Tt as cfnRefValueFromPhysicalId, U as bold, Un as AssetPublisher, Ur as PARTITION_TABLE, Ut as replayWarn, V as toFiniteNumber, Vn as orphansCarriedFrom, Vr as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Vt as configStringRefusal, W as cyan, Wn as stringifyValue, Wr as canonicalizeRegion, Wt as requireConfigArray, X as collectPublishedOutputNames, Xn as AssetModeResolver, Xr as AwsClients, Xt as s3BucketArn, Y as collectDeclaredOutputNames, Yn as rewriteTemplateAssetReferences, Yr as resolveBucketRegion, Yt as withSharedDrainBudget, Z as exportAliasCollisionScrubWarning, Zn as BOOTSTRAP_MARKER_PREFIX, Zr as getAwsClients, Zt as s3BucketDomainName, _ as isSecretBearingReferenceString, _i as StackTerminationProtectionError, _n as errorCauseChain, _r as runDockerForeground, _t as slowCcOperationTimeoutMs, a as DeploymentEventsStore, ai as DeployCancelledError, an as findSilentDropProperties, ar as validateContainerRepoName, at as clearOnUpdateRemoval, b as bindingSkippedOutputs, bn as markSameGenerationBag, br as stripControlChars, bt as deleteSkipReason, c as replayFailedOperations, ci as LocalInvokeBuildError$1, cn as DagBuilder, cr as buildDockerImage, ct as createMaskedRetryLogger, d as updatePartialReason, di as NestedStackChildDirectDestroyError, dn as STATE_SOURCED_CROSS_GENERATION_RULES, dr as describeDockerFailure, dt as beginCommandInterruptScope, en as s3BucketWebsiteUrl, er as getBootstrapMarkerKey, et as secretBearingStateKeyWarning, f as withResourceDeadline, fi as PartialFailureError, fn as STATE_SOURCED_READBACK_RULES, fr as dockerSpawnEnvWithSensitive, ft as endCommandInterruptScope, g as bagHoldsSecretExpression, gi as StackHasActiveImportsError, gn as dynamicReferenceTokens, gr as redactDockerArgvValues, gt as CloudControlProvider, h as computeImplicitDeleteEdges, hi as ResourceUpdateNotSupportedError, hn as createSecretMasker, hr as partitionSensitiveEnv, ht as startInterruptWatch, i as DeploymentEventsReader, in as findActionableSilentDrops, ir as validateAssetBucketName, it as collectInlinePolicyNamesManagedBySiblings, j as makeSiblingClaimReader, jn as buildLockContentionMessage, jr as resolveStateBucketWithDefault, jt as refStateLookupFromResource, k as makeCanonicalizePropertiesFn, ki as markRedactedCause, kn as rebuildClientForBucketRegion, kr as resolveCaptureObservedState, kt as isUnboundTemplateParameter, l as replayRollback, li as LocalStartServiceError, ln as TemplateParser, lr as describeDockerCapturedOutput, lt as maskDeep, m as IMPLICIT_DELETE_DEPENDENCIES, mi as ResourceTimeoutError, mn as carriesSecretMask, mr as getDockerCmd, mt as isInterruptedWaitError, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as DiffCalculator, nr as parseBootstrapMarker, nt as getCurrentResourceSecrets, o as planFailedOps, oi as DynamicReferenceRegionAmbiguousError, on as describeTypeWithThrottleRetry, or as buildDenyExternalAccessPolicy, ot as ProviderRegistry, p as maskingRetryLogger, pi as ProvisioningError, pn as TEMPLATE_SOURCED_RULES, pr as formatDockerLoginError, pt as interruptWatchListenerCount, q as red, qn as createAssetRedirectResolver, qr as processStackMessages, qt as classifyReplaySecretRegion, r as DeployEngine, ri as CrossAccountSecretRefusalError, rn as INTRINSIC_KEYS, rr as readBootstrapMarkerBody, rt as IAMRoleProvider, s as planRollback, si as IntrinsicResolutionRefusalError, sn as withRetry, sr as describeAwsFailure, st as wouldReturnToSdkProvider, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as CdkdError, tn as applyRoleArnIfSet, tr as isCrossRegionRedirect, tt as secretSafeKeyDisplay, u as updatePartialMessage, un as STATE_SOURCED_BASELINE_RULES, ur as describeDockerExecFailure, ut as maskerOrIdentity, v as keptWholeReasonText, vn as identityKeyFor, vr as runDockerStreaming, vt as UNSPECIFIED_SKIP_REASON, w as ccRoutedFinalSnapshotError, wi as isMarkedNonRetryable, wn as recoverMaskedOutput, wr as synthesisStatusMessage, wt as carriesDynamicReference, x as ATOMIC_FINAL_SNAPSHOT_TYPES, xn as maskSecretsInError, xr as AssetManifestLoader, xt as disableInstanceApiTermination, y as mergeNoChangeOutputs, yn as isSingleDynamicReferenceToken, yr as escapeRegExp, yt as deleteIndeterminateGuards, z as isWarmThroughputDecrease, zn as importableOutputs, zr as findLargeInlineResources, zt as configBooleanRefusal } from "./deploy-engine-B3yP0pHa.js";
|
|
2
2
|
import { C as truncateCodePoints, S as displaySafe, _ as withSkipPrefix, b as UNRENDERABLE, c as PATTERN_B_NAME_OPTIONS, f as explicitNamePropertyFor, h as getCurrentStackName, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as generateResourceNameWithFallback, n as getLogger, o as runStackBuffered, p as generateResourceName, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES, v as withStackName, x as displayIdent, y as STACK_REF_MAX_CODE_POINTS } from "./logger-H8n5Qjhq.js";
|
|
3
3
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
4
|
-
import { t as getCdkdVersion } from "./version-
|
|
5
|
-
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as getCdkdVersion } from "./version-wXpog0Ci.js";
|
|
5
|
+
import { a as renderDisableCommand, i as protectedReplacementAdvice, r as UNNAMEABLE_ID_CLAUSE, t as ASGProvider } from "./asg-provider-BTsQjH_E.js";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
8
|
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, NotFound, 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";
|
|
@@ -67048,6 +67048,7 @@ async function scrubCommand(stacks, options) {
|
|
|
67048
67048
|
let totalStacksScrubbed = 0;
|
|
67049
67049
|
let totalStacksWithUnscrubbableKeys = 0;
|
|
67050
67050
|
let totalStacksWithUnverifiableReads = 0;
|
|
67051
|
+
let totalStacksWithUnverifiableLeaves = 0;
|
|
67051
67052
|
/**
|
|
67052
67053
|
* Stacks this `--dry-run` proceeded over with an UNREADABLE resources map
|
|
67053
67054
|
* (issue go-to-k/cdkd#3018). Tracked exactly like `indexUnreadable`: an
|
|
@@ -67129,8 +67130,12 @@ async function scrubCommand(stacks, options) {
|
|
|
67129
67130
|
if (scrubbed.recordsChanged > 0) {
|
|
67130
67131
|
totalStacksScrubbed++;
|
|
67131
67132
|
logger.info(`${options.dryRun ? "Would scrub" : "Scrubbed"} ${scrubbed.recordsChanged} resource record(s) in ${stack.stackName}`);
|
|
67132
|
-
} else if (scrubbed.secretBearingKeys === 0 && scrubbed.unverifiableReads === 0 && indexConverged === 0) logger.info(`No plaintext secrets found in ${stack.stackName}`);
|
|
67133
|
+
} else if (scrubbed.secretBearingKeys === 0 && scrubbed.unverifiableReads === 0 && scrubbed.unverifiableLeaves === 0 && indexConverged === 0) logger.info(`No plaintext secrets found in ${stack.stackName}`);
|
|
67133
67134
|
if (scrubbed.malformedResources) malformedRecords.push(stack.stackName);
|
|
67135
|
+
if (scrubbed.unverifiableLeaves > 0) {
|
|
67136
|
+
totalStacksWithUnverifiableLeaves++;
|
|
67137
|
+
logger.warn(`${scrubbed.unverifiableLeaves} scan(s) in ${stack.stackName} were ABANDONED mid-value because a {{resolve:...}} reference could not be resolved (see the warnings above). The resolver stops at the first failing token, so a real secret after it in the same value recorded no needle — this stack is not reported clean. The count is per abandoned SCAN, not per secret: one record can contribute more than once (an output whose Export.Name and Value both abandon), and one abandoned scan can hide several unexamined leaves.`);
|
|
67138
|
+
}
|
|
67134
67139
|
if (scrubbed.unverifiableReads > 0) {
|
|
67135
67140
|
totalStacksWithUnverifiableReads++;
|
|
67136
67141
|
logger.warn(`${scrubbed.unverifiableReads} cross-stack read(s) in ${stack.stackName} could NOT be verified — cdkd declines them by design (see the warnings above), so this stack is not reported clean.`);
|
|
@@ -67161,7 +67166,7 @@ async function scrubCommand(stacks, options) {
|
|
|
67161
67166
|
totalIndexEntriesUnexamined += unexamined.length;
|
|
67162
67167
|
logger.info(`Exports index (${indexRegion}): ${unexamined.length} entr${unexamined.length === 1 ? "y is" : "ies are"} published by a producer this run did not scrub, so nothing here read ${unexamined.length === 1 ? "its" : "their"} value. Coverage composes across the apps sharing this bucket and region — run each app's own cdkd scrub to reach its entries.`);
|
|
67163
67168
|
}
|
|
67164
|
-
if (totalStacksScrubbed === 0 && totalStacksWithUnscrubbableKeys === 0 && totalStacksWithUnverifiableReads === 0 && totalIndexEntriesConverged === 0 && indexUnwritten.length === 0 && indexUnreadable.length === 0 && malformedRecords.length === 0) {
|
|
67169
|
+
if (totalStacksScrubbed === 0 && totalStacksWithUnscrubbableKeys === 0 && totalStacksWithUnverifiableReads === 0 && totalStacksWithUnverifiableLeaves === 0 && totalIndexEntriesConverged === 0 && indexUnwritten.length === 0 && indexUnreadable.length === 0 && malformedRecords.length === 0) {
|
|
67165
67170
|
if (failures.length === 0) {
|
|
67166
67171
|
logger.info("\nNo plaintext secrets found in any target stack state. Nothing to scrub.");
|
|
67167
67172
|
return;
|
|
@@ -67171,6 +67176,7 @@ async function scrubCommand(stacks, options) {
|
|
|
67171
67176
|
}
|
|
67172
67177
|
const failureNote = failures.length > 0 ? ` ${failures.length} stack(s) could NOT be scrubbed at all and are NOT covered by this summary — see the errors above.` : "";
|
|
67173
67178
|
const keyNote = totalStacksWithUnscrubbableKeys > 0 ? ` ${totalStacksWithUnscrubbableKeys} stack(s) hold plaintext in an output KEY, which cdkd scrub cannot rewrite — rename that output's Export.Name and redeploy.` : "";
|
|
67179
|
+
const leafNote = totalStacksWithUnverifiableLeaves > 0 ? ` ${totalStacksWithUnverifiableLeaves} stack(s) had a {{resolve:...}} scan ABANDONED mid-value, so part of their state was never examined (see the warnings above).` : "";
|
|
67174
67180
|
const unverifiableNote = totalStacksWithUnverifiableReads > 0 ? ` ${totalStacksWithUnverifiableReads} stack(s) carry a cross-stack read cdkd declines to perform, so their imported values could NOT be checked — export a non-secret value (e.g. the secret's ARN) from the producer, or reference it from within its own account.` : "";
|
|
67175
67181
|
const indexNote = (() => {
|
|
67176
67182
|
const parts = [];
|
|
@@ -67180,20 +67186,20 @@ async function scrubCommand(stacks, options) {
|
|
|
67180
67186
|
return parts.join("");
|
|
67181
67187
|
})();
|
|
67182
67188
|
if (options.dryRun) {
|
|
67183
|
-
if (totalStacksScrubbed > 0) logger.info(`\nPlan: ${totalStacksScrubbed} stack(s) hold plaintext secrets and would be scrubbed (--dry-run, no state written).${keyNote}${unverifiableNote}${indexNote}${failureNote} ROTATE any exposed secret in Secrets Manager.`);
|
|
67184
|
-
else logger.info(`\nPlan: no state record can be rewritten.${keyNote}${unverifiableNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
|
|
67189
|
+
if (totalStacksScrubbed > 0) logger.info(`\nPlan: ${totalStacksScrubbed} stack(s) hold plaintext secrets and would be scrubbed (--dry-run, no state written).${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote} ROTATE any exposed secret in Secrets Manager.`);
|
|
67190
|
+
else logger.info(`\nPlan: no state record can be rewritten.${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
|
|
67185
67191
|
if (failures.length > 0) throw scrubStacksFailedError(failures);
|
|
67186
67192
|
if (indexUnwritten.length > 0 || indexUnreadable.length > 0) throw exportIndexIncompleteError(indexUnwritten, indexUnreadable, true);
|
|
67187
67193
|
if (malformedRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords);
|
|
67188
67194
|
if (options.fail) throw new ScrubNeededError();
|
|
67189
67195
|
return;
|
|
67190
67196
|
}
|
|
67191
|
-
if (totalStacksScrubbed > 0) logger.info(`\nDone: scrubbed ${totalStacksScrubbed} stack(s). The CURRENT state.json no longer holds the plaintext. Where the state bucket is VERSIONED — which cdkd bootstrap enables — the pre-scrub body survives as a noncurrent version, stays readable with GetObject and a VersionId, and scrub does not purge it. So a value that was ever persisted must be treated as compromised — ROTATE it in Secrets Manager (scrub matches the current value, so scrub BEFORE rotating); rotation is what makes any surviving version harmless.${keyNote}${unverifiableNote}${indexNote}${failureNote}`);
|
|
67192
|
-
else logger.info(`\nNo state record was rewritten.${keyNote}${unverifiableNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
|
|
67197
|
+
if (totalStacksScrubbed > 0) logger.info(`\nDone: scrubbed ${totalStacksScrubbed} stack(s). The CURRENT state.json no longer holds the plaintext. Where the state bucket is VERSIONED — which cdkd bootstrap enables — the pre-scrub body survives as a noncurrent version, stays readable with GetObject and a VersionId, and scrub does not purge it. So a value that was ever persisted must be treated as compromised — ROTATE it in Secrets Manager (scrub matches the current value, so scrub BEFORE rotating); rotation is what makes any surviving version harmless.${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote}`);
|
|
67198
|
+
else logger.info(`\nNo state record was rewritten.${keyNote}${unverifiableNote}${leafNote}${indexNote}${failureNote} ROTATE any exposed secret.`);
|
|
67193
67199
|
if (failures.length > 0) throw scrubStacksFailedError(failures);
|
|
67194
67200
|
if (indexUnwritten.length > 0 || indexUnreadable.length > 0) throw exportIndexIncompleteError(indexUnwritten, indexUnreadable, false);
|
|
67195
67201
|
if (malformedRecords.length > 0) throw malformedRecordsAuditedError(malformedRecords);
|
|
67196
|
-
if (options.fail && (totalStacksWithUnscrubbableKeys > 0 || totalStacksWithUnverifiableReads > 0)) throw new ScrubNeededError();
|
|
67202
|
+
if (options.fail && (totalStacksWithUnscrubbableKeys > 0 || totalStacksWithUnverifiableReads > 0 || totalStacksWithUnverifiableLeaves > 0)) throw new ScrubNeededError();
|
|
67197
67203
|
}
|
|
67198
67204
|
/**
|
|
67199
67205
|
* Every secret plaintext this run recorded, from ANY position in the template
|
|
@@ -68477,6 +68483,205 @@ function isNamelessDynamicReferenceFailure(err) {
|
|
|
68477
68483
|
return err instanceof Error && errorCauseChain(err).some((link) => link instanceof Error && link.message.includes(DYNAMIC_REFERENCE_PREFIX) && NAMELESS_DYNAMIC_REFERENCE_MARKERS.some((marker) => link.message.includes(marker)));
|
|
68478
68484
|
}
|
|
68479
68485
|
/**
|
|
68486
|
+
* The per-record line each counting site prints. Deliberately ONE SHORT
|
|
68487
|
+
* SENTENCE, and that is a fix rather than a style choice: the first cut emitted
|
|
68488
|
+
* a ~440-character paragraph PER RECORD at default verbosity, and the arm it
|
|
68489
|
+
* fires on is one a single `Default`-less parameter triggers for every resource
|
|
68490
|
+
* in the stack at once. Dozens of identical paragraphs bury the record names,
|
|
68491
|
+
* which are the only part a reader cannot reconstruct.
|
|
68492
|
+
*
|
|
68493
|
+
* The explanation is emitted ONCE PER STACK instead, by
|
|
68494
|
+
* {@link abandonedScanStackNote}.
|
|
68495
|
+
*
|
|
68496
|
+
* The CAUSE is absent on both arms: the sibling log at each site already prints
|
|
68497
|
+
* it through `maskSecretsInText`, and a resolver error echoes what it was
|
|
68498
|
+
* handed — which, after `pinCrossRegionSecrets`, can be a foreign plaintext.
|
|
68499
|
+
*
|
|
68500
|
+
* RESIDUAL on the `subject` the call sites pass. They compose
|
|
68501
|
+
* `maskSecretsInText(... displaySafe(id) ...)` — sanitise, then mask — and
|
|
68502
|
+
* `displaySafe` REPLACES a control character with a space rather than deleting
|
|
68503
|
+
* it, so it cannot JOIN a split needle (the go-to-k/cdkd#2874 direction) but it
|
|
68504
|
+
* can BREAK one, and it leaves `U+200B`-`U+200D` / `U+FEFF` untouched. A secret
|
|
68505
|
+
* would have to sit inside a template logical id or Outputs key to be reachable,
|
|
68506
|
+
* which is why `secretSafeKeyDisplay` is not used here — that one is for RESOLVED
|
|
68507
|
+
* state-bag keys. Recorded rather than closed: go-to-k/cdkd#3178 security review.
|
|
68508
|
+
*/
|
|
68509
|
+
function abandonedScanWarning(subject, verdict) {
|
|
68510
|
+
return verdict === "count" ? `The {{resolve:...}} scan of ${subject} was ABANDONED — this record is NOT certified clean.` : `The {{resolve:...}} scan of ${subject} was cut short by a TEMPLATE problem — this record is NOT certified clean.`;
|
|
68511
|
+
}
|
|
68512
|
+
/**
|
|
68513
|
+
* The one-per-stack explanation behind {@link abandonedScanWarning}'s lines.
|
|
68514
|
+
*
|
|
68515
|
+
* Two arms, because they need opposite things said. The `count` arm is a
|
|
68516
|
+
* FINDING and gates; the reader needs the cause, which is verbose-only. The
|
|
68517
|
+
* `warn` arm does NOT gate, and a reader seeing "NOT certified clean" beside a
|
|
68518
|
+
* green exit has no way to tell a passing run from a broken one unless it says
|
|
68519
|
+
* so.
|
|
68520
|
+
*
|
|
68521
|
+
* The `warn` arm's remedy is deliberately two-sided. The dominant population is
|
|
68522
|
+
* a parameter with no `Default` — legal CloudFormation, no defect to fix — so
|
|
68523
|
+
* telling the reader only to "fix the template reference" describes a repair
|
|
68524
|
+
* that does not exist for them. It names what they CAN do and then says plainly
|
|
68525
|
+
* that scrub may simply not be able to certify the record.
|
|
68526
|
+
*/
|
|
68527
|
+
function abandonedScanStackNote(verdict, records) {
|
|
68528
|
+
const subject = `${records} record(s) above`;
|
|
68529
|
+
return verdict === "count" ? `${subject}: the resolver stops at the first {{resolve:...}} token it cannot fetch, so any secret after it in the same value recorded no needle and could not be rewritten. Re-run with --verbose for the cause of each.` : `${subject}: the scan stopped on something cdkd scrub cannot resolve with template defaults alone — an unresolvable Ref or Fn::GetAtt, a parameter with no Default, or a reference whose own argument still holds an unsubstituted \${...}. These do NOT fail --fail, because scrub takes no --parameters and a gate failure here could not be cleared. Give the parameter a Default, or resolve the reference, to let scrub certify these records; otherwise accept that it cannot.`;
|
|
68530
|
+
}
|
|
68531
|
+
/**
|
|
68532
|
+
* The verdict a counting site acts on: `count` (a finding — warn AND raise the
|
|
68533
|
+
* exit code), `warn` (say it, but do not gate) or `silent`.
|
|
68534
|
+
*
|
|
68535
|
+
* The middle arm exists because the EXCLUSION has a cost in
|
|
68536
|
+
* go-to-k/cdkd#3160's OWN harm direction, and two round-4 reviewers found it
|
|
68537
|
+
* independently. `resolver.resolve` walks the whole properties bag and ONE
|
|
68538
|
+
* throw aborts the rest of it — so a bag holding `{A: {Ref: NoSuchThing},
|
|
68539
|
+
* B: '{{resolve:secretsmanager:prod/db:...}}'}` has `B` abandoned by a failure
|
|
68540
|
+
* that is nothing to do with `B`. Excluding it means `B` records no needle, its
|
|
68541
|
+
* legacy plaintext is never rewritten, and the stack can still print
|
|
68542
|
+
* `No plaintext secrets found` at exit 0. Reachable accidentally (one
|
|
68543
|
+
* `Default`-less parameter empties the bag, so every `{Ref: <param>}` throws)
|
|
68544
|
+
* and, on a repo whose CI runs `--dry-run --fail`, defeatable on purpose by
|
|
68545
|
+
* adding one dangling `Ref` ahead of the secret. That loss is REAL and is not
|
|
68546
|
+
* repaired by making it visible — go-to-k/cdkd#3196 tracks scoping the resolve
|
|
68547
|
+
* per property so the sibling reference is still scanned.
|
|
68548
|
+
*
|
|
68549
|
+
* Counting it is not the answer — that is the round-2 blocker, an unclearable
|
|
68550
|
+
* red gate on a healthy stack. What the two failure modes do NOT share is
|
|
68551
|
+
* VISIBILITY: an operator can act on a warning naming the record, and a warning
|
|
68552
|
+
* gates nothing. So the excluded arm still speaks, and only the exit code is
|
|
68553
|
+
* withheld.
|
|
68554
|
+
*
|
|
68555
|
+
* **The ORDER of the three tests is load-bearing, and getting it wrong is how
|
|
68556
|
+
* round 5 nearly shipped a silent regression.** An earlier cut asked
|
|
68557
|
+
* `carriesFetchableDynamicReference` FIRST and returned `silent` on a miss,
|
|
68558
|
+
* which made a gate-downgrade test into a universal silencer. Because
|
|
68559
|
+
* `DYNAMIC_REFERENCE_INNER_CHAR` is `[^}]`, a token with a placeholder
|
|
68560
|
+
* ANYWHERE in it fails that test — including the dominant CDK spelling, where
|
|
68561
|
+
* the reference is assembled by an `Fn::Sub` over parameters that DO have
|
|
68562
|
+
* defaults:
|
|
68563
|
+
*
|
|
68564
|
+
* ```json
|
|
68565
|
+
* {"A": {"Fn::Sub": "{{resolve:ssm-secure:/deleted/${Env}/p}}"},
|
|
68566
|
+
* "B": {"Fn::Sub": "{{resolve:secretsmanager:${Db}:SecretString:password}}"}}
|
|
68567
|
+
* ```
|
|
68568
|
+
*
|
|
68569
|
+
* Those assemble fine, `A` raises a RAW `ParameterNotFound`, `B` is abandoned
|
|
68570
|
+
* and records no needle — and the earlier order printed nothing at all. That is
|
|
68571
|
+
* go-to-k/cdkd#3160's own headline repro, silenced by the fix for a different
|
|
68572
|
+
* false positive.
|
|
68573
|
+
*
|
|
68574
|
+
* So the tests are separated by what each one is EVIDENCE of:
|
|
68575
|
+
*
|
|
68576
|
+
* - `carriesDynamicReference` — did this bag hold a reference at all? If not,
|
|
68577
|
+
* nothing was lost and the pre-existing `logger.debug` is the right level.
|
|
68578
|
+
* This, and only this, decides SILENCE.
|
|
68579
|
+
* - `isTemplateShapeResolutionFailure` / `carriesFetchableDynamicReference` —
|
|
68580
|
+
* is the abandonment one an operator could act on? If not, it still gets said;
|
|
68581
|
+
* it just does not gate. These decide the EXIT CODE, never the visibility.
|
|
68582
|
+
*/
|
|
68583
|
+
function abandonedScanVerdict(source, err) {
|
|
68584
|
+
if (!carriesDynamicReference(source)) return "silent";
|
|
68585
|
+
if (isTemplateShapeResolutionFailure(err) || !carriesFetchableDynamicReference(source)) return "warn";
|
|
68586
|
+
return "count";
|
|
68587
|
+
}
|
|
68588
|
+
/**
|
|
68589
|
+
* `source` carries a `{{resolve:...}}` token cdkd could actually have FETCHED —
|
|
68590
|
+
* i.e. one whose argument holds no surviving `${...}` placeholder.
|
|
68591
|
+
*
|
|
68592
|
+
* `carriesDynamicReference` alone is too generous here, and the gap is not
|
|
68593
|
+
* theoretical: scrub resolves with `bestEffort`, under which
|
|
68594
|
+
* `rethrowStructuralSubFailure` returns EARLY, so an `Fn::Sub` over an unbound
|
|
68595
|
+
* placeholder does not throw — it warn-and-KEEPS the literal `${Field}`. The
|
|
68596
|
+
* assembled token is then a request for a JSON key literally named `${Field}`,
|
|
68597
|
+
* which fails as `Dynamic reference: key '${Field}' not found in secret '<id>'`.
|
|
68598
|
+
* That is not a fetch that failed; it is a token that was never fetchable,
|
|
68599
|
+
* because scrub has only template DEFAULTS and accepts no `--parameters`. It is
|
|
68600
|
+
* ALSO not excludable by message, since `key ... not found` is a class
|
|
68601
|
+
* go-to-k/cdkd#3160 asks to COUNT when the key was real. So the discriminator
|
|
68602
|
+
* has to be the TOKEN, not the error.
|
|
68603
|
+
*
|
|
68604
|
+
* `some`, not `every`: a bag holding one placeholder-bearing token and one
|
|
68605
|
+
* fully substituted token still counts, because the second one genuinely was
|
|
68606
|
+
* fetchable and its scan genuinely stopped.
|
|
68607
|
+
*
|
|
68608
|
+
* WHAT THIS DOES AND DOES NOT BUY, since round 5 shipped a cut that confused
|
|
68609
|
+
* the two. A `false` here downgrades the GATE only — the record is still named
|
|
68610
|
+
* at default verbosity by the `warn` arm. It must never decide SILENCE: the
|
|
68611
|
+
* inner class is `[^}]`, so a placeholder ANYWHERE in a token fails this test,
|
|
68612
|
+
* and the reference assembled by an `Fn::Sub` over DEFAULTED parameters is the
|
|
68613
|
+
* dominant CDK spelling rather than an edge case.
|
|
68614
|
+
*
|
|
68615
|
+
* STATED RESIDUALS, both UNDER-counts against the gate, neither a silence:
|
|
68616
|
+
*
|
|
68617
|
+
* - A two-argument `Fn::Sub` whose placeholder IS bound
|
|
68618
|
+
* (`{'Fn::Sub': ['{{resolve:ssm:${N}}}', {N: 'x'}]}`) substitutes fine and
|
|
68619
|
+
* yields a fetchable token, but the raw bag still spells `${`, so a real
|
|
68620
|
+
* fetch failure there warns instead of gating.
|
|
68621
|
+
* - The same for a single-argument `Fn::Sub` over a defaulted parameter, and
|
|
68622
|
+
* for a placeholder mid-token, which yields no token at all.
|
|
68623
|
+
*
|
|
68624
|
+
* And one OVER-count, recorded for symmetry: this predicate is BAG-scoped while
|
|
68625
|
+
* the failure is TOKEN-scoped, so a bag holding a placeholder-bearing token
|
|
68626
|
+
* BESIDE a genuinely fetchable one returns `true`, and a `key '${Field}' not
|
|
68627
|
+
* found` raised for the first is gated as if it came from the second. Safety
|
|
68628
|
+
* is in the right direction (a gate that fires, not a plaintext under `clean`),
|
|
68629
|
+
* and same-bag co-location bounds it.
|
|
68630
|
+
*
|
|
68631
|
+
* go-to-k/cdkd#3181 is the fix that removes the need for any of these proxies.
|
|
68632
|
+
*/
|
|
68633
|
+
function carriesFetchableDynamicReference(source) {
|
|
68634
|
+
if (typeof source === "string") return dynamicReferenceTokens(source).some((token) => !token.includes("${"));
|
|
68635
|
+
if (Array.isArray(source)) return source.some(carriesFetchableDynamicReference);
|
|
68636
|
+
if (source !== null && typeof source === "object") return Object.values(source).some(carriesFetchableDynamicReference);
|
|
68637
|
+
return false;
|
|
68638
|
+
}
|
|
68639
|
+
/**
|
|
68640
|
+
* `err` is cdkd's OWN refusal to resolve a template SHAPE — a `Ref`, an
|
|
68641
|
+
* `Fn::GetAtt`, a parameter binding — rather than a failure to fetch a
|
|
68642
|
+
* `{{resolve:...}}` reference.
|
|
68643
|
+
*
|
|
68644
|
+
* Matched on cdkd-authored prose, which is sound here for the reason the
|
|
68645
|
+
* inverse is not: every string below is built by `intrinsic-function-resolver.ts`
|
|
68646
|
+
* itself, so it changes only when this repo changes it, and the fence in
|
|
68647
|
+
* `tests/unit/cli/scrub-abandoned-scan-origin.test.ts` reds when a throw site
|
|
68648
|
+
* there stops matching. An AWS-authored message could never carry that
|
|
68649
|
+
* guarantee.
|
|
68650
|
+
*/
|
|
68651
|
+
function isTemplateShapeResolutionFailure(err) {
|
|
68652
|
+
if (!(err instanceof Error)) return false;
|
|
68653
|
+
return TEMPLATE_SHAPE_FAILURE_PATTERNS.some((pattern) => pattern.test(err.message));
|
|
68654
|
+
}
|
|
68655
|
+
/**
|
|
68656
|
+
* The cdkd-authored messages {@link isTemplateShapeResolutionFailure} excludes,
|
|
68657
|
+
* ANCHORED. A substring test cannot be used here and the reason is concrete:
|
|
68658
|
+
* `'Parameter '` alone also matches the SDK's own `Parameter /deleted not
|
|
68659
|
+
* found.` — `ParameterNotFound`, this issue's HEADLINE repro — so the loose
|
|
68660
|
+
* spelling would have excluded the very case the positional half exists to
|
|
68661
|
+
* catch. Each pattern must stay tight enough that no AWS-authored message can
|
|
68662
|
+
* satisfy it.
|
|
68663
|
+
*
|
|
68664
|
+
* Deliberately just these THREE, not every shape failure the resolver can
|
|
68665
|
+
* raise. They are the ones that fire EN MASSE on a healthy stack:
|
|
68666
|
+
* `resolveParameters` is caught wholesale by `scrubStack`, so one
|
|
68667
|
+
* `Default`-less parameter empties the whole bag and every `{Ref: <param>}`
|
|
68668
|
+
* throws; and `resolveGetAtt` refuses on the same condition as `resolveRef`,
|
|
68669
|
+
* which a branch adding a not-yet-deployed resource hits for every
|
|
68670
|
+
* `Fn::GetAtt` to it. A rarer shape failure (an `Fn::Select` over a
|
|
68671
|
+
* non-array, say) is a genuine template defect, and counting a leaf whose
|
|
68672
|
+
* scan it abandoned is not wrong — the scan really did stop. Over-counting
|
|
68673
|
+
* THERE costs a warn on a broken template; over-counting on the three below
|
|
68674
|
+
* would cost a red CI gate on a working one.
|
|
68675
|
+
*
|
|
68676
|
+
* Per-pattern throw sites, and the fence that keeps them true, are in
|
|
68677
|
+
* `tests/unit/cli/scrub-abandoned-scan-origin.test.ts`.
|
|
68678
|
+
*/
|
|
68679
|
+
const TEMPLATE_SHAPE_FAILURE_PATTERNS = [
|
|
68680
|
+
/^Ref \S+ not found$/,
|
|
68681
|
+
/^Resource \S+ not found for Fn::GetAtt$/,
|
|
68682
|
+
/^Parameter \S+ is required but no value was provided/
|
|
68683
|
+
];
|
|
68684
|
+
/**
|
|
68480
68685
|
* A secrets map with its OWN identity whose entries ARE `target`'s (issue
|
|
68481
68686
|
* #2531): every read and write goes to `target`; nothing is stored here.
|
|
68482
68687
|
*
|
|
@@ -68907,6 +69112,33 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
68907
69112
|
*/
|
|
68908
69113
|
const orphanSecrets = /* @__PURE__ */ new Map();
|
|
68909
69114
|
const prePassFindings = { unverifiable: [] };
|
|
69115
|
+
/**
|
|
69116
|
+
* Leaves whose dynamic-reference scan was ABANDONED mid-token (issue
|
|
69117
|
+
* go-to-k/cdkd#3160). A counted finding, not a refusal — see
|
|
69118
|
+
* `abandonedScanVerdict` for why these cannot refuse.
|
|
69119
|
+
*/
|
|
69120
|
+
let unverifiableLeaves = 0;
|
|
69121
|
+
let ungateableAbandonedScans = 0;
|
|
69122
|
+
/**
|
|
69123
|
+
* ONE note per stack per arm. The per-record lines are deliberately short
|
|
69124
|
+
* (see `abandonedScanWarning`), so this is where the explanation lives, and
|
|
69125
|
+
* emitting it after the loops rather than at the first occurrence is what
|
|
69126
|
+
* lets it say HOW MANY records it is explaining.
|
|
69127
|
+
*
|
|
69128
|
+
* A CLOSURE called at BOTH returns, not a block at the late one. The early
|
|
69129
|
+
* `totalSecrets === 0` return is the DOMINANT path for an abandoned scan --
|
|
69130
|
+
* an abandoned scan records no needle, which is the whole defect -- so a
|
|
69131
|
+
* single late emission left the note unreachable for exactly the case it
|
|
69132
|
+
* exists to explain. `emitted` because the two call sites are exclusive
|
|
69133
|
+
* today and a third would not be.
|
|
69134
|
+
*/
|
|
69135
|
+
let notesEmitted = false;
|
|
69136
|
+
const emitAbandonedScanNotes = () => {
|
|
69137
|
+
if (notesEmitted) return;
|
|
69138
|
+
notesEmitted = true;
|
|
69139
|
+
if (unverifiableLeaves > 0) logger.warn(abandonedScanStackNote("count", unverifiableLeaves));
|
|
69140
|
+
if (ungateableAbandonedScans > 0) logger.warn(abandonedScanStackNote("warn", ungateableAbandonedScans));
|
|
69141
|
+
};
|
|
68910
69142
|
try {
|
|
68911
69143
|
const loaded = await stateBackend.getState(stack.stackName, region);
|
|
68912
69144
|
if (!loaded) {
|
|
@@ -68916,6 +69148,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
68916
69148
|
secretsFound: 0,
|
|
68917
69149
|
secretBearingKeys: 0,
|
|
68918
69150
|
unverifiableReads: 0,
|
|
69151
|
+
unverifiableLeaves: 0,
|
|
68919
69152
|
exportNameDisplay: (name) => secretSafeKeyDisplay(name, outputSecrets)
|
|
68920
69153
|
};
|
|
68921
69154
|
}
|
|
@@ -68989,6 +69222,12 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
68989
69222
|
await resolver.resolve(resolveInput, resourceContext);
|
|
68990
69223
|
} catch (err) {
|
|
68991
69224
|
if (isRegionAmbiguousRefusal(err) || isNamelessDynamicReferenceFailure(err)) throw err;
|
|
69225
|
+
const leafVerdict = abandonedScanVerdict(resolveInput, err);
|
|
69226
|
+
if (leafVerdict !== "silent") {
|
|
69227
|
+
if (leafVerdict === "count") unverifiableLeaves++;
|
|
69228
|
+
else ungateableAbandonedScans++;
|
|
69229
|
+
logger.warn(maskSecretsInText(abandonedScanWarning(`resource '${displaySafe(logicalId)}'`, leafVerdict), recordedSecretValues));
|
|
69230
|
+
}
|
|
68992
69231
|
logger.debug(`Resolution of ${logicalId} during scrub was partial: ${maskSecretsInText(err instanceof Error ? err.message : String(err), recordedSecretValues)}`);
|
|
68993
69232
|
}
|
|
68994
69233
|
perResourceTemplateProps.set(logicalId, templateResource.Properties);
|
|
@@ -69011,6 +69250,12 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
69011
69250
|
await resolver.resolve(resolveInput, resolverContext(recordedSecretValues));
|
|
69012
69251
|
} catch (err) {
|
|
69013
69252
|
if (isRegionAmbiguousRefusal(err) || isNamelessDynamicReferenceFailure(err)) throw err;
|
|
69253
|
+
const leafVerdict = abandonedScanVerdict(resolveInput, err);
|
|
69254
|
+
if (leafVerdict !== "silent") {
|
|
69255
|
+
if (leafVerdict === "count") unverifiableLeaves++;
|
|
69256
|
+
else ungateableAbandonedScans++;
|
|
69257
|
+
logger.warn(maskSecretsInText(abandonedScanWarning(`orphan record '${displaySafe(record.logicalId)}'`, leafVerdict), recordedSecretValues));
|
|
69258
|
+
}
|
|
69014
69259
|
logger.debug(`Resolution of orphan record ${record.logicalId} during scrub was partial: ${maskSecretsInText(err instanceof Error ? err.message : String(err), recordedSecretValues)}`);
|
|
69015
69260
|
}
|
|
69016
69261
|
}
|
|
@@ -69034,6 +69279,12 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
69034
69279
|
exportName = await resolver.resolve(nameSource, nameContext);
|
|
69035
69280
|
} catch (err) {
|
|
69036
69281
|
if (isRegionAmbiguousRefusal(err) || isNamelessDynamicReferenceFailure(err)) throw err;
|
|
69282
|
+
const leafVerdict = abandonedScanVerdict(nameSource, err);
|
|
69283
|
+
if (leafVerdict !== "silent") {
|
|
69284
|
+
if (leafVerdict === "count") unverifiableLeaves++;
|
|
69285
|
+
else ungateableAbandonedScans++;
|
|
69286
|
+
logger.warn(maskSecretsInText(abandonedScanWarning(`the Export.Name of output '${displaySafe(name)}'`, leafVerdict), outputSecrets));
|
|
69287
|
+
}
|
|
69037
69288
|
nameFailed = true;
|
|
69038
69289
|
nameError = err;
|
|
69039
69290
|
}
|
|
@@ -69072,6 +69323,12 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
69072
69323
|
await resolver.resolve(valueSource, valueContext);
|
|
69073
69324
|
} catch (err) {
|
|
69074
69325
|
if (isRegionAmbiguousRefusal(err) || isNamelessDynamicReferenceFailure(err)) throw err;
|
|
69326
|
+
const leafVerdict = abandonedScanVerdict(valueSource, err);
|
|
69327
|
+
if (leafVerdict !== "silent") {
|
|
69328
|
+
if (leafVerdict === "count") unverifiableLeaves++;
|
|
69329
|
+
else ungateableAbandonedScans++;
|
|
69330
|
+
logger.warn(maskSecretsInText(abandonedScanWarning(`output '${displaySafe(name)}'`, leafVerdict), outputSecrets));
|
|
69331
|
+
}
|
|
69075
69332
|
logger.debug(`Resolution of output ${name} during scrub was partial: ${maskSecretsInText(err instanceof Error ? err.message : String(err), outputSecrets)}`);
|
|
69076
69333
|
}
|
|
69077
69334
|
}
|
|
@@ -69085,15 +69342,19 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
69085
69342
|
logger.warn(secretBearingStateKeyWarning(stack.stackName, display));
|
|
69086
69343
|
}
|
|
69087
69344
|
const totalSecrets = outputSecrets.size + [...perResourceSecrets.values()].reduce((n, m) => n + m.size, 0) + [...orphanSecrets.values()].reduce((n, m) => n + m.size, 0);
|
|
69088
|
-
if (totalSecrets === 0)
|
|
69089
|
-
|
|
69090
|
-
|
|
69091
|
-
|
|
69092
|
-
|
|
69093
|
-
|
|
69094
|
-
|
|
69095
|
-
|
|
69096
|
-
|
|
69345
|
+
if (totalSecrets === 0) {
|
|
69346
|
+
emitAbandonedScanNotes();
|
|
69347
|
+
return {
|
|
69348
|
+
recordsChanged: 0,
|
|
69349
|
+
secretsFound: 0,
|
|
69350
|
+
secretBearingKeys: secretBearingKeys.length,
|
|
69351
|
+
unverifiableReads: prePassFindings.unverifiable.length,
|
|
69352
|
+
unverifiableLeaves,
|
|
69353
|
+
...malformedResources ? { malformedResources } : {},
|
|
69354
|
+
outputs: state.outputs,
|
|
69355
|
+
exportNameDisplay: (name) => secretSafeKeyDisplay(name, outputSecrets)
|
|
69356
|
+
};
|
|
69357
|
+
}
|
|
69097
69358
|
let recordsChanged = 0;
|
|
69098
69359
|
const newResources = {};
|
|
69099
69360
|
for (const [logicalId, record] of Object.entries(state.resources ?? {})) {
|
|
@@ -69122,6 +69383,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
69122
69383
|
};
|
|
69123
69384
|
});
|
|
69124
69385
|
const newOutputs = redactUnaccountedOutputs(outputSecrets.size > 0 ? redactSecretsForState(state.outputs, outputSecrets, outputsSourceUntrusted ? void 0 : outputsTemplateSource, TEMPLATE_SOURCED_RULES) : state.outputs, state.outputs, accountedOutputKeys, allRecordedSecrets(outputSecrets, perResourceSecrets, orphanSecrets));
|
|
69386
|
+
emitAbandonedScanNotes();
|
|
69125
69387
|
if (JSON.stringify(newOutputs) !== JSON.stringify(state.outputs)) recordsChanged++;
|
|
69126
69388
|
if (recordsChanged > 0 && !opts.dryRun) {
|
|
69127
69389
|
const { skippedOutputs: _droppedByScrub, ...carriedState } = state;
|
|
@@ -69139,6 +69401,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
69139
69401
|
secretsFound: totalSecrets,
|
|
69140
69402
|
secretBearingKeys: secretBearingKeys.length,
|
|
69141
69403
|
unverifiableReads: prePassFindings.unverifiable.length,
|
|
69404
|
+
unverifiableLeaves,
|
|
69142
69405
|
...malformedResources ? { malformedResources } : {},
|
|
69143
69406
|
outputs: newOutputs,
|
|
69144
69407
|
exportNameDisplay: (name) => secretSafeKeyDisplay(name, outputSecrets)
|
|
@@ -93473,4 +93736,4 @@ function buildProgram() {
|
|
|
93473
93736
|
|
|
93474
93737
|
//#endregion
|
|
93475
93738
|
export { buildProgram };
|
|
93476
|
-
//# sourceMappingURL=program-
|
|
93739
|
+
//# sourceMappingURL=program-BslZXdJR.js.map
|