@go-to-k/cdkd 0.284.9 → 0.284.11
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-B09ceB1r.js → asg-provider-C9OlqHee.js} +2 -2
- package/dist/{asg-provider-B09ceB1r.js.map → asg-provider-C9OlqHee.js.map} +1 -1
- package/dist/cli.js +241 -10
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-CY2fx4K1.js → deploy-engine-K5kHzsB-.js} +500 -41
- package/dist/deploy-engine-K5kHzsB-.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-CY2fx4K1.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as IntrinsicFunctionResolver, $n as StackHasActiveImportsError, $t as validateContainerRepoName, A as formatResourceLine, An as AssemblyReader, At as describeTypeWithThrottleRetry, B as isExportAliasCollision, Bt as WorkGraph, C as refusesFinalSnapshot, Cn as MIGRATE_TMP_PREFIX, Ct as s3BucketDomainName, D as MULTI_REGION_RECREATE_BLOCKED_TYPES, Dn as PARTITION_TABLE, Dt as applyRoleArnIfSet, E as extractDeploymentEventError, En as expectedOwnerParam, Et as s3BucketWebsiteUrl, F as red, Fn as getAwsClients, Ft as S3StateBackend, G as clearOnUpdateRemoval, Gn as LocalStartServiceError, Gt as escapeRegExp, H as stateKeySecretExposure, Hn as DeployCancelledError, Ht as createAssetRedirectResolver, I as yellow, In as resetAwsClients, It as rebuildClientForBucketRegion, J as findSilentDropProperties, Jn as NestedStackChildDirectDestroyError, Jt as BOOTSTRAP_MARKER_PREFIX, K as ProviderRegistry, Kt as stripControlChars, L as collectDeclaredOutputNames, Ln as setAwsClients, Lt as shouldRetainResource, M as cyan, Mt as DagBuilder, N as gray, Nn as resolveBucketRegion, Nt as TemplateParser, O as isStatefulRecreateTargetSync, On as canonicalizeRegion, Ot as DiffCalculator, P as green, Pn as AwsClients, Pt as LockManager, Q as isTerminationProtectionPropagationError, Qn as ResourceUpdateNotSupportedError, Qt as validateAssetBucketName, R as collectPublishedOutputNames, Rt as AssetPublisher, S as isFinalSnapshotError, Sn as CFN_TEMPLATE_URL_LIMIT, St as s3BucketArn, T as makeCanonicalizePropertiesFn, Tn as uploadCfnTemplate, Tt as s3BucketRegionalDomainName, U as IAMRoleProvider, Un as LocalInvokeBuildError$1, Ut as loadPublishableAssetManifest, V as secretBearingStateKeyWarning, Vt as buildAssetRedirectMap, W as collectInlinePolicyNamesManagedBySiblings, Wn as LocalMigrateError, Wt as rewriteTemplateAssetReferences, X as slowCcOperationTimeoutMs, Xn as ProvisioningError, Xt as getBootstrapMarkerKey, Y as CloudControlProvider, Yn as PartialFailureError, Yt as ensureAssetStorage, Z as disableInstanceApiTermination, Zn as ResourceTimeoutError, Zt as parseBootstrapMarker, _ as ATOMIC_FINAL_SNAPSHOT_TYPES, _n as resolveStateBucketWithDefaultAndSource, _t as TEMPLATE_SOURCED_RULES, a as DeploymentEventsStore, an as runDockerStreaming, ar as normalizeAwsError, at as resolveExplicitPhysicalId, b as ccRoutedFinalSnapshotError, bn as warnDeprecatedNoPrefixCliFlag, bt as redactSecretsForState, c as replayFailedOperations, cn as Synthesizer, cr as isRetryableTransientError, ct as configBooleanRefusal, d as updatePartialReason, dn as getLegacyStateBucketName, dt as replayWarn, en as buildDenyExternalAccessPolicy, er as StackTerminationProtectionError, et as cfnRefValueFromPhysicalId, f as UNSPECIFIED_SKIP_REASON, fn as resolveApp, ft as requireConfigArray, g as computeImplicitDeleteEdges, gn as resolveStateBucketWithDefault, gt as STATE_SOURCED_READBACK_RULES, h as IMPLICIT_DELETE_DEPENDENCIES, hn as resolveSkipPrefix, ht as STATE_SOURCED_CROSS_GENERATION_RULES, i as DeploymentEventsReader, in as runDockerForeground, it as normalizeAwsTagsToCfn, j as bold, jn as processStackMessages, jt as withRetry, k as renderStatefulReason, kn as derivePartitionAndUrlSuffix, kt as INTRINSIC_KEYS, l as replayRollback, ln as synthesisStatusMessage, lr as isThrottlingError, lt as configStringRefusal, m as withResourceDeadline, mn as resolveCaptureObservedState, mt as requireConfigString, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as formatDockerLoginError, nt as refStateLookupFromResource, o as planFailedOps, on as AssetManifestLoader, or as withErrorHandling, ot as assertRegionMatch, p as deleteSkipReason, pn as resolveAutoAssetStorage, pt as requireConfigObject, q as findActionableSilentDrops, qn as MissingCdkCliError, qt as AssetModeResolver, r as DeployEngine, rn as getDockerCmd, rt as WAFv2WebACLProvider, s as planRollback, sn as getDockerImageBySourceHash, sr as isMarkedNonRetryable, st as coerceCfnBoolean, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDockerImage, tt as getAccountInfo, u as updatePartialMessage, un as getDefaultStateBucketName, ur as markNonRetryable, ut as readConfigString, v as PRE_DELETE_SNAPSHOT_TYPES, vn as resolveUseCdkBootstrapAssets, vt as createSecretMasker, w as unsupportedFinalSnapshotError, wn as findLargeInlineResources, wt as s3BucketDualStackDomainName, x as createPreDeleteFinalSnapshot, xn as CFN_TEMPLATE_BODY_LIMIT, xt as scrubResourceRecord, y as buildFinalSnapshotIdentifier, yn as stateBucketExistenceConfirmed, yt as maskSecretsInText, z as exportAliasCollisionScrubWarning, zn as CdkdError, zt as stringifyValue } from "./deploy-engine-
|
|
2
|
+
import { $ as IntrinsicFunctionResolver, $n as StackHasActiveImportsError, $t as validateContainerRepoName, A as formatResourceLine, An as AssemblyReader, At as describeTypeWithThrottleRetry, B as isExportAliasCollision, Bt as WorkGraph, C as refusesFinalSnapshot, Cn as MIGRATE_TMP_PREFIX, Ct as s3BucketDomainName, D as MULTI_REGION_RECREATE_BLOCKED_TYPES, Dn as PARTITION_TABLE, Dt as applyRoleArnIfSet, E as extractDeploymentEventError, En as expectedOwnerParam, Et as s3BucketWebsiteUrl, F as red, Fn as getAwsClients, Ft as S3StateBackend, G as clearOnUpdateRemoval, Gn as LocalStartServiceError, Gt as escapeRegExp, H as stateKeySecretExposure, Hn as DeployCancelledError, Ht as createAssetRedirectResolver, I as yellow, In as resetAwsClients, It as rebuildClientForBucketRegion, J as findSilentDropProperties, Jn as NestedStackChildDirectDestroyError, Jt as BOOTSTRAP_MARKER_PREFIX, K as ProviderRegistry, Kt as stripControlChars, L as collectDeclaredOutputNames, Ln as setAwsClients, Lt as shouldRetainResource, M as cyan, Mt as DagBuilder, N as gray, Nn as resolveBucketRegion, Nt as TemplateParser, O as isStatefulRecreateTargetSync, On as canonicalizeRegion, Ot as DiffCalculator, P as green, Pn as AwsClients, Pt as LockManager, Q as isTerminationProtectionPropagationError, Qn as ResourceUpdateNotSupportedError, Qt as validateAssetBucketName, R as collectPublishedOutputNames, Rt as AssetPublisher, S as isFinalSnapshotError, Sn as CFN_TEMPLATE_URL_LIMIT, St as s3BucketArn, T as makeCanonicalizePropertiesFn, Tn as uploadCfnTemplate, Tt as s3BucketRegionalDomainName, U as IAMRoleProvider, Un as LocalInvokeBuildError$1, Ut as loadPublishableAssetManifest, V as secretBearingStateKeyWarning, Vt as buildAssetRedirectMap, W as collectInlinePolicyNamesManagedBySiblings, Wn as LocalMigrateError, Wt as rewriteTemplateAssetReferences, X as slowCcOperationTimeoutMs, Xn as ProvisioningError, Xt as getBootstrapMarkerKey, Y as CloudControlProvider, Yn as PartialFailureError, Yt as ensureAssetStorage, Z as disableInstanceApiTermination, Zn as ResourceTimeoutError, Zt as parseBootstrapMarker, _ as ATOMIC_FINAL_SNAPSHOT_TYPES, _n as resolveStateBucketWithDefaultAndSource, _t as TEMPLATE_SOURCED_RULES, a as DeploymentEventsStore, an as runDockerStreaming, ar as normalizeAwsError, at as resolveExplicitPhysicalId, b as ccRoutedFinalSnapshotError, bn as warnDeprecatedNoPrefixCliFlag, bt as redactSecretsForState, c as replayFailedOperations, cn as Synthesizer, cr as isRetryableTransientError, ct as configBooleanRefusal, d as updatePartialReason, dn as getLegacyStateBucketName, dt as replayWarn, en as buildDenyExternalAccessPolicy, er as StackTerminationProtectionError, et as cfnRefValueFromPhysicalId, f as UNSPECIFIED_SKIP_REASON, fn as resolveApp, ft as requireConfigArray, g as computeImplicitDeleteEdges, gn as resolveStateBucketWithDefault, gt as STATE_SOURCED_READBACK_RULES, h as IMPLICIT_DELETE_DEPENDENCIES, hn as resolveSkipPrefix, ht as STATE_SOURCED_CROSS_GENERATION_RULES, i as DeploymentEventsReader, in as runDockerForeground, it as normalizeAwsTagsToCfn, j as bold, jn as processStackMessages, jt as withRetry, k as renderStatefulReason, kn as derivePartitionAndUrlSuffix, kt as INTRINSIC_KEYS, l as replayRollback, ln as synthesisStatusMessage, lr as isThrottlingError, lt as configStringRefusal, m as withResourceDeadline, mn as resolveCaptureObservedState, mt as requireConfigString, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as formatDockerLoginError, nt as refStateLookupFromResource, o as planFailedOps, on as AssetManifestLoader, or as withErrorHandling, ot as assertRegionMatch, p as deleteSkipReason, pn as resolveAutoAssetStorage, pt as requireConfigObject, q as findActionableSilentDrops, qn as MissingCdkCliError, qt as AssetModeResolver, r as DeployEngine, rn as getDockerCmd, rt as WAFv2WebACLProvider, s as planRollback, sn as getDockerImageBySourceHash, sr as isMarkedNonRetryable, st as coerceCfnBoolean, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDockerImage, tt as getAccountInfo, u as updatePartialMessage, un as getDefaultStateBucketName, ur as markNonRetryable, ut as readConfigString, v as PRE_DELETE_SNAPSHOT_TYPES, vn as resolveUseCdkBootstrapAssets, vt as createSecretMasker, w as unsupportedFinalSnapshotError, wn as findLargeInlineResources, wt as s3BucketDualStackDomainName, x as createPreDeleteFinalSnapshot, xn as CFN_TEMPLATE_BODY_LIMIT, xt as scrubResourceRecord, y as buildFinalSnapshotIdentifier, yn as stateBucketExistenceConfirmed, yt as maskSecretsInText, z as exportAliasCollisionScrubWarning, zn as CdkdError, zt as stringifyValue } from "./deploy-engine-K5kHzsB-.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, 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-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-C9OlqHee.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";
|
|
@@ -60686,6 +60686,36 @@ var ScrubNeededError = class ScrubNeededError extends CdkdError {
|
|
|
60686
60686
|
* parameter; scrub only stops it from being re-read out of state going
|
|
60687
60687
|
* forward.
|
|
60688
60688
|
*
|
|
60689
|
+
* OUTPUTS: `state.outputs` is scrubbed alongside the resource records, and its
|
|
60690
|
+
* repair scope is WIDER than today's declared outputs (issue #2005). A stored
|
|
60691
|
+
* output key the template can still name is redacted BY POSITION against that
|
|
60692
|
+
* template; a key it cannot name is repaired when its stored value MATCHES a
|
|
60693
|
+
* secret plaintext recorded anywhere this run, including one only a RESOURCE
|
|
60694
|
+
* still references. The motivating member of that population is an output
|
|
60695
|
+
* DELETED in an ordinary refactor, but it is not the only one — a key this run
|
|
60696
|
+
* cannot COMPUTE is in it too, and a parameterized `Export.Name` (scrub has
|
|
60697
|
+
* only template defaults) leaves the deploy's real alias key unaccounted on
|
|
60698
|
+
* EVERY run. When nothing recorded the plaintext the value is left exactly as
|
|
60699
|
+
* it is: a scrub that cannot identify the needle must not guess, because
|
|
60700
|
+
* `state.outputs` is re-applied VERBATIM to consumer stacks — by the exports
|
|
60701
|
+
* index (`src/state/export-index-store.ts`) and by `Fn::ImportValue` /
|
|
60702
|
+
* `Fn::GetStackOutput` (`src/deployment/intrinsic-function-resolver.ts`) — so a
|
|
60703
|
+
* fabricated redaction ships a literal `{{resolve:...}}` token into a
|
|
60704
|
+
* consumer's own AWS call. (`cdkd drift` is NOT one of those readers: it reads
|
|
60705
|
+
* `state.resources`, never `state.outputs`.) See `redactUnaccountedOutputs`.
|
|
60706
|
+
*
|
|
60707
|
+
* A FOURTH re-apply reader exists and is recorded here so the next audit does
|
|
60708
|
+
* not have to re-derive it: `NestedStackProvider.buildOutputsAttributes`
|
|
60709
|
+
* projects a CHILD record's `state.outputs` into the PARENT's
|
|
60710
|
+
* `Fn::GetAtt Outputs.X` attributes, i.e. into a parent resource property. It
|
|
60711
|
+
* is OUT of this command's reach, not exempt from the hazard — `scrubCommand`
|
|
60712
|
+
* targets synth STACK ARTIFACTS, and a nested child is a `nestedTemplates`
|
|
60713
|
+
* entry on its parent's `StackInfo` rather than an `aws:cloudformation:stack`
|
|
60714
|
+
* artifact of its own (`src/synthesis/assembly-reader.ts`), so the
|
|
60715
|
+
* `{parent}~{Child}` record this walk reads is never a stack scrub writes. If
|
|
60716
|
+
* scrub ever gains nested-child targets, this reader joins the list above and
|
|
60717
|
+
* the fabrication bound has to be re-argued for it.
|
|
60718
|
+
*
|
|
60689
60719
|
* ORDERING: scrub matches the CURRENT resolved secret value against what state
|
|
60690
60720
|
* holds, so run it BEFORE rotating. Once the secret is rotated, the value in
|
|
60691
60721
|
* state no longer matches the current one and scrub cannot find it (it reports
|
|
@@ -60776,11 +60806,208 @@ async function scrubCommand(stacks, options) {
|
|
|
60776
60806
|
if (options.fail && totalStacksWithUnscrubbableKeys > 0) throw new ScrubNeededError();
|
|
60777
60807
|
}
|
|
60778
60808
|
/**
|
|
60809
|
+
* Every secret plaintext this run recorded, from ANY position in the template
|
|
60810
|
+
* (issue #2005) — the outputs' own map plus every resource's, filtered to the
|
|
60811
|
+
* values long enough to be a safe needle.
|
|
60812
|
+
*
|
|
60813
|
+
* Used ONLY by {@link redactUnaccountedOutputs}, which is why the union is built
|
|
60814
|
+
* here rather than kept as the pass's input everywhere: `outputSecrets` and
|
|
60815
|
+
* `perResourceSecrets` are deliberately SEPARATE bags so one resource's secret
|
|
60816
|
+
* value cannot rewrite another resource's coinciding literal (the collision the
|
|
60817
|
+
* deploy engine's `perResourceSecrets` doc describes), and widening the bag the
|
|
60818
|
+
* RESOURCE walk uses would re-open exactly that.
|
|
60819
|
+
*
|
|
60820
|
+
* On a value collision the outputs' expression wins: it is written last, and an
|
|
60821
|
+
* output KEY is the closest kin of the bag this map is scanned against. Both
|
|
60822
|
+
* expressions resolve to the same plaintext by construction (that is what makes
|
|
60823
|
+
* them collide), so the choice costs precision, not correctness — the residual
|
|
60824
|
+
* documented for the ambiguous-key fallback one screen up. A resource-vs-
|
|
60825
|
+
* resource collision is NOT ordered by anything: `perResourceSecrets` is
|
|
60826
|
+
* iterated in map order and the LAST logical id wins, so between two resources
|
|
60827
|
+
* resolving one plaintext the surviving expression is whichever the walk
|
|
60828
|
+
* reached last. Same trade as above (both resolve to the same value), stated
|
|
60829
|
+
* because only the outputs-vs-resource half used to be.
|
|
60830
|
+
*
|
|
60831
|
+
* **The needle floor is the security half, and it looks like it contradicts
|
|
60832
|
+
* repo policy without doing so.** `redactSecretsForState`'s no-source arm
|
|
60833
|
+
* matches a WHOLE VALUE at ANY length; only its substring arm honors
|
|
60834
|
+
* {@link MIN_NEEDLE_LENGTH}. That is sound for a POSITION-SCOPED bag, where the
|
|
60835
|
+
* candidate leaf is already known to belong to the reference. This union has no
|
|
60836
|
+
* position source at all, so an unfiltered 1-3 character plaintext recorded by
|
|
60837
|
+
* ANY resource would whole-value-match an unrelated stored output and rewrite
|
|
60838
|
+
* it onto that resource's expression. Issues #2012 / #2036 do say that with no
|
|
60839
|
+
* evidence OVER-redaction is the right failure, because it is "visible,
|
|
60840
|
+
* recoverable, and not a disclosure" — but that asymmetry is about a DRIFT
|
|
60841
|
+
* BASELINE, and it does not transfer here: `state.outputs` is re-applied
|
|
60842
|
+
* VERBATIM to consumer stacks by the exports index
|
|
60843
|
+
* (`src/state/export-index-store.ts`) and by `Fn::ImportValue` /
|
|
60844
|
+
* `Fn::GetStackOutput` (`src/deployment/intrinsic-function-resolver.ts`), so a
|
|
60845
|
+
* false redaction ships a literal `{{resolve:...}}` token into a consumer's own
|
|
60846
|
+
* AWS call — the #1934 class, a BREAK rather than a recoverable mismatch. (Two
|
|
60847
|
+
* readers, not the whole list: the module doc records a third that is out of
|
|
60848
|
+
* this command's reach, with the derivation.)
|
|
60849
|
+
*
|
|
60850
|
+
* The floor is applied to the WHOLE union, `outputSecrets` included, and that
|
|
60851
|
+
* narrows NOTHING that worked before. The positioning pass still runs FIRST
|
|
60852
|
+
* over the same bag with the UNFILTERED `outputSecrets`, whole-value-matching
|
|
60853
|
+
* at any length, and {@link redactUnaccountedOutputs} scans the STORED value —
|
|
60854
|
+
* so a sub-floor output secret it cannot see compares equal, falls through, and
|
|
60855
|
+
* the positioned leaf survives into `repaired ?? positioned`. Pinned by the
|
|
60856
|
+
* suite's "still repairs a SCALAR unaccounted key holding a sub-floor OUTPUT
|
|
60857
|
+
* secret" case (`ab1` -> its expression — the exact shape the false note
|
|
60858
|
+
* described) and by its non-scalar sibling "keeps the POSITIONED value". The
|
|
60859
|
+
* floor's effect is therefore confined to CROSS-RESOURCE needles: a sub-floor
|
|
60860
|
+
* plaintext recorded only by a RESOURCE never becomes a needle for the outputs
|
|
60861
|
+
* bag. Stated because an earlier revision claimed a narrowing here, and a false
|
|
60862
|
+
* "we gave this up" note is how a capability gets removed for real later.
|
|
60863
|
+
*
|
|
60864
|
+
* **What the floor does NOT bound, stated plainly because it is the residual a
|
|
60865
|
+
* reader will otherwise assume away.** `redactSecretsForState`'s SUBSTRING arm
|
|
60866
|
+
* runs over this union too, so a recorded plaintext of {@link
|
|
60867
|
+
* MIN_NEEDLE_LENGTH} characters or more occurring ANYWHERE inside an unrelated
|
|
60868
|
+
* unaccounted output WILL be rewritten. `secretValueFromJson('username')`
|
|
60869
|
+
* resolving `admin` turns a stored `https://admin.example.com` into
|
|
60870
|
+
* `https://{{resolve:secretsmanager:...}}.example.com`, which the exports index
|
|
60871
|
+
* republishes and a consumer's `Fn::ImportValue` then ships to AWS as a literal
|
|
60872
|
+
* token — the #1934 BREAK class, from a value that was never a secret.
|
|
60873
|
+
*
|
|
60874
|
+
* That arm is kept, and raising the floor for this union alone was considered
|
|
60875
|
+
* and REJECTED, on three grounds:
|
|
60876
|
+
*
|
|
60877
|
+
* - **Substring matching is what makes the repair work at all.** A connection
|
|
60878
|
+
* string embedding a password is #2005's own target shape and this suite's
|
|
60879
|
+
* own `DbUrl` case; gutting the arm would leave the motivating population
|
|
60880
|
+
* unrepairable, which is the disclosure the command exists to remove.
|
|
60881
|
+
* - **Length is a weak proxy for the hazard, in both directions.** The values
|
|
60882
|
+
* that collide with unrelated text are common tokens — `admin`, `prod`,
|
|
60883
|
+
* `root` — and raising the bar to 8 admits `password`, `postgres`,
|
|
60884
|
+
* `localhost`, which are longer AND likelier to occur inside a stored URL. A
|
|
60885
|
+
* higher floor would refuse genuine short secrets while still admitting the
|
|
60886
|
+
* colliding ones.
|
|
60887
|
+
* - **4 is the repo's ONE answer, and a second number would have no
|
|
60888
|
+
* derivation.** `secret-redaction.ts`'s own needles, `stateKeySecretExposure`
|
|
60889
|
+
* (#1919) and `drift.ts`'s `carriesRecordedSecret` all use it. A union-only 8
|
|
60890
|
+
* would be the only such constant in the tree justified by taste, and a later
|
|
60891
|
+
* reader could not tell which is authoritative.
|
|
60892
|
+
*
|
|
60893
|
+
* The trade is right for a GHSA repair command because the two failure
|
|
60894
|
+
* directions differ on REVERSIBILITY, not on likelihood. A fabricated rewrite
|
|
60895
|
+
* is loud and remediable: the consumer's next deploy fails on an unresolvable
|
|
60896
|
+
* token, and the operator fixes the template or edits the key out of state. An
|
|
60897
|
+
* unrepaired plaintext is silent and permanent — the key is one today's
|
|
60898
|
+
* template does not declare, so no redeploy ever rewrites it, and no other
|
|
60899
|
+
* command can. Refusing the substring arm here would trade the irreversible
|
|
60900
|
+
* failure for the reversible one in the wrong direction.
|
|
60901
|
+
*/
|
|
60902
|
+
function allRecordedSecrets(outputSecrets, perResourceSecrets) {
|
|
60903
|
+
const union = /* @__PURE__ */ new Map();
|
|
60904
|
+
for (const recorded of perResourceSecrets.values()) for (const [value, expression] of recorded) union.set(value, expression);
|
|
60905
|
+
for (const [value, expression] of outputSecrets) union.set(value, expression);
|
|
60906
|
+
for (const value of union.keys()) if (value.length < 4) union.delete(value);
|
|
60907
|
+
return union;
|
|
60908
|
+
}
|
|
60909
|
+
/**
|
|
60910
|
+
* Repair a stored output key today's template cannot ACCOUNT for (issue
|
|
60911
|
+
* [#2005](https://github.com/go-to-k/cdkd/issues/2005)) — the population `cdkd
|
|
60912
|
+
* scrub` is documented as the remedy for and could not actually remedy.
|
|
60913
|
+
*
|
|
60914
|
+
* `outputSecrets` is built from today's DECLARED outputs, and the outputs
|
|
60915
|
+
* redaction above is gated on it being non-empty. An output DELETED from the
|
|
60916
|
+
* template contributes nothing to that set, so a record whose only
|
|
60917
|
+
* secret-bearing output has since been removed was reported CLEAN by the
|
|
60918
|
+
* command whose job is to clean it, while the plaintext stayed in
|
|
60919
|
+
* `state.outputs` until an unrelated deploy happened to rewrite the bag. That
|
|
60920
|
+
* is precisely the record `cdkd diff` withholds from display
|
|
60921
|
+
* ([#1948](https://github.com/go-to-k/cdkd/issues/1948)): correctly hidden, and
|
|
60922
|
+
* until now not repairable.
|
|
60923
|
+
*
|
|
60924
|
+
* TWO scope decisions, and both are load-bearing.
|
|
60925
|
+
*
|
|
60926
|
+
* 1. **WHICH keys.** Only keys today's template cannot name — not in
|
|
60927
|
+
* `accountedKeys` (every declared output name plus every `Export.Name` this
|
|
60928
|
+
* run could compute). A key the template DOES name is left exactly as the
|
|
60929
|
+
* pass above leaves it, deliberately, because that pass POSITIONS it against
|
|
60930
|
+
* the template and this one cannot: scanning an accounted key against the
|
|
60931
|
+
* union bag would let one resource's secret plaintext rewrite a declared
|
|
60932
|
+
* output's coinciding literal onto that resource's expression — a value
|
|
60933
|
+
* state was never meant to hold, in the command that exists to make state
|
|
60934
|
+
* trustworthy. The accepted residual, stated because narrowing is what buys
|
|
60935
|
+
* the safety: a DECLARED output whose template value no longer resolves a
|
|
60936
|
+
* secret, but whose STORED value is still the stale plaintext of one a
|
|
60937
|
+
* resource carries, is not repaired here either. A redeploy rewrites it.
|
|
60938
|
+
*
|
|
60939
|
+
* 2. **WHAT may be rewritten.** Only a value that genuinely MATCHES a recorded
|
|
60940
|
+
* plaintext — `redactSecretsForState`'s value scan with no source, i.e. a
|
|
60941
|
+
* whole-value match and an embedded one, both bounded at
|
|
60942
|
+
* {@link MIN_NEEDLE_LENGTH} by {@link allRecordedSecrets}. A scrub that
|
|
60943
|
+
* cannot identify the needle must not guess: `state.outputs` is re-applied
|
|
60944
|
+
* VERBATIM to consumer stacks by the exports index and by `Fn::ImportValue`
|
|
60945
|
+
* / `Fn::GetStackOutput` (and by one more reader out of this command's
|
|
60946
|
+
* reach — see the module doc), so a fabricated redaction ships a literal
|
|
60947
|
+
* `{{resolve:...}}` token into a consumer's AWS call. No key is invented and
|
|
60948
|
+
* no key is removed for the same reason. When nothing this run recorded the
|
|
60949
|
+
* plaintext (the secret was deleted, rotated away, or the reference is gone
|
|
60950
|
+
* from the template too) the value is LEFT ALONE — but note that behavior
|
|
60951
|
+
* does NOT live in this function's `secrets.size === 0` guard, which is one
|
|
60952
|
+
* of three redundant reasons and fences none of them; see the guard's own
|
|
60953
|
+
* comment for what actually reaches it.
|
|
60954
|
+
*
|
|
60955
|
+
* **The scanned VALUE is the STORED one, never the positioned pass's output**
|
|
60956
|
+
* (blocker found in review). Scanning an already-positioned leaf means scanning
|
|
60957
|
+
* an expression the first pass just INSERTED, and `redactSecretsForState`'s
|
|
60958
|
+
* token guard protects only a WHOLE-VALUE token — so a MIXED leaf
|
|
60959
|
+
* (`postgres://admin:{{resolve:secretsmanager:prod/db:...}}@app-db`, the
|
|
60960
|
+
* `Fn::Join` shape CDK emits) gets re-scanned and any union needle occurring
|
|
60961
|
+
* INSIDE the inserted reference is spliced into it, corrupting the expression
|
|
60962
|
+
* into one no service can resolve (the corruption `secret-redaction.ts`'s own
|
|
60963
|
+
* token guard documents). A single union scan of the STORED value subsumes the
|
|
60964
|
+
* first pass for these keys rather than composing with it: the union is a
|
|
60965
|
+
* superset of `outputSecrets` with the outputs' expression winning a collision,
|
|
60966
|
+
* and an unaccounted key by construction has no position source in
|
|
60967
|
+
* `outputsTemplateSource` (every key that bag carries — a declared output name
|
|
60968
|
+
* or a literal `Export.Name` — is in `accountedKeys`), so the first pass could
|
|
60969
|
+
* only ever have value-scanned it too.
|
|
60970
|
+
*
|
|
60971
|
+
* **The repaired population is WIDER than "an output you deleted."** Any stored
|
|
60972
|
+
* key today's template cannot COMPUTE is in it, and a parameterized
|
|
60973
|
+
* `Export.Name` is the standing example: `scrub` has only template defaults, so
|
|
60974
|
+
* a name resolving to the literal `prefix-${Foo}` leaves the REAL alias key the
|
|
60975
|
+
* deploy wrote unaccounted on EVERY run, and that key gets cross-resource value
|
|
60976
|
+
* matching for as long as the parameter stays unresolvable here. That is the
|
|
60977
|
+
* accepted cost of not being able to reproduce the key — the alternative is
|
|
60978
|
+
* leaving a permanently unrepairable key — but it is a standing exposure to the
|
|
60979
|
+
* fabrication risk above rather than a one-off after a refactor.
|
|
60980
|
+
*
|
|
60981
|
+
* Returns the input by IDENTITY when nothing changed. That is worth doing on
|
|
60982
|
+
* its own terms (no churn, no needless clone), but it is NOT what keeps the
|
|
60983
|
+
* caller honest: the caller compares with `JSON.stringify` unconditionally, so
|
|
60984
|
+
* an identity return cannot make it cheaper and a fresh-but-equal object could
|
|
60985
|
+
* not make it report a repair it did not perform.
|
|
60986
|
+
*/
|
|
60987
|
+
function redactUnaccountedOutputs(positioned, stored, accountedKeys, secrets) {
|
|
60988
|
+
if (secrets.size === 0 || !positioned || !stored) return positioned;
|
|
60989
|
+
let repaired;
|
|
60990
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
60991
|
+
if (accountedKeys.has(key)) continue;
|
|
60992
|
+
const next = redactSecretsForState(value, secrets);
|
|
60993
|
+
if (JSON.stringify(next) === JSON.stringify(value)) continue;
|
|
60994
|
+
repaired ??= { ...positioned };
|
|
60995
|
+
repaired[key] = next;
|
|
60996
|
+
}
|
|
60997
|
+
return repaired ?? positioned;
|
|
60998
|
+
}
|
|
60999
|
+
/**
|
|
60779
61000
|
* Scrub one stack's state. Re-resolves the template's per-resource properties to
|
|
60780
61001
|
* learn the resolved secret VALUES, then replaces those values in the state
|
|
60781
61002
|
* record with their `{{resolve:...}}` expressions. Returns counts; performs no
|
|
60782
61003
|
* AWS mutation. Acquires the stack lock for the read-modify-write unless
|
|
60783
61004
|
* `dryRun`.
|
|
61005
|
+
*
|
|
61006
|
+
* The stack OUTPUTS bag is scrubbed too, in two passes: today's declared
|
|
61007
|
+
* outputs are redacted BY POSITION against the template, and a stored key
|
|
61008
|
+
* today's template cannot account for is repaired by VALUE MATCH alone — see
|
|
61009
|
+
* {@link redactUnaccountedOutputs} for why the two are separate and what each
|
|
61010
|
+
* deliberately declines to do.
|
|
60784
61011
|
*/
|
|
60785
61012
|
async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
60786
61013
|
const { logger } = opts;
|
|
@@ -60842,6 +61069,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
60842
61069
|
}
|
|
60843
61070
|
const templateOutputs = stack.template.Outputs ?? {};
|
|
60844
61071
|
const declaredOutputNames = collectDeclaredOutputNames(templateOutputs);
|
|
61072
|
+
const accountedOutputKeys = new Set(declaredOutputNames);
|
|
60845
61073
|
const ambiguousKeys = /* @__PURE__ */ new Set();
|
|
60846
61074
|
const collisions = [];
|
|
60847
61075
|
let outputsSourceUntrusted = false;
|
|
@@ -60863,7 +61091,9 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
60863
61091
|
logger.warn(`Export.Name of output ${name} could not be resolved during scrub (${err instanceof Error ? err.message : String(err)}) — redacting this stack's outputs by value match instead of by template position, since state may be keyed under a name this run cannot reproduce.`);
|
|
60864
61092
|
}
|
|
60865
61093
|
if (declaredExportName !== void 0 && typeof exportName !== "string") outputsSourceUntrusted = true;
|
|
60866
|
-
|
|
61094
|
+
const exportNameUnresolved = declaredExportName !== void 0 && typeof exportName === "string" && isUnresolvedValue(exportName, templateUsesSub(declaredExportName));
|
|
61095
|
+
if (typeof exportName === "string" && !exportNameUnresolved) accountedOutputKeys.add(exportName);
|
|
61096
|
+
if (exportNameUnresolved) {
|
|
60867
61097
|
outputsSourceUntrusted = true;
|
|
60868
61098
|
logger.warn(`Export.Name of output ${name} did not fully resolve during scrub — redacting this stack's outputs by value match instead of by template position, since state may be keyed under a name this run cannot reproduce.`);
|
|
60869
61099
|
} else if (typeof exportName === "string" && isExportAliasCollision(exportName, name, declaredOutputNames)) {
|
|
@@ -60919,7 +61149,7 @@ async function scrubStack(stack, region, stateBackend, lockManager, opts) {
|
|
|
60919
61149
|
if (JSON.stringify(scrubbed) !== JSON.stringify(record)) recordsChanged++;
|
|
60920
61150
|
newResources[logicalId] = scrubbed;
|
|
60921
61151
|
}
|
|
60922
|
-
const newOutputs = outputSecrets.size > 0 ? redactSecretsForState(state.outputs, outputSecrets, outputsSourceUntrusted ? void 0 : outputsTemplateSource) : state.outputs;
|
|
61152
|
+
const newOutputs = redactUnaccountedOutputs(outputSecrets.size > 0 ? redactSecretsForState(state.outputs, outputSecrets, outputsSourceUntrusted ? void 0 : outputsTemplateSource) : state.outputs, state.outputs, accountedOutputKeys, allRecordedSecrets(outputSecrets, perResourceSecrets));
|
|
60923
61153
|
if (JSON.stringify(newOutputs) !== JSON.stringify(state.outputs)) recordsChanged++;
|
|
60924
61154
|
if (recordsChanged > 0 && !opts.dryRun) {
|
|
60925
61155
|
const nextState = {
|
|
@@ -62060,11 +62290,12 @@ function containsDynamicReference(value) {
|
|
|
62060
62290
|
return false;
|
|
62061
62291
|
}
|
|
62062
62292
|
/**
|
|
62063
|
-
*
|
|
62064
|
-
*
|
|
62065
|
-
*
|
|
62066
|
-
*
|
|
62067
|
-
* that `redactSecretsForState` will not redact.
|
|
62293
|
+
* The threshold `secret-redaction.ts` applies when it builds its substring
|
|
62294
|
+
* needles. IMPORTED rather than copied since issue #2005 exported it (`cdkd
|
|
62295
|
+
* scrub` needed the same constant to bound its cross-resource union), so the
|
|
62296
|
+
* two can no longer disagree — and they must not, or this function would call a
|
|
62297
|
+
* leaf secret-bearing that `redactSecretsForState` will not redact. The local
|
|
62298
|
+
* alias is kept so the call sites below read unchanged.
|
|
62068
62299
|
*/
|
|
62069
62300
|
const MIN_SECRET_NEEDLE_LENGTH = 4;
|
|
62070
62301
|
/**
|
|
@@ -82481,7 +82712,7 @@ function createMigrateCommand() {
|
|
|
82481
82712
|
*/
|
|
82482
82713
|
function buildProgram() {
|
|
82483
82714
|
const program = new Command();
|
|
82484
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.
|
|
82715
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.11");
|
|
82485
82716
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
82486
82717
|
const { profile } = actionCommand.optsWithGlobals();
|
|
82487
82718
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|