@go-to-k/cdkd 0.290.4 → 0.290.6
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-NffO_bpL.js → asg-provider-CsdVn4cg.js} +2 -2
- package/dist/{asg-provider-NffO_bpL.js.map → asg-provider-CsdVn4cg.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-DD7JE_Ic.js → deploy-engine-BU7n9_qY.js} +387 -65
- package/dist/deploy-engine-BU7n9_qY.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-20PIJFu9.js → program-Cl3fCD6-.js} +573 -122
- package/dist/{program-20PIJFu9.js.map → program-Cl3fCD6-.js.map} +1 -1
- package/dist/{version-BUcQz4SU.js → version-BvSdNpMz.js} +2 -2
- package/dist/{version-BUcQz4SU.js.map → version-BvSdNpMz.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-DD7JE_Ic.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { b as displayIdent, 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, x as displaySafe, y as UNRENDERABLE } from "./logger-th1myfgh.js";
|
|
2
2
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-BvSdNpMz.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";
|
|
@@ -7218,6 +7218,94 @@ function describeDockerFailure(error, args) {
|
|
|
7218
7218
|
return redactDockerArgvInText(capturedStreamText(error, "stderr") || thrownMessageText(error) || safeStringify(error), args);
|
|
7219
7219
|
}
|
|
7220
7220
|
/**
|
|
7221
|
+
* A `cause` that is safe to CHAIN onto a wrapper error, for a caller that
|
|
7222
|
+
* wants the retry classifiers to see the failure as an object rather than as
|
|
7223
|
+
* an interpolated string (issue go-to-k/cdkd#2075).
|
|
7224
|
+
*
|
|
7225
|
+
* Attaching the raw `execFile` error reopens the channel go-to-k/cdkd#2440
|
|
7226
|
+
* closed. The mandate above applies to every docker failure text this module
|
|
7227
|
+
* hands out, "including one whose argv carries no user data today" — and a
|
|
7228
|
+
* chained cause IS such a text: `formatError` prints `cause.message`, and the
|
|
7229
|
+
* CLI's top-level handler walks the whole chain. The raw message is the
|
|
7230
|
+
* command line, so the moment an argv gains a `-e` or a `--build-arg` the
|
|
7231
|
+
* secret is in a printed line again, from a call site nobody re-reads.
|
|
7232
|
+
*
|
|
7233
|
+
* So the returned cause carries the REDACTED description as its message, and
|
|
7234
|
+
* chains nothing further. What the classifiers read is copied across — but
|
|
7235
|
+
* through an ALLOWLIST ({@link CLASSIFICATION_FIELDS}), never by sweeping the
|
|
7236
|
+
* original's own keys.
|
|
7237
|
+
*
|
|
7238
|
+
* The allowlist is the whole point, and a denylist was the first cut: it
|
|
7239
|
+
* skipped `message` / `stack` / `cause` and copied the rest, which carried
|
|
7240
|
+
* `err.cmd` — the full command line — onto the new error verbatim. That is
|
|
7241
|
+
* the SAME field that defeated four rounds of the go-to-k/cdkd#2440 fence
|
|
7242
|
+
* (`.claude/rules/docker-argv-redaction.md`), reintroduced inside the function
|
|
7243
|
+
* written to close it. `stderr` / `stdout` are excluded for the same reason:
|
|
7244
|
+
* the composer above has already read and REDACTED them into the message, so
|
|
7245
|
+
* copying the raw streams would hand back exactly what it removed.
|
|
7246
|
+
*
|
|
7247
|
+
* Fail-closed is the correct direction here for the reason the cache-param
|
|
7248
|
+
* allowlist gives: a field missing from the allowlist costs one degraded retry
|
|
7249
|
+
* classification, while a field missing from a denylist costs a printed
|
|
7250
|
+
* credential.
|
|
7251
|
+
*/
|
|
7252
|
+
/**
|
|
7253
|
+
* The fields {@link redactedDockerCause} carries from the original error onto
|
|
7254
|
+
* the redacted one.
|
|
7255
|
+
*
|
|
7256
|
+
* WHAT EACH HALF IS FOR, stated precisely because an earlier revision of this
|
|
7257
|
+
* comment got it wrong in a way that reads as more than it is. The AWS-shaped
|
|
7258
|
+
* entries (`$metadata`, `$fault`, `__type`, `code`, `statusCode`) are what
|
|
7259
|
+
* `isThrottlingError` / `isTransientServerError` /
|
|
7260
|
+
* `describeRetryClassificationSignals` actually consult, and they are reachable
|
|
7261
|
+
* here only when an SDK error is the cause -- the ECR-login path, whose
|
|
7262
|
+
* `GetAuthorizationToken` failure can be one.
|
|
7263
|
+
*
|
|
7264
|
+
* `exitCode` is NOT read by any classifier. It is carried because it is the
|
|
7265
|
+
* ONLY field the DOMINANT failure has: these four sites reject through
|
|
7266
|
+
* `spawnStreaming`, whose `SpawnError` sets `stderr` / `stdout` / `exitCode`
|
|
7267
|
+
* and nothing else, so without it a non-zero docker exit produced a cause with
|
|
7268
|
+
* a name and a message and no other content at all. What that buys is the
|
|
7269
|
+
* DIAGNOSTIC -- `cli/index.ts` renders the chain's own enumerable props -- not
|
|
7270
|
+
* a retry decision. Do not restate it as a classification field.
|
|
7271
|
+
*
|
|
7272
|
+
* `stderr` / `stdout` stay OFF: the composer has already read and REDACTED
|
|
7273
|
+
* them into the message, so copying the raw streams would hand back exactly
|
|
7274
|
+
* what it removed.
|
|
7275
|
+
*/
|
|
7276
|
+
const CLASSIFICATION_FIELDS = [
|
|
7277
|
+
"exitCode",
|
|
7278
|
+
"code",
|
|
7279
|
+
"errno",
|
|
7280
|
+
"signal",
|
|
7281
|
+
"status",
|
|
7282
|
+
"statusCode",
|
|
7283
|
+
"$metadata",
|
|
7284
|
+
"$fault",
|
|
7285
|
+
"__type"
|
|
7286
|
+
];
|
|
7287
|
+
function redactedDockerCause(error, args) {
|
|
7288
|
+
if (!(error instanceof Error)) return void 0;
|
|
7289
|
+
const cause = new Error(describeDockerFailure(error, args));
|
|
7290
|
+
try {
|
|
7291
|
+
cause.name = error.name;
|
|
7292
|
+
} catch {}
|
|
7293
|
+
const NON_RETRYABLE_MARKER = Symbol.for("cdkd.nonRetryable");
|
|
7294
|
+
try {
|
|
7295
|
+
if (error[NON_RETRYABLE_MARKER] === true) Object.defineProperty(cause, NON_RETRYABLE_MARKER, {
|
|
7296
|
+
value: true,
|
|
7297
|
+
enumerable: false,
|
|
7298
|
+
configurable: true
|
|
7299
|
+
});
|
|
7300
|
+
} catch {}
|
|
7301
|
+
const source = error;
|
|
7302
|
+
const target = cause;
|
|
7303
|
+
for (const key of CLASSIFICATION_FIELDS) try {
|
|
7304
|
+
if (key in source) target[key] = source[key];
|
|
7305
|
+
} catch {}
|
|
7306
|
+
return cause;
|
|
7307
|
+
}
|
|
7308
|
+
/**
|
|
7221
7309
|
* Composer for a captured-output failure where the diagnostic may be on
|
|
7222
7310
|
* STDOUT rather than stderr (`runDockerStreaming`'s non-zero-exit path, whose
|
|
7223
7311
|
* `SpawnError` carries both). `fallback` is used when neither stream said
|
|
@@ -7474,7 +7562,12 @@ var DockerAssetPublisher = class {
|
|
|
7474
7562
|
try {
|
|
7475
7563
|
await this.tagImage(actualTag, tag);
|
|
7476
7564
|
} catch (err) {
|
|
7477
|
-
|
|
7565
|
+
const e = err;
|
|
7566
|
+
throw new AssetError(`Docker tag failed re-tagging '${actualTag}' → '${tag}': ${e.message ?? String(err)}`, e.cause instanceof Error ? e.cause : redactedDockerCause(err, [
|
|
7567
|
+
"tag",
|
|
7568
|
+
actualTag,
|
|
7569
|
+
tag
|
|
7570
|
+
]));
|
|
7478
7571
|
}
|
|
7479
7572
|
}
|
|
7480
7573
|
}
|
|
@@ -7549,7 +7642,7 @@ var DockerAssetPublisher = class {
|
|
|
7549
7642
|
await runDockerStreaming(loginArgs, { input: password });
|
|
7550
7643
|
loggedInRegistries.add(registryKey);
|
|
7551
7644
|
} catch (err) {
|
|
7552
|
-
throw new AssetError(`ECR login failed: ${formatDockerLoginError(describeDockerFailure(err, loginArgs), endpoint)}
|
|
7645
|
+
throw new AssetError(`ECR login failed: ${formatDockerLoginError(describeDockerFailure(err, loginArgs), endpoint)}`, redactedDockerCause(err, loginArgs));
|
|
7553
7646
|
}
|
|
7554
7647
|
}
|
|
7555
7648
|
/**
|
|
@@ -7564,7 +7657,7 @@ var DockerAssetPublisher = class {
|
|
|
7564
7657
|
try {
|
|
7565
7658
|
await runDockerStreaming(tagArgs);
|
|
7566
7659
|
} catch (err) {
|
|
7567
|
-
throw new AssetError(`Docker tag failed: ${describeDockerFailure(err, tagArgs)}
|
|
7660
|
+
throw new AssetError(`Docker tag failed: ${describeDockerFailure(err, tagArgs)}`, redactedDockerCause(err, tagArgs));
|
|
7568
7661
|
}
|
|
7569
7662
|
}
|
|
7570
7663
|
/**
|
|
@@ -7578,7 +7671,7 @@ var DockerAssetPublisher = class {
|
|
|
7578
7671
|
try {
|
|
7579
7672
|
await runDockerStreaming(pushArgs);
|
|
7580
7673
|
} catch (err) {
|
|
7581
|
-
throw new AssetError(`Docker push failed: ${describeDockerFailure(err, pushArgs)}
|
|
7674
|
+
throw new AssetError(`Docker push failed: ${describeDockerFailure(err, pushArgs)}`, redactedDockerCause(err, pushArgs));
|
|
7582
7675
|
}
|
|
7583
7676
|
}
|
|
7584
7677
|
/**
|
|
@@ -9164,9 +9257,60 @@ function safeJournalText(value) {
|
|
|
9164
9257
|
return displaySafe(value, { asciiOnly: true }) || "<unrenderable>";
|
|
9165
9258
|
}
|
|
9166
9259
|
/**
|
|
9260
|
+
* The per-operation shape check (issue #3140). The journal is an unchecked
|
|
9261
|
+
* cast beyond `journalVersion` and `segments` being an array, and the
|
|
9262
|
+
* executor keys EVERY lookup on `op.logicalId` -- `stateResources[..]`,
|
|
9263
|
+
* `orphanLogicalIds.has(..)`, the events it records -- so a non-string id
|
|
9264
|
+
* planted in the bucket either coerces at each lookup (`123` finds the
|
|
9265
|
+
* record named `'123'`) or throws a raw `TypeError` at the first one (an
|
|
9266
|
+
* object whose `toString` is not callable, the issue #2947 shape). Refused
|
|
9267
|
+
* HERE, once, so no downstream reader has to remember; the executor keeps its
|
|
9268
|
+
* own `typeof` guard on the one pasted command as defense-in-depth, because
|
|
9269
|
+
* the deploy engine's in-process rollback reaches it without this parser.
|
|
9270
|
+
*
|
|
9271
|
+
* Only the fields a lookup or a route is keyed on: `logicalId` (non-empty
|
|
9272
|
+
* string), `resourceType` (string), `changeType` (string -- an unknown value
|
|
9273
|
+
* lands on the executor's UPDATE arm by design, pinned by a case there), and
|
|
9274
|
+
* `physicalId` when present (string). `previousState` and the rest stay as
|
|
9275
|
+
* they were; a bad value there fails the op it belongs to, not the parse.
|
|
9276
|
+
*
|
|
9277
|
+
* The refusal names the INDEX and the TYPE, never the value -- there is
|
|
9278
|
+
* nothing to sanitize, so nothing to get wrong.
|
|
9279
|
+
*/
|
|
9280
|
+
function refuseMalformedOperation(shownStack, where, op) {
|
|
9281
|
+
const fail = (field, value, expected) => refuseMalformed(shownStack, `${where}.${field} must be ${expected} (got ${kind(value)}).`);
|
|
9282
|
+
if (typeof op !== "object" || op === null || Array.isArray(op)) refuseMalformed(shownStack, `${where} must be an object (got ${kind(op)}).`);
|
|
9283
|
+
const o = op;
|
|
9284
|
+
if (typeof o["logicalId"] !== "string" || o["logicalId"].length === 0) fail("logicalId", o["logicalId"], "a non-empty string");
|
|
9285
|
+
if (typeof o["resourceType"] !== "string") fail("resourceType", o["resourceType"], "a string");
|
|
9286
|
+
if (typeof o["changeType"] !== "string") fail("changeType", o["changeType"], "a string");
|
|
9287
|
+
if (o["physicalId"] !== void 0 && typeof o["physicalId"] !== "string") fail("physicalId", o["physicalId"], "a string when present");
|
|
9288
|
+
}
|
|
9289
|
+
/** The TYPE of a JSON-derived value, for a refusal that must not echo the value. */
|
|
9290
|
+
function kind(v) {
|
|
9291
|
+
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
9292
|
+
}
|
|
9293
|
+
/**
|
|
9294
|
+
* The one spelling of a malformed-journal refusal. `detail` is built from
|
|
9295
|
+
* indices, field names and `kind()` words only -- never a journal value -- and
|
|
9296
|
+
* every refusal names the way out, as `UnknownRollbackJournalVersionError`
|
|
9297
|
+
* names `Upgrade cdkd`: the journal is a sibling of `state.json`, so removing
|
|
9298
|
+
* that one object discards it (`cdkd destroy` sweeps it as well), and no
|
|
9299
|
+
* other stack is affected because `cdkd rollback` never parses another
|
|
9300
|
+
* stack's journal. The same text reaches the deploy engine's `Failed to write
|
|
9301
|
+
* rollback journal` warning, since appending a segment parses the existing
|
|
9302
|
+
* journal first.
|
|
9303
|
+
*/
|
|
9304
|
+
function refuseMalformed(shownStack, detail) {
|
|
9305
|
+
throw new Error(`Rollback journal for '${shownStack}' is malformed: ${detail} Remove the stack's rollback-journal.json (next to its state.json) to discard it.`);
|
|
9306
|
+
}
|
|
9307
|
+
/**
|
|
9167
9308
|
* Parse + validate a journal body. Throws
|
|
9168
9309
|
* {@link UnknownRollbackJournalVersionError} on a newer version, and a plain
|
|
9169
|
-
* Error on a structurally-invalid body
|
|
9310
|
+
* Error on a structurally-invalid body -- including, since issue #3140, a
|
|
9311
|
+
* segment that is not an object, an `operations` that is not an array, and an
|
|
9312
|
+
* operation whose `logicalId` / `resourceType` / `changeType` / `physicalId`
|
|
9313
|
+
* is not the string the executor keys on (see `refuseMalformedOperation`).
|
|
9170
9314
|
*/
|
|
9171
9315
|
function parseRollbackJournal(bodyString, stackName) {
|
|
9172
9316
|
const shownStack = safeJournalText(stackName);
|
|
@@ -9186,6 +9330,16 @@ function parseRollbackJournal(bodyString, stackName) {
|
|
|
9186
9330
|
}
|
|
9187
9331
|
if (j.journalVersion > 1) throw new UnknownRollbackJournalVersionError(j.journalVersion, stackName);
|
|
9188
9332
|
if (!Array.isArray(j.segments)) throw new Error(`Rollback journal for '${shownStack}' is missing a 'segments' array.`);
|
|
9333
|
+
j.segments.forEach((segment, s) => {
|
|
9334
|
+
if (typeof segment !== "object" || segment === null || Array.isArray(segment)) refuseMalformed(shownStack, `segments[${s}] must be an object (got ${kind(segment)}).`);
|
|
9335
|
+
const seg = segment;
|
|
9336
|
+
if (!Array.isArray(seg["operations"])) refuseMalformed(shownStack, `segments[${s}].operations must be an array (got ${kind(seg["operations"])}).`);
|
|
9337
|
+
seg["operations"].forEach((op, i) => refuseMalformedOperation(shownStack, `segments[${s}].operations[${i}]`, op));
|
|
9338
|
+
if (seg["failedOperations"] !== void 0) {
|
|
9339
|
+
if (!Array.isArray(seg["failedOperations"])) refuseMalformed(shownStack, `segments[${s}].failedOperations must be an array when present (got ${kind(seg["failedOperations"])}).`);
|
|
9340
|
+
seg["failedOperations"].forEach((op, i) => refuseMalformedOperation(shownStack, `segments[${s}].failedOperations[${i}]`, op));
|
|
9341
|
+
}
|
|
9342
|
+
});
|
|
9189
9343
|
return {
|
|
9190
9344
|
journalVersion: j.journalVersion,
|
|
9191
9345
|
stackName: j.stackName ?? stackName,
|
|
@@ -9618,7 +9772,7 @@ var S3StateBackend = class {
|
|
|
9618
9772
|
}));
|
|
9619
9773
|
} catch (error) {
|
|
9620
9774
|
const name = error.name;
|
|
9621
|
-
if (name === "NotFound" || name === "NoSuchBucket") throw new StateError(`State bucket '${this.config.bucket}' does not exist. Run 'cdkd bootstrap' to create it, or specify an existing bucket via --state-bucket, CDKD_STATE_BUCKET, or cdk.json context.cdkd.stateBucket
|
|
9775
|
+
if (name === "NotFound" || name === "NoSuchBucket") throw new StateError(`State bucket '${this.config.bucket}' does not exist. Run 'cdkd bootstrap' to create it, or specify an existing bucket via --state-bucket, CDKD_STATE_BUCKET, or cdk.json context.cdkd.stateBucket.`, error instanceof Error ? error : void 0);
|
|
9622
9776
|
const normalized = normalizeAwsError(error, {
|
|
9623
9777
|
bucket: this.config.bucket,
|
|
9624
9778
|
operation: "HeadBucket"
|
|
@@ -9750,7 +9904,7 @@ var S3StateBackend = class {
|
|
|
9750
9904
|
}
|
|
9751
9905
|
return response.ETag;
|
|
9752
9906
|
} catch (error) {
|
|
9753
|
-
if (error.name === "PreconditionFailed") throw new StateError(`State has been modified by another process. Expected ETag: ${expectedEtag}, but state has changed
|
|
9907
|
+
if (error.name === "PreconditionFailed") throw new StateError(`State has been modified by another process. Expected ETag: ${expectedEtag}, but state has changed.`, error instanceof Error ? error : void 0);
|
|
9754
9908
|
const normalized = normalizeAwsError(error, {
|
|
9755
9909
|
bucket: this.config.bucket,
|
|
9756
9910
|
operation: "PutObject"
|
|
@@ -13148,7 +13302,11 @@ const UNKNOWN_PART_PLACEHOLDER = "\0";
|
|
|
13148
13302
|
* longer equals the leaf's text around the middle, so the arm refuses) -- and,
|
|
13149
13303
|
* through the frame arm's rewrite, for the nested-stack parameter recorder's
|
|
13150
13304
|
* `frameSpellingOf`, which carries a framed parameter to the child only when
|
|
13151
|
-
*
|
|
13305
|
+
* the position pass rewrote it -- which, on a value the recorder's gate (ii)
|
|
13306
|
+
* finds the scan silent on, only the frame arm can do (the value scan, the
|
|
13307
|
+
* skeleton arm and the cross-stack arm each rewrite an object-sourced leaf
|
|
13308
|
+
* whose WHOLE value is a recorded plaintext, and (ii) is what refuses that
|
|
13309
|
+
* value). The recorder's own rendering of such a part does not
|
|
13152
13310
|
* decide the case: a part outside the token leaves its one span and service
|
|
13153
13311
|
* prefix the same either way.
|
|
13154
13312
|
*
|
|
@@ -13422,8 +13580,15 @@ function positionByCrossStackSource(bag, source, secrets) {
|
|
|
13422
13580
|
* the source leaf is an `Fn::Join` / `Fn::Sub` there is none, so the leaf fell
|
|
13423
13581
|
* to the value scan — and the value map is keyed by the resolved PLAINTEXT, so
|
|
13424
13582
|
* a colliding pair collapses there exactly as it did before #1904. That is the
|
|
13425
|
-
* DOMINANT CDK shape rather than an edge case:
|
|
13426
|
-
* token renders the secret ARN as a `Ref`, hence an `Fn::Join`.
|
|
13583
|
+
* DOMINANT CDK shape rather than an edge case: a secret reached through an L2
|
|
13584
|
+
* token usually renders the secret ARN as a `Ref`, hence an `Fn::Join`. The
|
|
13585
|
+
* exceptions render a plain `{{resolve:...}}` string, which the literal arm
|
|
13586
|
+
* already positions: a secret imported by a LITERAL ARN
|
|
13587
|
+
* (`Secret.fromSecretCompleteArn` / `fromSecretPartialArn`, whose
|
|
13588
|
+
* `secretValue` and `secretValueFromJson` both inline it),
|
|
13589
|
+
* `SecretValue.secretsManager('<literal arn>')` and
|
|
13590
|
+
* `SecretValue.ssmSecure(...)` (synth-probed on this repo's aws-cdk-lib,
|
|
13591
|
+
* issue #3143; `new Secret(...)` and `fromSecretNameV2` are the join).
|
|
13427
13592
|
*
|
|
13428
13593
|
* Three conditions must ALL hold before an expression is persisted, and each
|
|
13429
13594
|
* removes a different way of being wrong:
|
|
@@ -23291,15 +23456,19 @@ function withoutProducerRegions(context) {
|
|
|
23291
23456
|
}
|
|
23292
23457
|
/**
|
|
23293
23458
|
* Masked log twins registered this pass, per pass bag (issue #3100;
|
|
23294
|
-
* `rememberLogTwin` / `logTwinOfProduct`).
|
|
23295
|
-
*
|
|
23459
|
+
* `rememberLogTwin` / `logTwinOfProduct`). Nothing that RESOLVES a value reads
|
|
23460
|
+
* it. One persistence decision does: `resolveBase64` registers its encoding
|
|
23461
|
+
* mask-only when the input's position mask fires (issue #3119), which a nested
|
|
23462
|
+
* child also reaches through the inherited-bag lookup (issue #3114).
|
|
23296
23463
|
*
|
|
23297
23464
|
* MODULE scope, not instance scope: a region-pinned sibling resolver
|
|
23298
23465
|
* (`resolverForProducerRegion`) resolves on behalf of the consumer with the
|
|
23299
23466
|
* consumer's bag, so an instance-local registry left the sibling's twin where
|
|
23300
23467
|
* the consumer's `Fn::Join` / `Fn::Sub` never looked. The key is still the
|
|
23301
|
-
* pass's own bag object, so scope does not widen:
|
|
23302
|
-
*
|
|
23468
|
+
* pass's own bag object, so scope does not widen to unrelated passes: a pass
|
|
23469
|
+
* reaches only the entries under its own bag and under the bag it was handed
|
|
23470
|
+
* as `inheritedSecrets` (a nested-stack child reading its parent's, issue
|
|
23471
|
+
* #3114), and the entries die with the bag.
|
|
23303
23472
|
*/
|
|
23304
23473
|
const LOG_TWINS_BY_PASS = /* @__PURE__ */ new WeakMap();
|
|
23305
23474
|
/**
|
|
@@ -23430,20 +23599,22 @@ const recordedSecretExpressions = {
|
|
|
23430
23599
|
*/
|
|
23431
23600
|
const cachedEc2InstanceAttributes = Object.create(null);
|
|
23432
23601
|
/**
|
|
23433
|
-
* The
|
|
23434
|
-
* #3096), each holding a value the resolver read LIVE because the
|
|
23435
|
-
* record omitted it — `definedAttributes` drops a member the provider
|
|
23436
|
-
* not read back (issue #3077), so the `Fn::GetAtt` falls out of the
|
|
23437
|
-
* lookup and into `constructAttribute`'s per-type arm. Keyed by physical
|
|
23438
|
-
*
|
|
23439
|
-
*
|
|
23440
|
-
*
|
|
23441
|
-
*
|
|
23442
|
-
*
|
|
23443
|
-
*
|
|
23444
|
-
*
|
|
23445
|
-
*
|
|
23446
|
-
*
|
|
23602
|
+
* The three sibling caches of {@link cachedEc2InstanceAttributes} (issues
|
|
23603
|
+
* #3096 / #3097), each holding a value the resolver read LIVE because the
|
|
23604
|
+
* state record omitted it — `definedAttributes` drops a member the provider
|
|
23605
|
+
* could not read back (issue #3077), so the `Fn::GetAtt` falls out of the
|
|
23606
|
+
* flat lookup and into `constructAttribute`'s per-type arm. Keyed by physical
|
|
23607
|
+
* id: the read is region-pinned (`clientsForRegion(this.explicitRegion)`),
|
|
23608
|
+
* so the key names one resource in that region, and the value — a default
|
|
23609
|
+
* security group id, a distribution hostname, a security group's VPC —
|
|
23610
|
+
* never changes; like the instance cache, a refusal caches nothing. Same
|
|
23611
|
+
* process lifetime, cleared together with it by {@link resetAccountInfoCache}.
|
|
23612
|
+
* The RDS `DBProxy` / `DBProxyEndpoint` `VpcId` arms have no cache because
|
|
23613
|
+
* they have no live read: `AwsClients` exposes no RDS client and this file
|
|
23614
|
+
* imports none, so those two arms refuse outright (see
|
|
23615
|
+
* `refuseUnservedAttribute`'s callers).
|
|
23616
|
+
*
|
|
23617
|
+
* All four are NULL-PROTOTYPE objects (#3096 delta review, measured): the
|
|
23447
23618
|
* key is a state-record physical id, and on a plain `{}` a record holding
|
|
23448
23619
|
* `constructor` / `__proto__` / `hasOwnProperty` read a FUNCTION out of
|
|
23449
23620
|
* `Object.prototype` as the cached value — served with zero AWS calls, ahead
|
|
@@ -23452,6 +23623,7 @@ const cachedEc2InstanceAttributes = Object.create(null);
|
|
|
23452
23623
|
*/
|
|
23453
23624
|
const cachedVpcDefaultSecurityGroups = Object.create(null);
|
|
23454
23625
|
const cachedCloudFrontDomainNames = Object.create(null);
|
|
23626
|
+
const cachedSecurityGroupVpcIds = Object.create(null);
|
|
23455
23627
|
/**
|
|
23456
23628
|
* The region this call answers for: the caller's override, else the ambient one.
|
|
23457
23629
|
*
|
|
@@ -23900,6 +24072,29 @@ async function resolveAccountIdentity() {
|
|
|
23900
24072
|
}
|
|
23901
24073
|
}
|
|
23902
24074
|
/**
|
|
24075
|
+
* Is a STORED `''` for this attribute a value the resource can never have —
|
|
24076
|
+
* so the flat lookup must read it as ABSENT and let the live arm run?
|
|
24077
|
+
*
|
|
24078
|
+
* Exactly one attribute qualifies today (issue #3097 review): an
|
|
24079
|
+
* `AWS::EC2::SecurityGroup`'s `VpcId`. Every security group lives in a VPC
|
|
24080
|
+
* (EC2-Classic retired 2022-08-15), so `''` can only be the pre-#3097
|
|
24081
|
+
* provider's copy of a template that declared no `VpcId` — and that record is
|
|
24082
|
+
* rewritten only by an `update()`, which a no-change deploy never issues, so
|
|
24083
|
+
* without this carve-out the stored `''` shadows the live arm for the life of
|
|
24084
|
+
* the record. The record itself stays `''` until the next update; only the
|
|
24085
|
+
* RESOLUTION changes.
|
|
24086
|
+
*
|
|
24087
|
+
* Deliberately NOT a general "`''` means absent" rule: #3077 records a settled
|
|
24088
|
+
* EC2 instance's missing public address as the KNOWN empty `''`
|
|
24089
|
+
* (CloudFormation's own answer), and that one must keep being served from
|
|
24090
|
+
* state rather than sent back to `DescribeInstances` on every resolution.
|
|
24091
|
+
* A new entry here needs the same argument — that `''` is IMPOSSIBLE for the
|
|
24092
|
+
* attribute, not merely unlikely.
|
|
24093
|
+
*/
|
|
24094
|
+
function isImpossibleEmptyStoredAttribute(resourceType, attributeName, storedValue) {
|
|
24095
|
+
return storedValue === "" && resourceType === "AWS::EC2::SecurityGroup" && attributeName === "VpcId";
|
|
24096
|
+
}
|
|
24097
|
+
/**
|
|
23903
24098
|
* Does a constructed `Fn::GetAtt` answer embed the placeholder account id?
|
|
23904
24099
|
*
|
|
23905
24100
|
* The guard in `constructGuardedAttribute` used to test
|
|
@@ -24625,6 +24820,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
24625
24820
|
async resolveParameters(template, userParameters, options) {
|
|
24626
24821
|
const inheritedSecrets = options?.inheritedSecrets;
|
|
24627
24822
|
const maskInherited = (text) => inheritedSecrets && inheritedSecrets.size > 0 ? maskSecretsInText(text, inheritedSecrets) : text;
|
|
24823
|
+
const inheritedLogContext = {
|
|
24824
|
+
template,
|
|
24825
|
+
resources: {},
|
|
24826
|
+
...inheritedSecrets && { inheritedSecrets }
|
|
24827
|
+
};
|
|
24628
24828
|
const parameters = Object.create(null);
|
|
24629
24829
|
const templateParameters = template.Parameters;
|
|
24630
24830
|
if (!templateParameters || typeof templateParameters !== "object") return parameters;
|
|
@@ -24637,7 +24837,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
24637
24837
|
if (userValue !== void 0) {
|
|
24638
24838
|
this.refuseCoercedInheritedSecret(name, paramDef, userValue, inheritedSecrets);
|
|
24639
24839
|
parameters[name] = this.coerceParameterValue(userValue, paramDef.Type);
|
|
24640
|
-
this.logger.debug(`Parameter ${name}: using user-provided value ${maskInherited(stringifyParameterForLog(paramDef, userValue))}`);
|
|
24840
|
+
this.logger.debug(`Parameter ${name}: using user-provided value ${maskInherited(stringifyParameterForLog(paramDef, this.maskValueLeaves(userValue, inheritedLogContext)))}`);
|
|
24641
24841
|
continue;
|
|
24642
24842
|
}
|
|
24643
24843
|
}
|
|
@@ -24966,7 +25166,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
24966
25166
|
const value = context.parameters[logicalId];
|
|
24967
25167
|
const declared = context.template.Parameters;
|
|
24968
25168
|
const paramDef = declared != null && Object.hasOwn(declared, logicalId) ? declared[logicalId] : void 0;
|
|
24969
|
-
this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.maskSecretsForLog(stringifyParameterForLog(paramDef, value), context)}`);
|
|
25169
|
+
this.logger.debug(`Resolved Ref to parameter: ${logicalId} -> ${this.maskSecretsForLog(stringifyParameterForLog(paramDef, this.maskValueLeaves(value, context)), context)}`);
|
|
24970
25170
|
this.recordInheritedParameterSecrets(logicalId, value, context);
|
|
24971
25171
|
return value;
|
|
24972
25172
|
}
|
|
@@ -25133,15 +25333,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
25133
25333
|
const resource = Object.hasOwn(context.resources, logicalId) ? context.resources[logicalId] : void 0;
|
|
25134
25334
|
if (!resource) throw markNonRetryable(/* @__PURE__ */ new Error(`Resource ${logicalId} not found for Fn::GetAtt`));
|
|
25135
25335
|
if (!(resource.resourceType === "AWS::EC2::VPC" && attributeName === "Ipv6CidrBlocks") && resource.attributes !== void 0) {
|
|
25136
|
-
const
|
|
25336
|
+
const storedValue = Object.hasOwn(resource.attributes, attributeName) ? resource.attributes[attributeName] : void 0;
|
|
25337
|
+
const flatValue = isImpossibleEmptyStoredAttribute(resource.resourceType, attributeName, storedValue) ? void 0 : storedValue;
|
|
25137
25338
|
if (flatValue !== void 0) {
|
|
25138
25339
|
this.rejectPlaceholderArnAttribute(resource, attributeName, flatValue, logicalId, context);
|
|
25139
25340
|
if (resource.resourceType === "AWS::Route53::HostedZone" && attributeName === "NameServers" && typeof flatValue === "string") {
|
|
25140
25341
|
const nameServers = flatValue === "" ? [] : flatValue.split(",");
|
|
25141
|
-
this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, nameServers), context)}`);
|
|
25342
|
+
this.logger.debug(`Normalized legacy Fn::GetAtt attribute: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(nameServers, context)), context)}`);
|
|
25142
25343
|
return this.noteAttributeSecrecy(logicalId, attributeName, nameServers, context);
|
|
25143
25344
|
}
|
|
25144
|
-
this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, flatValue), context)}`);
|
|
25345
|
+
this.logger.debug(`Resolved Fn::GetAtt from attributes: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(flatValue, context)), context)}`);
|
|
25145
25346
|
if (resource.resourceType === NESTED_STACK_RESOURCE_TYPE$1 && attributeName.startsWith(NESTED_STACK_OUTPUT_ATTRIBUTE_PREFIX) && carriesDynamicReference(flatValue)) return await this.reresolveCrossStackValue(flatValue, nestedStackChildRegionFromLocalArn(resource.physicalId), context, `nested stack ${logicalId} ${attributeName}`, crossStackSourceKey({ "Fn::GetAtt": getAtt }));
|
|
25146
25347
|
return this.noteAttributeSecrecy(logicalId, attributeName, flatValue, context);
|
|
25147
25348
|
}
|
|
@@ -25154,7 +25355,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
25154
25355
|
break;
|
|
25155
25356
|
}
|
|
25156
25357
|
if (cursor !== void 0) {
|
|
25157
|
-
this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, cursor), context)}`);
|
|
25358
|
+
this.logger.debug(`Resolved Fn::GetAtt from nested attributes: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(cursor, context)), context)}`);
|
|
25158
25359
|
return this.noteAttributeSecrecy(logicalId, attributeName, cursor, context);
|
|
25159
25360
|
}
|
|
25160
25361
|
}
|
|
@@ -25164,7 +25365,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
25164
25365
|
throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${this.maskSecretsForLog(attributeName, context)}]: the nested stack '${logicalId}' declares no output named '${this.maskSecretsForLog(attributeName.slice(8), context)}'. Its outputs are ${declared.length > 0 ? declared.join(", ") : "(none)"}. Check the output name in the nested stack's template, and deploy the child stack again if you have just added it.`));
|
|
25165
25366
|
}
|
|
25166
25367
|
const value = await this.constructGuardedAttribute(resource, attributeName, context, logicalId);
|
|
25167
|
-
this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, value), context)}`);
|
|
25368
|
+
this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${this.maskSecretsForLog(attributeName, context)} -> ${this.maskSecretsForLog(stringifyAttributeForLog(attributeName, this.maskValueLeaves(value, context)), context)}`);
|
|
25168
25369
|
return value;
|
|
25169
25370
|
}
|
|
25170
25371
|
/**
|
|
@@ -25578,7 +25779,38 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
25578
25779
|
}
|
|
25579
25780
|
if (resourceType === "AWS::EC2::SecurityGroup") switch (attributeName) {
|
|
25580
25781
|
case "GroupId": return physicalId;
|
|
25581
|
-
case "VpcId":
|
|
25782
|
+
case "VpcId": {
|
|
25783
|
+
if (!/^sg-[0-9a-f]+$/.test(physicalId)) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, VpcId] for AWS::EC2::SecurityGroup: the state record's physical id "${displaySafe(this.maskThenStripThenMask(physicalId, context)).slice(0, 64)}" is not a security group id (sg-<hex>), so cdkd will not look it up. Repair the record (cdkd import, or re-create the security group) and deploy again.`));
|
|
25784
|
+
const cachedVpcId = cachedSecurityGroupVpcIds[physicalId];
|
|
25785
|
+
if (cachedVpcId !== void 0) return cachedVpcId;
|
|
25786
|
+
let vpcId;
|
|
25787
|
+
try {
|
|
25788
|
+
vpcId = (await this.clientsForRegion(this.explicitRegion).ec2.send(new DescribeSecurityGroupsCommand({ GroupIds: [physicalId] }))).SecurityGroups?.[0]?.VpcId;
|
|
25789
|
+
} catch (err) {
|
|
25790
|
+
this.refuseUnservedAttribute({
|
|
25791
|
+
logicalId,
|
|
25792
|
+
attributeName,
|
|
25793
|
+
resourceType,
|
|
25794
|
+
physicalId,
|
|
25795
|
+
context,
|
|
25796
|
+
observed: this.describeFailureObserved("DescribeSecurityGroups", err, context),
|
|
25797
|
+
remedy: "Fix the read (the ec2:DescribeSecurityGroups permission, or the region) and deploy again."
|
|
25798
|
+
});
|
|
25799
|
+
}
|
|
25800
|
+
if (vpcId) {
|
|
25801
|
+
cachedSecurityGroupVpcIds[physicalId] = vpcId;
|
|
25802
|
+
return vpcId;
|
|
25803
|
+
}
|
|
25804
|
+
this.refuseUnservedAttribute({
|
|
25805
|
+
logicalId,
|
|
25806
|
+
attributeName,
|
|
25807
|
+
resourceType,
|
|
25808
|
+
physicalId,
|
|
25809
|
+
context,
|
|
25810
|
+
observed: "DescribeSecurityGroups reports no VpcId for the group",
|
|
25811
|
+
remedy: "Check the security group in the console, or reference the attribute from a later deploy."
|
|
25812
|
+
});
|
|
25813
|
+
}
|
|
25582
25814
|
default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId, context);
|
|
25583
25815
|
}
|
|
25584
25816
|
if (resourceType === "AWS::EC2::Subnet") switch (attributeName) {
|
|
@@ -25716,9 +25948,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
25716
25948
|
* belongs, a `vpc-...` in a security-group slot, a distribution id where a
|
|
25717
25949
|
* hostname belongs, a proxy name in a `VpcId` position. Loud only where AWS
|
|
25718
25950
|
* rejects the shape; an Output, an export or a free-text property carried it
|
|
25719
|
-
* silently. The
|
|
25720
|
-
*
|
|
25721
|
-
*
|
|
25951
|
+
* silently. The security group `VpcId` arm (issue #3097) joined with the
|
|
25952
|
+
* opposite pre-fix defect — it answered `undefined`, which `Fn::Join`
|
|
25953
|
+
* renders `'undefined'` — and takes the same refusal. The physical id can
|
|
25954
|
+
* never be the value for these attributes, so the honest answer is a
|
|
25955
|
+
* refusal naming the resource, the attribute, what was observed and the
|
|
25956
|
+
* remedy.
|
|
25722
25957
|
*
|
|
25723
25958
|
* DELIBERATELY NOT `markNonRetryable`, unlike every other refusal in this
|
|
25724
25959
|
* file but the fabricated-account guard: the verdict is TIME-DEPENDENT. A
|
|
@@ -25734,7 +25969,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
25734
25969
|
* interpolated hole happens to carry a pattern — the `logicalId` (the #1838
|
|
25735
25970
|
* hazard: `DependencyViolation` and two other bare words), or the error
|
|
25736
25971
|
* CLASS name `describeFailureObserved` puts in `observed`, which none of
|
|
25737
|
-
* these
|
|
25972
|
+
* these four reads raises — and then re-describes and can heal.
|
|
25738
25973
|
* Measured, not designed: the fabricated-account guard sits in exactly the
|
|
25739
25974
|
* same place. Threading a sanitized SDK error as `cause` so a throttled
|
|
25740
25975
|
* describe classifies as transient was considered and left out — a
|
|
@@ -25822,7 +26057,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
25822
26057
|
const literalList = Array.isArray(rawValues);
|
|
25823
26058
|
const parts = (await allSettledKeepingFirstRejection(values.map(async (v) => {
|
|
25824
26059
|
if (typeof v === "string") return {
|
|
25825
|
-
...v.includes("{{resolve:") ? await this.resolveDynamicReferencesWithLogTwin(v,
|
|
26060
|
+
...v.includes("{{resolve:") ? await this.resolveDynamicReferencesWithLogTwin(v, this.logTwinOfProduct({
|
|
26061
|
+
result: v,
|
|
26062
|
+
twin: v
|
|
26063
|
+
}, context).twin, context) : {
|
|
25826
26064
|
result: v,
|
|
25827
26065
|
twin: v
|
|
25828
26066
|
},
|
|
@@ -27144,8 +27382,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27144
27382
|
* pass is masked too: the over-masking direction `logTwinOfProduct` accepts.
|
|
27145
27383
|
*/
|
|
27146
27384
|
splitLogTwins(value, delimiter, pieces, context) {
|
|
27147
|
-
const
|
|
27148
|
-
const sourceTwin = this.isRecordedSecretForLog(value, context) ? "***" : bag ? LOG_TWINS_BY_PASS.get(bag)?.get(value) : void 0;
|
|
27385
|
+
const sourceTwin = this.isRecordedSecretForLog(value, context) ? "***" : this.registeredLogTwin(value, context);
|
|
27149
27386
|
if (sourceTwin === void 0) return pieces;
|
|
27150
27387
|
const twinPieces = sourceTwin.split(delimiter);
|
|
27151
27388
|
const aligned = twinPieces.length === pieces.length && !"***".includes(delimiter);
|
|
@@ -27199,6 +27436,48 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27199
27436
|
return context?.recordedSecretValues ?? context?.inheritedSecrets;
|
|
27200
27437
|
}
|
|
27201
27438
|
/**
|
|
27439
|
+
* The masked twin registered for `value`, looked up under the pass's OWN bag
|
|
27440
|
+
* and under the INHERITED one (issue
|
|
27441
|
+
* [#3114](https://github.com/go-to-k/cdkd/issues/3114)). A nested-stack
|
|
27442
|
+
* child receives, as `inheritedSecrets`, the very object its parent resolved
|
|
27443
|
+
* the `AWS::CloudFormation::Stack` resource with: the deploy engine binds the
|
|
27444
|
+
* resource context's `recordedSecretValues` as the resource's secrets, and
|
|
27445
|
+
* `NestedStackProvider` hands that binding to the child. So a parameter value
|
|
27446
|
+
* the parent built around a short secret (`port:q7` -> `port:***`) is
|
|
27447
|
+
* registered under the child's inherited bag, while the child registers its
|
|
27448
|
+
* own writes under its own bag. Two DIFFERENT registered twins mask the
|
|
27449
|
+
* whole string, the rule `rememberLogTwin` applies within one bag. Lookups
|
|
27450
|
+
* only: `rememberLogTwin` still registers under `logTwinBag`, the context's
|
|
27451
|
+
* own `recordedSecretValues` whenever it carries one (every context
|
|
27452
|
+
* `buildResolverContext` returns does; the log-only context
|
|
27453
|
+
* `resolveParameters` builds carries none, and nothing on that path
|
|
27454
|
+
* registers), so such a child never writes into its parent's registry. A
|
|
27455
|
+
* context with ONLY an inherited bag registers into that bag, as it did
|
|
27456
|
+
* before this lookup existed.
|
|
27457
|
+
*
|
|
27458
|
+
* ONE LEVEL ONLY. The bag a child hands its own nested stack is the child's
|
|
27459
|
+
* OWN resource bag, so no twin registered ABOVE the child is reachable from a
|
|
27460
|
+
* grandchild. Where the middle stack passes the value THROUGH
|
|
27461
|
+
* (`{ Ref: Param }` straight into the grandchild's `Parameters`) nothing ever
|
|
27462
|
+
* registers a twin in that bag, so the grandchild prints the value whether or
|
|
27463
|
+
* not the bag holds pairs. Where the middle RE-WRAPS it (an `Fn::Join` around
|
|
27464
|
+
* the `Ref`) the middle registers its own twin, and the hand-off still drops
|
|
27465
|
+
* a bag holding no pairs (it passes `inheritedSecrets` only when `size` is
|
|
27466
|
+
* nonzero). Both are issue #3156.
|
|
27467
|
+
*
|
|
27468
|
+
* The registry is keyed by VALUE, and `splitLogTwins`' unaligned arm
|
|
27469
|
+
* registers every piece, public ones included. So a child `Fn::Base64` over a
|
|
27470
|
+
* string exactly equal to such a piece of its parent's persists `***` for a
|
|
27471
|
+
* non-secret property, the over-masking hazard issue #3119 already accepts
|
|
27472
|
+
* within one pass, now reachable across the parent / child boundary.
|
|
27473
|
+
*/
|
|
27474
|
+
registeredLogTwin(value, context) {
|
|
27475
|
+
const own = context?.recordedSecretValues ? LOG_TWINS_BY_PASS.get(context.recordedSecretValues)?.get(value) : void 0;
|
|
27476
|
+
const inherited = context?.inheritedSecrets ? LOG_TWINS_BY_PASS.get(context.inheritedSecrets)?.get(value) : void 0;
|
|
27477
|
+
if (own === void 0) return inherited;
|
|
27478
|
+
return inherited === void 0 || inherited === own ? own : "***";
|
|
27479
|
+
}
|
|
27480
|
+
/**
|
|
27202
27481
|
* The log twin of a RESOLUTION PRODUCT — an intrinsic part, variable or
|
|
27203
27482
|
* placeholder — for issue #3100. Masked whole when its value is a recorded
|
|
27204
27483
|
* secret. Otherwise a part that carries no mask of its own takes the masked
|
|
@@ -27226,8 +27505,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27226
27505
|
result: part.result,
|
|
27227
27506
|
twin: "***"
|
|
27228
27507
|
};
|
|
27229
|
-
const
|
|
27230
|
-
const registered = bag ? LOG_TWINS_BY_PASS.get(bag)?.get(part.result) : void 0;
|
|
27508
|
+
const registered = this.registeredLogTwin(part.result, context);
|
|
27231
27509
|
if (registered === void 0 || registered === part.twin) return part;
|
|
27232
27510
|
return {
|
|
27233
27511
|
result: part.result,
|
|
@@ -27241,7 +27519,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
27241
27519
|
* two DIFFERENT masked twins for one string register `***` for the whole
|
|
27242
27520
|
* string, since their spans cannot be merged. Scoped to the pass's bag
|
|
27243
27521
|
* through a `WeakMap`, like the cross-stack associations, so it dies with
|
|
27244
|
-
* the bag
|
|
27522
|
+
* the bag. An unrelated pass cannot reach these strings; a nested child
|
|
27523
|
+
* whose `inheritedSecrets` is this bag reads them (`registeredLogTwin`).
|
|
27245
27524
|
*/
|
|
27246
27525
|
rememberLogTwin(context, result, twin) {
|
|
27247
27526
|
if (twin === result) return;
|
|
@@ -29016,7 +29295,7 @@ var CloudControlProvider = class {
|
|
|
29016
29295
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
29017
29296
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
29018
29297
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
29019
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
29298
|
+
const { ASGProvider } = await import("./asg-provider-CsdVn4cg.js").then((n) => n.n);
|
|
29020
29299
|
const asgProvider = new ASGProvider();
|
|
29021
29300
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
29022
29301
|
}
|
|
@@ -34214,20 +34493,22 @@ const WARM_THROUGHPUT_MEMBERS = ["ReadUnitsPerSecond", "WriteUnitsPerSecond"];
|
|
|
34214
34493
|
*
|
|
34215
34494
|
* The accepted STRING set is `Number()`'s, which is WIDER than a decimal
|
|
34216
34495
|
* integer: `'0x1e'`, `'0o36'`, `'1e3'`, `'30.5'` and `' 30 '` all coerce.
|
|
34217
|
-
* CloudFormation accepts
|
|
34218
|
-
*
|
|
34219
|
-
*
|
|
34220
|
-
*
|
|
34221
|
-
*
|
|
34222
|
-
* the
|
|
34223
|
-
*
|
|
34224
|
-
*
|
|
34225
|
-
*
|
|
34226
|
-
*
|
|
34227
|
-
*
|
|
34228
|
-
*
|
|
34229
|
-
*
|
|
34230
|
-
*
|
|
34496
|
+
* CloudFormation accepts NONE of those on a DynamoDB capacity and only the
|
|
34497
|
+
* LAST on `AWS::Logs::LogGroup.RetentionInDays` (both measured — the tables
|
|
34498
|
+
* are on {@link toCfnInteger}), so this helper is for the GUARD callers only:
|
|
34499
|
+
* `stateful-types.ts`'s `has-retention`, the DynamoDB already-matches /
|
|
34500
|
+
* decrease / zero-capacity tests, the live-readback compares. A guard is safe
|
|
34501
|
+
* on the wider set — over-acceptance there can only produce MORE refusals or
|
|
34502
|
+
* skip fewer calls. A caller that FORWARDS the number to AWS reads through the
|
|
34503
|
+
* grammar CloudFormation measured for that handler instead:
|
|
34504
|
+
* `logs-loggroup-provider.ts` through {@link toCfnInteger} (issue
|
|
34505
|
+
* [#2698](https://github.com/go-to-k/cdkd/issues/2698)), every DynamoDB
|
|
34506
|
+
* capacity / throughput forwarder (`coerceWarmThroughput` below, the
|
|
34507
|
+
* `GlobalTable` provider's `ProvisionedThroughput` / `OnDemandThroughput`
|
|
34508
|
+
* readers AND the diagnostic / mirror predicates paired with them) through
|
|
34509
|
+
* `config-shape.ts`'s `coerceCfnInteger` (issue
|
|
34510
|
+
* [#3135](https://github.com/go-to-k/cdkd/issues/3135)). A forwarder left on
|
|
34511
|
+
* THIS helper sends `Number('0x9')` for a template CloudFormation refuses.
|
|
34231
34512
|
*
|
|
34232
34513
|
* EXPORTED, and not because warm throughput needs it exported. This exact rule
|
|
34233
34514
|
* was hand-written three times — here, as `dynamodb-table-provider.ts`'s
|
|
@@ -34289,6 +34570,37 @@ function toFiniteNumber(value) {
|
|
|
34289
34570
|
* behaviour varies on padding, so each helper carries its own measurement and
|
|
34290
34571
|
* a caller picks by the property it forwards.
|
|
34291
34572
|
*
|
|
34573
|
+
* The DynamoDB measurement, read beside the Logs one so the two grammars sit
|
|
34574
|
+
* side by side (`AWS::DynamoDB::Table.ProvisionedThroughput.ReadCapacityUnits`
|
|
34575
|
+
* plus one `WriteCapacityUnits` row, us-east-1, 2026-09-14, issue
|
|
34576
|
+
* [#3135](https://github.com/go-to-k/cdkd/issues/3135); same protocol, live
|
|
34577
|
+
* value read back with `dynamodb describe-table`). "properties validation" is
|
|
34578
|
+
* CloudFormation's own template-property check, which fires BEFORE the
|
|
34579
|
+
* handler; "handler" is the DynamoDB resource handler's model validation:
|
|
34580
|
+
*
|
|
34581
|
+
* ```
|
|
34582
|
+
* template spelling CloudFormation live RCU after
|
|
34583
|
+
* 5 (JSON number) accepted 5
|
|
34584
|
+
* "6" accepted 6
|
|
34585
|
+
* "+8" accepted 8
|
|
34586
|
+
* "010" accepted (decimal, not octal) 10
|
|
34587
|
+
* " 7 " (padded) REJECTED (properties validation) unchanged
|
|
34588
|
+
* "7 " (trailing space) REJECTED (properties validation) unchanged
|
|
34589
|
+
* "" (empty string) REJECTED (properties validation) unchanged
|
|
34590
|
+
* "0x9" (RCU or WCU) REJECTED (handler) unchanged
|
|
34591
|
+
* "1e1" REJECTED (Validation failed) unchanged
|
|
34592
|
+
* "6.0" REJECTED (same) unchanged
|
|
34593
|
+
* "6.5" REJECTED (same) unchanged
|
|
34594
|
+
* ```
|
|
34595
|
+
*
|
|
34596
|
+
* The two agree on the digits (`/^[+-]?\d+$/`, hex / exponent / decimal-point
|
|
34597
|
+
* rejected by both) and DISAGREE on padding: the Logs handler trims, DynamoDB
|
|
34598
|
+
* rejects `" 7 "` and `"7 "` before any handler runs. So the DynamoDB
|
|
34599
|
+
* forwarders read through {@link coerceCfnInteger} itself — the no-trim
|
|
34600
|
+
* grammar — and NOT through this helper, whose trim is licensed by the Logs
|
|
34601
|
+
* measurement alone. `""` is not portable as "absent" either: on a REQUIRED
|
|
34602
|
+
* Integer it is rejected outright, where `RetentionInDays` reads it as absent.
|
|
34603
|
+
*
|
|
34292
34604
|
* What this helper does NOT decide: the empty / whitespace-only strings above
|
|
34293
34605
|
* are CloudFormation's spelling of "property absent", which is a statement
|
|
34294
34606
|
* about the PROPERTY, not about the number — they answer `undefined` here, and
|
|
@@ -34322,6 +34634,16 @@ function toCfnInteger(value) {
|
|
|
34322
34634
|
* Pure: takes the raw bag, returns numbers, logs nothing. Each caller owns its
|
|
34323
34635
|
* own message, so the wording stays consistent across that provider's send
|
|
34324
34636
|
* sites without forcing one wording across both providers.
|
|
34637
|
+
*
|
|
34638
|
+
* A member is read through {@link coerceCfnInteger} — CloudFormation's
|
|
34639
|
+
* measured DynamoDB Integer grammar (issue #3135, table on
|
|
34640
|
+
* {@link toCfnInteger}) — because this block is FORWARDED. A spelling
|
|
34641
|
+
* CloudFormation rejects (`" 7 "`, `"0x9"`, `"1e1"`, `"6.5"`) is therefore a
|
|
34642
|
+
* DROPPED member, named in `droppedMembers` exactly like an unresolved
|
|
34643
|
+
* intrinsic: the caller's diagnostic reports it and the live AWS value is left
|
|
34644
|
+
* alone. It is never read as ABSENT (the `=== undefined` test above runs on
|
|
34645
|
+
* the RAW value), and it is never forwarded as `Number()`'s reading, which
|
|
34646
|
+
* sent `7` for a template CloudFormation refuses.
|
|
34325
34647
|
*/
|
|
34326
34648
|
function coerceWarmThroughput(raw) {
|
|
34327
34649
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return { droppedMembers: [] };
|
|
@@ -34330,7 +34652,7 @@ function coerceWarmThroughput(raw) {
|
|
|
34330
34652
|
const droppedMembers = [];
|
|
34331
34653
|
for (const member of WARM_THROUGHPUT_MEMBERS) {
|
|
34332
34654
|
if (bag[member] === void 0) continue;
|
|
34333
|
-
const coerced =
|
|
34655
|
+
const coerced = coerceCfnInteger(bag[member]);
|
|
34334
34656
|
if (coerced === void 0) {
|
|
34335
34657
|
droppedMembers.push(member);
|
|
34336
34658
|
continue;
|
|
@@ -41672,5 +41994,5 @@ var DeployEngine = class DeployEngine {
|
|
|
41672
41994
|
};
|
|
41673
41995
|
|
|
41674
41996
|
//#endregion
|
|
41675
|
-
export { secretSafeKeyDisplay as $,
|
|
41676
|
-
//# sourceMappingURL=deploy-engine-
|
|
41997
|
+
export { secretSafeKeyDisplay as $, isCrossRegionRedirect as $n, CdkdError as $r, applyRoleArnIfSet as $t, planOrphanAdoption as A, forceQuitRecoveryClause as An, resolveStateBucketWithDefaultAndSource as Ar, isSettledInstanceState as At, formatResourceLine as B, shouldRetainResource as Bn, expectedOwnerParam as Br, readConfigString as Bt, createPreDeleteFinalSnapshot as C, isRetryableTransientError as Ci, redactSecretsForState as Cn, getDefaultStateBucketName as Cr, cfnRefValueFromPhysicalId as Ct, makeCanonicalizePropertiesFn as D, markRedactedCause as Di, rebuildClientForBucketRegion as Dn, resolveCaptureObservedState as Dr, isUnboundTemplateParameter as Dt, unsupportedFinalSnapshotError as E, markNonRetryable as Ei, S3StateBackend as En, resolveAutoAssetStorage as Er, isImpossibleEmptyStoredAttribute as Et, WARM_THROUGHPUT_MEMBERS as F, exportNamesCarriedFrom as Fn, CFN_TEMPLATE_URL_LIMIT as Fr, coerceCfnBoolean as Ft, red as G, createAssetRedirectResolver as Gn, processStackMessages as Gr, classifyReplaySecretRegion as Gt, cyan as H, stringifyValue as Hn, canonicalizeRegion as Hr, requireConfigArray as Ht, coerceWarmThroughput as I, importableOutputKeys as In, MIGRATE_TMP_PREFIX as Ir, coerceCfnInteger as It, collectPublishedOutputNames as J, AssetModeResolver as Jn, AwsClients as Jr, s3BucketArn as Jt, yellow as K, loadPublishableAssetManifest as Kn, clearBucketRegionCache as Kr, producerRegionsFromState as Kt, isWarmThroughputDecrease as L, importableOutputs as Ln, findLargeInlineResources as Lr, configBooleanRefusal as Lt, isStatefulRecreateTargetForReplace as M, shellQuote as Mn, stateBucketExistenceConfirmed as Mr, normalizeAwsTagsToCfn as Mt, isStatefulRecreateTargetSync as N, CUSTOM_RESOURCE_RESPONSE_PREFIX as Nn, warnDeprecatedNoPrefixCliFlag as Nr, resolveExplicitPhysicalId as Nt, extractDeploymentEventError as O, retryClassificationText as Oi, buildForceUnlockCommand as On, resolveSkipPrefix as Or, parameterTypeMayLoseSecretIdentity as Ot, renderStatefulReason as P, DEFAULT_STATE_PREFIX as Pn, CFN_TEMPLATE_BODY_LIMIT as Pr, assertRegionMatch as Pt, secretBearingStateKeyWarning as Q, getBootstrapMarkerKey as Qn, AssetError as Qr, s3BucketWebsiteUrl as Qt, toCfnInteger as R, orphansAfterRollback as Rn, uploadCfnTemplate as Rr, configIntegerRefusal as Rt, ccRoutedFinalSnapshotError as S, isMarkedNonRetryable as Si, recoverMaskedOutput as Sn, synthesisStatusMessage as Sr, carriesDynamicReference as St, refusesFinalSnapshot as T, isTransientServerError as Ti, LockManager as Tn, resolveApp as Tr, getAccountInfo as Tt, gray as U, WorkGraph as Un, derivePartitionAndUrlSuffix as Ur, requireConfigObject as Ut, bold as V, AssetPublisher as Vn, PARTITION_TABLE as Vr, replayWarn as Vt, green as W, buildAssetRedirectMap as Wn, AssemblyReader as Wr, requireConfigString as Wt, isExportAliasCollision as X, assertAssetBucketRegion as Xn, resetAwsClients as Xr, s3BucketDualStackDomainName as Xt, exportAliasCollisionScrubWarning as Y, BOOTSTRAP_MARKER_PREFIX as Yn, getAwsClients as Yr, s3BucketDomainName as Yt, secretBearing as Z, ensureAssetStorage as Zn, setAwsClients as Zr, s3BucketRegionalDomainName as Zt, isSecretBearingReferenceString as _, SynthesisError as _i, isSingleDynamicReferenceToken as _n, escapeRegExp$1 as _r, deleteIndeterminateGuards as _t, DeploymentEventsStore as a, IntrinsicResolutionRefusalError as ai, withRetry as an, describeAwsFailure as ar, wouldReturnToSdkProvider as at, PRE_DELETE_SNAPSHOT_TYPES as b, normalizeAwsError as bi, maskSecretsInText as bn, getDockerImageBySourceHash as br, isTerminationProtectionPropagationError as bt, replayFailedOperations as c, LockError as ci, STATE_SOURCED_BASELINE_RULES as cn, describeDockerExecFailure as cr, maskerOrIdentity as ct, updatePartialReason as d, ProvisioningError as di, TEMPLATE_SOURCED_RULES as dn, formatDockerLoginError as dr, interruptWatchListenerCount as dt, ConfigError as ei, DiffCalculator as en, parseBootstrapMarker as er, getCurrentResourceSecrets as et, withResourceDeadline as f, ResourceTimeoutError as fi, carriesSecretMask as fn, getDockerCmd as fr, isInterruptedWaitError as ft, bagHoldsSecretExpression as g, StateError as gi, identityKeyFor as gn, runDockerStreaming as gr, UNSPECIFIED_SKIP_REASON as gt, computeImplicitDeleteEdges as h, StackTerminationProtectionError as hi, errorCauseChain as hn, runDockerForeground as hr, slowCcOperationTimeoutMs as ht, DeploymentEventsReader as i, DynamicReferenceRegionAmbiguousError as ii, describeTypeWithThrottleRetry as in, buildDenyExternalAccessPolicy as ir, ProviderRegistry as it, MULTI_REGION_RECREATE_BLOCKED_TYPES as j, formatLockExpiry as jn, resolveUseCdkBootstrapAssets as jr, WAFv2WebACLProvider as jt, makeSiblingClaimReader as k, __exportAll as ki, buildLockContentionMessage as kn, resolveStateBucketWithDefault as kr, refStateLookupFromResource as kt, replayRollback as l, NestedStackChildDirectDestroyError as li, STATE_SOURCED_CROSS_GENERATION_RULES as ln, describeDockerFailure as lr, beginCommandInterruptScope as lt, IMPLICIT_DELETE_DEPENDENCIES as m, StackHasActiveImportsError as mi, dynamicReferenceTokens as mn, redactDockerArgvValues as mr, CloudControlProvider as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, DependencyError as ni, findActionableSilentDrops as nn, validateAssetBucketName as nr, collectInlinePolicyNamesManagedBySiblings as nt, planFailedOps as o, LocalInvokeBuildError as oi, DagBuilder as on, buildDockerImage as or, createMaskedRetryLogger as ot, maskingRetryLogger as p, ResourceUpdateNotSupportedError as pi, createSecretMasker as pn, partitionSensitiveEnv as pr, startInterruptWatch as pt, collectDeclaredOutputNames as q, rewriteTemplateAssetReferences as qn, resolveBucketRegion as qr, withSharedDrainBudget as qt, DeployEngine as r, DeployCancelledError as ri, findSilentDropProperties as rn, validateContainerRepoName as rr, clearOnUpdateRemoval as rt, planRollback as s, LocalStartServiceError as si, TemplateParser as sn, describeDockerCapturedOutput as sr, maskDeep as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, CrossAccountSecretRefusalError as ti, INTRINSIC_KEYS as tn, readBootstrapMarkerBody as tr, IAMRoleProvider as tt, updatePartialMessage as u, PartialFailureError as ui, STATE_SOURCED_READBACK_RULES as un, dockerSpawnEnvWithSensitive as ur, endCommandInterruptScope as ut, bindingSkippedOutputs as v, formatError as vi, markSameGenerationBag as vn, stripControlChars as vr, deleteSkipReason as vt, isFinalSnapshotError as w, isThrottlingError as wi, scrubResourceRecord as wn, getLegacyStateBucketName as wr, coerceParameterTypedValue as wt, buildFinalSnapshotIdentifier as x, withErrorHandling as xi, recordMaskOnlyValue as xn, Synthesizer as xr, IntrinsicFunctionResolver as xt, ATOMIC_FINAL_SNAPSHOT_TYPES as y, isCdkdError as yi, maskSecretsInError as yn, AssetManifestLoader as yr, disableInstanceApiTermination as yt, toFiniteNumber as z, orphansCarriedFrom as zn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as zr, configStringRefusal as zt };
|
|
41998
|
+
//# sourceMappingURL=deploy-engine-BU7n9_qY.js.map
|