@go-to-k/cdkd 0.291.22 → 0.291.23
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-B5acrjOr.js → asg-provider-ChgXLoRA.js} +2 -2
- package/dist/{asg-provider-B5acrjOr.js.map → asg-provider-ChgXLoRA.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-DGV6XNWO.js → deploy-engine-CCvNP_dA.js} +544 -169
- package/dist/deploy-engine-CCvNP_dA.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-BKAJWkPf.js → program-nxeZp1Qs.js} +40 -228
- package/dist/{program-BKAJWkPf.js.map → program-nxeZp1Qs.js.map} +1 -1
- package/dist/{version-BuD41yiK.js → version-BLo_Pzd-.js} +2 -2
- package/dist/{version-BuD41yiK.js.map → version-BLo_Pzd-.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-DGV6XNWO.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as displayAwsMessage, D as isPasteableIdent, E as displayStackName, O as truncateCodePoints, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-Bt0RGDfb.js";
|
|
2
2
|
import { a as setAssumedRoleCredentials, c as currentStackAwsScope, i as getPreAssumeEnvCredentials, l as runInStackAwsScope, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-jISU4PHo.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-BLo_Pzd-.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -7389,7 +7389,7 @@ var Synthesizer = class {
|
|
|
7389
7389
|
let accountId;
|
|
7390
7390
|
try {
|
|
7391
7391
|
const stsClient = new STSClient({
|
|
7392
|
-
...
|
|
7392
|
+
...ambientClientDefaults(),
|
|
7393
7393
|
...region && { region }
|
|
7394
7394
|
});
|
|
7395
7395
|
accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
@@ -7485,7 +7485,7 @@ var Synthesizer = class {
|
|
|
7485
7485
|
let accountId = resolved?.accountId;
|
|
7486
7486
|
if (resolved === void 0 && !options.stateBucket) try {
|
|
7487
7487
|
const stsClient = new STSClient({
|
|
7488
|
-
...
|
|
7488
|
+
...ambientClientDefaults(),
|
|
7489
7489
|
region
|
|
7490
7490
|
});
|
|
7491
7491
|
accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
@@ -16238,13 +16238,14 @@ const CONFLICTING_PLAINTEXT = Symbol("conflicting plaintext");
|
|
|
16238
16238
|
* no bag can equal, which refuses it exactly as the scan did.
|
|
16239
16239
|
*
|
|
16240
16240
|
* The branch is HARD to reach from the resolver — one resolver's
|
|
16241
|
-
* `cachedDynamicReferences` yields one plaintext per expression
|
|
16242
|
-
* pass cannot produce two — but it is no
|
|
16243
|
-
* that cache became per-resolver (issue
|
|
16244
|
-
*
|
|
16245
|
-
* merging their maps lands exactly here.
|
|
16246
|
-
* API regardless, and it is FENCED, by
|
|
16247
|
-
* plaintext" case. An earlier draft of
|
|
16241
|
+
* `cachedDynamicReferences` yields one plaintext per expression and credential
|
|
16242
|
+
* identity (issue #3660), so a single pass cannot produce two — but it is no
|
|
16243
|
+
* longer unreachable from there since that cache became per-resolver (issue
|
|
16244
|
+
* #1933): two resolvers in two regions legitimately resolve one expression to
|
|
16245
|
+
* two different plaintexts, and a caller merging their maps lands exactly here.
|
|
16246
|
+
* It is reachable through this module's API regardless, and it is FENCED, by
|
|
16247
|
+
* the "recorded against MORE THAN ONE plaintext" case. An earlier draft of
|
|
16248
|
+
* this comment claimed the divergence was
|
|
16248
16249
|
* unobservable, reasoning that `plaintextOf[E] === bag` implies
|
|
16249
16250
|
* `secrets.get(bag) === E` so accepting and falling back agree. That misses the
|
|
16250
16251
|
* case where a SECOND candidate also matches: accepting `E` then makes it two
|
|
@@ -29809,11 +29810,26 @@ function isDeliberateResolutionRefusal(err) {
|
|
|
29809
29810
|
if (errorCauseChain(err).some((link) => link instanceof IntrinsicResolutionRefusalError)) return true;
|
|
29810
29811
|
return isNamelessDynamicReferenceError(err);
|
|
29811
29812
|
}
|
|
29812
|
-
let cachedAccountIdentity = null;
|
|
29813
29813
|
/**
|
|
29814
|
-
*
|
|
29814
|
+
* The real (non-fabricated) account identity, per CREDENTIAL IDENTITY: keyed by
|
|
29815
|
+
* {@link credentialFingerprint} of the active `AwsClients`' credential
|
|
29816
|
+
* configuration (issue [#3660](https://github.com/go-to-k/cdkd/issues/3660)).
|
|
29817
|
+
*
|
|
29818
|
+
* Process-wide, and a CLI run has one identity, so for the CLI this holds one
|
|
29819
|
+
* entry. A LIBRARY caller can install `AwsClients` for account A, deploy, then
|
|
29820
|
+
* install account B's in the same process (or run both in per-stack scopes);
|
|
29821
|
+
* keyed by nothing, B's `AWS::AccountId` and every ARN built from it resolved
|
|
29822
|
+
* as A's. The fabricated window and the in-flight slot below share the key.
|
|
29823
|
+
*/
|
|
29824
|
+
const cachedAccountIdentities = /* @__PURE__ */ new Map();
|
|
29825
|
+
/**
|
|
29826
|
+
* Availability-zone names per (credential identity, region): keyed by
|
|
29827
|
+
* `injectiveKey(credentialFingerprint, region)` (issue
|
|
29828
|
+
* [#3660](https://github.com/go-to-k/cdkd/issues/3660)). The zone list is an
|
|
29829
|
+
* ACCOUNT's answer — an opt-in or restricted zone is visible to one account and
|
|
29830
|
+
* not another — so a region-only key served one identity's list to the next.
|
|
29815
29831
|
*/
|
|
29816
|
-
const cachedAvailabilityZones =
|
|
29832
|
+
const cachedAvailabilityZones = /* @__PURE__ */ new Map();
|
|
29817
29833
|
/**
|
|
29818
29834
|
* The `{{resolve:...}}` expressions this process has PROVEN resolve to a
|
|
29819
29835
|
* SECRET, reached through `secret-redaction.ts`'s `recordedSecretExpressions`
|
|
@@ -30288,9 +30304,12 @@ function isClientSafeRegion(region) {
|
|
|
30288
30304
|
}
|
|
30289
30305
|
/** Test seam for {@link FABRICATED_ACCOUNT_INFO_TTL_MS} expiry. */
|
|
30290
30306
|
const accountInfoClock = { now: () => Date.now() };
|
|
30291
|
-
|
|
30307
|
+
/** The bounded fabricated-answer window, per credential identity (see {@link cachedAccountIdentities}). */
|
|
30308
|
+
const fabricatedAccountIdentities = /* @__PURE__ */ new Map();
|
|
30292
30309
|
/**
|
|
30293
|
-
* The single in-flight lookup, so N concurrent callers
|
|
30310
|
+
* The single in-flight lookup PER CREDENTIAL IDENTITY, so N concurrent callers
|
|
30311
|
+
* of one identity share ONE round trip, and a second identity never joins the
|
|
30312
|
+
* first's (issue #3660).
|
|
30294
30313
|
*
|
|
30295
30314
|
* The TTL above collapses SEQUENTIAL callers; this collapses PARALLEL ones
|
|
30296
30315
|
* (PR review). `cdkd deploy --concurrency 10` resolves ten resources' intrinsics
|
|
@@ -30298,34 +30317,43 @@ let fabricatedAccountIdentity = null;
|
|
|
30298
30317
|
* each with the SDK's own 3-attempt retry — and ten identical warnings per
|
|
30299
30318
|
* window. Cleared in a `finally` so a failure cannot wedge it.
|
|
30300
30319
|
*/
|
|
30301
|
-
|
|
30320
|
+
const accountInfoInFlight = /* @__PURE__ */ new Map();
|
|
30302
30321
|
/**
|
|
30303
30322
|
* Bumped by {@link resetAccountInfoCache}, so a lookup that was already in
|
|
30304
30323
|
* flight cannot write the cache it was asked to forget.
|
|
30305
30324
|
*
|
|
30306
30325
|
* Without it the reset only cleared the SETTLED caches: an in-flight resolve
|
|
30307
|
-
* would land afterwards and re-populate `
|
|
30326
|
+
* would land afterwards and re-populate `cachedAccountIdentities`, so the next
|
|
30308
30327
|
* caller read the pre-reset account. That is the `*Once`-leak shape one layer
|
|
30309
30328
|
* down — a later test silently inheriting an earlier one's answer — and the
|
|
30310
30329
|
* reset's own comment already claimed to forget it.
|
|
30311
30330
|
*/
|
|
30312
30331
|
let accountInfoGeneration = 0;
|
|
30313
30332
|
/**
|
|
30314
|
-
* Get AWS account information from STS
|
|
30333
|
+
* Get AWS account information from STS, for the ACTIVE credential identity.
|
|
30334
|
+
*
|
|
30335
|
+
* `identityKey` is read FIRST and synchronously, and `resolveAccountIdentity`
|
|
30336
|
+
* reads `getAwsClients().sts` before its first `await`, so the key and the
|
|
30337
|
+
* client that answers come from ONE reading of the active clients (issue
|
|
30338
|
+
* #3660). Never log the key.
|
|
30315
30339
|
*/
|
|
30316
30340
|
async function getAccountInfo(overrideRegion) {
|
|
30317
|
-
|
|
30318
|
-
|
|
30319
|
-
if (
|
|
30320
|
-
const
|
|
30321
|
-
|
|
30341
|
+
const identityKey = credentialFingerprint(ambientCredentialConfig());
|
|
30342
|
+
const cached = cachedAccountIdentities.get(identityKey);
|
|
30343
|
+
if (cached) return accountInfoFor(cached, overrideRegion);
|
|
30344
|
+
const fabricated = fabricatedAccountIdentities.get(identityKey);
|
|
30345
|
+
if (fabricated && accountInfoClock.now() < fabricated.expiresAt) return accountInfoFor(fabricated.identity, overrideRegion);
|
|
30346
|
+
const pending = accountInfoInFlight.get(identityKey);
|
|
30347
|
+
if (pending) return accountInfoFor(await pending, overrideRegion);
|
|
30348
|
+
const inFlight = resolveAccountIdentity(identityKey);
|
|
30349
|
+
accountInfoInFlight.set(identityKey, inFlight);
|
|
30322
30350
|
try {
|
|
30323
30351
|
return accountInfoFor(await inFlight, overrideRegion);
|
|
30324
30352
|
} finally {
|
|
30325
|
-
if (accountInfoInFlight === inFlight) accountInfoInFlight
|
|
30353
|
+
if (accountInfoInFlight.get(identityKey) === inFlight) accountInfoInFlight.delete(identityKey);
|
|
30326
30354
|
}
|
|
30327
30355
|
}
|
|
30328
|
-
async function resolveAccountIdentity() {
|
|
30356
|
+
async function resolveAccountIdentity(identityKey) {
|
|
30329
30357
|
const generation = accountInfoGeneration;
|
|
30330
30358
|
const stillCurrent = () => generation === accountInfoGeneration;
|
|
30331
30359
|
const logger = getLogger().child("IntrinsicFunctionResolver");
|
|
@@ -30337,13 +30365,13 @@ async function resolveAccountIdentity() {
|
|
|
30337
30365
|
accountId,
|
|
30338
30366
|
...response.Account ? {} : { fabricated: true }
|
|
30339
30367
|
};
|
|
30340
|
-
if (!stillCurrent()) {} else if (resolved.fabricated)
|
|
30368
|
+
if (!stillCurrent()) {} else if (resolved.fabricated) fabricatedAccountIdentities.set(identityKey, {
|
|
30341
30369
|
identity: resolved,
|
|
30342
30370
|
expiresAt: accountInfoClock.now() + FABRICATED_ACCOUNT_INFO_TTL_MS
|
|
30343
|
-
};
|
|
30371
|
+
});
|
|
30344
30372
|
else {
|
|
30345
|
-
|
|
30346
|
-
|
|
30373
|
+
cachedAccountIdentities.set(identityKey, resolved);
|
|
30374
|
+
fabricatedAccountIdentities.delete(identityKey);
|
|
30347
30375
|
}
|
|
30348
30376
|
logger.debug(`Retrieved AWS account info: ${accountId}`);
|
|
30349
30377
|
return resolved;
|
|
@@ -30355,13 +30383,13 @@ async function resolveAccountIdentity() {
|
|
|
30355
30383
|
};
|
|
30356
30384
|
if (!stillCurrent()) return fallback;
|
|
30357
30385
|
if (fallback.fabricated) {
|
|
30358
|
-
if (!
|
|
30386
|
+
if (!cachedAccountIdentities.has(identityKey)) fabricatedAccountIdentities.set(identityKey, {
|
|
30359
30387
|
identity: fallback,
|
|
30360
30388
|
expiresAt: accountInfoClock.now() + FABRICATED_ACCOUNT_INFO_TTL_MS
|
|
30361
|
-
};
|
|
30389
|
+
});
|
|
30362
30390
|
} else {
|
|
30363
|
-
|
|
30364
|
-
|
|
30391
|
+
cachedAccountIdentities.set(identityKey, fallback);
|
|
30392
|
+
fabricatedAccountIdentities.delete(identityKey);
|
|
30365
30393
|
}
|
|
30366
30394
|
return fallback;
|
|
30367
30395
|
}
|
|
@@ -30738,10 +30766,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30738
30766
|
* ({@link cfnExportsPromises}, {@link cfnStackOutputsCache}) carry the
|
|
30739
30767
|
* fingerprint too.
|
|
30740
30768
|
*
|
|
30741
|
-
*
|
|
30742
|
-
* (
|
|
30743
|
-
*
|
|
30744
|
-
*
|
|
30769
|
+
* The VALUE caches carry the fingerprint as well (issue
|
|
30770
|
+
* [#3660](https://github.com/go-to-k/cdkd/issues/3660)):
|
|
30771
|
+
* {@link cachedDynamicReferences}, and the process-global account identity
|
|
30772
|
+
* behind `getAccountInfo` and `cachedAvailabilityZones`.
|
|
30745
30773
|
*/
|
|
30746
30774
|
regionScopedClients = /* @__PURE__ */ new Map();
|
|
30747
30775
|
/**
|
|
@@ -30759,9 +30787,9 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30759
30787
|
*
|
|
30760
30788
|
* A whole resolver rather than a client bag, because what has to be
|
|
30761
30789
|
* region-scoped is not only the lookup but the VALUE CACHE behind it:
|
|
30762
|
-
* {@link cachedDynamicReferences} is keyed by the expression
|
|
30763
|
-
*
|
|
30764
|
-
* #1933). Resolving a producer's expression inside THIS resolver would put a
|
|
30790
|
+
* {@link cachedDynamicReferences} is keyed by the expression (plus the
|
|
30791
|
+
* credential identity, issue #3660) and is sound only because one resolver
|
|
30792
|
+
* stands for one stack in one region (issue #1933). Resolving a producer's expression inside THIS resolver would put a
|
|
30765
30793
|
* foreign region's answer under a key the consumer's own lookups read — the
|
|
30766
30794
|
* exact cross-region leak that field's instance scope closed. A separate
|
|
30767
30795
|
* resolver per producer region keeps that invariant by construction.
|
|
@@ -30858,7 +30886,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
30858
30886
|
physicalIdFallbackCount = 0;
|
|
30859
30887
|
/**
|
|
30860
30888
|
* Resolved `{{resolve:secretsmanager:...}}` / `{{resolve:ssm:...}}` values,
|
|
30861
|
-
* keyed by the full expression
|
|
30889
|
+
* keyed by the full expression and the credential identity that read it
|
|
30890
|
+
* (`injectiveKey(credentialFingerprint, expression)`, issue
|
|
30891
|
+
* [#3660](https://github.com/go-to-k/cdkd/issues/3660): a library caller can
|
|
30892
|
+
* drive one resolver under two `AwsClients` identities, and the same NAME
|
|
30893
|
+
* read by two accounts is two values) — INSTANCE-scoped, which closes the CACHE half
|
|
30862
30894
|
* of issue [#1933](https://github.com/go-to-k/cdkd/issues/1933). The issue is
|
|
30863
30895
|
* only PARTIALLY addressed by this field: see "what this does NOT settle"
|
|
30864
30896
|
* below.
|
|
@@ -34124,7 +34156,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
34124
34156
|
region = (await getAccountInfo(this.resolverRegion)).region;
|
|
34125
34157
|
clientRegion = this.explicitRegion;
|
|
34126
34158
|
}
|
|
34127
|
-
const
|
|
34159
|
+
const azCacheKey = injectiveKey(credentialFingerprint(ambientCredentialConfig()), region);
|
|
34160
|
+
const cached = cachedAvailabilityZones.get(azCacheKey);
|
|
34128
34161
|
if (cached) {
|
|
34129
34162
|
this.logger.debug(`Resolved Fn::GetAZs from cache: ${this.displayMasked(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(cached, context))}`);
|
|
34130
34163
|
return cached;
|
|
@@ -34145,7 +34178,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
34145
34178
|
throw new Error(`Fn::GetAZs: failed to describe availability zones for region '${loggedRegion}': ${masks.text(error instanceof Error ? error.message : String(error))}`);
|
|
34146
34179
|
}
|
|
34147
34180
|
if (azNames.length === 0) throw new Error(`Fn::GetAZs: no availability zones returned for region '${this.displayMasked(loggedRegionText ?? region, context)}'. Either the region is not enabled on this account (opt-in regions must be enabled before use), or the request was answered by a different region's endpoint.`);
|
|
34148
|
-
cachedAvailabilityZones
|
|
34181
|
+
cachedAvailabilityZones.set(azCacheKey, azNames);
|
|
34149
34182
|
this.logger.debug(`Resolved Fn::GetAZs: ${this.displayMasked(loggedRegionText ?? region, context)} -> ${JSON.stringify(this.maskValueLeaves(azNames, context))}`);
|
|
34150
34183
|
return azNames;
|
|
34151
34184
|
}
|
|
@@ -34860,7 +34893,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
34860
34893
|
if (!foreign.complete || foreign.substitutions.length !== 1) complete = false;
|
|
34861
34894
|
continue;
|
|
34862
34895
|
}
|
|
34863
|
-
const
|
|
34896
|
+
const dynamicReferenceCacheKey = injectiveKey(credentialFingerprint(ambientCredentialConfig()), fullMatch);
|
|
34897
|
+
const cached = this.cachedDynamicReferences.get(dynamicReferenceCacheKey);
|
|
34864
34898
|
if (cached) {
|
|
34865
34899
|
if (cached.secret && cached.value) {
|
|
34866
34900
|
context?.recordedSecretValues?.set(cached.value, fullMatch);
|
|
@@ -34917,7 +34951,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
34917
34951
|
complete = false;
|
|
34918
34952
|
continue;
|
|
34919
34953
|
}
|
|
34920
|
-
if (cacheable) this.cachedDynamicReferences.set(
|
|
34954
|
+
if (cacheable) this.cachedDynamicReferences.set(dynamicReferenceCacheKey, {
|
|
34921
34955
|
value: resolved,
|
|
34922
34956
|
secret: isSecret
|
|
34923
34957
|
});
|
|
@@ -35900,6 +35934,454 @@ function unescapeJsonPointerSegment(segment) {
|
|
|
35900
35934
|
return segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
35901
35935
|
}
|
|
35902
35936
|
|
|
35937
|
+
//#endregion
|
|
35938
|
+
//#region src/provisioning/masked-retry-logger.ts
|
|
35939
|
+
/**
|
|
35940
|
+
* The masker a caller supplied, or the identity function when it supplied
|
|
35941
|
+
* none.
|
|
35942
|
+
*
|
|
35943
|
+
* ABSENT MEANS UNMASKED, and that is the back-compatible default the contract
|
|
35944
|
+
* mandates — `create()` / `update()` are also reached from the import path,
|
|
35945
|
+
* from `cdkd drift --revert`, and from tests, and a provider must not care
|
|
35946
|
+
* which caller it got. Centralised here so no call site re-spells the `??`
|
|
35947
|
+
* and accidentally makes the capability required.
|
|
35948
|
+
*/
|
|
35949
|
+
function maskerOrIdentity(maskSecrets) {
|
|
35950
|
+
return maskSecrets ?? ((text) => text);
|
|
35951
|
+
}
|
|
35952
|
+
/**
|
|
35953
|
+
* A {@link RetryLogger} whose every line is routed through `maskSecrets`.
|
|
35954
|
+
*
|
|
35955
|
+
* `warn` is ALWAYS provided, never omitted. It is optional on `RetryLogger`,
|
|
35956
|
+
* and dropping it would silence the give-up summary on the calling path only —
|
|
35957
|
+
* trading a disclosure for the reporting hole issue #2018 closed. It goes
|
|
35958
|
+
* through the SAME mask as `debug` precisely because it is the line that
|
|
35959
|
+
* survives a run without `--verbose`; forwarding it unmasked would defeat the
|
|
35960
|
+
* fence at a HIGHER log level than the one the fence was written for.
|
|
35961
|
+
*
|
|
35962
|
+
* `logger` is typed structurally rather than as the concrete `Logger` so this
|
|
35963
|
+
* module stays a leaf and a test can pass two spies. That matches the
|
|
35964
|
+
* precedent already in the tree (`buildMfaConfigRequest` in
|
|
35965
|
+
* `cognito-provider.ts` takes an injected `logger?: { warn }`).
|
|
35966
|
+
*/
|
|
35967
|
+
function createMaskedRetryLogger(logger, maskSecrets) {
|
|
35968
|
+
const mask = maskerOrIdentity(maskSecrets);
|
|
35969
|
+
return {
|
|
35970
|
+
debug: (message) => logger.debug(mask(message)),
|
|
35971
|
+
warn: (message) => logger.warn(mask(message))
|
|
35972
|
+
};
|
|
35973
|
+
}
|
|
35974
|
+
/**
|
|
35975
|
+
* Depth cap for {@link maskDeep}.
|
|
35976
|
+
*
|
|
35977
|
+
* What it actually bounds is unbounded WORK on a pathologically deep bag — see
|
|
35978
|
+
* the rationale at the cap itself, which is the authority. It is NOT primarily
|
|
35979
|
+
* a cycle guard: a template-derived bag cannot be cyclic, so that justification
|
|
35980
|
+
* over-claims (issue #2176 security review). It does still terminate one, which
|
|
35981
|
+
* is why the walk is safe to share with a caller that might hand it something
|
|
35982
|
+
* other than a template bag.
|
|
35983
|
+
*
|
|
35984
|
+
* Deliberately generous. Every shape these warnings exist to describe is a
|
|
35985
|
+
* scalar, a list of scalars, or a small record, so the walk does not reach
|
|
35986
|
+
* depth 2 in practice.
|
|
35987
|
+
*/
|
|
35988
|
+
const MASK_WALK_MAX_DEPTH = 8;
|
|
35989
|
+
/**
|
|
35990
|
+
* What {@link maskDeep} substitutes for a subtree it declines to descend into.
|
|
35991
|
+
*
|
|
35992
|
+
* MUST equal `SECRET_MASK` in `src/deployment/secret-redaction.ts`. It is
|
|
35993
|
+
* spelled here rather than imported so this module keeps its single-import leaf
|
|
35994
|
+
* property (see the module note above); the two are fenced against drift by a
|
|
35995
|
+
* test that imports both.
|
|
35996
|
+
*/
|
|
35997
|
+
const MASK_WALK_DEPTH_CAP_MARKER = "***";
|
|
35998
|
+
/**
|
|
35999
|
+
* Mask every string LEAF and KEY of an arbitrary value, returning a structure
|
|
36000
|
+
* safe to `JSON.stringify` into a log line or an error message (issue
|
|
36001
|
+
* [#2176](https://github.com/go-to-k/cdkd/issues/2176)).
|
|
36002
|
+
*
|
|
36003
|
+
* THE ORDERING IS THE WHOLE POINT, and it is why masking the finished message
|
|
36004
|
+
* is not a substitute. `maskSecretsInText` matches by literal occurrence, so:
|
|
36005
|
+
*
|
|
36006
|
+
* 1. ESCAPING. `JSON.stringify` escapes `"`, `\` and newlines, so a secret
|
|
36007
|
+
* containing any of them no longer OCCURS in the stringified text and a
|
|
36008
|
+
* mask applied afterwards cannot find it. That is not an exotic case — it
|
|
36009
|
+
* is every Secrets Manager JSON document, the commonest real secret shape.
|
|
36010
|
+
* Measured on the pre-fix tree for #2176: a plaintext of
|
|
36011
|
+
* `{"user":"admin","pw":"hunter2"}` interpolated as
|
|
36012
|
+
* `${JSON.stringify(value)}` came through a message-level mask COMPLETELY
|
|
36013
|
+
* unchanged, while masking the leaves first rendered it `"***"`.
|
|
36014
|
+
* 2. LENGTH. A finished message is always longer than the value inside it, so
|
|
36015
|
+
* it can only ever reach `maskSecretsInText`'s SUBSTRING arm, which ignores
|
|
36016
|
+
* needles below `MIN_NEEDLE_LENGTH` (4). Handing the masker each RAW leaf
|
|
36017
|
+
* reaches the WHOLE-VALUE arm, which has no floor. Measured the same way: a
|
|
36018
|
+
* 3-character secret survived `Value 'abc' at 'pin' failed ...` intact.
|
|
36019
|
+
*
|
|
36020
|
+
* Neither pass subsumes the other, so providers do BOTH — this walk on the
|
|
36021
|
+
* value, and the assembled message through the masker as well. The masker is
|
|
36022
|
+
* idempotent, so the overlap is free.
|
|
36023
|
+
*
|
|
36024
|
+
* Keys are masked as well as values: `JSON.stringify` renders them into the
|
|
36025
|
+
* same line, so a resolved secret used as a map key would otherwise escape.
|
|
36026
|
+
*
|
|
36027
|
+
* WHY THIS LIVES HERE rather than as a private walk per provider. SIX
|
|
36028
|
+
* hand-rolled copies had already accumulated — `elbv2-provider.ts`,
|
|
36029
|
+
* `cognito-provider.ts`, `sns-topic-provider.ts`, `dynamodb-table-provider.ts`,
|
|
36030
|
+
* `dynamodb-globaltable-provider.ts` and `apigatewayv2-provider.ts` — and
|
|
36031
|
+
* `elbv2-provider.ts` carried the standing instruction that "a THIRD site is
|
|
36032
|
+
* the point at which this should move into `../masked-retry-logger.ts` ... and
|
|
36033
|
+
* all three converge on it". That trigger had fired and been missed twice over,
|
|
36034
|
+
* and the copies had already diverged exactly as predicted: FOUR of the six
|
|
36035
|
+
* carried NO depth cap at all.
|
|
36036
|
+
*
|
|
36037
|
+
* Worth recording how the last two were nearly missed AGAIN, because it is the
|
|
36038
|
+
* same failure one level up: the first sweep for this issue grepped for
|
|
36039
|
+
* `maskDeep` / `MASK_WALK_MAX_DEPTH` — the SPELLINGS the known copies used —
|
|
36040
|
+
* and the two survivors spell it `maskLeaf` / `maskLeafValue` with no named
|
|
36041
|
+
* constant. Grep for the SHAPE, not the name. These copies encode a security
|
|
36042
|
+
* contract, and a hardening applied to one silently leaves the others behind.
|
|
36043
|
+
*/
|
|
36044
|
+
function maskDeep(value, mask, depth = 0) {
|
|
36045
|
+
if (typeof value === "string") return mask(value);
|
|
36046
|
+
if (depth >= 8) return "***";
|
|
36047
|
+
if (Array.isArray(value)) return value.map((entry) => maskDeep(entry, mask, depth + 1));
|
|
36048
|
+
if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [mask(k), maskDeep(v, mask, depth + 1)]));
|
|
36049
|
+
return value;
|
|
36050
|
+
}
|
|
36051
|
+
|
|
36052
|
+
//#endregion
|
|
36053
|
+
//#region src/provisioning/composite-id.ts
|
|
36054
|
+
/**
|
|
36055
|
+
* Refuse a composite physicalId whose segments would make it ambiguous
|
|
36056
|
+
* (issue [#1672](https://github.com/go-to-k/cdkd/issues/1672)).
|
|
36057
|
+
*
|
|
36058
|
+
* ## Why a composite id exists at all
|
|
36059
|
+
*
|
|
36060
|
+
* `ResourceProvider` passes a SINGLE `physicalId: string` as a resource's
|
|
36061
|
+
* identity, while CloudFormation keeps the physical id and its containing
|
|
36062
|
+
* scope as separate values. So every provider whose type needs more than one
|
|
36063
|
+
* value to address the resource PACKS them into one string joined by
|
|
36064
|
+
* {@link COMPOSITE_ID_SEPARATOR} — `<databaseName>|<tableName>` for
|
|
36065
|
+
* `AWS::Glue::Table`, `<apiId>|<typeName>|<fieldName>` for
|
|
36066
|
+
* `AWS::AppSync::Resolver`, and so on.
|
|
36067
|
+
*
|
|
36068
|
+
* ## Why it has to be guarded
|
|
36069
|
+
*
|
|
36070
|
+
* The separator is NOT escaped, so a segment that itself contains a `|`
|
|
36071
|
+
* produces an id with the wrong arity. Every decode site splits the stored id
|
|
36072
|
+
* back apart and the extra segment shifts everything — for a Glue table named
|
|
36073
|
+
* `a|b` in database `mydb` the recorded id `mydb|a|b` decodes to database
|
|
36074
|
+
* `mydb`, table `a`. Both halves are non-empty, so every existing
|
|
36075
|
+
* "is this id well-formed?" guard passes and the deploy SUCCEEDS. What breaks
|
|
36076
|
+
* is everything afterwards: `cdkd destroy` deletes the WRONG resource if one
|
|
36077
|
+
* exists under the decoded pair (or warn-and-skips, reporting success while
|
|
36078
|
+
* the real resource stays alive and billing), and `cdkd drift` reads back
|
|
36079
|
+
* `undefined` forever.
|
|
36080
|
+
*
|
|
36081
|
+
* AWS itself accepts such a name — verified live us-east-1 2026-08-12,
|
|
36082
|
+
* `glue:CreateTable` with `TableInput.Name: 'a|b'` succeeds — and
|
|
36083
|
+
* CloudFormation manages the resource fine. The limitation is cdkd's own, so
|
|
36084
|
+
* the honest answer is to REFUSE at deploy time with a message that says so,
|
|
36085
|
+
* rather than to record an id that silently names something else. Escaping the
|
|
36086
|
+
* separator, or dropping the packing where the decode sites already receive
|
|
36087
|
+
* the properties bag, are the two real fixes and both remain open on #1672;
|
|
36088
|
+
* this guard is what closes the data-loss path in the meantime.
|
|
36089
|
+
*
|
|
36090
|
+
* ## Two entry points, mirroring `config-shape.ts`
|
|
36091
|
+
*
|
|
36092
|
+
* {@link packCompositeId} is the ACTION (throw, or warn-and-pack under the
|
|
36093
|
+
* replay downgrade) and {@link compositeIdSeparatorRefusal} is the bare
|
|
36094
|
+
* PREDICATE, for a caller whose response is neither — the `import()` paths
|
|
36095
|
+
* warn and return `null` (`skipped-not-found`), which is the idiom those
|
|
36096
|
+
* methods already use for every other unusable id. Both share one message
|
|
36097
|
+
* builder so the two can never drift.
|
|
36098
|
+
*
|
|
36099
|
+
* ## Where to place the call
|
|
36100
|
+
*
|
|
36101
|
+
* Prefer computing the id BEFORE the create call rather than after it. The
|
|
36102
|
+
* refusal is a pre-flight check, and a throw placed after the AWS mutation has
|
|
36103
|
+
* already committed would leave an orphan resource behind with no state
|
|
36104
|
+
* record. Only where a segment is derived from the create RESPONSE (an
|
|
36105
|
+
* AWS-generated id) does the call have to sit after it — and those segments
|
|
36106
|
+
* are structurally incapable of carrying a `|` anyway. A post-call site must
|
|
36107
|
+
* also sit OUTSIDE the `try` whose `catch` re-wraps as
|
|
36108
|
+
* `ProvisioningError('Failed to create …')`, or a refusal would be
|
|
36109
|
+
* mis-reported as an AWS creation failure.
|
|
36110
|
+
*
|
|
36111
|
+
* ## What this does NOT cover
|
|
36112
|
+
*
|
|
36113
|
+
* The claim is "every composite packer this change reaches", not "every `|`
|
|
36114
|
+
* join in the tree". One deliberate exclusion, recorded so the scope is not
|
|
36115
|
+
* read as wider than it is:
|
|
36116
|
+
*
|
|
36117
|
+
* - **`intrinsic-function-resolver.ts`'s `|` joins** (the WAFv2 and
|
|
36118
|
+
* `AWS::Events::Rule` arms) are out of scope by KIND, not by ownership: they
|
|
36119
|
+
* build a `Ref` VALUE for the template resolver, never a physicalId cdkd
|
|
36120
|
+
* records and later splits. Nothing decodes them, so an ambiguous join has
|
|
36121
|
+
* no decode site to go wrong at.
|
|
36122
|
+
*
|
|
36123
|
+
* ## Guarding the PACKERS is not the whole story
|
|
36124
|
+
*
|
|
36125
|
+
* A packer that refuses ambiguity does nothing about an id an EARLIER binary
|
|
36126
|
+
* already recorded, or one an `import` was handed — and a type whose decoder is
|
|
36127
|
+
* loose will mis-decode both. Issue #1711 is where that was learned:
|
|
36128
|
+
* `AWS::Route53::RecordSet`'s `parseRecordSetCompositeId` accepts any three
|
|
36129
|
+
* NON-EMPTY segments, so a record name carrying two pipes decoded to a
|
|
36130
|
+
* DIFFERENT hosted zone at three sites this helper never sees — an `import`
|
|
36131
|
+
* early accept, an identity-resolution short-circuit, and `delete`'s own parse,
|
|
36132
|
+
* where `cdkd destroy` then reported success over a live record. When adopting
|
|
36133
|
+
* this guard for a new type, audit its DECODE sites in the same change and make
|
|
36134
|
+
* each one cross-check the parsed segments against something it can verify them
|
|
36135
|
+
* with (Route 53 uses the template's own `Name` / `Type`).
|
|
36136
|
+
*/
|
|
36137
|
+
/** The character every composite physicalId in this codebase joins segments with. */
|
|
36138
|
+
const COMPOSITE_ID_SEPARATOR = "|";
|
|
36139
|
+
/** The issue tracking the real fix (escaping, or not packing at all). */
|
|
36140
|
+
const COMPOSITE_ID_ISSUE_URL = "https://github.com/go-to-k/cdkd/issues/1672";
|
|
36141
|
+
/** Render `<a>|<b>|<c>` from the segment names. */
|
|
36142
|
+
function idShape(segments) {
|
|
36143
|
+
return segments.map((segment) => `<${segment.name}>`).join("|");
|
|
36144
|
+
}
|
|
36145
|
+
/**
|
|
36146
|
+
* The refusal sentence for a composite id whose segments carry the separator,
|
|
36147
|
+
* or `undefined` when the id is unambiguous.
|
|
36148
|
+
*
|
|
36149
|
+
* Every offending segment is named, not just the first: a template that
|
|
36150
|
+
* mis-names one value frequently mis-names its sibling too, and reporting them
|
|
36151
|
+
* one deploy at a time is the shape of remedy nobody finishes.
|
|
36152
|
+
*/
|
|
36153
|
+
function compositeIdSeparatorRefusal(resourceType, logicalId, segments, maskSecrets) {
|
|
36154
|
+
const offending = segments.filter((segment) => String(segment.value).includes("|"));
|
|
36155
|
+
if (offending.length === 0) return void 0;
|
|
36156
|
+
const mask = maskerOrIdentity(maskSecrets);
|
|
36157
|
+
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.`;
|
|
36158
|
+
}
|
|
36159
|
+
/**
|
|
36160
|
+
* Join the segments into a composite physicalId, REFUSING one that would be
|
|
36161
|
+
* ambiguous.
|
|
36162
|
+
*
|
|
36163
|
+
* @throws ProvisioningError when a segment contains {@link COMPOSITE_ID_SEPARATOR},
|
|
36164
|
+
* unless `options.onRefusal` is supplied — under which the message is handed
|
|
36165
|
+
* to the callback and the ambiguous id is returned, keeping the pre-guard
|
|
36166
|
+
* behavior.
|
|
36167
|
+
*/
|
|
36168
|
+
function packCompositeId(resourceType, logicalId, segments, options) {
|
|
36169
|
+
const refusal = compositeIdSeparatorRefusal(resourceType, logicalId, segments, options?.maskSecrets);
|
|
36170
|
+
if (refusal !== void 0) {
|
|
36171
|
+
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.`);
|
|
36172
|
+
else throw new ProvisioningError(refusal, resourceType, logicalId);
|
|
36173
|
+
}
|
|
36174
|
+
return segments.map((segment) => String(segment.value)).join("|");
|
|
36175
|
+
}
|
|
36176
|
+
/** Render the expected id shape, e.g. `<databaseName>|<tableName>`. */
|
|
36177
|
+
function formatShape(segments) {
|
|
36178
|
+
return segments.map((segment) => `<${segment}>`).join("|");
|
|
36179
|
+
}
|
|
36180
|
+
/**
|
|
36181
|
+
* The message EVERY malformed-composite-id decode site emits (issue
|
|
36182
|
+
* [#1657](https://github.com/go-to-k/cdkd/issues/1657)).
|
|
36183
|
+
*
|
|
36184
|
+
* ## Why one builder
|
|
36185
|
+
*
|
|
36186
|
+
* Roughly half the decode sites stated the expected shape and half only echoed
|
|
36187
|
+
* the value back, and the format is documented nowhere else — so on the silent
|
|
36188
|
+
* half the message was the user's only possible route to the answer and it did
|
|
36189
|
+
* not carry it. Routing the sites that already got it right through the same
|
|
36190
|
+
* builder is what stops them drifting back.
|
|
36191
|
+
*
|
|
36192
|
+
* ## This is a WORDING fix, not an acceptance change
|
|
36193
|
+
*
|
|
36194
|
+
* Each caller keeps its own split-and-guard exactly as it was. The arities
|
|
36195
|
+
* genuinely differ (some sites accept AT LEAST N segments, some destructure the
|
|
36196
|
+
* first N and tolerate extras — the #1672 ambiguity, tracked separately), and
|
|
36197
|
+
* folding them into one predicate here would silently change which ids deploy.
|
|
36198
|
+
*
|
|
36199
|
+
* ## The `skipping` variant
|
|
36200
|
+
*
|
|
36201
|
+
* On the DELETE path the established policy is warn-and-continue, so a
|
|
36202
|
+
* malformed record leaves the AWS resource ALIVE. That policy is unchanged —
|
|
36203
|
+
* the warning says what the skip costs, because an orphan the user is never
|
|
36204
|
+
* told about is one they cannot go and delete.
|
|
36205
|
+
*
|
|
36206
|
+
* Since issue [#1752](https://github.com/go-to-k/cdkd/issues/1752) the ACCOUNTING
|
|
36207
|
+
* around it is no longer a lie: an arm emitting this message must also return
|
|
36208
|
+
* `{ outcome: 'skipped', reason: COMPOSITE_ID_SKIP_REASON }` so `cdkd destroy`
|
|
36209
|
+
* prints a `⚠ … skipped` line instead of `✓ … deleted`, reports
|
|
36210
|
+
* `N deleted, 1 skipped`, KEEPS the state record, and exits non-zero. The
|
|
36211
|
+
* wording here changed accordingly — it used to promise unconditionally that
|
|
36212
|
+
* cdkd "will report this delete as successful", which is no longer true on
|
|
36213
|
+
* either path. Issue [#1762](https://github.com/go-to-k/cdkd/issues/1762) then
|
|
36214
|
+
* taught the deploy engine to consume the outcome too, so the message no longer
|
|
36215
|
+
* warns that deploy drops the record: the template-removal DELETE keeps it,
|
|
36216
|
+
* exactly like destroy. What it DOES still distinguish is the replacement /
|
|
36217
|
+
* rollback delete, which fails the resource and can leave the old one
|
|
36218
|
+
* untracked — there "drop the record with 'cdkd state orphan'" is not the
|
|
36219
|
+
* remedy, deleting the resource by hand is.
|
|
36220
|
+
*/
|
|
36221
|
+
function compositeIdFormatMessage(format, logicalId, physicalId, options) {
|
|
36222
|
+
const accepted = format.alsoAccepts ? `"${formatShape(format.segments)}" or ${format.alsoAccepts}` : `"${formatShape(format.segments)}"`;
|
|
36223
|
+
const head = `Invalid physicalId format for ${format.label} ${logicalId}: expected ${accepted}, got "${physicalId}"`;
|
|
36224
|
+
if (!options?.skipping) return head;
|
|
36225
|
+
return `${head}. Skipping the delete — no AWS call is issued, so the resource is LEFT IN PLACE. The state record is KEPT and the run reports the skip: on 'cdkd destroy' / 'cdkd state destroy' (which exits non-zero) and, since issue 1762, on the plain DELETE of a resource removed from the template during cdkd deploy (which re-attempts it on the next deploy). So repair the id in state.json and re-run, or delete the resource by hand and drop the record with 'cdkd state orphan'. NOTE a deploy-side REPLACEMENT or rollback delete instead FAILS the resource (https://github.com/go-to-k/cdkd/issues/1762) — the old resource is left untracked there, so delete it by hand.`;
|
|
36226
|
+
}
|
|
36227
|
+
/**
|
|
36228
|
+
* The short `ResourceDeleteResult.reason` every malformed-composite-id DELETE
|
|
36229
|
+
* skip reports (issue [#1752](https://github.com/go-to-k/cdkd/issues/1752)).
|
|
36230
|
+
*
|
|
36231
|
+
* Deliberately short and IDENTICAL across the five arms: it is rendered inline
|
|
36232
|
+
* on the per-resource status line (`⚠ MyTable (AWS::Glue::Table) skipped
|
|
36233
|
+
* (malformed physicalId in state — no delete issued)`), where the full
|
|
36234
|
+
* remediation sentence built by {@link compositeIdFormatMessage} would not fit.
|
|
36235
|
+
* The full text is still emitted, as the `logger.warn` right beside it.
|
|
36236
|
+
*
|
|
36237
|
+
* A shared constant rather than five literals so the five arms cannot drift and
|
|
36238
|
+
* a grep for the wording finds all of them.
|
|
36239
|
+
*/
|
|
36240
|
+
const COMPOSITE_ID_SKIP_REASON = "malformed physicalId in state — no delete issued";
|
|
36241
|
+
/**
|
|
36242
|
+
* The `ResourceDeleteResult` a malformed-composite-id DELETE arm returns.
|
|
36243
|
+
*
|
|
36244
|
+
* Pairs with the `{ skipping: true }` warning: emit the warning, return this.
|
|
36245
|
+
* Providers call it as `return compositeIdSkipResult();` so the arm reads as
|
|
36246
|
+
* the deliberate skip it is rather than as the bare `return;` that made issue
|
|
36247
|
+
* #1752's mis-accounting possible.
|
|
36248
|
+
*/
|
|
36249
|
+
function compositeIdSkipResult() {
|
|
36250
|
+
return {
|
|
36251
|
+
outcome: "skipped",
|
|
36252
|
+
reason: COMPOSITE_ID_SKIP_REASON
|
|
36253
|
+
};
|
|
36254
|
+
}
|
|
36255
|
+
|
|
36256
|
+
//#endregion
|
|
36257
|
+
//#region src/provisioning/cc-import-identifier.ts
|
|
36258
|
+
/**
|
|
36259
|
+
* Map the physical id `cdkd import` hands `CloudControlProvider.import()` onto
|
|
36260
|
+
* the identifier Cloud Control's `GetResource` accepts (issue
|
|
36261
|
+
* [#3672](https://github.com/go-to-k/cdkd/issues/3672)).
|
|
36262
|
+
*
|
|
36263
|
+
* ## The mismatch
|
|
36264
|
+
*
|
|
36265
|
+
* Cloud Control identifies a resource whose schema `primaryIdentifier` has more
|
|
36266
|
+
* than one field by the field VALUES joined with `|`, in schema order. That
|
|
36267
|
+
* joined string is also what cdkd records as the physical id of every resource
|
|
36268
|
+
* it creates through Cloud Control. CloudFormation's `PhysicalResourceId` for
|
|
36269
|
+
* the same resource is often ONE of those values only — `AWS::EC2::VPCCidrBlock`
|
|
36270
|
+
* reports the bare `vpc-cidr-assoc-…` association id, while Cloud Control wants
|
|
36271
|
+
* `<Id>|<VpcId>`. `cdkd import --migrate-from-cloudformation` passes
|
|
36272
|
+
* CloudFormation's value through as `knownPhysicalId`, so `GetResource` failed,
|
|
36273
|
+
* the resource was not adopted, and retiring the stack orphaned it.
|
|
36274
|
+
*
|
|
36275
|
+
* ## The rule — schema-driven, not a per-type table
|
|
36276
|
+
*
|
|
36277
|
+
* The composite types that reach this path are exactly the ones with no SDK
|
|
36278
|
+
* provider, and the committed schema fixtures (`tests/fixtures/cfn-schemas/`)
|
|
36279
|
+
* cover SDK-provider types only, so no committed source enumerates them. The
|
|
36280
|
+
* registry schema is read at run time instead (`cloudformation:DescribeType`,
|
|
36281
|
+
* one call per type, cached), and a bare id is completed from the template:
|
|
36282
|
+
*
|
|
36283
|
+
* - every identifier field the template declares as a literal string — after
|
|
36284
|
+
* `cdkd import`'s `Ref` pre-substitution, so `VpcId: {Ref: Vpc}` arrives as
|
|
36285
|
+
* `vpc-…` under `--migrate-from-cloudformation` — takes that value;
|
|
36286
|
+
* - EXACTLY ONE field left over takes the supplied id;
|
|
36287
|
+
* - NO field left over means the template alone names the resource, and that
|
|
36288
|
+
* composite is used.
|
|
36289
|
+
*
|
|
36290
|
+
* Everything else is REFUSED with the composite shape to pass instead, rather
|
|
36291
|
+
* than guessed: two or more fields the template cannot supply, or a supplied id
|
|
36292
|
+
* equal to a value the template already gave another field (it cannot then be
|
|
36293
|
+
* told which field it is). A refused import is reported `failed` with that
|
|
36294
|
+
* remedy. It does NOT stop `--migrate-from-cloudformation` from retiring the
|
|
36295
|
+
* stack: `import.ts` counts it among the not-imported resources, warns that
|
|
36296
|
+
* they stay in AWS unmanaged, and retires anyway — the same outcome an
|
|
36297
|
+
* unresolvable id had before this module, now with the value to re-import by.
|
|
36298
|
+
*
|
|
36299
|
+
* Template values are read as literal strings, numbers or booleans (a
|
|
36300
|
+
* `RuleNumber: 100` renders `100`, as Cloud Control joins it); anything else —
|
|
36301
|
+
* an unresolved intrinsic, an object, a blank string — counts as unknown.
|
|
36302
|
+
*
|
|
36303
|
+
* An id that already carries the full arity, a single-field type, and a type
|
|
36304
|
+
* whose schema cannot be read are passed through UNCHANGED — the behaviour
|
|
36305
|
+
* before this module existed.
|
|
36306
|
+
*/
|
|
36307
|
+
/**
|
|
36308
|
+
* Per-type cache of SUCCESSFUL lookups only (same discipline as
|
|
36309
|
+
* `read-only-properties.ts`): the stored promise is the recovered one, and a
|
|
36310
|
+
* failure deletes its own entry so a later call retries.
|
|
36311
|
+
*/
|
|
36312
|
+
const primaryIdentifierCache = /* @__PURE__ */ new Map();
|
|
36313
|
+
/**
|
|
36314
|
+
* The type's schema `primaryIdentifier` as top-level property names, in schema
|
|
36315
|
+
* order — the order Cloud Control joins them in.
|
|
36316
|
+
*
|
|
36317
|
+
* `undefined` when the schema could not be read, declares no identifier, or
|
|
36318
|
+
* names a NESTED pointer (`/properties/A/B`), which no template property maps
|
|
36319
|
+
* onto. Never throws.
|
|
36320
|
+
*/
|
|
36321
|
+
function getPrimaryIdentifierFields(resourceType) {
|
|
36322
|
+
if (hasNoRegistrySchema(resourceType)) return Promise.resolve(void 0);
|
|
36323
|
+
const cached = primaryIdentifierCache.get(resourceType);
|
|
36324
|
+
if (cached) return cached;
|
|
36325
|
+
const entry = fetchPrimaryIdentifierFields(resourceType).catch((error) => {
|
|
36326
|
+
primaryIdentifierCache.delete(resourceType);
|
|
36327
|
+
getLogger().child("CcImportIdentifier").debug(`Failed to resolve the primary identifier for ${displaySafe(resourceType, { asciiOnly: true })} via cloudformation:DescribeType (${describeAwsFailure(error).detail}); the import id is passed to Cloud Control unchanged.`);
|
|
36328
|
+
});
|
|
36329
|
+
primaryIdentifierCache.set(resourceType, entry);
|
|
36330
|
+
return entry;
|
|
36331
|
+
}
|
|
36332
|
+
async function fetchPrimaryIdentifierFields(resourceType) {
|
|
36333
|
+
const response = await describeTypeWithThrottleRetry(resourceType);
|
|
36334
|
+
if (!response.Schema) return void 0;
|
|
36335
|
+
const primary = JSON.parse(response.Schema).primaryIdentifier;
|
|
36336
|
+
if (!Array.isArray(primary) || primary.length === 0) return void 0;
|
|
36337
|
+
const fields = [];
|
|
36338
|
+
for (const pointer of primary) {
|
|
36339
|
+
if (typeof pointer !== "string") return void 0;
|
|
36340
|
+
const match = /^\/properties\/([^/]+)$/.exec(pointer);
|
|
36341
|
+
if (!match?.[1]) return void 0;
|
|
36342
|
+
fields.push(match[1].replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
36343
|
+
}
|
|
36344
|
+
return fields;
|
|
36345
|
+
}
|
|
36346
|
+
/**
|
|
36347
|
+
* The identifier to send to Cloud Control `GetResource` — and to record as the
|
|
36348
|
+
* physical id, since it is the value every later Cloud Control call on the
|
|
36349
|
+
* resource needs. Pure; see the module header for the rule.
|
|
36350
|
+
*
|
|
36351
|
+
* @throws Error when the id cannot be completed unambiguously. The message names
|
|
36352
|
+
* the composite shape `--resource` accepts.
|
|
36353
|
+
*/
|
|
36354
|
+
function toCloudControlIdentifier(input) {
|
|
36355
|
+
const { resourceType, logicalId, physicalId, properties, fields } = input;
|
|
36356
|
+
if (fields === void 0 || fields.length < 2) return physicalId;
|
|
36357
|
+
if (physicalId.split("|").length === fields.length) return physicalId;
|
|
36358
|
+
if (physicalId.trimStart().startsWith("{")) return physicalId;
|
|
36359
|
+
const safeType = displaySafe(resourceType, { asciiOnly: true });
|
|
36360
|
+
const safeLogicalId = displaySafe(logicalId, { asciiOnly: true });
|
|
36361
|
+
const safeId = displaySafe(physicalId, { asciiOnly: true });
|
|
36362
|
+
const shape = fields.map((field) => `<${field}>`).join("|");
|
|
36363
|
+
const remedy = `Pass the Cloud Control identifier instead: --resource '${safeLogicalId}=${shape}'.`;
|
|
36364
|
+
if (physicalId.includes("|")) throw new Error(`${safeType} ${safeLogicalId}: '${safeId}' has ${physicalId.split("|").length} '${"|"}'-separated segments, but Cloud Control identifies this type by ${fields.length} (${shape}). ${remedy}`);
|
|
36365
|
+
const values = fields.map((field) => {
|
|
36366
|
+
const value = Object.hasOwn(properties, field) ? properties[field] : void 0;
|
|
36367
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
36368
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
36369
|
+
});
|
|
36370
|
+
const missing = fields.filter((_, index) => values[index] === void 0);
|
|
36371
|
+
if (missing.length > 1) throw new Error(`${safeType} ${safeLogicalId}: Cloud Control identifies this type by ${shape}, and the template supplies no literal value for ${missing.join(" or ")}, so '${safeId}' cannot be placed. ${remedy}`);
|
|
36372
|
+
if (missing.length === 1 && values.includes(physicalId)) throw new Error(`${safeType} ${safeLogicalId}: '${safeId}' equals a value the template already gives another field of the identifier ${shape}, so it cannot be told which field it is. ${remedy}`);
|
|
36373
|
+
const completed = values.map((value) => value ?? physicalId);
|
|
36374
|
+
const refusal = compositeIdSeparatorRefusal(resourceType, logicalId, fields.map((name, index) => ({
|
|
36375
|
+
name,
|
|
36376
|
+
value: completed[index]
|
|
36377
|
+
})));
|
|
36378
|
+
if (refusal !== void 0) throw new Error(refusal);
|
|
36379
|
+
const identifier = completed.join("|");
|
|
36380
|
+
if (missing.length === 0 && !values.includes(physicalId)) getLogger().child("CcImportIdentifier").debug(`${safeType} ${safeLogicalId}: the template supplies every field of the Cloud Control identifier ${shape}, so '${displaySafe(identifier, { asciiOnly: true })}' is looked up and the supplied id '${safeId}' is not used.`);
|
|
36381
|
+
getLogger().child("CcImportIdentifier").debug(`${safeType} ${safeLogicalId}: completed '${safeId}' to the Cloud Control identifier '${displaySafe(identifier, { asciiOnly: true })}' (${shape}) from the template.`);
|
|
36382
|
+
return identifier;
|
|
36383
|
+
}
|
|
36384
|
+
|
|
35903
36385
|
//#endregion
|
|
35904
36386
|
//#region src/provisioning/cc-protection-properties.ts
|
|
35905
36387
|
const CC_PROTECTION_PROPERTIES = {
|
|
@@ -37004,7 +37486,7 @@ var CloudControlProvider = class {
|
|
|
37004
37486
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
37005
37487
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
37006
37488
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
37007
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
37489
|
+
const { ASGProvider } = await import("./asg-provider-ChgXLoRA.js").then((n) => n.n);
|
|
37008
37490
|
const asgProvider = new ASGProvider();
|
|
37009
37491
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
37010
37492
|
}
|
|
@@ -37999,7 +38481,9 @@ var CloudControlProvider = class {
|
|
|
37999
38481
|
* parse `ResourceModel` (returned as a JSON string by CC API), and
|
|
38000
38482
|
* return the ATTRIBUTE keys as `attributes` — see
|
|
38001
38483
|
* {@link maskUncertifiedModelValues} for what "attribute" means here and
|
|
38002
|
-
* why every other key comes back MASKED rather than dropped.
|
|
38484
|
+
* why every other key comes back MASKED rather than dropped. A bare id
|
|
38485
|
+
* for a type with a COMPOSITE primary identifier is first completed from
|
|
38486
|
+
* the template (`cc-import-identifier.ts`, issue #3672).
|
|
38003
38487
|
* - Without `knownPhysicalId`: return `null`. CC API has no efficient
|
|
38004
38488
|
* `aws:cdk:path`-tag lookup — `ListResources` returns identifiers
|
|
38005
38489
|
* only, so tag lookup would require one `GetResource` per resource
|
|
@@ -38115,13 +38599,20 @@ var CloudControlProvider = class {
|
|
|
38115
38599
|
*/
|
|
38116
38600
|
async import(input) {
|
|
38117
38601
|
if (!input.knownPhysicalId) return null;
|
|
38602
|
+
const identifier = toCloudControlIdentifier({
|
|
38603
|
+
resourceType: input.resourceType,
|
|
38604
|
+
logicalId: input.logicalId,
|
|
38605
|
+
physicalId: input.knownPhysicalId,
|
|
38606
|
+
properties: input.properties,
|
|
38607
|
+
fields: await getPrimaryIdentifierFields(input.resourceType)
|
|
38608
|
+
});
|
|
38118
38609
|
try {
|
|
38119
38610
|
const resp = await this.cloudControlClient.send(new GetResourceCommand({
|
|
38120
38611
|
TypeName: input.resourceType,
|
|
38121
|
-
Identifier:
|
|
38612
|
+
Identifier: identifier
|
|
38122
38613
|
}));
|
|
38123
38614
|
const safeType = displaySafe(input.resourceType, { asciiOnly: true });
|
|
38124
|
-
const safeId = displaySafe(
|
|
38615
|
+
const safeId = displaySafe(identifier, { asciiOnly: true });
|
|
38125
38616
|
let parsedModel;
|
|
38126
38617
|
const raw = resp.ResourceDescription?.Properties;
|
|
38127
38618
|
if (typeof raw === "string" && raw.length > 0) try {
|
|
@@ -38134,10 +38625,9 @@ var CloudControlProvider = class {
|
|
|
38134
38625
|
} catch (parseErr) {
|
|
38135
38626
|
this.logger.warn(`Failed to parse CC API ResourceModel for ${safeType}/${safeId}: ${parseErr instanceof Error ? parseErr.name : typeof parseErr}. Recording no attributes for it; an Fn::GetAtt against this resource will fall back to a value constructed from its physical id.`);
|
|
38136
38627
|
}
|
|
38137
|
-
const attributes = parsedModel === void 0 ? {} : await this.maskUncertifiedModelValues(parsedModel, input.resourceType, input.knownPhysicalId);
|
|
38138
38628
|
return {
|
|
38139
|
-
physicalId:
|
|
38140
|
-
attributes
|
|
38629
|
+
physicalId: identifier,
|
|
38630
|
+
attributes: parsedModel === void 0 ? {} : await this.maskUncertifiedModelValues(parsedModel, input.resourceType, identifier)
|
|
38141
38631
|
};
|
|
38142
38632
|
} catch (error) {
|
|
38143
38633
|
if (error.name === "ResourceNotFoundException") return null;
|
|
@@ -38547,121 +39037,6 @@ function interruptWatchListenerCount() {
|
|
|
38547
39037
|
return sharedSigintHandler === void 0 ? 0 : 1;
|
|
38548
39038
|
}
|
|
38549
39039
|
|
|
38550
|
-
//#endregion
|
|
38551
|
-
//#region src/provisioning/masked-retry-logger.ts
|
|
38552
|
-
/**
|
|
38553
|
-
* The masker a caller supplied, or the identity function when it supplied
|
|
38554
|
-
* none.
|
|
38555
|
-
*
|
|
38556
|
-
* ABSENT MEANS UNMASKED, and that is the back-compatible default the contract
|
|
38557
|
-
* mandates — `create()` / `update()` are also reached from the import path,
|
|
38558
|
-
* from `cdkd drift --revert`, and from tests, and a provider must not care
|
|
38559
|
-
* which caller it got. Centralised here so no call site re-spells the `??`
|
|
38560
|
-
* and accidentally makes the capability required.
|
|
38561
|
-
*/
|
|
38562
|
-
function maskerOrIdentity(maskSecrets) {
|
|
38563
|
-
return maskSecrets ?? ((text) => text);
|
|
38564
|
-
}
|
|
38565
|
-
/**
|
|
38566
|
-
* A {@link RetryLogger} whose every line is routed through `maskSecrets`.
|
|
38567
|
-
*
|
|
38568
|
-
* `warn` is ALWAYS provided, never omitted. It is optional on `RetryLogger`,
|
|
38569
|
-
* and dropping it would silence the give-up summary on the calling path only —
|
|
38570
|
-
* trading a disclosure for the reporting hole issue #2018 closed. It goes
|
|
38571
|
-
* through the SAME mask as `debug` precisely because it is the line that
|
|
38572
|
-
* survives a run without `--verbose`; forwarding it unmasked would defeat the
|
|
38573
|
-
* fence at a HIGHER log level than the one the fence was written for.
|
|
38574
|
-
*
|
|
38575
|
-
* `logger` is typed structurally rather than as the concrete `Logger` so this
|
|
38576
|
-
* module stays a leaf and a test can pass two spies. That matches the
|
|
38577
|
-
* precedent already in the tree (`buildMfaConfigRequest` in
|
|
38578
|
-
* `cognito-provider.ts` takes an injected `logger?: { warn }`).
|
|
38579
|
-
*/
|
|
38580
|
-
function createMaskedRetryLogger(logger, maskSecrets) {
|
|
38581
|
-
const mask = maskerOrIdentity(maskSecrets);
|
|
38582
|
-
return {
|
|
38583
|
-
debug: (message) => logger.debug(mask(message)),
|
|
38584
|
-
warn: (message) => logger.warn(mask(message))
|
|
38585
|
-
};
|
|
38586
|
-
}
|
|
38587
|
-
/**
|
|
38588
|
-
* Depth cap for {@link maskDeep}.
|
|
38589
|
-
*
|
|
38590
|
-
* What it actually bounds is unbounded WORK on a pathologically deep bag — see
|
|
38591
|
-
* the rationale at the cap itself, which is the authority. It is NOT primarily
|
|
38592
|
-
* a cycle guard: a template-derived bag cannot be cyclic, so that justification
|
|
38593
|
-
* over-claims (issue #2176 security review). It does still terminate one, which
|
|
38594
|
-
* is why the walk is safe to share with a caller that might hand it something
|
|
38595
|
-
* other than a template bag.
|
|
38596
|
-
*
|
|
38597
|
-
* Deliberately generous. Every shape these warnings exist to describe is a
|
|
38598
|
-
* scalar, a list of scalars, or a small record, so the walk does not reach
|
|
38599
|
-
* depth 2 in practice.
|
|
38600
|
-
*/
|
|
38601
|
-
const MASK_WALK_MAX_DEPTH = 8;
|
|
38602
|
-
/**
|
|
38603
|
-
* What {@link maskDeep} substitutes for a subtree it declines to descend into.
|
|
38604
|
-
*
|
|
38605
|
-
* MUST equal `SECRET_MASK` in `src/deployment/secret-redaction.ts`. It is
|
|
38606
|
-
* spelled here rather than imported so this module keeps its single-import leaf
|
|
38607
|
-
* property (see the module note above); the two are fenced against drift by a
|
|
38608
|
-
* test that imports both.
|
|
38609
|
-
*/
|
|
38610
|
-
const MASK_WALK_DEPTH_CAP_MARKER = "***";
|
|
38611
|
-
/**
|
|
38612
|
-
* Mask every string LEAF and KEY of an arbitrary value, returning a structure
|
|
38613
|
-
* safe to `JSON.stringify` into a log line or an error message (issue
|
|
38614
|
-
* [#2176](https://github.com/go-to-k/cdkd/issues/2176)).
|
|
38615
|
-
*
|
|
38616
|
-
* THE ORDERING IS THE WHOLE POINT, and it is why masking the finished message
|
|
38617
|
-
* is not a substitute. `maskSecretsInText` matches by literal occurrence, so:
|
|
38618
|
-
*
|
|
38619
|
-
* 1. ESCAPING. `JSON.stringify` escapes `"`, `\` and newlines, so a secret
|
|
38620
|
-
* containing any of them no longer OCCURS in the stringified text and a
|
|
38621
|
-
* mask applied afterwards cannot find it. That is not an exotic case — it
|
|
38622
|
-
* is every Secrets Manager JSON document, the commonest real secret shape.
|
|
38623
|
-
* Measured on the pre-fix tree for #2176: a plaintext of
|
|
38624
|
-
* `{"user":"admin","pw":"hunter2"}` interpolated as
|
|
38625
|
-
* `${JSON.stringify(value)}` came through a message-level mask COMPLETELY
|
|
38626
|
-
* unchanged, while masking the leaves first rendered it `"***"`.
|
|
38627
|
-
* 2. LENGTH. A finished message is always longer than the value inside it, so
|
|
38628
|
-
* it can only ever reach `maskSecretsInText`'s SUBSTRING arm, which ignores
|
|
38629
|
-
* needles below `MIN_NEEDLE_LENGTH` (4). Handing the masker each RAW leaf
|
|
38630
|
-
* reaches the WHOLE-VALUE arm, which has no floor. Measured the same way: a
|
|
38631
|
-
* 3-character secret survived `Value 'abc' at 'pin' failed ...` intact.
|
|
38632
|
-
*
|
|
38633
|
-
* Neither pass subsumes the other, so providers do BOTH — this walk on the
|
|
38634
|
-
* value, and the assembled message through the masker as well. The masker is
|
|
38635
|
-
* idempotent, so the overlap is free.
|
|
38636
|
-
*
|
|
38637
|
-
* Keys are masked as well as values: `JSON.stringify` renders them into the
|
|
38638
|
-
* same line, so a resolved secret used as a map key would otherwise escape.
|
|
38639
|
-
*
|
|
38640
|
-
* WHY THIS LIVES HERE rather than as a private walk per provider. SIX
|
|
38641
|
-
* hand-rolled copies had already accumulated — `elbv2-provider.ts`,
|
|
38642
|
-
* `cognito-provider.ts`, `sns-topic-provider.ts`, `dynamodb-table-provider.ts`,
|
|
38643
|
-
* `dynamodb-globaltable-provider.ts` and `apigatewayv2-provider.ts` — and
|
|
38644
|
-
* `elbv2-provider.ts` carried the standing instruction that "a THIRD site is
|
|
38645
|
-
* the point at which this should move into `../masked-retry-logger.ts` ... and
|
|
38646
|
-
* all three converge on it". That trigger had fired and been missed twice over,
|
|
38647
|
-
* and the copies had already diverged exactly as predicted: FOUR of the six
|
|
38648
|
-
* carried NO depth cap at all.
|
|
38649
|
-
*
|
|
38650
|
-
* Worth recording how the last two were nearly missed AGAIN, because it is the
|
|
38651
|
-
* same failure one level up: the first sweep for this issue grepped for
|
|
38652
|
-
* `maskDeep` / `MASK_WALK_MAX_DEPTH` — the SPELLINGS the known copies used —
|
|
38653
|
-
* and the two survivors spell it `maskLeaf` / `maskLeafValue` with no named
|
|
38654
|
-
* constant. Grep for the SHAPE, not the name. These copies encode a security
|
|
38655
|
-
* contract, and a hardening applied to one silently leaves the others behind.
|
|
38656
|
-
*/
|
|
38657
|
-
function maskDeep(value, mask, depth = 0) {
|
|
38658
|
-
if (typeof value === "string") return mask(value);
|
|
38659
|
-
if (depth >= 8) return "***";
|
|
38660
|
-
if (Array.isArray(value)) return value.map((entry) => maskDeep(entry, mask, depth + 1));
|
|
38661
|
-
if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([k, v]) => [mask(k), maskDeep(v, mask, depth + 1)]));
|
|
38662
|
-
return value;
|
|
38663
|
-
}
|
|
38664
|
-
|
|
38665
39040
|
//#endregion
|
|
38666
39041
|
//#region src/provisioning/providers/custom-resource-provider.ts
|
|
38667
39042
|
/**
|
|
@@ -52421,5 +52796,5 @@ var DeployEngine = class DeployEngine {
|
|
|
52421
52796
|
};
|
|
52422
52797
|
|
|
52423
52798
|
//#endregion
|
|
52424
|
-
export { yellow as $, MalformedProducerRecordRefusalError as $a, resolveSkipPrefix as $i, refuseMalformedOutputsForDestroy as $n, hasReadableExportSet as $r, requireConfigArray as $t, createPreDeleteFinalSnapshot as A, ownValue as Aa, describeDockerFailure as Ai, malformedExportSourceWarning as An, identityKeyFor as Ar, IntrinsicFunctionResolver as At, isStatefulRecreateTargetSync as B, runWithStackAwsClients as Ba, runDockerStreaming as Bi, malformedResourceEntriesWarning as Bn, redactSecretsForState as Br, refStateLookupFromResource as Bt, namesAnyDeclaredParameter as C, failedStageNote as Ca, validateAssetBucketName as Ci, displayLogicalId as Cn, safeStringify$1 as Co, STATE_SOURCED_CROSS_GENERATION_RULES as Cr, ccProtectionRegistryTypes as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, hasOwnKey as Da, resolveDockerContextDirectory as Di, isReadableOrphanRecord as Dn, createSecretMasker as Dr, isWaitAbandonedError as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, defineOwnKey as Ea, buildDockerImage as Ei, hasReadableResources as En, carriesSecretMask as Er, deleteSkipReason as Et, extractDeploymentEventError as F, clearBucketRegionCache as Fa, isDockerClientEnvKey as Fi, malformedOrphansRefusalMessage as Fn, maskSecretsInText as Fr, getAccountInfo as Ft, toCfnInteger as G, CrossAccountSecretRefusalError as Ga, getDockerImageBySourceHash as Gi, refuseDivergentRecordRegionForDestroy as Gn, UNREPRODUCIBLE_LOCK_CLAUSE as Gr, assertRegionMatch as Gt, WARM_THROUGHPUT_MEMBERS as H, AssetError as Ha, escapeRegExp$1 as Hi, malformedResourcesWarning as Hn, LockManager as Hr, WAFv2WebACLProvider as Ht, makeSiblingClaimReader as I, resolveBucketRegion as Ia, isMalformedEnvKey as Ii, malformedOrphansWarning as In, pathCrossesDottedKey as Ir, isImpossibleEmptyStoredAttribute as It, bold as J, DynamicReferenceRegionAmbiguousError as Ja, getDefaultStateBucketName as Ji, refuseMalformedOrphanRecordsForDestroy as Jn, forceQuitRecoveryClause as Jr, configBooleanRefusal as Jt, toFiniteNumber as K, DependencyError as Ka, Synthesizer as Ki, refuseMalformedNestedChildOutputs as Kn, buildForceUnlockCommand as Kr, coerceCfnBoolean as Kt, planOrphanAdoption as L, AwsClients as La, partitionSensitiveEnv as Li, malformedOutputsRefusalMessage as Ln, recordMaskOnlyValue as Lr, isNamelessDynamicReferenceError as Lt, refusesFinalSnapshot as M, displayAssemblyPath as Ma, finchSecretArgvRefusal as Mi, malformedOrphanRecordsRefusalMessage as Mn, isUncertifiedBaselineMaskPosition as Mr, carriesFetchableDynamicReference as Mt, unsupportedFinalSnapshotError as N, renderAssemblyPathEscape as Na, formatDockerLoginError as Ni, malformedOrphanRecordsWarning as Nn, markSameGenerationBag as Nr, cfnRefValueFromPhysicalId as Nt, buildFinalSnapshotIdentifier as O, hasPlainPrototype$1 as Oa, describeDockerCapturedOutput as Oi, isReadableResourceEntry as On, dynamicReferenceTokens as Or, disableInstanceApiTermination as Ot, makeCanonicalizePropertiesFn as P, resolveAssemblyPath as Pa, getDockerCmd as Pi, malformedOrphanRowsKeptWarning as Pn, maskSecretsInError as Pr, coerceParameterTypedValue as Pt, red as Q, LockError as Qa, resolveCaptureObservedState as Qi, refuseMalformedOutputs as Qn, exportNamesCarriedFrom as Qr, replayWarn as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, getAwsClients as Ra, redactDockerArgvValues as Ri, malformedOutputsWarning as Rn, recordMaskOnlyValuesIn as Rr, isUnboundTemplateParameter as Rt, computeParameterTaint as S, AssemblyReader as Sa, readBootstrapMarkerBody as Si, deployRefusesOrphanRowsReason as Sn, describeAwsFailure as So, STATE_SOURCED_BASELINE_RULES as Sr, slowCcOperationTimeoutMs as St, resourcesNamingDeclaredParameter as T, processStackMessages as Ta, buildDenyExternalAccessPolicy as Ti, hasReadableOutputs as Tn, TEMPLATE_SOURCED_RULES as Tr, deleteIndeterminateGuards as Tt, coerceWarmThroughput as U, CdkdError as Ua, stripControlChars as Ui, malformedStateRefusalMessage as Un, S3StateBackend as Ur, normalizeAwsTagsToCfn as Ut, renderStatefulReason as V, setAwsClients as Va, warnFinchArgvExposure as Vi, malformedResourcePropertiesWarning as Vn, scrubResourceRecord as Vr, isSettledInstanceState as Vt, isWarmThroughputDecrease as W, ConfigError as Wa, AssetManifestLoader as Wi, previewableOrphanRecords as Wn, rebuildClientForBucketRegion as Wr, resolveExplicitPhysicalId as Wt, gray as X, LocalInvokeBuildError as Xa, resolveApp as Xi, refuseMalformedOrphansForDestroy as Xn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Xr, configStringRefusal as Xt, cyan as Y, IntrinsicResolutionRefusalError as Ya, getLegacyStateBucketName as Yi, refuseMalformedOrphans as Yn, formatLockExpiry as Yr, configIntegerRefusal as Yt, green as Z, LocalStartServiceError as Za, resolveAutoAssetStorage as Zi, refuseMalformedOrphansForOrphan as Zn, DEFAULT_STATE_PREFIX as Zr, readConfigString as Zt, bagHoldsSecretExpression as _, expectedOwnerParam as _a, assertAssetBucketRegion as _i, DiffCalculator as _n, isThrottlingError as _o, findSilentDropProperties as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, ssmResolvedValueType as aa, orphansAfterRollback as ai, producerRegionsFromState as an, StackHasActiveImportsError as ao, refuseMalformedState as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, derivePartitionAndUrlSuffix as ba, isCrossRegionRedirect as bi, UNREADABLE_ORPHANS_CONTAINER_ROW as bn, markRedactedCause as bo, DagBuilder as br, startInterruptWatch as bt, replayFailedOperations as c, MIGRATE_TMP_PREFIX as ca, AssetPublisher as ci, s3BucketDomainName as cn, SynthesisError as co, repairMalformedOutputsForReadOnly as cr, IAMRoleProvider as ct, updatePartialMessage as d, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as da, buildAssetRedirectMap as di, s3BucketWebsiteUrl as dn, normalizeAwsError as do, repairMalformedResourcesForReadOnly as dr, ProviderRegistry as dt, resolveStateBucketWithDefault as ea, hasReasonlessBaselineRefusal as ei, requireConfigObject as en, NestedStackChildDirectDestroyError as eo, refuseMalformedResourceAttributesForOrphan as er, collectDeclaredOutputNames as et, updatePartialReason as f, injectiveKey as fa, createAssetRedirectResolver as fi, applyRoleArnIfSet as fn, withErrorHandling as fo, safeRegion as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, decodeListingKey as ga, BOOTSTRAP_MARKER_PREFIX as gi, envCredentialsAreAssumedRole as gn, isRetryableTransientError as go, findActionableSilentDrops as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, producerRecordKey as ha, AssetModeResolver as hi, callerEnvCredentials as hn, isMarkedNonRetryable as ho, unreadableOrphanRecords as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, warnDeprecatedNoPrefixCliFlag as ia, isReadableBag as ii, classifyReplaySecretRegion as in, ResourceUpdateNotSupportedError as io, refuseMalformedResourcesForDestroy as ir, secretBearing as it, isFinalSnapshotError as j, absoluteAssemblyPathEscape as ja, dockerSpawnEnvWithSensitive as ji, malformedLocalOutputsWarning as jn, isSingleDynamicReferenceToken as jr, carriesDynamicReference as jt, ccRoutedFinalSnapshotError as k, nullPrototypeRecord as ka, describeDockerExecFailure as ki, malformedExportNamesWarning as kn, errorCauseChain as kr, isTerminationProtectionPropagationError as kt, replayRollback as l, findLargeInlineResources as la, stringifyValue as li, s3BucketDualStackDomainName as ln, formatError as lo, repairMalformedResourceEntriesForReadOnly as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, producerCoordinateKey as ma, rewriteTemplateAssetReferences as mi, applyCallerIdentityCredentials as mn, shellQuote as mo, unpreviewableOrphanRecords as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveUseCdkBootstrapAssets as na, importableOutputKeys as ni, definedAttributes as nn, ProvisioningError as no, refuseMalformedResourceEntriesForOrphan as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, CFN_TEMPLATE_BODY_LIMIT as oa, orphansCarriedFrom as oi, withSharedDrainBudget as on, StackTerminationProtectionError as oo, repairMalformedOrphanRecordsForReadOnly as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, injectiveKeyPrefix as pa, loadPublishableAssetManifest as pi, AWS_CREDENTIAL_ENV_KEYS as pn, pasteableCommand as po, safeStackName as pr, createMaskedRetryLogger as pt, formatResourceLine as q, DeployCancelledError as qa, synthesisStatusMessage as qi, refuseMalformedOrphanRecords as qn, buildLockContentionMessage as qr, coerceCfnInteger as qt, DeployEngine as r, stateBucketExistenceConfirmed as ra, importableOutputs as ri, stringifyIfAssigned as rn, ResourceTimeoutError as ro, refuseMalformedResourcePropertiesForOrphan as rr, isExportAliasCollision as rt, planRollback as s, CFN_TEMPLATE_URL_LIMIT as sa, shouldRetainResource as si, s3BucketArn as sn, StateError as so, repairMalformedOrphansForReadOnly as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, resolveStateBucketWithDefaultAndSource as ta, hasUnverifiableParameterRefusal as ti, requireConfigString as tn, PartialFailureError as to, refuseMalformedResourceEntries as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, uploadCfnTemplate as ua, WorkGraph as ui, s3BucketRegionalDomainName as un, isCdkdError as uo, repairMalformedResourcePropertiesForReadOnly as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, PARTITION_TABLE as va, ensureAssetStorage as vi, INTRINSIC_KEYS as vn, isTransientServerError as vo, describeTypeWithThrottleRetry as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, globMatches as wa, validateContainerRepoName as wi, hasReadableOrphans as wn, __exportAll as wo, STATE_SOURCED_READBACK_RULES as wr, UNSPECIFIED_SKIP_REASON as wt, bindingSkippedOutputs as x, ambientClientDefaults as xa, parseBootstrapMarker as xi, UNREADABLE_RESOURCES_MAP_ROW as xn, retryClassificationText as xo, TemplateParser as xr, CloudControlProvider as xt, keptWholeReasonText as y, canonicalizeRegion as ya, getBootstrapMarkerKey as yi, STATE_RESOURCES_MALFORMED as yn, markNonRetryable as yo, withRetry as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, resetAwsClients as za, runDockerForeground as zi, malformedRenderedContainersWarning as zn, recoverMaskedOutput as zr, parameterTypeMayLoseSecretIdentity as zt };
|
|
52425
|
-
//# sourceMappingURL=deploy-engine-
|
|
52799
|
+
export { yellow as $, DependencyError as $a, getLegacyStateBucketName as $i, refuseMalformedOrphans as $n, formatLockExpiry as $r, configIntegerRefusal as $t, createPreDeleteFinalSnapshot as A, failedStageNote as Aa, buildDockerImage as Ai, hasReadableResources as An, safeStringify$1 as Ao, carriesSecretMask as Ar, deleteSkipReason as At, isStatefulRecreateTargetSync as B, renderAssemblyPathEscape as Ba, isMalformedEnvKey as Bi, malformedOrphansWarning as Bn, pathCrossesDottedKey as Br, isImpossibleEmptyStoredAttribute as Bt, namesAnyDeclaredParameter as C, canonicalizeRegion as Ca, getBootstrapMarkerKey as Ci, STATE_RESOURCES_MALFORMED as Cn, isRetryableTransientError as Co, withRetry as Cr, compositeIdSkipResult as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, clientDefaultsFor as Da, validateAssetBucketName as Di, displayLogicalId as Dn, markRedactedCause as Do, STATE_SOURCED_CROSS_GENERATION_RULES as Dr, maskerOrIdentity as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, ambientCredentialConfig as Ea, readBootstrapMarkerBody as Ei, deployRefusesOrphanRowsReason as En, markNonRetryable as Eo, STATE_SOURCED_BASELINE_RULES as Er, maskDeep as Et, extractDeploymentEventError as F, hasPlainPrototype$1 as Fa, dockerSpawnEnvWithSensitive as Fi, malformedLocalOutputsWarning as Fn, isSingleDynamicReferenceToken as Fr, carriesDynamicReference as Ft, toCfnInteger as G, getAwsClients as Ga, warnFinchArgvExposure as Gi, malformedResourcePropertiesWarning as Gn, scrubResourceRecord as Gr, isSettledInstanceState as Gt, WARM_THROUGHPUT_MEMBERS as H, clearBucketRegionCache as Ha, redactDockerArgvValues as Hi, malformedOutputsWarning as Hn, recordMaskOnlyValuesIn as Hr, isUnboundTemplateParameter as Ht, makeSiblingClaimReader as I, nullPrototypeRecord as Ia, finchSecretArgvRefusal as Ii, malformedOrphanRecordsRefusalMessage as In, isUncertifiedBaselineMaskPosition as Ir, carriesFetchableDynamicReference as It, bold as J, setAwsClients as Ja, AssetManifestLoader as Ji, previewableOrphanRecords as Jn, rebuildClientForBucketRegion as Jr, resolveExplicitPhysicalId as Jt, toFiniteNumber as K, resetAwsClients as Ka, escapeRegExp$1 as Ki, malformedResourcesWarning as Kn, LockManager as Kr, WAFv2WebACLProvider as Kt, planOrphanAdoption as L, ownValue as La, formatDockerLoginError as Li, malformedOrphanRecordsWarning as Ln, markSameGenerationBag as Lr, cfnRefValueFromPhysicalId as Lt, refusesFinalSnapshot as M, processStackMessages as Ma, describeDockerCapturedOutput as Mi, isReadableResourceEntry as Mn, dynamicReferenceTokens as Mr, disableInstanceApiTermination as Mt, unsupportedFinalSnapshotError as N, defineOwnKey as Na, describeDockerExecFailure as Ni, malformedExportNamesWarning as Nn, errorCauseChain as Nr, isTerminationProtectionPropagationError as Nt, buildFinalSnapshotIdentifier as O, credentialFingerprint as Oa, validateContainerRepoName as Oi, hasReadableOrphans as On, retryClassificationText as Oo, STATE_SOURCED_READBACK_RULES as Or, UNSPECIFIED_SKIP_REASON as Ot, makeCanonicalizePropertiesFn as P, hasOwnKey as Pa, describeDockerFailure as Pi, malformedExportSourceWarning as Pn, identityKeyFor as Pr, IntrinsicFunctionResolver as Pt, red as Q, CrossAccountSecretRefusalError as Qa, getDefaultStateBucketName as Qi, refuseMalformedOrphanRecordsForDestroy as Qn, forceQuitRecoveryClause as Qr, configBooleanRefusal as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, absoluteAssemblyPathEscape as Ra, getDockerCmd as Ri, malformedOrphanRowsKeptWarning as Rn, maskSecretsInError as Rr, coerceParameterTypedValue as Rt, computeParameterTaint as S, PARTITION_TABLE as Sa, ensureAssetStorage as Si, INTRINSIC_KEYS as Sn, isMarkedNonRetryable as So, describeTypeWithThrottleRetry as Sr, compositeIdSeparatorRefusal as St, resourcesNamingDeclaredParameter as T, ambientClientDefaults as Ta, parseBootstrapMarker as Ti, UNREADABLE_RESOURCES_MAP_ROW as Tn, isTransientServerError as To, TemplateParser as Tr, createMaskedRetryLogger as Tt, coerceWarmThroughput as U, resolveBucketRegion as Ua, runDockerForeground as Ui, malformedRenderedContainersWarning as Un, recoverMaskedOutput as Ur, parameterTypeMayLoseSecretIdentity as Ut, renderStatefulReason as V, resolveAssemblyPath as Va, partitionSensitiveEnv as Vi, malformedOutputsRefusalMessage as Vn, recordMaskOnlyValue as Vr, isNamelessDynamicReferenceError as Vt, isWarmThroughputDecrease as W, AwsClients as Wa, runDockerStreaming as Wi, malformedResourceEntriesWarning as Wn, redactSecretsForState as Wr, refStateLookupFromResource as Wt, gray as X, CdkdError as Xa, Synthesizer as Xi, refuseMalformedNestedChildOutputs as Xn, buildForceUnlockCommand as Xr, coerceCfnBoolean as Xt, cyan as Y, AssetError as Ya, getDockerImageBySourceHash as Yi, refuseDivergentRecordRegionForDestroy as Yn, UNREPRODUCIBLE_LOCK_CLAUSE as Yr, assertRegionMatch as Yt, green as Z, ConfigError as Za, synthesisStatusMessage as Zi, refuseMalformedOrphanRecords as Zn, buildLockContentionMessage as Zr, coerceCfnInteger as Zt, bagHoldsSecretExpression as _, injectiveKeyPrefix as _a, loadPublishableAssetManifest as _i, AWS_CREDENTIAL_ENV_KEYS as _n, isCdkdError as _o, safeStackName as _r, startInterruptWatch as _t, DeploymentEventsStore as a, resolveStateBucketWithDefaultAndSource as aa, hasUnverifiableParameterRefusal as ai, requireConfigString as an, LockError as ao, refuseMalformedResourceEntries as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, decodeListingKey as ba, BOOTSTRAP_MARKER_PREFIX as bi, envCredentialsAreAssumedRole as bn, pasteableCommand as bo, findActionableSilentDrops as br, ccProtectionRegistryTypes as bt, replayFailedOperations as c, warnDeprecatedNoPrefixCliFlag as ca, isReadableBag as ci, classifyReplaySecretRegion as cn, PartialFailureError as co, refuseMalformedResourcesForDestroy as cr, IAMRoleProvider as ct, updatePartialMessage as d, CFN_TEMPLATE_URL_LIMIT as da, shouldRetainResource as di, s3BucketArn as dn, ResourceUpdateNotSupportedError as do, repairMalformedOrphansForReadOnly as dr, ProviderRegistry as dt, resolveApp as ea, CUSTOM_RESOURCE_RESPONSE_PREFIX as ei, configStringRefusal as en, DeployCancelledError as eo, refuseMalformedOrphansForDestroy as er, collectDeclaredOutputNames as et, updatePartialReason as f, MIGRATE_TMP_PREFIX as fa, AssetPublisher as fi, s3BucketDomainName as fn, StackHasActiveImportsError as fo, repairMalformedOutputsForReadOnly as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, injectiveKey as ga, createAssetRedirectResolver as gi, applyRoleArnIfSet as gn, formatError as go, safeRegion as gr, isInterruptedWaitError as gt, IMPLICIT_DELETE_DEPENDENCIES as h, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as ha, buildAssetRedirectMap as hi, s3BucketWebsiteUrl as hn, SynthesisError as ho, repairMalformedResourcesForReadOnly as hr, interruptWatchListenerCount as ht, DeploymentEventsReader as i, resolveStateBucketWithDefault as ia, hasReasonlessBaselineRefusal as ii, requireConfigObject as in, LocalStartServiceError as io, refuseMalformedResourceAttributesForOrphan as ir, secretBearing as it, isFinalSnapshotError as j, globMatches as ja, resolveDockerContextDirectory as ji, isReadableOrphanRecord as jn, __exportAll as jo, createSecretMasker as jr, isWaitAbandonedError as jt, ccRoutedFinalSnapshotError as k, AssemblyReader as ka, buildDenyExternalAccessPolicy as ki, hasReadableOutputs as kn, describeAwsFailure as ko, TEMPLATE_SOURCED_RULES as kr, deleteIndeterminateGuards as kt, replayRollback as l, ssmResolvedValueType as la, orphansAfterRollback as li, producerRegionsFromState as ln, ProvisioningError as lo, refuseMalformedState as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, uploadCfnTemplate as ma, WorkGraph as mi, s3BucketRegionalDomainName as mn, StateError as mo, repairMalformedResourcePropertiesForReadOnly as mr, endCommandInterruptScope as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, resolveCaptureObservedState as na, exportNamesCarriedFrom as ni, replayWarn as nn, IntrinsicResolutionRefusalError as no, refuseMalformedOutputs as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, resolveUseCdkBootstrapAssets as oa, importableOutputKeys as oi, definedAttributes as on, MalformedProducerRecordRefusalError as oo, refuseMalformedResourceEntriesForOrphan as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, findLargeInlineResources as pa, stringifyValue as pi, s3BucketDualStackDomainName as pn, StackTerminationProtectionError as po, repairMalformedResourceEntriesForReadOnly as pr, beginCommandInterruptScope as pt, formatResourceLine as q, runWithStackAwsClients as qa, stripControlChars as qi, malformedStateRefusalMessage as qn, S3StateBackend as qr, normalizeAwsTagsToCfn as qt, DeployEngine as r, resolveSkipPrefix as ra, hasReadableExportSet as ri, requireConfigArray as rn, LocalInvokeBuildError as ro, refuseMalformedOutputsForDestroy as rr, isExportAliasCollision as rt, planRollback as s, stateBucketExistenceConfirmed as sa, importableOutputs as si, stringifyIfAssigned as sn, NestedStackChildDirectDestroyError as so, refuseMalformedResourcePropertiesForOrphan as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, resolveAutoAssetStorage as ta, DEFAULT_STATE_PREFIX as ti, readConfigString as tn, DynamicReferenceRegionAmbiguousError as to, refuseMalformedOrphansForOrphan as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, CFN_TEMPLATE_BODY_LIMIT as ua, orphansCarriedFrom as ui, withSharedDrainBudget as un, ResourceTimeoutError as uo, repairMalformedOrphanRecordsForReadOnly as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, producerCoordinateKey as va, rewriteTemplateAssetReferences as vi, applyCallerIdentityCredentials as vn, normalizeAwsError as vo, unpreviewableOrphanRecords as vr, CloudControlProvider as vt, reachableDivergentParameters as w, derivePartitionAndUrlSuffix as wa, isCrossRegionRedirect as wi, UNREADABLE_ORPHANS_CONTAINER_ROW as wn, isThrottlingError as wo, DagBuilder as wr, packCompositeId as wt, bindingSkippedOutputs as x, expectedOwnerParam as xa, assertAssetBucketRegion as xi, DiffCalculator as xn, shellQuote as xo, findSilentDropProperties as xr, compositeIdFormatMessage as xt, keptWholeReasonText as y, producerRecordKey as ya, AssetModeResolver as yi, callerEnvCredentials as yn, withErrorHandling as yo, unreadableOrphanRecords as yr, slowCcOperationTimeoutMs as yt, isStatefulRecreateTargetForReplace as z, displayAssemblyPath as za, isDockerClientEnvKey as zi, malformedOrphansRefusalMessage as zn, maskSecretsInText as zr, getAccountInfo as zt };
|
|
52800
|
+
//# sourceMappingURL=deploy-engine-CCvNP_dA.js.map
|